From 489921b003affda4eb030dea61fbb78e546d404c Mon Sep 17 00:00:00 2001 From: Frederic Riss <friss@apple.com> Date: Thu, 5 Nov 2015 17:54:40 -0800 Subject: [PATCH 001/582] Apply Swift-related changes to the swift-clang repo apple-llvm-split-commit: 84b5a21c31cb5b0d7d958a478bc01964939b6952 apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesManager.h | 91 ++ clang/include/clang/APINotes/APINotesReader.h | 144 ++ clang/include/clang/APINotes/APINotesWriter.h | 98 ++ .../clang/APINotes/APINotesYAMLCompiler.h | 56 + clang/include/clang/APINotes/Types.h | 409 ++++++ clang/include/clang/AST/DeclBase.h | 9 +- clang/include/clang/Basic/Attr.td | 46 + clang/include/clang/Basic/AttrDocs.td | 27 + .../clang/Basic/DiagnosticCommonKinds.td | 5 + .../clang/Basic/DiagnosticFrontendKinds.td | 4 + .../clang/Basic/DiagnosticSemaKinds.td | 25 + clang/include/clang/Basic/FileSystemOptions.h | 3 + clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Basic/SourceMgrAdapter.h | 83 ++ clang/include/clang/Driver/Options.td | 8 + clang/include/clang/Sema/Sema.h | 21 +- clang/lib/APINotes/APINotesFormat.h | 239 +++ clang/lib/APINotes/APINotesManager.cpp | 453 ++++++ clang/lib/APINotes/APINotesReader.cpp | 1297 +++++++++++++++++ clang/lib/APINotes/APINotesWriter.cpp | 851 +++++++++++ clang/lib/APINotes/APINotesYAMLCompiler.cpp | 884 +++++++++++ clang/lib/APINotes/CMakeLists.txt | 15 + clang/lib/APINotes/Makefile | 18 + clang/lib/APINotes/Types.cpp | 55 + clang/lib/AST/DeclBase.cpp | 22 +- clang/lib/Basic/CMakeLists.txt | 1 + clang/lib/Basic/SourceMgrAdapter.cpp | 137 ++ clang/lib/CMakeLists.txt | 1 + clang/lib/CodeGen/CGObjCGNU.cpp | 5 +- clang/lib/CodeGen/CGObjCMac.cpp | 67 +- clang/lib/CodeGen/CGObjCRuntime.h | 5 +- clang/lib/CodeGen/CodeGenModule.cpp | 4 +- clang/lib/Driver/Job.cpp | 3 +- clang/lib/Driver/Tools.cpp | 27 + clang/lib/Frontend/CompilerInvocation.cpp | 9 + clang/lib/Lex/PPMacroExpansion.cpp | 1 + clang/lib/Makefile | 6 +- clang/lib/Parse/ParseCXXInlineMethods.cpp | 1 + clang/lib/Parse/ParseDeclCXX.cpp | 4 +- clang/lib/Parse/ParseObjc.cpp | 2 + clang/lib/Sema/CMakeLists.txt | 2 + clang/lib/Sema/Sema.cpp | 2 +- clang/lib/Sema/SemaAPINotes.cpp | 359 +++++ clang/lib/Sema/SemaDecl.cpp | 11 + clang/lib/Sema/SemaDeclAttr.cpp | 281 ++++ clang/lib/Sema/SemaDeclCXX.cpp | 3 + clang/lib/Sema/SemaDeclObjC.cpp | 33 +- clang/lib/Sema/SemaTemplate.cpp | 5 + clang/lib/Sema/SemaType.cpp | 85 +- .../Inputs/BrokenHeaders/APINotes.apinotes | 4 + .../Inputs/BrokenHeaders/SomeBrokenLib.h | 6 + .../Inputs/BrokenHeaders2/APINotes.apinotes | 7 + .../Inputs/BrokenHeaders2/SomeBrokenLib.h | 6 + .../APINotes/SomeKit.apinotes | 24 + .../APINotes/SomeKit_private.apinotes | 15 + .../SomeKit.framework/Headers/SomeKit.h | 23 + .../Headers/SomeKitForNullAnnotation.h | 55 + .../PrivateHeaders/SomeKit_Private.h | 16 + .../SomeKit_PrivateForNullAnnotation.h | 17 + .../APINotes/Inputs/Headers/APINotes.apinotes | 17 + .../test/APINotes/Inputs/Headers/HeaderLib.h | 13 + .../APINotes/Inputs/os-availability.apinotes | 53 + clang/test/APINotes/Inputs/roundtrip.apinotes | 79 + clang/test/APINotes/availability.m | 29 + clang/test/APINotes/cache.m | 33 + clang/test/APINotes/cache_pruning.m | 49 + clang/test/APINotes/nullability.c | 14 + clang/test/APINotes/nullability.m | 13 + clang/test/APINotes/objc_designated_inits.m | 16 + clang/test/APINotes/yaml-convert-diags.c | 6 + clang/test/APINotes/yaml-os-availability.c | 31 + clang/test/APINotes/yaml-parse-diags.c | 6 + clang/test/APINotes/yaml-reader-errors.c | 65 + clang/test/APINotes/yaml-reader-test.c | 102 ++ clang/test/APINotes/yaml-roundtrip.c | 10 + clang/test/Misc/warning-flags.c | 3 +- clang/test/Sema/attr-availability.c | 15 + clang/test/Sema/attr-noescape.c | 12 + clang/test/SemaObjC/attr-swift.m | 158 ++ .../SemaObjC/subclassing-restricted-attr.m | 23 + clang/tools/arcmt-test/Makefile | 2 +- clang/tools/c-arcmt-test/Makefile | 3 +- clang/tools/c-index-test/Makefile | 2 +- clang/tools/clang-check/CMakeLists.txt | 1 + clang/tools/clang-check/Makefile | 2 +- clang/tools/clang-format/Makefile | 2 +- clang/tools/diagtool/Makefile | 2 +- clang/tools/driver/CMakeLists.txt | 2 + clang/tools/driver/Makefile | 3 +- clang/tools/driver/apinotes_main.cpp | 149 ++ clang/tools/driver/driver.cpp | 4 + clang/tools/libclang/CMakeLists.txt | 1 + clang/tools/libclang/Makefile | 2 +- clang/unittests/AST/DeclTest.cpp | 51 + clang/unittests/AST/Makefile | 3 +- clang/unittests/ASTMatchers/Dynamic/Makefile | 3 +- clang/unittests/ASTMatchers/Makefile | 3 +- clang/unittests/CodeGen/Makefile | 2 +- clang/unittests/Format/Makefile | 2 +- clang/unittests/Frontend/Makefile | 2 +- clang/unittests/Lex/CMakeLists.txt | 1 + clang/unittests/Lex/Makefile | 2 +- clang/unittests/Sema/Makefile | 3 +- clang/unittests/Tooling/Makefile | 2 +- clang/unittests/libclang/Makefile | 2 +- 105 files changed, 6983 insertions(+), 106 deletions(-) create mode 100644 clang/include/clang/APINotes/APINotesManager.h create mode 100644 clang/include/clang/APINotes/APINotesReader.h create mode 100644 clang/include/clang/APINotes/APINotesWriter.h create mode 100644 clang/include/clang/APINotes/APINotesYAMLCompiler.h create mode 100644 clang/include/clang/APINotes/Types.h create mode 100644 clang/include/clang/Basic/SourceMgrAdapter.h create mode 100644 clang/lib/APINotes/APINotesFormat.h create mode 100644 clang/lib/APINotes/APINotesManager.cpp create mode 100644 clang/lib/APINotes/APINotesReader.cpp create mode 100644 clang/lib/APINotes/APINotesWriter.cpp create mode 100644 clang/lib/APINotes/APINotesYAMLCompiler.cpp create mode 100644 clang/lib/APINotes/CMakeLists.txt create mode 100644 clang/lib/APINotes/Makefile create mode 100644 clang/lib/APINotes/Types.cpp create mode 100644 clang/lib/Basic/SourceMgrAdapter.cpp create mode 100644 clang/lib/Sema/SemaAPINotes.cpp create mode 100644 clang/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes create mode 100644 clang/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h create mode 100644 clang/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes create mode 100644 clang/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h create mode 100644 clang/test/APINotes/Inputs/Headers/APINotes.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/HeaderLib.h create mode 100644 clang/test/APINotes/Inputs/os-availability.apinotes create mode 100644 clang/test/APINotes/Inputs/roundtrip.apinotes create mode 100644 clang/test/APINotes/availability.m create mode 100644 clang/test/APINotes/cache.m create mode 100644 clang/test/APINotes/cache_pruning.m create mode 100644 clang/test/APINotes/nullability.c create mode 100644 clang/test/APINotes/nullability.m create mode 100644 clang/test/APINotes/objc_designated_inits.m create mode 100644 clang/test/APINotes/yaml-convert-diags.c create mode 100644 clang/test/APINotes/yaml-os-availability.c create mode 100644 clang/test/APINotes/yaml-parse-diags.c create mode 100644 clang/test/APINotes/yaml-reader-errors.c create mode 100644 clang/test/APINotes/yaml-reader-test.c create mode 100644 clang/test/APINotes/yaml-roundtrip.c create mode 100644 clang/test/Sema/attr-noescape.c create mode 100644 clang/test/SemaObjC/attr-swift.m create mode 100644 clang/test/SemaObjC/subclassing-restricted-attr.m create mode 100644 clang/tools/driver/apinotes_main.cpp diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h new file mode 100644 index 0000000000000..503b36536e621 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -0,0 +1,91 @@ +//===--- APINotesManager.h - Manage API Notes Files -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the HeaderSearch interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H +#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H + +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace clang { + +class DirectoryEntry; +class FileEntry; +class SourceManager; + +namespace api_notes { + +class APINotesReader; + +/// The API notes manager helps find API notes associated with declarations. +/// +/// API notes are externally-provided annotations for declarations that can +/// introduce new attributes (covering availability, nullability of +/// parameters/results, and so on) for specific declarations without directly +/// modifying the headers that contain those declarations. +/// +/// The API notes manager is responsible for finding and loading the +/// external API notes files that correspond to a given header. Its primary +/// operation is \c findAPINotes(), which finds the API notes reader that +/// provides information about the declarations at that location. +class APINotesManager { + typedef llvm::PointerUnion<const DirectoryEntry *, APINotesReader *> + ReaderEntry; + + SourceManager &SourceMgr; + + /// Whether we have already pruned the API notes cache. + bool PrunedCache; + + /// A mapping from header file directories to the API notes reader for + /// that directory, or a redirection to another directory entry that may + /// have more information, or NULL to indicate that there is no API notes + /// reader for this directory. + llvm::DenseMap<const DirectoryEntry *, ReaderEntry> Readers; + + /// Load the given API notes file for the given header directory. + /// + /// \param HeaderDir The directory at which we + /// + /// \returns true if an error occurred. + bool loadAPINotes(const DirectoryEntry *HeaderDir, + const FileEntry *APINotesFile); + + /// Attempt to load API notes for the given framework. + /// + /// \param FrameworkPath The path to the framework. + /// \param Public Whether to load the public API notes. Otherwise, attempt + /// to load the private API notes. + /// + /// \returns the header directory entry (e.g., for Headers or PrivateHeaders) + /// for which the API notes were successfully loaded, or NULL if API notes + /// could not be loaded for any reason. + const DirectoryEntry *loadFrameworkAPINotes(llvm::StringRef FrameworkPath, + llvm::StringRef FrameworkName, + bool Public); + +public: + APINotesManager(SourceManager &SourceMgr); + ~APINotesManager(); + + /// Find the API notes reader that corresponds to the given source location. + APINotesReader *findAPINotes(SourceLocation Loc); +}; + +} // end namespace api_notes +} // end namespace clang + +#endif diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h new file mode 100644 index 0000000000000..77b5f16bb42f4 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -0,0 +1,144 @@ +//===--- APINotesReader.h - API Notes Reader ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the \c APINotesReader class that reads source +// API notes data providing additional information about source code as +// a separate input, such as the non-nil/nilable annotations for +// method parameters. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_API_NOTES_READER_H +#define LLVM_CLANG_API_NOTES_READER_H + +#include "clang/APINotes/Types.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +namespace clang { +namespace api_notes { + +/// A class that reads API notes data from a binary file that was written by +/// the \c APINotesWriter. +class APINotesReader { + class Implementation; + + Implementation &Impl; + + APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, bool &failed); + +public: + /// Create a new API notes reader from the given member buffer, which + /// contains the contents of a binary API notes file. + /// + /// \returns the new API notes reader, or null if an error occurred. + static std::unique_ptr<APINotesReader> + get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer); + + ~APINotesReader(); + + APINotesReader(const APINotesReader &) = delete; + APINotesReader &operator=(const APINotesReader &) = delete; + + /// Retrieve the name of the module for which this reader is providing API + /// notes. + StringRef getModuleName() const; + + /// Look for information regarding the given Objective-C class. + /// + /// \param name The name of the class we're looking for. + /// + /// \returns The ID and information about the class, if known. + Optional<std::pair<ContextID, ObjCContextInfo>> + lookupObjCClass(StringRef name); + + /// Look for information regarding the given Objective-C protocol. + /// + /// \param name The name of the protocol we're looking for. + /// + /// \returns The ID and information about the protocol, if known. + Optional<std::pair<ContextID, ObjCContextInfo>> + lookupObjCProtocol(StringRef name); + + /// Look for information regarding the given Objective-C property in + /// the given context. + /// + /// \param contextID The ID that references the context we are looking for. + /// \param name The name of the property we're looking for. + /// + /// \returns Information about the property, if known. + Optional<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID, + StringRef name); + + /// Look for information regarding the given Objective-C method in + /// the given context. + /// + /// \param contextID The ID that references the context we are looking for. + /// \param selector The selector naming the method we're looking for. + /// \param isInstanceMethod Whether we are looking for an instance method. + /// + /// \returns Information about the method, if known. + Optional<ObjCMethodInfo> lookupObjCMethod(ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod); + + /// Look for information regarding the given global variable. + /// + /// \param name The name of the global variable. + /// + /// \returns information about the global variable, if known. + Optional<GlobalVariableInfo> lookupGlobalVariable(StringRef name); + + /// Look for information regarding the given global function. + /// + /// \param name The name of the global function. + /// + /// \returns information about the global function, if known. + Optional<GlobalFunctionInfo> lookupGlobalFunction(StringRef name); + + /// Visitor used when walking the contents of the API notes file. + class Visitor { + public: + virtual ~Visitor(); + + /// Visit an Objective-C class. + virtual void visitObjCClass(ContextID contextID, StringRef name, + const ObjCContextInfo &info); + + /// Visit an Objective-C protocol. + virtual void visitObjCProtocol(ContextID contextID, StringRef name, + const ObjCContextInfo &info); + + /// Visit an Objective-C method. + virtual void visitObjCMethod(ContextID contextID, StringRef selector, + bool isInstanceMethod, + const ObjCMethodInfo &info); + + /// Visit an Objective-C property. + virtual void visitObjCProperty(ContextID contextID, StringRef name, + const ObjCPropertyInfo &info); + + /// Visit a global variable. + virtual void visitGlobalVariable(StringRef name, + const GlobalVariableInfo &info); + + /// Visit a global function. + virtual void visitGlobalFunction(StringRef name, + const GlobalFunctionInfo &info); + }; + + /// Visit the contents of the API notes file, passing each entity to the + /// given visitor. + void visit(Visitor &visitor); +}; + +} // end namespace api_notes +} // end namespace clang + +#endif // LLVM_CLANG_API_NOTES_READER_H diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h new file mode 100644 index 0000000000000..dca0773aa28f8 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -0,0 +1,98 @@ +//===--- APINotesWriter.h - API Notes Writer ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the \c APINotesWriter class that writes out source +// API notes data providing additional information about source code as +// a separate input, such as the non-nil/nilable annotations for +// method parameters. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_API_NOTES_WRITER_H +#define LLVM_CLANG_API_NOTES_WRITER_H + +#include "clang/APINotes/Types.h" + +namespace llvm { + class raw_ostream; +} + +namespace clang { +namespace api_notes { + +/// A class that writes API notes data to a binary representation that can be +/// read by the \c APINotesReader. +class APINotesWriter { + class Implementation; + Implementation &Impl; + +public: + /// Create a new API notes writer with the given module name. + APINotesWriter(StringRef moduleName); + ~APINotesWriter(); + + APINotesWriter(const APINotesWriter &) = delete; + APINotesWriter &operator=(const APINotesWriter &) = delete; + + /// Write the API notes data to the given stream. + void writeToStream(llvm::raw_ostream &os); + + /// Add information about a specific Objective-C class. + /// + /// \param name The name of this class. + /// \param info Information about this class. + /// + /// \returns the ID of the class, which can be used to add properties and + /// methods to the class. + ContextID addObjCClass(StringRef name, const ObjCContextInfo &info); + + /// Add information about a specific Objective-C protocol. + /// + /// \param name The name of this protocol. + /// \param info Information about this protocol. + /// + /// \returns the ID of the protocol, which can be used to add properties and + /// methods to the protocol. + ContextID addObjCProtocol(StringRef name, const ObjCContextInfo &info); + + /// Add information about a specific Objective-C property. + /// + /// \param contextID The context in which this property resides. + /// \param name The name of this property. + /// \param info Information about this property. + void addObjCProperty(ContextID contextID, StringRef name, + const ObjCPropertyInfo &info); + + /// Add information about a specific Objective-C method. + /// + /// \param contextID The context in which this method resides. + /// \param selector The selector that names this method. + /// \param isInstanceMethod Whether this method is an instance method + /// (vs. a class method). + /// \param info Information about this method. + void addObjCMethod(ContextID contextID, ObjCSelectorRef selector, + bool isInstanceMethod, const ObjCMethodInfo &info); + + /// Add information about a global variable. + /// + /// \param name The name of this global variable. + /// \param info Information about this global variable. + void addGlobalVariable(StringRef name, const GlobalVariableInfo &info); + + /// Add information about a global function. + /// + /// \param name The name of this global function. + /// \param info Information about this global function. + void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); +}; + +} // end namespace api_notes +} // end namespace clang + +#endif // LLVM_CLANG_API_NOTES_WRITER_H + diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h new file mode 100644 index 0000000000000..e897b5cf90496 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h @@ -0,0 +1,56 @@ +//=== APINotesYAMLCompiler.h - API Notes YAML to binary compiler *- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file reads sidecar API notes specified in YAML format. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_API_NOTES_YAML_COMPILER_H +#define LLVM_CLANG_API_NOTES_YAML_COMPILER_H +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/SourceMgr.h" +#include <memory> + +namespace llvm { + class raw_ostream; + class MemoryBuffer; +} + +namespace clang { +namespace api_notes { + + enum class ActionType { + None, + YAMLToBinary, + BinaryToYAML, + Dump, + }; + + enum class OSType { + OSX, + IOS, + Absent + }; + + /// Converts API notes from YAML format to binary format. + bool compileAPINotes(llvm::StringRef yamlInput, + llvm::raw_ostream &os, + OSType targetOS, + llvm::SourceMgr::DiagHandlerTy diagHandler = nullptr, + void *diagHandlerCtxt = nullptr); + + bool parseAndDumpAPINotes(llvm::StringRef yamlInput); + + /// Converts API notes from the compiled binary format to the YAML format. + bool decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, + llvm::raw_ostream &os); +} // end namespace api_notes +} // end namespace clang + +#endif // LLVM_CLANG_API_NOTES_YAML_COMPILER_H diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h new file mode 100644 index 0000000000000..8173bf4f3a25b --- /dev/null +++ b/clang/include/clang/APINotes/Types.h @@ -0,0 +1,409 @@ +//===--- Types.h - API Notes Data Types --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines data types used in the representation of API notes data. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_API_NOTES_TYPES_H +#define LLVM_CLANG_API_NOTES_TYPES_H +#include "clang/Basic/Specifiers.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include <cassert> +#include <climits> + +namespace llvm { + class raw_ostream; +} + +namespace clang { +namespace api_notes { + +/// The file extension used for the source representation of API notes. +static const char SOURCE_APINOTES_EXTENSION[] = "apinotes"; + +/// The file extension used for the binary representation of API notes. +static const char BINARY_APINOTES_EXTENSION[] = "apinotesc"; + +using llvm::ArrayRef; +using llvm::StringRef; +using llvm::Optional; +using llvm::None; + +/// Describes whether to classify a factory method as an initializer. +enum class FactoryAsInitKind { + /// Infer based on name and type (the default). + Infer, + /// Treat as a class method. + AsClassMethod, + /// Treat as an initializer. + AsInitializer +}; + +/// Opaque context ID used to refer to an Objective-C class or protocol. +class ContextID { +public: + unsigned Value; + + explicit ContextID(unsigned value) : Value(value) { } +}; + +/// Describes API notes data for any entity. +/// +/// This is used as the base of +class CommonEntityInfo { +public: + /// Message to use when this entity is unavailable. + std::string UnavailableMsg; + + /// Whether this entity is marked unavailable. + unsigned Unavailable : 1; + + CommonEntityInfo() : Unavailable(0) { } + + friend bool operator==(const CommonEntityInfo &lhs, + const CommonEntityInfo &rhs) { + return lhs.UnavailableMsg == rhs.UnavailableMsg && + lhs.Unavailable == rhs.Unavailable; + } + + friend bool operator!=(const CommonEntityInfo &lhs, + const CommonEntityInfo &rhs) { + return !(lhs == rhs); + } + + friend CommonEntityInfo &operator|=(CommonEntityInfo &lhs, + const CommonEntityInfo &rhs) { + // Merge unavailability. + if (rhs.Unavailable) { + lhs.Unavailable = true; + if (rhs.UnavailableMsg.length() != 0 && + lhs.UnavailableMsg.length() == 0) { + lhs.UnavailableMsg = rhs.UnavailableMsg; + } + } + + return lhs; + } + +}; + +/// Describes API notes data for an Objective-C class or protocol. +class ObjCContextInfo : public CommonEntityInfo { + /// Whether this class has a default nullability. + unsigned HasDefaultNullability : 1; + + /// The default nullability. + unsigned DefaultNullability : 2; + + /// Whether this class has designated initializers recorded. + unsigned HasDesignatedInits : 1; + +public: + ObjCContextInfo() + : CommonEntityInfo(), + HasDefaultNullability(0), + DefaultNullability(0), + HasDesignatedInits(0) + { } + + /// Determine the default nullability for properties and methods of this + /// class. + /// + /// \returns the default nullability, if implied, or None if there is no + Optional<NullabilityKind> getDefaultNullability() const { + if (HasDefaultNullability) + return static_cast<NullabilityKind>(DefaultNullability); + + return None; + } + + /// Set the default nullability for properties and methods of this class. + void setDefaultNullability(NullabilityKind kind) { + HasDefaultNullability = true; + DefaultNullability = static_cast<unsigned>(kind); + } + + bool hasDesignatedInits() const { return HasDesignatedInits; } + void setHasDesignatedInits(bool value) { HasDesignatedInits = value; } + + /// Strip off any information within the class information structure that is + /// module-local, such as 'audited' flags. + void stripModuleLocalInfo() { + HasDefaultNullability = false; + DefaultNullability = 0; + } + + friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { + return static_cast<const CommonEntityInfo &>(lhs) == rhs && + lhs.HasDefaultNullability == rhs.HasDefaultNullability && + lhs.DefaultNullability == rhs.DefaultNullability && + lhs.HasDesignatedInits == rhs.HasDesignatedInits; + } + + friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { + return !(lhs == rhs); + } + + friend ObjCContextInfo &operator|=(ObjCContextInfo &lhs, + const ObjCContextInfo &rhs) { + // Merge inherited info. + static_cast<CommonEntityInfo &>(lhs) |= rhs; + + // Merge nullability. + if (!lhs.getDefaultNullability()) { + if (auto nullable = rhs.getDefaultNullability()) { + lhs.setDefaultNullability(*nullable); + } + } + + lhs.HasDesignatedInits |= rhs.HasDesignatedInits; + return lhs; + } + + void dump(llvm::raw_ostream &os); +}; + +/// API notes for a variable/property. +class VariableInfo : public CommonEntityInfo { + /// Whether this property has been audited for nullability. + unsigned NullabilityAudited : 1; + + /// The kind of nullability for this property. Only valid if the nullability + /// has been audited. + unsigned Nullable : 2; + +public: + VariableInfo() + : CommonEntityInfo(), + NullabilityAudited(false), + Nullable(0) { } + + Optional<NullabilityKind> getNullability() const { + if (NullabilityAudited) + return static_cast<NullabilityKind>(Nullable); + + return None; + } + + void setNullabilityAudited(NullabilityKind kind) { + NullabilityAudited = true; + Nullable = static_cast<unsigned>(kind); + } + + + friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) { + return static_cast<const CommonEntityInfo &>(lhs) == rhs && + lhs.NullabilityAudited == rhs.NullabilityAudited && + lhs.Nullable == rhs.Nullable; + } + + friend bool operator!=(const VariableInfo &lhs, const VariableInfo &rhs) { + return !(lhs == rhs); + } + +}; + +/// Describes API notes data for an Objective-C property. +class ObjCPropertyInfo : public VariableInfo { +public: + ObjCPropertyInfo() : VariableInfo() { } + + /// Merge class-wide information into the given property. + friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs, + const ObjCContextInfo &rhs) { + // Merge nullability. + if (!lhs.getNullability()) { + if (auto nullable = rhs.getDefaultNullability()) { + lhs.setNullabilityAudited(*nullable); + } + } + + return lhs; + } +}; + +/// A temporary reference to an Objective-C selector, suitable for +/// referencing selector data on the stack. +/// +/// Instances of this struct do not store references to any of the +/// data they contain; it is up to the user to ensure that the data +/// referenced by the identifier list persists. +struct ObjCSelectorRef { + unsigned NumPieces; + ArrayRef<StringRef> Identifiers; +}; + +/// API notes for a function or method. +class FunctionInfo : public CommonEntityInfo { +private: + static unsigned const NullabilityKindMask = 0x3; + static unsigned const NullabilityKindSize = 2; + +public: + /// Whether the signature has been audited with respect to nullability. + /// If yes, we consider all types to be non-nullable unless otherwise noted. + /// If this flag is not set, the pointer types are considered to have + /// unknown nullability. + unsigned NullabilityAudited : 1; + + /// Number of types whose nullability is encoded with the NullabilityPayload. + unsigned NumAdjustedNullable : 8; + + /// Stores the nullability of the return type and the parameters. + // NullabilityKindSize bits are used to encode the nullability. The info + // about the return type is stored at position 0, followed by the nullability + // of the parameters. + uint64_t NullabilityPayload = 0; + + FunctionInfo() + : CommonEntityInfo(), + NullabilityAudited(false), + NumAdjustedNullable(0) { } + + static unsigned getMaxNullabilityIndex() { + return ((sizeof(NullabilityPayload) * CHAR_BIT)/NullabilityKindSize); + } + + void addTypeInfo(unsigned index, NullabilityKind kind) { + assert(index <= getMaxNullabilityIndex()); + assert(static_cast<unsigned>(kind) < NullabilityKindMask); + NullabilityAudited = true; + if (NumAdjustedNullable < index + 1) + NumAdjustedNullable = index + 1; + + // Mask the bits. + NullabilityPayload &= ~(NullabilityKindMask << (index * NullabilityKindSize)); + + // Set the value. + unsigned kindValue = + (static_cast<unsigned>(kind)) << (index * NullabilityKindSize); + NullabilityPayload |= kindValue; + } + + /// Adds the return type info. + void addReturnTypeInfo(NullabilityKind kind) { + addTypeInfo(0, kind); + } + + /// Adds the parameter type info. + void addParamTypeInfo(unsigned index, NullabilityKind kind) { + addTypeInfo(index + 1, kind); + } + +private: + NullabilityKind getTypeInfo(unsigned index) const { + assert(NullabilityAudited && + "Checking the type adjustment on non-audited method."); + // If we don't have info about this parameter, return the default. + if (index > NumAdjustedNullable) + return NullabilityKind::NonNull; + return static_cast<NullabilityKind>(( NullabilityPayload + >> (index * NullabilityKindSize) ) + & NullabilityKindMask); + } + +public: + NullabilityKind getParamTypeInfo(unsigned index) const { + return getTypeInfo(index + 1); + } + + NullabilityKind getReturnTypeInfo() const { + return getTypeInfo(0); + } + + friend bool operator==(const FunctionInfo &lhs, const FunctionInfo &rhs) { + return static_cast<const CommonEntityInfo &>(lhs) == rhs && + lhs.NullabilityAudited == rhs.NullabilityAudited && + lhs.NumAdjustedNullable == rhs.NumAdjustedNullable && + lhs.NullabilityPayload == rhs.NullabilityPayload; + } + + friend bool operator!=(const FunctionInfo &lhs, const FunctionInfo &rhs) { + return !(lhs == rhs); + } + +}; + +/// Describes API notes data for an Objective-C method. +class ObjCMethodInfo : public FunctionInfo { +public: + /// Whether this is a designated initializer of its class. + unsigned DesignatedInit : 1; + + /// Whether to treat this method as a factory or initializer. + unsigned FactoryAsInit : 2; + + /// Whether this is a required initializer. + unsigned Required : 1; + + ObjCMethodInfo() + : FunctionInfo(), + DesignatedInit(false), + FactoryAsInit(static_cast<unsigned>(FactoryAsInitKind::Infer)), + Required(false) { } + + FactoryAsInitKind getFactoryAsInitKind() const { + return static_cast<FactoryAsInitKind>(FactoryAsInit); + } + + void setFactoryAsInitKind(FactoryAsInitKind kind) { + FactoryAsInit = static_cast<unsigned>(kind); + } + + friend bool operator==(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) { + return static_cast<const FunctionInfo &>(lhs) == rhs && + lhs.DesignatedInit == rhs.DesignatedInit && + lhs.FactoryAsInit == rhs.FactoryAsInit && + lhs.Required == rhs.Required; + } + + friend bool operator!=(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) { + return !(lhs == rhs); + } + + void mergePropInfoIntoSetter(const ObjCPropertyInfo &pInfo); + + void mergePropInfoIntoGetter(const ObjCPropertyInfo &pInfo); + + /// Merge class-wide information into the given method. + friend ObjCMethodInfo &operator|=(ObjCMethodInfo &lhs, + const ObjCContextInfo &rhs) { + // Merge nullability. + if (!lhs.NullabilityAudited) { + if (auto nullable = rhs.getDefaultNullability()) { + lhs.NullabilityAudited = true; + lhs.addTypeInfo(0, *nullable); + } + } + + return lhs; + } + + void dump(llvm::raw_ostream &os); +}; + +/// Describes API notes data for a global variable. +class GlobalVariableInfo : public VariableInfo { +public: + GlobalVariableInfo() : VariableInfo() { } +}; + +/// Describes API notes data for a global function. +class GlobalFunctionInfo : public FunctionInfo { +public: + GlobalFunctionInfo() : FunctionInfo() { } +}; + +} // end namespace api_notes +} // end namespace clang + +#endif // LLVM_CLANG_API_NOTES_TYPES_H diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index f563a04d06dab..9250a9d322fd2 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -17,6 +17,7 @@ #include "clang/AST/AttrIterator.h" #include "clang/AST/DeclarationName.h" #include "clang/Basic/Specifiers.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" @@ -586,11 +587,15 @@ class Decl { /// the given declaration (e.g., preferring 'unavailable' to /// 'deprecated'). /// - /// \param Message If non-NULL and the result is not \c + /// \param[out] Message If non-NULL and the result is not \c /// AR_Available, will be set to a (possibly empty) message /// describing why the declaration has not been introduced, is /// deprecated, or is unavailable. - AvailabilityResult getAvailability(std::string *Message = nullptr) const; + /// \param Version The version of the target OS to determine availability for. + /// If \c None, uses the version specified in the ASTContext's target info. + AvailabilityResult + getAvailability(std::string *Message = nullptr, + Optional<VersionTuple> Version = None) const; /// \brief Determine whether this declaration is marked 'deprecated'. /// diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index bd9b7ec1a59c5..61e00b7dc66d1 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -85,6 +85,9 @@ def NormalVar : SubsetSubject<Var, def NonBitField : SubsetSubject<Field, [{!S->isBitField()}]>; +def ObjCClassMethod : SubsetSubject<ObjCMethod, + [{!S->isInstanceMethod()}]>; + def ObjCInstanceMethod : SubsetSubject<ObjCMethod, [{S->isInstanceMethod()}]>; @@ -462,6 +465,7 @@ def Availability : InheritableAttr { .Case("macosx_app_extension", "OS X (App Extension)") .Case("tvos_app_extension", "tvOS (App Extension)") .Case("watchos_app_extension", "watchOS (App Extension)") + .Case("swift", "Swift") .Default(llvm::StringRef()); } }]; let HasCustomParsing = 1; @@ -1010,6 +1014,12 @@ def ObjCKindOf : TypeAttr { let Documentation = [Undocumented]; } +def NoEscape : InheritableAttr { + let Spellings = [GCC<"noescape">]; + let Subjects = SubjectList<[ParmVar], WarnDiag, "ExpectedParameter">; + let Documentation = [Undocumented]; +} + def AssumeAligned : InheritableAttr { let Spellings = [GCC<"assume_aligned">]; let Subjects = SubjectList<[ObjCMethod, Function]>; @@ -1144,6 +1154,18 @@ def ObjCRootClass : InheritableAttr { let Documentation = [Undocumented]; } +def ObjCSubclassingRestricted : InheritableAttr { + let Spellings = [GNU<"objc_subclassing_restricted">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def ObjCCompleteDefinition : InheritableAttr { + let Spellings = [GNU<"objc_complete_definition">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [Undocumented]; +} + def ObjCExplicitProtocolImpl : InheritableAttr { let Spellings = [GNU<"objc_protocol_requires_explicit_implementation">]; let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>; @@ -1240,6 +1262,30 @@ def Regparm : TypeAttr { let Documentation = [RegparmDocs]; } +def SwiftError : InheritableAttr { + let Spellings = [GCC<"swift_error">]; + let Args = [EnumArgument<"Convention", "ConventionKind", + ["none", "nonnull_error", "null_result", "zero_result", "nonzero_result"], + ["None", "NonNullError", "NullResult", "ZeroResult", "NonZeroResult"]>]; + let Subjects = SubjectList<[ObjCMethod, Function], ErrorDiag>; + let Documentation = [SwiftErrorDocs]; +} + +def SwiftName : InheritableAttr { + let Spellings = [GCC<"swift_name">]; + let Args = [StringArgument<"Name">]; + // Proper subject list disabled because of the custom error needed. + // Let's avoid merge conflicts for now. +// let Subjects = SubjectList<[EnumConstant, ObjCProtocol, ObjCClassMethod], +// ErrorDiag, "ExpectedSwiftNameSubjects">; + let Documentation = [Undocumented]; +} + +def SwiftPrivate : InheritableAttr { + let Spellings = [GCC<"swift_private">]; + let Documentation = [Undocumented]; +} + def ReqdWorkGroupSize : InheritableAttr { let Spellings = [GNU<"reqd_work_group_size">]; let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">, diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 30b6789cfd238..4743ddcabe8de 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1620,3 +1620,30 @@ function are loads and stores from objects pointed to by its pointer-typed arguments, with arbitrary offsets. }]; } + +def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + let Content = [{ +Clang supports additional attributes for controlling how APIs are imported into Swift. + }]; +} + +def SwiftErrorDocs : Documentation { + let Category = SwiftDocs; + let Heading = "swift_error"; + let Content = [{ +The ``swift_error`` attribute controls whether a particular function (or Objective-C method) is imported into Swift as a throwing function, and if so, the dynamic convention it uses. + +All of these conventions except ``none`` require the function to have an error parameter. Currently, the error parameter is always the last parameter of type ``NSError**`` or ``CFErrorRef*``. Swift will remove the error parameter from the imported API, and dynamically will always pass a valid address initialized to a null pointer. + +* ``swift_error(none)`` means that the function should not be imported as throwing. The error parameter and result type will be left alone. + +* ``swift_error(null_result)`` means that calls to the function should be considered to have thrown if they return a null value. The return type must be a pointer type, and it will be imported into Swift with a non-optional type. This is the default error convention for Objective-C methods that return pointers. + +* ``swift_error(zero_result)`` means that calls to the function should be considered to have thrown if they return a zero result. The return type must be an integral type. If the return type would have been imported as ``Bool``, it is instead imported as ``Void``. This is the default error convention for Objective-C methods that return a type that would be imported as ``Bool``. + +* ``swift_error(nonzero_result)`` means that calls to the function should be considered to have thrown if they return a non-zero result. The return type must be an integral type. If the return type would have been imported as ``Bool``, it is instead imported as ``Void``. + +* ``swift_error(nonnull_error)`` means that calls to the function should be considered to have thrown if they leave a non-null error in the error parameter. The return type is left unmodified. + +}]; +} diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 42e761a630bd1..dc28200da94a2 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -211,6 +211,11 @@ def note_mt_message : Note<"[rewriter] %0">; def warn_arcmt_nsalloc_realloc : Warning<"[rewriter] call returns pointer to GC managed memory; it will become unmanaged in ARC">; def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to be used with an object with ownership other than __unsafe_unretained">; +// API notes +def err_apinotes_message : Error<"%0">; +def warn_apinotes_message : Warning<"%0">; +def note_apinotes_message : Note<"%0">; + // OpenMP def err_omp_more_one_clause : Error< "directive '#pragma omp %0' cannot contain more than one '%1' clause%select{| with '%3' name modifier}2">; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 033834bc505da..7dcf697306f3b 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -216,4 +216,8 @@ def err_missing_vfs_overlay_file : Error< "virtual filesystem overlay file '%0' not found">, DefaultFatal; def err_invalid_vfs_overlay : Error< "invalid virtual filesystem overlay file '%0'">, DefaultFatal; + +def err_no_apinotes_cache_path : Error< + "-fapinotes was provided without -fapinotes-cache-path=<directory>">, + DefaultFatal; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index de623ca9b035c..a85c68b16a465 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -649,6 +649,8 @@ def note_suppressed_class_declare : Note< "class with specified objc_requires_property_definitions attribute is declared here">; def err_objc_root_class_subclass : Error< "objc_root_class attribute may only be specified on a root class declaration">; +def err_restricted_superclass_mismatch : Error< + "cannot subclass a class with objc_subclassing_restricted attribute">; def warn_objc_root_class_missing : Warning< "class %0 defined without specifying a base class">, InGroup<ObjCRootClass>; @@ -2431,6 +2433,9 @@ def warn_mismatched_availability_override_unavail : Warning< InGroup<Availability>; def note_overridden_method : Note< "overridden method is here">; +def warn_availability_swift_unavailable_only : Warning< + "only 'unavailable' is supported for Swift availability">, + InGroup<Availability>; def note_protocol_method : Note< "protocol method is here">; @@ -2765,6 +2770,9 @@ def warn_attribute_nonnull_no_pointers : Warning< def warn_attribute_nonnull_parm_no_args : Warning< "'nonnull' attribute when used on parameters takes no arguments">, InGroup<IgnoredAttributes>; +def warn_attribute_noescape_non_pointer : Warning< + "'noescape' attribute ignored on parameter of non-pointer type %0">, + InGroup<IgnoredAttributes>; def warn_attribute_sentinel_named_arguments : Warning< "'sentinel' attribute requires named arguments">, InGroup<IgnoredAttributes>; @@ -2882,6 +2890,23 @@ def err_objc_bridged_related_known_method : Error< def err_objc_attr_protocol_requires_definition : Error< "attribute %0 can only be applied to @protocol definitions, not forward declarations">; +// Swift attributes +def err_attr_swift_name_decl_kind : Error< + "%0 attribute cannot be applied to this declaration">; +def err_attr_swift_name_identifier : Error< + "parameter of %0 attribute must be an ASCII identifier string">; +def err_attr_swift_name_function : Error< + "parameter of %0 attribute must be a Swift function name string">; +def warn_attr_swift_name_num_params : Warning< + "too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">, + InGroup<DiagGroup<"swift-name-attribute">>; +def err_attr_swift_error_no_error_parameter : Error< + "%0 attribute can only be applied to a %select{function|method}1 " + "with an error parameter">; +def err_attr_swift_error_return_type : Error< + "%0 attribute with '%1' convention can only be applied to a " + "%select{function|method}2 returning %select{an integral type|a pointer}3">; + // Function Parameter Semantic Analysis. def err_param_with_void_type : Error<"argument may not have 'void' type">; def err_void_only_param : Error< diff --git a/clang/include/clang/Basic/FileSystemOptions.h b/clang/include/clang/Basic/FileSystemOptions.h index 38f1346312489..1beb2e2907210 100644 --- a/clang/include/clang/Basic/FileSystemOptions.h +++ b/clang/include/clang/Basic/FileSystemOptions.h @@ -25,6 +25,9 @@ class FileSystemOptions { /// \brief If set, paths are resolved as if the working directory was /// set to the value of WorkingDir. std::string WorkingDir; + + /// The path to the API notes cache. + std::string APINotesCachePath; }; } // end namespace clang diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 75816e9a2671f..72c260ea5fab3 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -230,6 +230,7 @@ VALUE_LANGOPT(VtorDispMode, 2, 1, "How many vtordisps to insert") LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling") LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST") +LANGOPT(APINotes, 1, 0, "use external API notes") LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " "field padding (0: none, 1:least " diff --git a/clang/include/clang/Basic/SourceMgrAdapter.h b/clang/include/clang/Basic/SourceMgrAdapter.h new file mode 100644 index 0000000000000..6782aebbeff58 --- /dev/null +++ b/clang/include/clang/Basic/SourceMgrAdapter.h @@ -0,0 +1,83 @@ +//=== SourceMgrAdapter.h - SourceMgr to SourceManager Adapter ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides an adapter that maps diagnostics from llvm::SourceMgr +// to Clang's SourceManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SOURCEMGRADAPTER_H +#define LLVM_CLANG_SOURCEMGRADAPTER_H + +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/SourceMgr.h" +#include <string> +#include <utility> + +namespace clang { + +class DiagnosticsEngine; +class FileEntry; + +/// An adapter that can be used to translate diagnostics from one or more +/// llvm::SourceMgr instances to a , +class SourceMgrAdapter { + /// Clang source manager. + SourceManager &SrcMgr; + + /// Clang diagnostics engine. + DiagnosticsEngine &Diag; + + /// Diagnostic IDs for errors, warnings, and notes. + unsigned ErrorDiagID, WarningDiagID, NoteDiagID; + + /// The default file to use when mapping buffers. + const FileEntry *DefaultFile; + + /// A mapping from (LLVM source manager, buffer ID) pairs to the + /// corresponding file ID within the Clang source manager. + llvm::DenseMap<std::pair<const llvm::SourceMgr *, unsigned>, FileID> + FileIDMapping; + + /// Diagnostic handler. + static void handleDiag(const llvm::SMDiagnostic &diag, void *context); + +public: + /// Create a new \c SourceMgr adaptor that maps to the given source + /// manager and diagnostics engine. + SourceMgrAdapter(SourceManager &srcMgr, DiagnosticsEngine &diag, + unsigned errorDiagID, unsigned warningDiagID, + unsigned noteDiagID, const FileEntry *defaultFile = nullptr); + + ~SourceMgrAdapter(); + + /// Map a source location in the given LLVM source manager to its + /// corresponding location in the Clang source manager. + SourceLocation mapLocation(const llvm::SourceMgr &llvmSrcMgr,llvm::SMLoc loc); + + /// Map a source range in the given LLVM source manager to its corresponding + /// range in the Clang source manager. + SourceRange mapRange(const llvm::SourceMgr &llvmSrcMgr, llvm::SMRange range); + + /// Handle the given diagnostic from an LLVM source manager. + void handleDiag(const llvm::SMDiagnostic &diag); + + /// Retrieve the diagnostic handler to use with the underlying SourceMgr. + llvm::SourceMgr::DiagHandlerTy getDiagHandler() { return &handleDiag; } + + /// Retrieve the context to use with the diagnostic handler produced by + /// \c getDiagHandler(). + void *getDiagContext() { return this; } +}; + + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 65dd87ce438b0..eb016f358d284 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -464,6 +464,14 @@ def fno_profile_instr_use : Flag<["-"], "fno-profile-instr-use">, def fno_profile_use : Flag<["-"], "fno-profile-use">, Alias<fno_profile_instr_use>; +def fapinotes : Flag<["-"], "fapinotes">, Group<f_clang_Group>, + Flags<[CC1Option]>, HelpText<"Enable external API notes support">; +def fno_apinotes : Flag<["-"], "fno-apinotes">, Group<f_clang_Group>, + Flags<[CC1Option]>, HelpText<"Disable external API notes support">; +def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, + Group<i_Group>, Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">, + HelpText<"Specify the API notes cache path">; + def fblocks : Flag<["-"], "fblocks">, Group<f_Group>, Flags<[CC1Option]>, HelpText<"Enable the 'blocks' language feature">; def fbootclasspath_EQ : Joined<["-"], "fbootclasspath=">, Group<f_Group>; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 39b7d8fcb5957..d58052b93a93a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -24,6 +24,7 @@ #include "clang/AST/NSAPI.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/TypeLoc.h" +#include "clang/APINotes/APINotesManager.h" #include "clang/Basic/ExpressionTraits.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/Module.h" @@ -296,6 +297,7 @@ class Sema { ASTConsumer &Consumer; DiagnosticsEngine &Diags; SourceManager &SourceMgr; + api_notes::APINotesManager APINotes; /// \brief Flag indicating whether or not to collect detailed statistics. bool CollectStats; @@ -2115,6 +2117,9 @@ class Sema { unsigned AttrSpellingListIndex); OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex); + SwiftNameAttr *mergeSwiftNameAttr(Decl *D, SourceRange Range, + StringRef Name, bool Override, + unsigned AttrSpellingListIndex); void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); @@ -2896,6 +2901,12 @@ class Sema { void checkUnusedDeclAttributes(Declarator &D); + /// Map any API notes provided for this declaration to attributes on the + /// declaration. + /// + /// Triggered by declaration-attribute processing. + void ProcessAPINotes(Decl *D); + /// Determine if type T is a valid subject for a nonnull and similar /// attributes. By default, we look through references (the behavior used by /// nonnull), but if the second parameter is true, then we treat a reference @@ -2950,7 +2961,8 @@ class Sema { /// \returns true if nullability cannot be applied, false otherwise. bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability, SourceLocation nullabilityLoc, - bool isContextSensitive); + bool isContextSensitive, + bool implicit); /// \brief Stmt attributes - this routine is the top level dispatcher. StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs, @@ -7485,6 +7497,12 @@ class Sema { RTC_Unknown }; + /// Check whether the declared result type of the given Objective-C + /// method declaration is compatible with the method's class. + ResultTypeCompatibilityKind + checkRelatedResultTypeCompatibility(const ObjCMethodDecl *Method, + const ObjCInterfaceDecl *CurrentClass); + void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, ObjCInterfaceDecl *CurrentClass, ResultTypeCompatibilityKind RTC); @@ -9061,6 +9079,7 @@ class Sema { /// The struct behind the CFErrorRef pointer. RecordDecl *CFError = nullptr; + bool isCFError(RecordDecl *D); /// Retrieve the identifier "NSError". IdentifierInfo *getNSErrorIdent(); diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h new file mode 100644 index 0000000000000..5462e5a558625 --- /dev/null +++ b/clang/lib/APINotes/APINotesFormat.h @@ -0,0 +1,239 @@ +//===--- APINotesFormat.h - The internals of API notes files ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Contains various constants and helper types to deal with API notes +/// files. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_API_NOTES_FORMAT_H +#define LLVM_CLANG_API_NOTES_FORMAT_H + +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitcode/RecordLayout.h" + +namespace clang { +namespace api_notes { + +using namespace llvm; + +/// Magic number for API notes files. +const unsigned char API_NOTES_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x01 }; + +/// API notes file major version number. +/// +const uint16_t VERSION_MAJOR = 0; + +/// API notes file minor version number. +/// +/// When the format changes IN ANY WAY, this number should be incremented. +const uint16_t VERSION_MINOR = 6; + +using IdentifierID = Fixnum<31>; +using IdentifierIDField = BCVBR<16>; + +using SelectorID = Fixnum<31>; +using SelectorIDField = BCVBR<16>; + +using StoredContextID = Fixnum<31>; + +/// The various types of blocks that can occur within a API notes file. +/// +/// These IDs must \em not be renumbered or reordered without incrementing +/// VERSION_MAJOR. +enum BlockID { + /// The control block, which contains all of the information that needs to + /// be validated prior to committing to loading the API notes file. + /// + /// \sa control_block + CONTROL_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + + /// The identifier data block, which maps identifier strings to IDs. + IDENTIFIER_BLOCK_ID, + + /// The Objective-C class data block, which maps Objective-C class + /// names to information about the class. + OBJC_CONTEXT_BLOCK_ID, + + /// The Objective-C property data block, which maps Objective-C + /// (class name, property name) pairs to information about the + /// property. + OBJC_PROPERTY_BLOCK_ID, + + /// The Objective-C property data block, which maps Objective-C + /// (class name, selector, is_instance_method) tuples to information + /// about the method. + OBJC_METHOD_BLOCK_ID, + + /// The Objective-C selector data block, which maps Objective-C + /// selector names (# of pieces, identifier IDs) to the selector ID + /// used in other tables. + OBJC_SELECTOR_BLOCK_ID, + + /// The global variables data block, which maps global variable names to + /// information about the global variable. + GLOBAL_VARIABLE_BLOCK_ID, + + /// The (global) functions data block, which maps global function names to + /// information about the global function. + GLOBAL_FUNCTION_BLOCK_ID +}; + +namespace control_block { + // These IDs must \em not be renumbered or reordered without incrementing + // VERSION_MAJOR. + enum { + METADATA = 1, + MODULE_NAME = 2 + }; + + using MetadataLayout = BCRecordLayout< + METADATA, // ID + BCFixed<16>, // Module format major version + BCFixed<16> // Module format minor version + >; + + using ModuleNameLayout = BCRecordLayout< + MODULE_NAME, + BCBlob // Module name + >; +} + +namespace identifier_block { + enum { + IDENTIFIER_DATA = 1, + }; + + using IdentifierDataLayout = BCRecordLayout< + IDENTIFIER_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from identifier strings to decl kinds / decl IDs + >; +} + +namespace objc_context_block { + enum { + OBJC_CONTEXT_DATA = 1, + }; + + using ObjCContextDataLayout = BCRecordLayout< + OBJC_CONTEXT_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from ObjC class names (as IDs) to ObjC class information + >; +} + +namespace objc_property_block { + enum { + OBJC_PROPERTY_DATA = 1, + }; + + using ObjCPropertyDataLayout = BCRecordLayout< + OBJC_PROPERTY_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from ObjC (class name, property name) pairs to ObjC + // property information + >; +} + +namespace objc_method_block { + enum { + OBJC_METHOD_DATA = 1, + }; + + using ObjCMethodDataLayout = BCRecordLayout< + OBJC_METHOD_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from ObjC (class names, selector, + // is-instance-method) tuples to ObjC method information + >; +} + +namespace objc_selector_block { + enum { + OBJC_SELECTOR_DATA = 1, + }; + + using ObjCSelectorDataLayout = BCRecordLayout< + OBJC_SELECTOR_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from (# pieces, identifier IDs) to Objective-C selector ID. + >; +} + +namespace global_variable_block { + enum { + GLOBAL_VARIABLE_DATA = 1 + }; + + using GlobalVariableDataLayout = BCRecordLayout< + GLOBAL_VARIABLE_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to global variable information + >; +} + +namespace global_function_block { + enum { + GLOBAL_FUNCTION_DATA = 1 + }; + + using GlobalFunctionDataLayout = BCRecordLayout< + GLOBAL_FUNCTION_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to global function information + >; +} + +/// A stored Objective-C selector. +struct StoredObjCSelector { + unsigned NumPieces; + llvm::SmallVector<IdentifierID, 2> Identifiers; +}; + +} // end namespace api_notes +} // end namespace clang + +namespace llvm { + template<> + struct DenseMapInfo<clang::api_notes::StoredObjCSelector> { + typedef DenseMapInfo<unsigned> UnsignedInfo; + + static inline clang::api_notes::StoredObjCSelector getEmptyKey() { + return clang::api_notes::StoredObjCSelector{ + UnsignedInfo::getEmptyKey(), { } }; + } + + static inline clang::api_notes::StoredObjCSelector getTombstoneKey() { + return clang::api_notes::StoredObjCSelector{ + UnsignedInfo::getTombstoneKey(), { } }; + } + + static unsigned getHashValue( + const clang::api_notes::StoredObjCSelector& value) { + auto hash = llvm::hash_value(value.NumPieces); + hash = hash_combine(hash, value.Identifiers.size()); + for (auto piece : value.Identifiers) + hash = hash_combine(hash, static_cast<unsigned>(piece)); + // FIXME: Mix upper/lower 32-bit values together to produce + // unsigned rather than truncating. + return hash; + } + + static bool isEqual(const clang::api_notes::StoredObjCSelector &lhs, + const clang::api_notes::StoredObjCSelector &rhs) { + return lhs.NumPieces == rhs.NumPieces && + lhs.Identifiers == rhs.Identifiers; + } + }; +} + +#endif // LLVM_CLANG_API_NOTES_FORMAT_H diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp new file mode 100644 index 0000000000000..50736939a60c1 --- /dev/null +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -0,0 +1,453 @@ +//===--- APINotesMAnager.cpp - Manage API Notes Files ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the APINotesManager class. +// +//===----------------------------------------------------------------------===// + +#include "clang/APINotes/APINotesManager.h" +#include "clang/APINotes/APINotesReader.h" +#include "clang/APINotes/APINotesYAMLCompiler.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SourceMgrAdapter.h" +#include "clang/Basic/Version.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/Path.h" +#include <sys/stat.h> + +using namespace clang; +using namespace api_notes; + +#define DEBUG_TYPE "API Notes" +STATISTIC(NumHeaderAPINotes, + "non-framework API notes files loaded"); +STATISTIC(NumPublicFrameworkAPINotes, + "framework public API notes loaded"); +STATISTIC(NumPrivateFrameworkAPINotes, + "framework private API notes loaded"); +STATISTIC(NumFrameworksSearched, + "frameworks searched"); +STATISTIC(NumDirectoriesSearched, + "header directories searched"); +STATISTIC(NumDirectoryCacheHits, + "directory cache hits"); +STATISTIC(NumBinaryCacheHits, + "binary form cache hits"); +STATISTIC(NumBinaryCacheMisses, + "binary form cache misses"); +STATISTIC(NumBinaryCacheRebuilds, + "binary form cache rebuilds"); + +APINotesManager::APINotesManager(SourceManager &SourceMgr) + : SourceMgr(SourceMgr), PrunedCache(false) { } + + +APINotesManager::~APINotesManager() { + // Free the API notes readers. + for (const auto &entry : Readers) { + if (auto reader = entry.second.dyn_cast<APINotesReader *>()) { + delete reader; + } + } +} + +/// \brief Write a new timestamp file with the given path. +static void writeTimestampFile(StringRef TimestampFile) { + std::error_code EC; + llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::F_None); +} + +/// \brief Prune the API notes cache of API notes that haven't been accessed in +/// a long time. +static void pruneAPINotesCache(StringRef APINotesCachePath) { + struct stat StatBuf; + llvm::SmallString<128> TimestampFile; + TimestampFile = APINotesCachePath; + llvm::sys::path::append(TimestampFile, "APINotes.timestamp"); + + // Try to stat() the timestamp file. + if (::stat(TimestampFile.c_str(), &StatBuf)) { + // If the timestamp file wasn't there, create one now. + if (errno == ENOENT) { + llvm::sys::fs::create_directories(APINotesCachePath); + writeTimestampFile(TimestampFile); + } + return; + } + + const unsigned APINotesCachePruneInterval = 7 * 24 * 60 * 60; + const unsigned APINotesCachePruneAfter = 31 * 24 * 60 * 60; + + // Check whether the time stamp is older than our pruning interval. + // If not, do nothing. + time_t TimeStampModTime = StatBuf.st_mtime; + time_t CurrentTime = time(nullptr); + if (CurrentTime - TimeStampModTime <= time_t(APINotesCachePruneInterval)) + return; + + // Write a new timestamp file so that nobody else attempts to prune. + // There is a benign race condition here, if two Clang instances happen to + // notice at the same time that the timestamp is out-of-date. + writeTimestampFile(TimestampFile); + + // Walk the entire API notes cache, looking for unused compiled API notes. + std::error_code EC; + SmallString<128> APINotesCachePathNative; + llvm::sys::path::native(APINotesCachePath, APINotesCachePathNative); + for (llvm::sys::fs::directory_iterator + File(APINotesCachePathNative.str(), EC), DirEnd; + File != DirEnd && !EC; File.increment(EC)) { + StringRef Extension = llvm::sys::path::extension(File->path()); + if (Extension.empty()) + continue; + + if (Extension.substr(1) != BINARY_APINOTES_EXTENSION) + continue; + + // Look at this file. If we can't stat it, there's nothing interesting + // there. + if (::stat(File->path().c_str(), &StatBuf)) + continue; + + // If the file has been used recently enough, leave it there. + time_t FileAccessTime = StatBuf.st_atime; + if (CurrentTime - FileAccessTime <= time_t(APINotesCachePruneAfter)) { + continue; + } + + // Remove the file. + llvm::sys::fs::remove(File->path()); + } +} + +bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, + const FileEntry *APINotesFile) { + assert(Readers.find(HeaderDir) == Readers.end()); + + FileManager &FileMgr = SourceMgr.getFileManager(); + + // If the API notes file is already in the binary form, load it directly. + StringRef APINotesFileName = APINotesFile->getName(); + StringRef APINotesFileExt = llvm::sys::path::extension(APINotesFileName); + if (!APINotesFileExt.empty() && + APINotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { + // Load the file. + auto Buffer = FileMgr.getBufferForFile(APINotesFile); + if (!Buffer) { + Readers[HeaderDir] = nullptr; + return true; + } + + // Load the binary form. + auto Reader = APINotesReader::get(std::move(Buffer.get())); + if (!Reader) { + Readers[HeaderDir] = nullptr; + return true; + } + + // Record the reader. + Readers[HeaderDir] = Reader.release(); + return false; + } + + // If we haven't pruned the API notes cache yet during this execution, do + // so now. + if (!PrunedCache) { + pruneAPINotesCache(FileMgr.getFileSystemOpts().APINotesCachePath); + PrunedCache = true; + } + + // Compute a hash of the API notes file's directory and the Clang version, + // to be used as part of the filename for the cached binary copy. + auto code = llvm::hash_value(StringRef(APINotesFile->getDir()->getName())); + code = hash_combine(code, getClangFullRepositoryVersion()); + + // Determine the file name for the cached binary form. + SmallString<128> CompiledFileName; + CompiledFileName += FileMgr.getFileSystemOpts().APINotesCachePath; + assert(!CompiledFileName.empty() && "No API notes cache path provided?"); + llvm::sys::path::append(CompiledFileName, + (llvm::Twine(llvm::sys::path::stem(APINotesFileName)) + "-" + + llvm::APInt(64, code).toString(36, /*Signed=*/false) + "." + + BINARY_APINOTES_EXTENSION)); + + // Try to open the cached binary form. + if (const FileEntry *CompiledFile = FileMgr.getFile(CompiledFileName, + /*openFile=*/true, + /*cacheFailure=*/false)) { + // Load the file contents. + if (auto Buffer = FileMgr.getBufferForFile(CompiledFile)) { + // Make sure the file is up-to-date. + if (CompiledFile->getModificationTime() + >= APINotesFile->getModificationTime()) { + // Load the file. + if (auto Reader = APINotesReader::get(std::move(Buffer.get()))) { + // Success. + ++NumBinaryCacheHits; + Readers[HeaderDir] = Reader.release(); + return false; + } + } + } + + // The cache entry was somehow broken; delete this one so we can build a + // new one below. + llvm::sys::fs::remove(CompiledFileName.str()); + ++NumBinaryCacheRebuilds; + } else { + ++NumBinaryCacheMisses; + } + + // Open the source file. + auto Buffer = FileMgr.getBufferForFile(APINotesFile); + if (!Buffer) { + Readers[HeaderDir] = nullptr; + return true; + } + + // Compile the API notes source into a buffer. + // FIXME: Either propagate OSType through or, better yet, improve the binary + // APINotes format to maintain complete availability information. + llvm::SmallVector<char, 1024> APINotesBuffer; + { + SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(), + diag::err_apinotes_message, + diag::warn_apinotes_message, + diag::note_apinotes_message, + APINotesFile); + llvm::raw_svector_ostream OS(APINotesBuffer); + if (api_notes::compileAPINotes(Buffer.get()->getBuffer(), + OS, + api_notes::OSType::Absent, + srcMgrAdapter.getDiagHandler(), + srcMgrAdapter.getDiagContext())) { + Readers[HeaderDir] = nullptr; + return true; + } + + // Make a copy of the compiled form into the buffer. + Buffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + } + + // Save the binary form into the cache. Perform this operation + // atomically. + SmallString<64> TemporaryBinaryFileName = CompiledFileName.str(); + TemporaryBinaryFileName.erase( + TemporaryBinaryFileName.end() + - llvm::sys::path::extension(TemporaryBinaryFileName).size(), + TemporaryBinaryFileName.end()); + TemporaryBinaryFileName += "-%%%%%%."; + TemporaryBinaryFileName += BINARY_APINOTES_EXTENSION; + + int TemporaryFD; + llvm::sys::fs::create_directories( + FileMgr.getFileSystemOpts().APINotesCachePath); + if (!llvm::sys::fs::createUniqueFile(TemporaryBinaryFileName.str(), + TemporaryFD, TemporaryBinaryFileName)) { + // Write the contents of the buffer. + bool hadError; + { + llvm::raw_fd_ostream Out(TemporaryFD, /*shouldClose=*/true); + Out.write(Buffer.get()->getBufferStart(), Buffer.get()->getBufferSize()); + Out.flush(); + + hadError = Out.has_error(); + } + + if (!hadError) { + // Rename the temporary file to the actual compiled file. + llvm::sys::fs::rename(TemporaryBinaryFileName.str(), + CompiledFileName.str()); + } + } + + // Load the binary form we just compiled. + auto Reader = APINotesReader::get(std::move(*Buffer)); + assert(Reader && "Could not load the API notes we just generated?"); + + // Record the reader. + Readers[HeaderDir] = Reader.release(); + return false; +} + +const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( + llvm::StringRef FrameworkPath, + llvm::StringRef FrameworkName, + bool Public) { + FileManager &FileMgr = SourceMgr.getFileManager(); + + llvm::SmallString<128> Path; + Path += FrameworkPath; + unsigned FrameworkNameLength = Path.size(); + + // Form the path to the APINotes file. + llvm::sys::path::append(Path, "APINotes"); + if (Public) + llvm::sys::path::append(Path, + (llvm::Twine(FrameworkName) + "." + + SOURCE_APINOTES_EXTENSION)); + else + llvm::sys::path::append(Path, + (llvm::Twine(FrameworkName) + "_private." + + SOURCE_APINOTES_EXTENSION)); + + // Try to open the APINotes file. + const FileEntry *APINotesFile = FileMgr.getFile(Path); + if (!APINotesFile) + return nullptr; + + // Form the path to the corresponding header directory. + Path.resize(FrameworkNameLength); + if (Public) + llvm::sys::path::append(Path, "Headers"); + else + llvm::sys::path::append(Path, "PrivateHeaders"); + + // Try to access the header directory. + const DirectoryEntry *HeaderDir = FileMgr.getDirectory(Path); + if (!HeaderDir) + return nullptr; + + // Try to load the API notes. + if (loadAPINotes(HeaderDir, APINotesFile)) + return nullptr; + + // Success: return the header directory. + if (Public) + ++NumPublicFrameworkAPINotes; + else + ++NumPrivateFrameworkAPINotes; + return HeaderDir; +} + +APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { + // API notes are associated with the expansion location. Retrieve the + // file for this location. + SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc); + FileID ID = SourceMgr.getFileID(ExpansionLoc); + if (ID.isInvalid()) + return nullptr; + const FileEntry *File = SourceMgr.getFileEntryForID(ID); + if (!File) + return nullptr; + + // Look for API notes in the directory corresponding to this file, or one of + // its its parent directories. + const DirectoryEntry *Dir = File->getDir(); + FileManager &FileMgr = SourceMgr.getFileManager(); + llvm::SetVector<const DirectoryEntry *, + SmallVector<const DirectoryEntry *, 4>, + llvm::SmallPtrSet<const DirectoryEntry *, 4>> DirsVisited; + APINotesReader *Result = nullptr; + do { + // Look for an API notes reader for this header search directory. + auto Known = Readers.find(Dir); + + // If we already know the answer, chase it. + if (Known != Readers.end()) { + ++NumDirectoryCacheHits; + + // We've been redirected to another directory for answers. Follow it. + if (auto OtherDir = Known->second.dyn_cast<const DirectoryEntry *>()) { + DirsVisited.insert(Dir); + Dir = OtherDir; + continue; + } + + // We have the answer. + Result = Known->second.dyn_cast<APINotesReader *>(); + break; + } + + // Look for API notes corresponding to this directory. + StringRef Path = Dir->getName(); + if (llvm::sys::path::extension(Path) == ".framework") { + // If this is a framework directory, check whether there are API notes + // in the APINotes subdirectory. + auto FrameworkName = llvm::sys::path::stem(Path); + ++NumFrameworksSearched; + + // Look for API notes for both the public and private headers. + const DirectoryEntry *PublicDir + = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true); + const DirectoryEntry *PrivateDir + = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false); + + if (PublicDir || PrivateDir) { + // We found API notes: don't ever look past the framework directory. + Readers[Dir] = nullptr; + + // Pretend we found the result in the public or private directory, + // as appropriate. All headers should be in one of those two places, + // but be defensive here. + if (!DirsVisited.empty()) { + if (DirsVisited.back() == PublicDir) { + DirsVisited.pop_back(); + Dir = PublicDir; + } else if (DirsVisited.back() == PrivateDir) { + DirsVisited.pop_back(); + Dir = PrivateDir; + } + } + + // Grab the result. + Result = Readers[Dir].dyn_cast<APINotesReader *>();; + break; + } + } else { + // Look for an APINotes file in this directory. + llvm::SmallString<128> APINotesPath; + APINotesPath += Dir->getName(); + llvm::sys::path::append(APINotesPath, + (llvm::Twine("APINotes.") + + SOURCE_APINOTES_EXTENSION)); + + // If there is an API notes file here, try to load it. + ++NumDirectoriesSearched; + if (const FileEntry *APINotesFile = FileMgr.getFile(APINotesPath)) { + if (!loadAPINotes(Dir, APINotesFile)) { + ++NumHeaderAPINotes; + Result = Readers[Dir].dyn_cast<APINotesReader *>(); + break; + } + } + } + + // We didn't find anything. Look at the parent directory. + if (!DirsVisited.insert(Dir)) { + Dir = 0; + break; + } + + StringRef ParentPath = llvm::sys::path::parent_path(Path); + while (llvm::sys::path::stem(ParentPath) == "..") { + ParentPath = llvm::sys::path::parent_path(ParentPath); + } + if (ParentPath.empty()) { + Dir = nullptr; + } else { + Dir = FileMgr.getDirectory(ParentPath); + } + } while (Dir); + + // Path compression for all of the directories we visited, redirecting + // them to the directory we ended on. If no API notes were found, the + // resulting directory will be NULL, indicating no API notes. + for (const auto Visited : DirsVisited) { + Readers[Visited] = Dir; + } + + return Result; +} diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp new file mode 100644 index 0000000000000..a84f2d70491fe --- /dev/null +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -0,0 +1,1297 @@ +//===--- APINotesReader.cpp - Side Car Reader --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the \c APINotesReader class that reads source +// API notes data providing additional information about source code as +// a separate input, such as the non-nil/nilable annotations for +// method parameters. +// +//===----------------------------------------------------------------------===// +#include "clang/APINotes/APINotesReader.h" +#include "APINotesFormat.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/OnDiskHashTable.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringExtras.h" + +using namespace clang; +using namespace api_notes; +using namespace llvm::support; +using namespace llvm; + +namespace { + /// Read serialized CommonEntityInfo. + void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) { + info.Unavailable = *data++; + + unsigned msgLength = endian::readNext<uint16_t, little, unaligned>(data); + info.UnavailableMsg + = std::string(reinterpret_cast<const char *>(data), + reinterpret_cast<const char *>(data) + msgLength); + data += msgLength; + } + + /// Used to deserialize the on-disk identifier table. + class IdentifierTableInfo { + public: + using internal_key_type = StringRef; + using external_key_type = StringRef; + using data_type = IdentifierID; + using hash_value_type = uint32_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return llvm::HashString(key); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + return StringRef(reinterpret_cast<const char *>(data), length); + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + return endian::readNext<uint32_t, little, unaligned>(data); + } + }; + + /// Used to deserialize the on-disk Objective-C class table. + class ObjCContextTableInfo { + public: + // identifier ID, is-protocol + using internal_key_type = std::pair<unsigned, char>; + using external_key_type = internal_key_type; + using data_type = std::pair<unsigned, ObjCContextInfo>; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID + = endian::readNext<IdentifierID, little, unaligned>(data); + auto isProtocol = endian::readNext<uint8_t, little, unaligned>(data); + return { nameID, isProtocol }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + data_type result; + result.first = endian::readNext<StoredContextID, little, unaligned>(data); + readCommonEntityInfo(data, result.second); + if (*data++) { + result.second.setDefaultNullability(static_cast<NullabilityKind>(*data)); + } + ++data; + result.second.setHasDesignatedInits(*data++); + return result; + } + }; + + /// Read serialized VariableInfo. + void readVariableInfo(const uint8_t *&data, VariableInfo &info) { + readCommonEntityInfo(data, info); + if (*data++) { + info.setNullabilityAudited(static_cast<NullabilityKind>(*data)); + } + ++data; + } + + /// Used to deserialize the on-disk Objective-C property table. + class ObjCPropertyTableInfo { + public: + // (context ID, name ID) + using internal_key_type = std::pair<unsigned, unsigned>; + using external_key_type = internal_key_type; + using data_type = ObjCPropertyInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto classID = endian::readNext<IdentifierID, little, unaligned>(data); + auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + return { classID, nameID }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + ObjCPropertyInfo info; + readVariableInfo(data, info); + return info; + } + }; + + /// Read serialized FunctionInfo. + void readFunctionInfo(const uint8_t *&data, FunctionInfo &info) { + readCommonEntityInfo(data, info); + info.NullabilityAudited + = endian::readNext<uint8_t, little, unaligned>(data); + info.NumAdjustedNullable + = endian::readNext<uint8_t, little, unaligned>(data); + info.NullabilityPayload + = endian::readNext<uint64_t, little, unaligned>(data); + } + + /// Used to deserialize the on-disk Objective-C method table. + class ObjCMethodTableInfo { + public: + // (class ID, selector ID, is-instance) + using internal_key_type = std::tuple<unsigned, unsigned, char>; + using external_key_type = internal_key_type; + using data_type = ObjCMethodInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return llvm::hash_combine(std::get<0>(key), + std::get<1>(key), + std::get<2>(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto classID = endian::readNext<IdentifierID, little, unaligned>(data); + auto selectorID = endian::readNext<SelectorID, little, unaligned>(data); + auto isInstance = endian::readNext<uint8_t, little, unaligned>(data); + return internal_key_type{ classID, selectorID, isInstance }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + ObjCMethodInfo info; + readFunctionInfo(data, info); + info.DesignatedInit = endian::readNext<uint8_t, little, unaligned>(data); + info.FactoryAsInit = endian::readNext<uint8_t, little, unaligned>(data); + info.Required = endian::readNext<uint8_t, little, unaligned>(data); + return info; + } + }; + + /// Used to deserialize the on-disk Objective-C selector table. + class ObjCSelectorTableInfo { + public: + using internal_key_type = StoredObjCSelector; + using external_key_type = internal_key_type; + using data_type = SelectorID; + using hash_value_type = unsigned; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(key); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return llvm::DenseMapInfo<StoredObjCSelector>::isEqual(lhs, rhs); + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + internal_key_type key; + key.NumPieces = endian::readNext<uint16_t, little, unaligned>(data); + unsigned numIdents = (length - sizeof(uint16_t)) / sizeof(IdentifierID); + for (unsigned i = 0; i != numIdents; ++i) { + key.Identifiers.push_back( + endian::readNext<IdentifierID, little, unaligned>(data)); + } + return key; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + return endian::readNext<SelectorID, little, unaligned>(data); + } + }; + + /// Used to deserialize the on-disk global variable table. + class GlobalVariableTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = GlobalVariableInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + GlobalVariableInfo info; + readVariableInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk global function table. + class GlobalFunctionTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = GlobalFunctionInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + GlobalFunctionInfo info; + readFunctionInfo(data, info); + return info; + } + }; +} // end anonymous namespace + +class APINotesReader::Implementation { +public: + /// The input buffer for the API notes data. + std::unique_ptr<llvm::MemoryBuffer> InputBuffer; + + /// The reader attached to \c InputBuffer. + llvm::BitstreamReader InputReader; + + /// The name of the module that we read from the control block. + std::string ModuleName; + + using SerializedIdentifierTable = + llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>; + + /// The identifier table. + std::unique_ptr<SerializedIdentifierTable> IdentifierTable; + + using SerializedObjCContextTable = + llvm::OnDiskIterableChainedHashTable<ObjCContextTableInfo>; + + /// The Objective-C context table. + std::unique_ptr<SerializedObjCContextTable> ObjCContextTable; + + using SerializedObjCPropertyTable = + llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>; + + /// The Objective-C property table. + std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable; + + using SerializedObjCMethodTable = + llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>; + + /// The Objective-C method table. + std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable; + + using SerializedObjCSelectorTable = + llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>; + + /// The Objective-C selector table. + std::unique_ptr<SerializedObjCSelectorTable> ObjCSelectorTable; + + using SerializedGlobalVariableTable = + llvm::OnDiskIterableChainedHashTable<GlobalVariableTableInfo>; + + /// The global variable table. + std::unique_ptr<SerializedGlobalVariableTable> GlobalVariableTable; + + using SerializedGlobalFunctionTable = + llvm::OnDiskIterableChainedHashTable<GlobalFunctionTableInfo>; + + /// The global function table. + std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable; + + /// Retrieve the identifier ID for the given string, or an empty + /// optional if the string is unknown. + Optional<IdentifierID> getIdentifier(StringRef str); + + /// Retrieve the selector ID for the given selector, or an empty + /// optional if the string is unknown. + Optional<SelectorID> getSelector(ObjCSelectorRef selector); + + bool readControlBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readIdentifierBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readObjCContextBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readObjCPropertyBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readObjCMethodBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readObjCSelectorBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readGlobalVariableBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); +}; + +Optional<IdentifierID> APINotesReader::Implementation::getIdentifier( + StringRef str) { + if (!IdentifierTable) + return None; + + if (str.empty()) + return IdentifierID(0); + + auto known = IdentifierTable->find(str); + if (known == IdentifierTable->end()) + return None; + + return *known; +} + +Optional<SelectorID> APINotesReader::Implementation::getSelector( + ObjCSelectorRef selector) { + if (!ObjCSelectorTable || !IdentifierTable) + return None; + + // Translate the identifiers. + StoredObjCSelector key; + key.NumPieces = selector.NumPieces; + for (auto ident : selector.Identifiers) { + if (auto identID = getIdentifier(ident)) { + key.Identifiers.push_back(*identID); + } else { + return None; + } + } + + auto known = ObjCSelectorTable->find(key); + if (known == ObjCSelectorTable->end()) + return None; + + return *known; + +} + +bool APINotesReader::Implementation::readControlBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(CONTROL_BLOCK_ID)) + return true; + + bool sawMetadata = false; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown metadata sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case control_block::METADATA: + // Already saw metadata. + if (sawMetadata) + return true; + + if (scratch[0] != VERSION_MAJOR || scratch[1] != VERSION_MINOR) + return true; + + sawMetadata = true; + break; + + case control_block::MODULE_NAME: + ModuleName = blobData.str(); + break; + + default: + // Unknown metadata record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return !sawMetadata; +} + +bool APINotesReader::Implementation::readIdentifierBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case identifier_block::IDENTIFIER_DATA: { + // Already saw identifier table. + if (IdentifierTable) + return true; + + uint32_t tableOffset; + identifier_block::IdentifierDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + IdentifierTable.reset( + SerializedIdentifierTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCContextBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case objc_context_block::OBJC_CONTEXT_DATA: { + // Already saw Objective-C class table. + if (ObjCContextTable) + return true; + + uint32_t tableOffset; + objc_context_block::ObjCContextDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + ObjCContextTable.reset( + SerializedObjCContextTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCPropertyBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case objc_property_block::OBJC_PROPERTY_DATA: { + // Already saw Objective-C property table. + if (ObjCPropertyTable) + return true; + + uint32_t tableOffset; + objc_property_block::ObjCPropertyDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + ObjCPropertyTable.reset( + SerializedObjCPropertyTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCMethodBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case objc_method_block::OBJC_METHOD_DATA: { + // Already saw Objective-C method table. + if (ObjCMethodTable) + return true; + + uint32_t tableOffset; + objc_method_block::ObjCMethodDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + ObjCMethodTable.reset( + SerializedObjCMethodTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCSelectorBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case objc_selector_block::OBJC_SELECTOR_DATA: { + // Already saw Objective-C selector table. + if (ObjCSelectorTable) + return true; + + uint32_t tableOffset; + objc_selector_block::ObjCSelectorDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + ObjCSelectorTable.reset( + SerializedObjCSelectorTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readGlobalVariableBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case global_variable_block::GLOBAL_VARIABLE_DATA: { + // Already saw global variable table. + if (GlobalVariableTable) + return true; + + uint32_t tableOffset; + global_variable_block::GlobalVariableDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + GlobalVariableTable.reset( + SerializedGlobalVariableTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readGlobalFunctionBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case global_function_block::GLOBAL_FUNCTION_DATA: { + // Already saw global function table. + if (GlobalFunctionTable) + return true; + + uint32_t tableOffset; + global_function_block::GlobalFunctionDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + GlobalFunctionTable.reset( + SerializedGlobalFunctionTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +APINotesReader::APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, + bool &failed) + : Impl(*new Implementation) +{ + failed = false; + + // Initialize the input buffer. + Impl.InputBuffer = std::move(inputBuffer); + Impl.InputReader.init( + reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferStart()), + reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferEnd())); + llvm::BitstreamCursor cursor(Impl.InputReader); + + // Validate signature. + for (auto byte : API_NOTES_SIGNATURE) { + if (cursor.AtEndOfStream() || cursor.Read(8) != byte) { + failed = true; + return; + } + } + + // Look at all of the blocks. + bool hasValidControlBlock = false; + SmallVector<uint64_t, 64> scratch; + auto topLevelEntry = cursor.advance(); + while (topLevelEntry.Kind == llvm::BitstreamEntry::SubBlock) { + switch (topLevelEntry.ID) { + case llvm::bitc::BLOCKINFO_BLOCK_ID: + if (cursor.ReadBlockInfoBlock()) { + failed = true; + break; + } + break; + + case CONTROL_BLOCK_ID: + // Only allow a single control block. + if (hasValidControlBlock || Impl.readControlBlock(cursor, scratch)) { + failed = true; + return; + } + + hasValidControlBlock = true; + break; + + case IDENTIFIER_BLOCK_ID: + if (!hasValidControlBlock || Impl.readIdentifierBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case OBJC_CONTEXT_BLOCK_ID: + if (!hasValidControlBlock || Impl.readObjCContextBlock(cursor, scratch)) { + failed = true; + return; + } + + break; + + case OBJC_PROPERTY_BLOCK_ID: + if (!hasValidControlBlock || + Impl.readObjCPropertyBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case OBJC_METHOD_BLOCK_ID: + if (!hasValidControlBlock || Impl.readObjCMethodBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case OBJC_SELECTOR_BLOCK_ID: + if (!hasValidControlBlock || + Impl.readObjCSelectorBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case GLOBAL_VARIABLE_BLOCK_ID: + if (!hasValidControlBlock || + Impl.readGlobalVariableBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case GLOBAL_FUNCTION_BLOCK_ID: + if (!hasValidControlBlock || + Impl.readGlobalFunctionBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + default: + // Unknown top-level block, possibly for use by a future version of the + // module format. + if (cursor.SkipBlock()) { + failed = true; + return; + } + break; + } + + topLevelEntry = cursor.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + } + + if (topLevelEntry.Kind != llvm::BitstreamEntry::EndBlock) { + failed = true; + return; + } +} + +APINotesReader::~APINotesReader() { + delete &Impl; +} + +std::unique_ptr<APINotesReader> +APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer) { + bool failed = false; + std::unique_ptr<APINotesReader> + reader(new APINotesReader(std::move(inputBuffer), failed)); + if (failed) + return nullptr; + + return reader; +} + +StringRef APINotesReader::getModuleName() const { + return Impl.ModuleName; +} + +auto APINotesReader::lookupObjCClass(StringRef name) + -> Optional<std::pair<ContextID, ObjCContextInfo>> { + if (!Impl.ObjCContextTable) + return None; + + Optional<IdentifierID> classID = Impl.getIdentifier(name); + if (!classID) + return None; + + auto known = Impl.ObjCContextTable->find({*classID, '\0'}); + if (known == Impl.ObjCContextTable->end()) + return None; + + auto result = *known; + return std::make_pair(ContextID(result.first), result.second); +} + +auto APINotesReader::lookupObjCProtocol(StringRef name) + -> Optional<std::pair<ContextID, ObjCContextInfo>> { + if (!Impl.ObjCContextTable) + return None; + + Optional<IdentifierID> classID = Impl.getIdentifier(name); + if (!classID) + return None; + + auto known = Impl.ObjCContextTable->find({*classID, '\1'}); + if (known == Impl.ObjCContextTable->end()) + return None; + + auto result = *known; + return std::make_pair(ContextID(result.first), result.second); +} + +Optional<ObjCPropertyInfo> APINotesReader::lookupObjCProperty( + ContextID contextID, + StringRef name) { + if (!Impl.ObjCPropertyTable) + return None; + + Optional<IdentifierID> propertyID = Impl.getIdentifier(name); + if (!propertyID) + return None; + + auto known = Impl.ObjCPropertyTable->find({contextID.Value, *propertyID}); + if (known == Impl.ObjCPropertyTable->end()) + return None; + + return *known; +} + +Optional<ObjCMethodInfo> APINotesReader::lookupObjCMethod( + ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod) { + if (!Impl.ObjCMethodTable) + return None; + + Optional<SelectorID> selectorID = Impl.getSelector(selector); + if (!selectorID) + return None; + + auto known = Impl.ObjCMethodTable->find( + ObjCMethodTableInfo::internal_key_type{ + contextID.Value, *selectorID, isInstanceMethod}); + if (known == Impl.ObjCMethodTable->end()) + return None; + + return *known; +} + +Optional<GlobalVariableInfo> APINotesReader::lookupGlobalVariable( + StringRef name) { + if (!Impl.GlobalVariableTable) + return None; + + Optional<IdentifierID> nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.GlobalVariableTable->find(*nameID); + if (known == Impl.GlobalVariableTable->end()) + return None; + + return *known; +} + +Optional<GlobalFunctionInfo> APINotesReader::lookupGlobalFunction( + StringRef name) { + if (!Impl.GlobalFunctionTable) + return None; + + Optional<IdentifierID> nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.GlobalFunctionTable->find(*nameID); + if (known == Impl.GlobalFunctionTable->end()) + return None; + + return *known; +} +APINotesReader::Visitor::~Visitor() { } + +void APINotesReader::Visitor::visitObjCClass(ContextID contextID, + StringRef name, + const ObjCContextInfo &info) { } + +void APINotesReader::Visitor::visitObjCProtocol(ContextID contextID, + StringRef name, + const ObjCContextInfo &info) { } + +void APINotesReader::Visitor::visitObjCMethod(ContextID contextID, + StringRef selector, + bool isInstanceMethod, + const ObjCMethodInfo &info) { } + +void APINotesReader::Visitor::visitObjCProperty(ContextID contextID, + StringRef name, + const ObjCPropertyInfo &info) { } + +void APINotesReader::Visitor::visitGlobalVariable( + StringRef name, + const GlobalVariableInfo &info) { } + +void APINotesReader::Visitor::visitGlobalFunction( + StringRef name, + const GlobalFunctionInfo &info) { } + +void APINotesReader::visit(Visitor &visitor) { + // FIXME: All of these iterations would be significantly more efficient if we + // could get the keys and data together, but OnDiskIterableHashTable doesn't + // support that. + + // Build an identifier ID -> string mapping, which we'll need when visiting + // any of the tables. + llvm::DenseMap<unsigned, StringRef> identifiers; + if (Impl.IdentifierTable) { + for (auto key : Impl.IdentifierTable->keys()) { + unsigned ID = *Impl.IdentifierTable->find(key); + assert(identifiers.count(ID) == 0); + identifiers[ID] = key; + } + } + + // Visit classes and protocols. + if (Impl.ObjCContextTable) { + for (auto key : Impl.ObjCContextTable->keys()) { + auto name = identifiers[key.first]; + auto info = *Impl.ObjCContextTable->find(key); + + if (key.second) + visitor.visitObjCProtocol(ContextID(info.first), name, info.second); + else + visitor.visitObjCClass(ContextID(info.first), name, info.second); + } + } + + // Build a selector ID -> stored Objective-C selector mapping, which we need + // when visiting the method tables. + llvm::DenseMap<unsigned, std::string> selectors; + if (Impl.ObjCSelectorTable) { + for (auto key : Impl.ObjCSelectorTable->keys()) { + std::string selector; + if (key.NumPieces == 0) + selector = identifiers[key.Identifiers[0]]; + else { + for (auto identID : key.Identifiers) { + selector += identifiers[identID]; + selector += ':'; + } + } + + unsigned selectorID = *Impl.ObjCSelectorTable->find(key); + selectors[selectorID] = selector; + } + } + + // Visit methods. + if (Impl.ObjCMethodTable) { + for (auto key : Impl.ObjCMethodTable->keys()) { + ContextID contextID(std::get<0>(key)); + const auto &selector = selectors[std::get<1>(key)]; + auto info = *Impl.ObjCMethodTable->find(key); + visitor.visitObjCMethod(contextID, selector, std::get<2>(key), info); + } + } + + // Visit properties. + if (Impl.ObjCPropertyTable) { + for (auto key : Impl.ObjCPropertyTable->keys()) { + ContextID contextID(key.first); + auto name = identifiers[key.second]; + auto info = *Impl.ObjCPropertyTable->find(key); + visitor.visitObjCProperty(contextID, name, info); + } + } + + // Visit global functions. + if (Impl.GlobalFunctionTable) { + for (auto key : Impl.GlobalFunctionTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.GlobalFunctionTable->find(key); + visitor.visitGlobalFunction(name, info); + } + } + + // Visit global variables. + if (Impl.GlobalVariableTable) { + for (auto key : Impl.GlobalVariableTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.GlobalVariableTable->find(key); + visitor.visitGlobalVariable(name, info); + } + } +} + diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp new file mode 100644 index 0000000000000..400bd08e01393 --- /dev/null +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -0,0 +1,851 @@ +//===--- APINotesWriter.cpp - API Notes Writer --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the \c APINotesWriter class that writes out +// source API notes data providing additional information about source +// code as a separate input, such as the non-nil/nilable annotations +// for method parameters. +// +//===----------------------------------------------------------------------===// +#include "clang/APINotes/APINotesWriter.h" +#include "APINotesFormat.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/OnDiskHashTable.h" +#include "llvm/Support/raw_ostream.h" +#include <tuple> +#include <vector> +using namespace clang; +using namespace api_notes; +using namespace llvm::support; + +class APINotesWriter::Implementation { + /// Mapping from strings to identifier IDs. + llvm::StringMap<IdentifierID> IdentifierIDs; + + /// Mapping from selectors to selector ID. + llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs; + + /// Scratch space for bitstream writing. + SmallVector<uint64_t, 64> ScratchRecord; + +public: + /// The name of the module + std::string ModuleName; + + /// Information about Objective-C contexts (classes or protocols). + /// + /// Indexed by the identifier ID and a bit indication whether we're looking + /// for a class (0) or protocol (1) and provides both the context ID and + /// information describing the context within that module. + llvm::DenseMap<std::pair<unsigned, char>, + std::pair<unsigned, ObjCContextInfo>> ObjCContexts; + + /// Mapping from context IDs to the identifier ID holding the name. + llvm::DenseMap<unsigned, unsigned> ObjCContextNames; + + /// Information about Objective-C properties. + /// + /// Indexed by the context ID and property name. + llvm::DenseMap<std::pair<unsigned, unsigned>, ObjCPropertyInfo> + ObjCProperties; + + /// Information about Objective-C methods. + /// + /// Indexed by the context ID, selector ID, and Boolean (stored as a + /// char) indicating whether this is a class or instance method. + llvm::DenseMap<std::tuple<unsigned, unsigned, char>, ObjCMethodInfo> + ObjCMethods; + + /// Information about global variables. + /// + /// Indexed by the identifier ID. + llvm::DenseMap<unsigned, GlobalVariableInfo> GlobalVariables; + + /// Information about global functions. + /// + /// Indexed by the identifier ID. + llvm::DenseMap<unsigned, GlobalFunctionInfo> GlobalFunctions; + + /// Retrieve the ID for the given identifier. + IdentifierID getIdentifier(StringRef identifier) { + if (identifier.empty()) + return 0; + + auto known = IdentifierIDs.find(identifier); + if (known != IdentifierIDs.end()) + return known->second; + + // Add to the identifier table. + known = IdentifierIDs.insert({identifier, IdentifierIDs.size() + 1}).first; + return known->second; + } + + /// Retrieve the ID for the given selector. + SelectorID getSelector(ObjCSelectorRef selectorRef) { + // Translate the selector reference into a stored selector. + StoredObjCSelector selector; + selector.NumPieces = selectorRef.NumPieces; + selector.Identifiers.reserve(selectorRef.Identifiers.size()); + for (auto piece : selectorRef.Identifiers) { + selector.Identifiers.push_back(getIdentifier(piece)); + } + + // Look for the stored selector. + auto known = SelectorIDs.find(selector); + if (known != SelectorIDs.end()) + return known->second; + + // Add to the selector table. + known = SelectorIDs.insert({selector, SelectorIDs.size()}).first; + return known->second; + } + + void writeToStream(llvm::raw_ostream &os); + +private: + void writeBlockInfoBlock(llvm::BitstreamWriter &writer); + void writeControlBlock(llvm::BitstreamWriter &writer); + void writeIdentifierBlock(llvm::BitstreamWriter &writer); + void writeObjCContextBlock(llvm::BitstreamWriter &writer); + void writeObjCPropertyBlock(llvm::BitstreamWriter &writer); + void writeObjCMethodBlock(llvm::BitstreamWriter &writer); + void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); + void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); + void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); +}; + +/// Record the name of a block. +static void emitBlockID(llvm::BitstreamWriter &out, unsigned ID, + StringRef name, + SmallVectorImpl<unsigned char> &nameBuffer) { + SmallVector<unsigned, 1> idBuffer; + idBuffer.push_back(ID); + out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, idBuffer); + + // Emit the block name if present. + if (name.empty()) + return; + nameBuffer.resize(name.size()); + memcpy(nameBuffer.data(), name.data(), name.size()); + out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, nameBuffer); +} + +/// Record the name of a record within a block. +static void emitRecordID(llvm::BitstreamWriter &out, unsigned ID, + StringRef name, + SmallVectorImpl<unsigned char> &nameBuffer) { + assert(ID < 256 && "can't fit record ID in next to name"); + nameBuffer.resize(name.size()+1); + nameBuffer[0] = ID; + memcpy(nameBuffer.data()+1, name.data(), name.size()); + out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, nameBuffer); +} + +void APINotesWriter::Implementation::writeBlockInfoBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, llvm::bitc::BLOCKINFO_BLOCK_ID, 2); + + SmallVector<unsigned char, 64> nameBuffer; +#define BLOCK(X) emitBlockID(writer, X ## _ID, #X, nameBuffer) +#define BLOCK_RECORD(K, X) emitRecordID(writer, K::X, #X, nameBuffer) + + BLOCK(CONTROL_BLOCK); + BLOCK_RECORD(control_block, METADATA); + BLOCK_RECORD(control_block, MODULE_NAME); + + BLOCK(IDENTIFIER_BLOCK); + BLOCK_RECORD(identifier_block, IDENTIFIER_DATA); + + BLOCK(OBJC_CONTEXT_BLOCK); + BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_DATA); + + BLOCK(OBJC_PROPERTY_BLOCK); + BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA); + + BLOCK(OBJC_METHOD_BLOCK); + BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA); + + BLOCK(OBJC_SELECTOR_BLOCK); + BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA); + + BLOCK(GLOBAL_VARIABLE_BLOCK); + BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA); + + BLOCK(GLOBAL_FUNCTION_BLOCK); + BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA); +#undef BLOCK +#undef BLOCK_RECORD +} + +void APINotesWriter::Implementation::writeControlBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, CONTROL_BLOCK_ID, 3); + control_block::MetadataLayout metadata(writer); + metadata.emit(ScratchRecord, VERSION_MAJOR, VERSION_MINOR); + + control_block::ModuleNameLayout moduleName(writer); + moduleName.emit(ScratchRecord, ModuleName); +} + +namespace { + /// Used to serialize the on-disk identifier table. + class IdentifierTableInfo { + public: + using key_type = StringRef; + using key_type_ref = key_type; + using data_type = IdentifierID; + using data_type_ref = const data_type &; + using hash_value_type = uint32_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return llvm::HashString(key); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = key.size(); + uint32_t dataLength = sizeof(uint32_t); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + out << key; + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + static_assert(sizeof(IdentifierID) <= 4, "DeclID too large"); + endian::Writer<little> writer(out); + writer.write<uint32_t>(data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeIdentifierBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, IDENTIFIER_BLOCK_ID, 3); + + if (IdentifierIDs.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<IdentifierTableInfo> generator; + for (auto &entry : IdentifierIDs) + generator.insert(entry.first(), entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + identifier_block::IdentifierDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Retrieve the serialized size of the given CommonEntityInfo, for use in + /// on-disk hash tables. + static unsigned getCommonEntityInfoSize(const CommonEntityInfo &info) { + return 3 + info.UnavailableMsg.size(); + } + + /// Emit a serialized representation of the common entity information. + static void emitCommonEntityInfo(raw_ostream &out, + const CommonEntityInfo &info) { + endian::Writer<little> writer(out); + writer.write<uint8_t>(info.Unavailable); + writer.write<uint16_t>(info.UnavailableMsg.size()); + out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); + } + + /// Used to serialize the on-disk Objective-C context table. + class ObjCContextTableInfo { + public: + using key_type = std::pair<unsigned, char>; // identifier ID, is-protocol + using key_type_ref = key_type; + using data_type = std::pair<unsigned, ObjCContextInfo>; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + /// The number of bytes in a data entry. + static const unsigned dataBytes = 3; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID) + 1; + uint32_t dataLength = sizeof(ContextID) + + getCommonEntityInfoSize(data.second) + + dataBytes; + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<IdentifierID>(key.first); + writer.write<uint8_t>(key.second); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + endian::Writer<little> writer(out); + writer.write<StoredContextID >(data.first); + + emitCommonEntityInfo(out, data.second); + + // FIXME: Inefficient representation. + uint8_t bytes[dataBytes] = { 0, 0, 0 }; + if (auto nullable = data.second.getDefaultNullability()) { + bytes[0] = 1; + bytes[1] = static_cast<uint8_t>(*nullable); + } else { + // Nothing to do. + } + bytes[2] = data.second.hasDesignatedInits(); + + out.write(reinterpret_cast<const char *>(bytes), dataBytes); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeObjCContextBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3); + + if (ObjCContexts.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<ObjCContextTableInfo> generator; + for (auto &entry : ObjCContexts) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Retrieve the serialized size of the given VariableInfo, for use in + /// on-disk hash tables. + static unsigned getVariableInfoSize(const VariableInfo &info) { + return 2 + getCommonEntityInfoSize(info); + } + + /// Emit a serialized representation of the variable information. + static void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { + emitCommonEntityInfo(out, info); + + uint8_t bytes[2] = { 0, 0 }; + if (auto nullable = info.getNullability()) { + bytes[0] = 1; + bytes[1] = static_cast<uint8_t>(*nullable); + } else { + // Nothing to do. + } + + out.write(reinterpret_cast<const char *>(bytes), 2); + } + + /// Used to serialize the on-disk Objective-C property table. + class ObjCPropertyTableInfo { + public: + using key_type = std::pair<unsigned, unsigned>; // (class ID, name ID) + using key_type_ref = key_type; + using data_type = ObjCPropertyInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID) + sizeof(IdentifierID); + uint32_t dataLength = getVariableInfoSize(data); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<IdentifierID>(key.first); + writer.write<IdentifierID>(key.second); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitVariableInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeObjCPropertyBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, OBJC_PROPERTY_BLOCK_ID, 3); + + if (ObjCProperties.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<ObjCPropertyTableInfo> generator; + for (auto &entry : ObjCProperties) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + objc_property_block::ObjCPropertyDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Retrieve the serialized size of the given FunctionInfo, for use in + /// on-disk hash tables. + static unsigned getFunctionInfoSize(const FunctionInfo &info) { + return 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info); + } + + /// Emit a serialized representation of the function information. + static void emitFunctionInfo(raw_ostream &out, const FunctionInfo &info) { + emitCommonEntityInfo(out, info); + + endian::Writer<little> writer(out); + writer.write<uint8_t>(info.NullabilityAudited); + writer.write<uint8_t>(info.NumAdjustedNullable); + writer.write<uint64_t>(info.NullabilityPayload); + } + + /// Used to serialize the on-disk Objective-C method table. + class ObjCMethodTableInfo { + public: + // (class ID, selector ID, is-instance) + using key_type = std::tuple<unsigned, unsigned, char>; + using key_type_ref = key_type; + using data_type = ObjCMethodInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return llvm::hash_combine(std::get<0>(key), + std::get<1>(key), + std::get<2>(key)); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID) + sizeof(SelectorID) + 1; + uint32_t dataLength = getFunctionInfoSize(data) + 3; + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<IdentifierID>(std::get<0>(key)); + writer.write<SelectorID>(std::get<1>(key)); + writer.write<uint8_t>(std::get<2>(key)); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitFunctionInfo(out, data); + + endian::Writer<little> writer(out); + + // FIXME: Inefficient representation + writer.write<uint8_t>(data.DesignatedInit); + writer.write<uint8_t>(data.FactoryAsInit); + writer.write<uint8_t>(data.Required); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeObjCMethodBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, OBJC_METHOD_BLOCK_ID, 3); + + if (ObjCMethods.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<ObjCMethodTableInfo> generator; + for (auto &entry : ObjCMethods) { + generator.insert(entry.first, entry.second); + } + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + objc_method_block::ObjCMethodDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Used to serialize the on-disk Objective-C selector table. + class ObjCSelectorTableInfo { + public: + using key_type = StoredObjCSelector; + using key_type_ref = const key_type &; + using data_type = SelectorID; + using data_type_ref = data_type; + using hash_value_type = unsigned; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(key); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(uint16_t) + + sizeof(IdentifierID) * key.Identifiers.size(); + uint32_t dataLength = sizeof(SelectorID); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<uint16_t>(key.NumPieces); + for (auto piece : key.Identifiers) { + writer.write<IdentifierID>(piece); + } + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + endian::Writer<little> writer(out); + writer.write<SelectorID>(data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeObjCSelectorBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, OBJC_SELECTOR_BLOCK_ID, 3); + + if (SelectorIDs.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<ObjCSelectorTableInfo> generator; + for (auto &entry : SelectorIDs) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + objc_selector_block::ObjCSelectorDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Used to serialize the on-disk global variable table. + class GlobalVariableTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = GlobalVariableInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getVariableInfoSize(data); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<IdentifierID>(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitVariableInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeGlobalVariableBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, GLOBAL_VARIABLE_BLOCK_ID, 3); + + if (GlobalVariables.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<GlobalVariableTableInfo> generator; + for (auto &entry : GlobalVariables) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + global_variable_block::GlobalVariableDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Used to serialize the on-disk global function table. + class GlobalFunctionTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = GlobalFunctionInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return llvm::hash_value(key); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getFunctionInfoSize(data); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<IdentifierID>(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitFunctionInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeGlobalFunctionBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, GLOBAL_FUNCTION_BLOCK_ID, 3); + + if (GlobalFunctions.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<GlobalFunctionTableInfo> generator; + for (auto &entry : GlobalFunctions) { + generator.insert(entry.first, entry.second); + } + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + global_function_block::GlobalFunctionDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { + // Write the API notes file into a buffer. + SmallVector<char, 0> buffer; + { + llvm::BitstreamWriter writer(buffer); + + // Emit the signature. + for (unsigned char byte : API_NOTES_SIGNATURE) + writer.Emit(byte, 8); + + // Emit the blocks. + writeBlockInfoBlock(writer); + writeControlBlock(writer); + writeIdentifierBlock(writer); + writeObjCContextBlock(writer); + writeObjCPropertyBlock(writer); + writeObjCMethodBlock(writer); + writeObjCSelectorBlock(writer); + writeGlobalVariableBlock(writer); + writeGlobalFunctionBlock(writer); + } + + // Write the buffer to the stream. + os.write(buffer.data(), buffer.size()); + os.flush(); +} + +APINotesWriter::APINotesWriter(StringRef moduleName) + : Impl(*new Implementation) +{ + Impl.ModuleName = moduleName; +} + +APINotesWriter::~APINotesWriter() { + delete &Impl; +} + + +void APINotesWriter::writeToStream(raw_ostream &os) { + Impl.writeToStream(os); +} + +ContextID APINotesWriter::addObjCClass(StringRef name, + const ObjCContextInfo &info) { + IdentifierID classID = Impl.getIdentifier(name); + + std::pair<unsigned, char> key(classID, 0); + auto known = Impl.ObjCContexts.find(key); + if (known != Impl.ObjCContexts.end()) { + known->second.second |= info; + } else { + unsigned nextID = Impl.ObjCContexts.size() + 1; + + known = Impl.ObjCContexts.insert( + std::make_pair(key, std::make_pair(nextID, info))) + .first; + + Impl.ObjCContextNames[nextID] = classID; + } + + return ContextID(known->second.first); +} + +ContextID APINotesWriter::addObjCProtocol(StringRef name, + const ObjCContextInfo &info) { + IdentifierID protocolID = Impl.getIdentifier(name); + + std::pair<unsigned, char> key(protocolID, 1); + auto known = Impl.ObjCContexts.find(key); + if (known != Impl.ObjCContexts.end()) { + known->second.second |= info; + } else { + unsigned nextID = Impl.ObjCContexts.size() + 1; + + known = Impl.ObjCContexts.insert( + std::make_pair(key, std::make_pair(nextID, info))) + .first; + + Impl.ObjCContextNames[nextID] = protocolID; + } + + return ContextID(known->second.first); +} +void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name, + const ObjCPropertyInfo &info) { + IdentifierID nameID = Impl.getIdentifier(name); + assert(!Impl.ObjCProperties.count({contextID.Value, nameID})); + Impl.ObjCProperties[{contextID.Value, nameID}] = info; +} + +void APINotesWriter::addObjCMethod(ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod, + const ObjCMethodInfo &info) { + SelectorID selectorID = Impl.getSelector(selector); + auto key = std::tuple<unsigned, unsigned, char>{ + contextID.Value, selectorID, isInstanceMethod}; + assert(!Impl.ObjCMethods.count(key)); + Impl.ObjCMethods[key] = info; + + // If this method is a designated initializer, update the class to note that + // it has designated initializers. + if (info.DesignatedInit) { + assert(Impl.ObjCContexts.count({Impl.ObjCContextNames[contextID.Value], + (char)0})); + Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}] + .second.setHasDesignatedInits(true); + } +} + +void APINotesWriter::addGlobalVariable(llvm::StringRef name, + const GlobalVariableInfo &info) { + IdentifierID variableID = Impl.getIdentifier(name); + assert(!Impl.GlobalVariables.count(variableID)); + Impl.GlobalVariables[variableID] = info; +} + +void APINotesWriter::addGlobalFunction(llvm::StringRef name, + const GlobalFunctionInfo &info) { + IdentifierID nameID = Impl.getIdentifier(name); + assert(!Impl.GlobalFunctions.count(nameID)); + Impl.GlobalFunctions[nameID] = info; +} diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp new file mode 100644 index 0000000000000..1f299873ce31c --- /dev/null +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -0,0 +1,884 @@ +//===--- APINotesYAMLCompiler.cpp - API Notes YAML format reader *- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file reads API notes specified in YAML format. +// +//===----------------------------------------------------------------------===// +#include "clang/APINotes/APINotesYAMLCompiler.h" +#include "clang/APINotes/APINotesReader.h" +#include "clang/APINotes/Types.h" +#include "clang/APINotes/APINotesWriter.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include <algorithm> + +/* + + YAML Format specification. + + Nullability should be expressed using one of the following values: + O - Optional (or Nullable) + N - Not Optional + S - Scalar + U - Unknown + Note, the API is considered 'audited' when at least the return value or a + parameter has a nullability value. For 'audited' APIs, we assume the default + nullability for any underspecified type. + + FactoryAsInit can have the following values: + C - Treat as class method. + I - Treat as initializer. + A - Automatically infer based on the name and type (default). + +--- + Name: AppKit # The name of the framework + + Availability: OSX # Optional: Specifies which platform the API is + # available on. [OSX / iOS / none/ + # available] + + AvailabilityMsg: "" # Optional: Custom availability message to display to + # the user, when API is not available. + + Classes: # List of classes + ... + Protocols: # List of protocols + ... + Functions: # List of functions + ... + Globals: # List of globals + ... + + Each class and protocol is defined as following: + + - Name: NSView # The name of the class + + AuditedForNullability: false # Optional: Specifies if the whole class + # has been audited for nullability. + # If yes, we assume all the methods and + # properties of the class have default + # nullability unless it is overwritten by + # a method/property specific info below. + # This applies to all classes, extensions, + # and categories of the class defined in + # the current framework/module. + # (false/true) + + Availability: OSX + + AvailabilityMsg: "" + + Methods: + - Selector: "setSubviews:" # Full name + + MethodKind: Instance # [Class/Instance] + + Nullability: [N, N, O, S] # The nullability of parameters in + # the signature. + + NullabilityOfRet: O # The nullability of the return value. + + Availability: OSX + + AvailabilityMsg: "" + + FactoryAsInit: C # Optional: Specifies if this method is a + # factory initializer (false/true) + DesignatedInit: false # Optional: Specifies if this method is a + # designated initializer (false/true) + + Required: false # Optional: Specifies if this method is a + # required initializer (false/true) + + Properties: + - Name: window + + Nullability: O + + Availability: OSX + + AvailabilityMsg: "" + + The protocol definition format is the same as the class definition. + + Each function definition is of the following form: + + - Name: "myGlobalFunction" # Full name + + Nullability: [N, N, O, S] # The nullability of parameters in + # the signature. + + NullabilityOfRet: O # The nullability of the return value. + + Availability: OSX + + AvailabilityMsg: "" + +Each global variable definition is of the following form: + + - Name: MyGlobalVar + + Nullability: O + + Availability: OSX + + AvailabilityMsg: "" + +*/ + +using llvm::StringRef; +using namespace clang; +namespace { + enum class APIAvailability { + Available = 0, + OSX, + IOS, + None, + }; + + enum class MethodKind { + Class, + Instance, + }; + + struct AvailabilityItem { + APIAvailability Mode = APIAvailability::Available; + StringRef Msg; + AvailabilityItem() : Mode(APIAvailability::Available), Msg("") {} + }; + + static llvm::Optional<NullabilityKind> AbsentNullability = llvm::None; + static llvm::Optional<NullabilityKind> DefaultNullability = + NullabilityKind::NonNull; + typedef std::vector<clang::NullabilityKind> NullabilitySeq; + + struct Method { + StringRef Selector; + MethodKind Kind; + NullabilitySeq Nullability; + llvm::Optional<NullabilityKind> NullabilityOfRet; + AvailabilityItem Availability; + api_notes::FactoryAsInitKind FactoryAsInit + = api_notes::FactoryAsInitKind::Infer; + bool DesignatedInit = false; + bool Required = false; + }; + typedef std::vector<Method> MethodsSeq; + + struct Property { + StringRef Name; + llvm::Optional<NullabilityKind> Nullability; + AvailabilityItem Availability; + }; + typedef std::vector<Property> PropertiesSeq; + + struct Class { + StringRef Name; + bool AuditedForNullability = false; + AvailabilityItem Availability; + MethodsSeq Methods; + PropertiesSeq Properties; + }; + typedef std::vector<Class> ClassesSeq; + + struct Function { + StringRef Name; + NullabilitySeq Nullability; + llvm::Optional<NullabilityKind> NullabilityOfRet; + AvailabilityItem Availability; + }; + typedef std::vector<Function> FunctionsSeq; + + struct GlobalVariable { + StringRef Name; + llvm::Optional<NullabilityKind> Nullability; + AvailabilityItem Availability; + }; + typedef std::vector<GlobalVariable> GlobalVariablesSeq; + + struct Module { + StringRef Name; + AvailabilityItem Availability; + ClassesSeq Classes; + ClassesSeq Protocols; + FunctionsSeq Functions; + GlobalVariablesSeq Globals; + + LLVM_ATTRIBUTE_DEPRECATED( + void dump() LLVM_ATTRIBUTE_USED, + "only for use within the debugger"); + }; +} + +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(clang::NullabilityKind) +LLVM_YAML_IS_SEQUENCE_VECTOR(Method) +LLVM_YAML_IS_SEQUENCE_VECTOR(Property) +LLVM_YAML_IS_SEQUENCE_VECTOR(Class) +LLVM_YAML_IS_SEQUENCE_VECTOR(Function) +LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) + +namespace llvm { + namespace yaml { + + template <> + struct ScalarEnumerationTraits<NullabilityKind > { + static void enumeration(IO &io, NullabilityKind &value) { + io.enumCase(value, "N", NullabilityKind::NonNull); + io.enumCase(value, "O", NullabilityKind::Nullable); + io.enumCase(value, "U", NullabilityKind::Unspecified); + // TODO: Mapping this to it's own value would allow for better cross + // checking. Also the default should be Unknown. + io.enumCase(value, "S", NullabilityKind::Unspecified); + } + }; + + template <> + struct ScalarEnumerationTraits<api_notes::FactoryAsInitKind > { + static void enumeration(IO &io, api_notes::FactoryAsInitKind &value) { + io.enumCase(value, "A", api_notes::FactoryAsInitKind::Infer); + io.enumCase(value, "C", api_notes::FactoryAsInitKind::AsClassMethod); + io.enumCase(value, "I", api_notes::FactoryAsInitKind::AsInitializer); + } + }; + + template <> + struct ScalarEnumerationTraits<MethodKind> { + static void enumeration(IO &io, MethodKind &value) { + io.enumCase(value, "Class", MethodKind::Class); + io.enumCase(value, "Instance", MethodKind::Instance); + } + }; + + template <> + struct ScalarEnumerationTraits<APIAvailability> { + static void enumeration(IO &io, APIAvailability &value) { + io.enumCase(value, "OSX", APIAvailability::OSX); + io.enumCase(value, "iOS", APIAvailability::IOS); + io.enumCase(value, "none", APIAvailability::None); + io.enumCase(value, "available", APIAvailability::Available); + } + }; + + template <> + struct MappingTraits<Property> { + static void mapping(IO &io, Property& p) { + io.mapRequired("Name", p.Name); + io.mapOptional("Nullability", p.Nullability, + AbsentNullability); + io.mapOptional("Availability", p.Availability.Mode); + io.mapOptional("AvailabilityMsg", p.Availability.Msg); + } + }; + + template <> + struct MappingTraits<Method> { + static void mapping(IO &io, Method& m) { + io.mapRequired("Selector", m.Selector); + io.mapRequired("MethodKind", m.Kind); + io.mapOptional("Nullability", m.Nullability); + io.mapOptional("NullabilityOfRet", m.NullabilityOfRet, + AbsentNullability); + io.mapOptional("Availability", m.Availability.Mode); + io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("FactoryAsInit", m.FactoryAsInit, + api_notes::FactoryAsInitKind::Infer); + io.mapOptional("DesignatedInit", m.DesignatedInit, false); + io.mapOptional("Required", m.Required, false); + } + }; + + template <> + struct MappingTraits<Class> { + static void mapping(IO &io, Class& c) { + io.mapRequired("Name", c.Name); + io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); + io.mapOptional("Availability", c.Availability.Mode); + io.mapOptional("AvailabilityMsg", c.Availability.Msg); + io.mapOptional("Methods", c.Methods); + io.mapOptional("Properties", c.Properties); + } + }; + + template <> + struct MappingTraits<Function> { + static void mapping(IO &io, Function& f) { + io.mapRequired("Name", f.Name); + io.mapOptional("Nullability", f.Nullability); + io.mapOptional("NullabilityOfRet", f.NullabilityOfRet, + AbsentNullability); + io.mapOptional("Availability", f.Availability.Mode); + io.mapOptional("AvailabilityMsg", f.Availability.Msg); + } + }; + + template <> + struct MappingTraits<GlobalVariable> { + static void mapping(IO &io, GlobalVariable& v) { + io.mapRequired("Name", v.Name); + io.mapOptional("Nullability", v.Nullability, + AbsentNullability); + io.mapOptional("Availability", v.Availability.Mode); + io.mapOptional("AvailabilityMsg", v.Availability.Msg); + } + }; + + template <> + struct MappingTraits<Module> { + static void mapping(IO &io, Module& m) { + io.mapRequired("Name", m.Name); + io.mapOptional("Availability", m.Availability.Mode); + io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("Classes", m.Classes); + io.mapOptional("Protocols", m.Protocols); + io.mapOptional("Functions", m.Functions); + io.mapOptional("Globals", m.Globals); + } + }; + } +} + +using llvm::yaml::Input; +using llvm::yaml::Output; + +void Module::dump() { + Output yout(llvm::errs()); + yout << *this; +} + +static bool parseAPINotes(StringRef yamlInput, Module &module, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt) { + Input yin(yamlInput, nullptr, diagHandler, diagHandlerCtxt); + yin >> module; + + return static_cast<bool>(yin.error()); +} + +static bool compile(const Module &module, + llvm::raw_ostream &os, + api_notes::OSType targetOS, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt){ + using namespace api_notes; + + class YAMLConverter { + const Module &TheModule; + APINotesWriter *Writer; + OSType TargetOS; + llvm::raw_ostream &OS; + llvm::SourceMgr::DiagHandlerTy DiagHandler; + void *DiagHandlerCtxt; + bool ErrorOccured; + + /// Emit a diagnostic + bool emitError(llvm::Twine message) { + DiagHandler(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, + message.str()), + DiagHandlerCtxt); + ErrorOccured = true; + return true; + } + + public: + YAMLConverter(const Module &module, + OSType targetOS, + llvm::raw_ostream &os, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt) : + TheModule(module), Writer(0), TargetOS(targetOS), OS(os), + DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt), + ErrorOccured(false) {} + + bool isAvailable(const AvailabilityItem &in) { + // Check if the API is available on the OS for which we are building. + if (in.Mode == APIAvailability::OSX && TargetOS != OSType::OSX) + return false; + if (in.Mode == APIAvailability::IOS && TargetOS != OSType::IOS) + return false; + return true; + } + + bool convertAvailability(const AvailabilityItem &in, + CommonEntityInfo &outInfo, + llvm::StringRef apiName) { + // Populate the 'Unavailable' information. + outInfo.Unavailable = (in.Mode == APIAvailability::None); + if (outInfo.Unavailable) { + outInfo.UnavailableMsg = in.Msg; + } else { + if (!in.Msg.empty()) { + emitError("availability message for available class '" + + apiName + "' will not be used"); + } + } + return false; + } + + void convertNullability(const NullabilitySeq &nullability, + Optional<NullabilityKind> nullabilityOfRet, + FunctionInfo &outInfo, + llvm::StringRef apiName) { + if (nullability.size() > FunctionInfo::getMaxNullabilityIndex()) { + emitError("nullability info for " + apiName + " does not fit"); + return; + } + + bool audited = false; + unsigned int idx = 1; + for (auto i = nullability.begin(), + e = nullability.end(); i != e; ++i, ++idx){ + outInfo.addTypeInfo(idx, *i); + audited = true; + } + if (nullabilityOfRet) { + outInfo.addTypeInfo(0, *nullabilityOfRet); + audited = true; + } else if (audited) { + outInfo.addTypeInfo(0, *DefaultNullability); + } + if (audited) { + outInfo.NullabilityAudited = audited; + outInfo.NumAdjustedNullable = idx; + } + } + + // Translate from Method into ObjCMethodInfo and write it out. + void convertMethod(const Method &meth, + ContextID classID, StringRef className) { + ObjCMethodInfo mInfo; + + if (!isAvailable(meth.Availability)) + return; + + convertAvailability(meth.Availability, mInfo, meth.Selector); + + // Check if the selector ends with ':' to determine if it takes arguments. + bool takesArguments = meth.Selector.endswith(":"); + + // Split the selector into pieces. + llvm::SmallVector<StringRef, 4> a; + meth.Selector.split(a, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false); + if (!takesArguments && a.size() > 1 ) { + emitError("selector " + meth.Selector + "is missing a ':' at the end"); + return; + } + + // Construct ObjCSelectorRef. + api_notes::ObjCSelectorRef selectorRef; + selectorRef.NumPieces = !takesArguments ? 0 : a.size(); + selectorRef.Identifiers = a; + + // Translate the initializer info. + mInfo.DesignatedInit = meth.DesignatedInit; + mInfo.Required = meth.Required; + if (meth.FactoryAsInit != FactoryAsInitKind::Infer) + mInfo.setFactoryAsInitKind(meth.FactoryAsInit); + + // Translate nullability info. + convertNullability(meth.Nullability, meth.NullabilityOfRet, + mInfo, meth.Selector); + + // Write it. + Writer->addObjCMethod(classID, selectorRef, + meth.Kind == MethodKind::Instance, + mInfo); + } + + void convertContext(const Class &cl, bool isClass) { + // Write the class. + ObjCContextInfo cInfo; + + // First, translate and check availability info. + if (!isAvailable(cl.Availability)) + return; + + convertAvailability(cl.Availability, cInfo, cl.Name); + + if (cl.AuditedForNullability) + cInfo.setDefaultNullability(*DefaultNullability); + + ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : + Writer->addObjCProtocol(cl.Name, cInfo); + + // Write all methods. + llvm::StringMap<std::pair<bool, bool>> knownMethods; + for (const auto &method : cl.Methods) { + // Check for duplicate method definitions. + bool isInstanceMethod = method.Kind == MethodKind::Instance; + bool &known = isInstanceMethod ? knownMethods[method.Selector].first + : knownMethods[method.Selector].second; + if (known) { + emitError(llvm::Twine("duplicate definition of method '") + + (isInstanceMethod? "-" : "+") + "[" + cl.Name + " " + + method.Selector + "]'"); + continue; + } + known = true; + + convertMethod(method, clID, cl.Name); + } + + // Write all properties. + llvm::StringSet<> knownProperties; + for (const auto &prop : cl.Properties) { + // Check for duplicate property definitions. + if (!knownProperties.insert(prop.Name).second) { + emitError("duplicate definition of property '" + cl.Name + "." + + prop.Name + "'"); + continue; + } + + // Translate from Property into ObjCPropertyInfo. + ObjCPropertyInfo pInfo; + if (!isAvailable(prop.Availability)) + continue; + convertAvailability(prop.Availability, pInfo, prop.Name); + if (prop.Nullability) + pInfo.setNullabilityAudited(*prop.Nullability); + Writer->addObjCProperty(clID, prop.Name, pInfo); + } + } + + bool convertModule() { + if (!isAvailable(TheModule.Availability)) + return false; + + // Set up the writer. + // FIXME: This is kindof ugly. + APINotesWriter writer(TheModule.Name); + Writer = &writer; + + // Write all classes. + llvm::StringSet<> knownClasses; + for (const auto &cl : TheModule.Classes) { + // Check for duplicate class definitions. + if (!knownClasses.insert(cl.Name).second) { + emitError("multiple definitions of class '" + cl.Name + "'"); + continue; + } + + convertContext(cl, /*isClass*/ true); + } + + // Write all protocols. + llvm::StringSet<> knownProtocols; + for (const auto &pr : TheModule.Protocols) { + // Check for duplicate protocol definitions. + if (!knownProtocols.insert(pr.Name).second) { + emitError("multiple definitions of protocol '" + pr.Name + "'"); + continue; + } + + convertContext(pr, /*isClass*/ false); + } + + // Write all global variables. + llvm::StringSet<> knownGlobals; + for (const auto &global : TheModule.Globals) { + // Check for duplicate global variables. + if (!knownGlobals.insert(global.Name).second) { + emitError("multiple definitions of global variable '" + + global.Name + "'"); + continue; + } + + GlobalVariableInfo info; + if (!isAvailable(global.Availability)) + continue; + convertAvailability(global.Availability, info, global.Name); + if (global.Nullability) + info.setNullabilityAudited(*global.Nullability); + Writer->addGlobalVariable(global.Name, info); + } + + // Write all global functions. + llvm::StringSet<> knownFunctions; + for (const auto &function : TheModule.Functions) { + // Check for duplicate global functions. + if (!knownFunctions.insert(function.Name).second) { + emitError("multiple definitions of global function '" + + function.Name + "'"); + continue; + } + + GlobalFunctionInfo info; + if (!isAvailable(function.Availability)) + continue; + convertAvailability(function.Availability, info, function.Name); + convertNullability(function.Nullability, + function.NullabilityOfRet, + info, function.Name); + + Writer->addGlobalFunction(function.Name, info); + } + + if (!ErrorOccured) + Writer->writeToStream(OS); + + return ErrorOccured; + } + }; + + YAMLConverter c(module, targetOS, os, diagHandler, diagHandlerCtxt); + return c.convertModule(); +} + +bool api_notes::parseAndDumpAPINotes(StringRef yamlInput) { + Module module; + + if (parseAPINotes(yamlInput, module, nullptr, nullptr)) + return true; + + Output yout(llvm::outs()); + yout << module; + + return false; +} + +/// Simple diagnostic handler that prints diagnostics to standard error. +static void printDiagnostic(const llvm::SMDiagnostic &diag, void *context) { + diag.print(nullptr, llvm::errs()); +} + +bool api_notes::compileAPINotes(StringRef yamlInput, + llvm::raw_ostream &os, + OSType targetOS, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt) { + Module module; + + if (!diagHandler) { + diagHandler = &printDiagnostic; + } + + if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt)) + return true; + + return compile(module, os, targetOS, diagHandler, diagHandlerCtxt); +} + +bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, + llvm::raw_ostream &os) { + // Try to read the file. + auto reader = APINotesReader::get(std::move(input)); + if (!reader) { + llvm::errs() << "not a well-formed API notes binary file\n"; + return true; + } + + // Deserialize the API notes file into a module. + class DecompileVisitor : public APINotesReader::Visitor { + /// Allocator used to clone those strings that need it. + llvm::BumpPtrAllocator Allocator; + + /// The module we're building. + Module TheModule; + + /// A mapping from context ID to a pair (index, is-protocol) that indicates + /// the index of that class or protocol in the global "classes" or + /// "protocols" list. + llvm::DenseMap<unsigned, std::pair<unsigned, bool>> knownContexts; + + /// Copy a string into allocated memory so it does disappear on us. + StringRef copyString(StringRef string) { + if (string.empty()) return StringRef(); + + void *ptr = Allocator.Allocate(string.size(), 1); + memcpy(ptr, string.data(), string.size()); + return StringRef(reinterpret_cast<const char *>(ptr), string.size()); + } + + /// Map Objective-C context info. + void handleObjCContext(Class &record, StringRef name, + const ObjCContextInfo &info) { + record.Name = name; + + // Handle class information. + handleAvailability(record.Availability, info); + if (info.getDefaultNullability()) { + record.AuditedForNullability = true; + } + } + + /// Map availability information, if present. + void handleAvailability(AvailabilityItem &availability, + const CommonEntityInfo &info) { + if (info.Unavailable) { + availability.Mode = APIAvailability::None; + availability.Msg = copyString(info.UnavailableMsg); + } + } + + /// Map nullability information for a function. + void handleNullability(NullabilitySeq &nullability, + llvm::Optional<NullabilityKind> &nullabilityOfRet, + const FunctionInfo &info, + unsigned numParams) { + if (info.NullabilityAudited) { + nullabilityOfRet = info.getReturnTypeInfo(); + + // Figure out the number of parameters from the selector. + for (unsigned i = 0; i != numParams; ++i) + nullability.push_back(info.getParamTypeInfo(i)); + } + } + + public: + virtual void visitObjCClass(ContextID contextID, StringRef name, + const ObjCContextInfo &info) { + // Record this known context. + knownContexts[contextID.Value] = { TheModule.Classes.size(), false }; + + // Add the class. + TheModule.Classes.push_back(Class()); + handleObjCContext(TheModule.Classes.back(), name, info); + } + + virtual void visitObjCProtocol(ContextID contextID, StringRef name, + const ObjCContextInfo &info) { + // Record this known context. + knownContexts[contextID.Value] = { TheModule.Protocols.size(), true }; + + // Add the protocol. + TheModule.Protocols.push_back(Class()); + handleObjCContext(TheModule.Protocols.back(), name, info); + } + + virtual void visitObjCMethod(ContextID contextID, StringRef selector, + bool isInstanceMethod, + const ObjCMethodInfo &info) { + Method method; + method.Selector = copyString(selector); + method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; + + handleNullability(method.Nullability, method.NullabilityOfRet, info, + selector.count(':')); + handleAvailability(method.Availability, info); + method.FactoryAsInit = info.getFactoryAsInitKind(); + method.DesignatedInit = info.DesignatedInit; + method.Required = info.Required; + + auto known = knownContexts[contextID.Value]; + if (known.second) + TheModule.Protocols[known.first].Methods.push_back(method); + else + TheModule.Classes[known.first].Methods.push_back(method); + } + + virtual void visitObjCProperty(ContextID contextID, StringRef name, + const ObjCPropertyInfo &info) { + Property property; + property.Name = name; + handleAvailability(property.Availability, info); + + // FIXME: No way to represent "not audited for nullability". + if (auto nullability = info.getNullability()) { + property.Nullability = *nullability; + } + + auto known = knownContexts[contextID.Value]; + if (known.second) + TheModule.Protocols[known.first].Properties.push_back(property); + else + TheModule.Classes[known.first].Properties.push_back(property); + } + + virtual void visitGlobalFunction(StringRef name, + const GlobalFunctionInfo &info) { + Function function; + function.Name = name; + handleAvailability(function.Availability, info); + if (info.NumAdjustedNullable > 0) + handleNullability(function.Nullability, function.NullabilityOfRet, + info, info.NumAdjustedNullable-1); + + TheModule.Functions.push_back(function); + } + + virtual void visitGlobalVariable(StringRef name, + const GlobalVariableInfo &info) { + GlobalVariable global; + global.Name = name; + handleAvailability(global.Availability, info); + + // FIXME: No way to represent "not audited for nullability". + if (auto nullability = info.getNullability()) { + global.Nullability = *nullability; + } + + TheModule.Globals.push_back(global); + } + + /// Retrieve the module. + Module &getModule() { return TheModule; } + } decompileVisitor; + + reader->visit(decompileVisitor); + + // Sort the data in the module, because the API notes reader doesn't preserve + // order. + auto &module = decompileVisitor.getModule(); + + // Set module name. + module.Name = reader->getModuleName(); + + // Sort classes. + std::sort(module.Classes.begin(), module.Classes.end(), + [](const Class &lhs, const Class &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Sort protocols. + std::sort(module.Protocols.begin(), module.Protocols.end(), + [](const Class &lhs, const Class &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Sort methods and properties within each class and protocol. + auto sortMembers = [](Class &record) { + // Sort properties. + std::sort(record.Properties.begin(), record.Properties.end(), + [](const Property &lhs, const Property &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Sort methods. + std::sort(record.Methods.begin(), record.Methods.end(), + [](const Method &lhs, const Method &rhs) -> bool { + return lhs.Selector < rhs.Selector || + (lhs.Selector == rhs.Selector && + static_cast<unsigned>(lhs.Kind) + < static_cast<unsigned>(rhs.Kind)); + }); + }; + std::for_each(module.Classes.begin(), module.Classes.end(), sortMembers); + std::for_each(module.Protocols.begin(), module.Protocols.end(), sortMembers); + + // Sort functions. + std::sort(module.Functions.begin(), module.Functions.end(), + [](const Function &lhs, const Function &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Sort global variables. + std::sort(module.Globals.begin(), module.Globals.end(), + [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Output the YAML representation. + Output yout(os); + yout << module; + + return false; +} + diff --git a/clang/lib/APINotes/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt new file mode 100644 index 0000000000000..da9d0d1e55078 --- /dev/null +++ b/clang/lib/APINotes/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS + BitReader + Support + ) + +add_clang_library(clangAPINotes + APINotesManager.cpp + APINotesWriter.cpp + APINotesReader.cpp + APINotesYAMLCompiler.cpp + Types.cpp + + LINK_LIBS + clangBasic +) diff --git a/clang/lib/APINotes/Makefile b/clang/lib/APINotes/Makefile new file mode 100644 index 0000000000000..69ddcd45b3198 --- /dev/null +++ b/clang/lib/APINotes/Makefile @@ -0,0 +1,18 @@ +##===- clang/lib/APINotes/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements the APINotes library for the C-Language front-end. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangAPINotes + +include $(CLANG_LEVEL)/Makefile + diff --git a/clang/lib/APINotes/Types.cpp b/clang/lib/APINotes/Types.cpp new file mode 100644 index 0000000000000..963780fb6f32c --- /dev/null +++ b/clang/lib/APINotes/Types.cpp @@ -0,0 +1,55 @@ +//===--- Types.cpp - API Notes Data Types ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines data types used in the representation of API notes data. +// +//===----------------------------------------------------------------------===// +#include "clang/APINotes/Types.h" +#include "llvm/Support/raw_ostream.h" + +void clang::api_notes::ObjCMethodInfo::dump(llvm::raw_ostream &os) { + os << DesignatedInit << " " << FactoryAsInit << " " << Unavailable << " " + << NullabilityAudited << " " << NumAdjustedNullable << " " + << NullabilityPayload << " " << UnavailableMsg << "\n"; +} + +void clang::api_notes::ObjCContextInfo::dump(llvm::raw_ostream &os) { + os << HasDefaultNullability << " " << DefaultNullability << " " + << HasDesignatedInits << "\n"; +} + +void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoSetter( + const ObjCPropertyInfo &pInfo) { + // Set the type of the first argument of the the setter or check that the + // value we have is consistent with the property. + // TODO: Can we provide proper error handling here? + if (auto pNullability = pInfo.getNullability()) { + if (!NullabilityAudited) { + addParamTypeInfo(0, *pNullability); + assert(NumAdjustedNullable == 2); + } else { + assert(getParamTypeInfo(0) == *pNullability); + } + } +} + +void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoGetter( + const ObjCPropertyInfo &pInfo) { + // Set the return type of the getter or check that the value we have is + // consistent with the property. + // TODO: Can we provide proper error handling here? + if (auto pNullability = pInfo.getNullability()) { + if (!NullabilityAudited) { + addReturnTypeInfo(*pNullability); + assert(NumAdjustedNullable == 1); + } else { + assert(getReturnTypeInfo() == *pNullability); + } + } +} diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index fd6df024bb850..3da2546a7d8c8 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -371,11 +371,14 @@ bool Decl::isReferenced() const { /// /// FIXME: Make these strings localizable, since they end up in /// diagnostics. -static AvailabilityResult CheckAvailability(ASTContext &Context, - const AvailabilityAttr *A, - std::string *Message) { - VersionTuple TargetMinVersion = - Context.getTargetInfo().getPlatformMinVersion(); +static AvailabilityResult +checkAvailability(ASTContext &Context, const AvailabilityAttr *A, + Optional<VersionTuple> Version, std::string *Message) { + VersionTuple TargetMinVersion; + if (Version.hasValue()) + TargetMinVersion = Version.getValue(); + else + TargetMinVersion = Context.getTargetInfo().getPlatformMinVersion(); if (TargetMinVersion.empty()) return AR_Available; @@ -466,7 +469,8 @@ static AvailabilityResult CheckAvailability(ASTContext &Context, return AR_Available; } -AvailabilityResult Decl::getAvailability(std::string *Message) const { +AvailabilityResult Decl::getAvailability(std::string *Message, + Optional<VersionTuple> Version) const { AvailabilityResult Result = AR_Available; std::string ResultMessage; @@ -489,8 +493,8 @@ AvailabilityResult Decl::getAvailability(std::string *Message) const { } if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) { - AvailabilityResult AR = CheckAvailability(getASTContext(), Availability, - Message); + AvailabilityResult AR = checkAvailability(getASTContext(), Availability, + Version, Message); if (AR == AR_Unavailable) return AR_Unavailable; @@ -549,7 +553,7 @@ bool Decl::isWeakImported() const { return true; if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) { - if (CheckAvailability(getASTContext(), Availability, + if (checkAvailability(getASTContext(), Availability, None, nullptr) == AR_NotYetIntroduced) return true; } diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index cfad8c3649edd..f5e7f74c56c49 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -74,6 +74,7 @@ add_clang_library(clangBasic Sanitizers.cpp SourceLocation.cpp SourceManager.cpp + SourceMgrAdapter.cpp TargetInfo.cpp Targets.cpp TokenKinds.cpp diff --git a/clang/lib/Basic/SourceMgrAdapter.cpp b/clang/lib/Basic/SourceMgrAdapter.cpp new file mode 100644 index 0000000000000..1d52a2438594b --- /dev/null +++ b/clang/lib/Basic/SourceMgrAdapter.cpp @@ -0,0 +1,137 @@ +//=== SourceMgrAdapter.cpp - SourceMgr to SourceManager Adapter -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the adapter that maps diagnostics from llvm::SourceMgr +// to Clang's SourceManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/SourceMgrAdapter.h" +#include "clang/Basic/Diagnostic.h" + +using namespace clang; + +void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &diag, + void *context) { + static_cast<SourceMgrAdapter *>(context)->handleDiag(diag); +} + +SourceMgrAdapter::SourceMgrAdapter(SourceManager &srcMgr, + DiagnosticsEngine &diag, + unsigned errorDiagID, + unsigned warningDiagID, + unsigned noteDiagID, + const FileEntry *defaultFile) + : SrcMgr(srcMgr), Diag(diag), ErrorDiagID(errorDiagID), + WarningDiagID(warningDiagID), NoteDiagID(noteDiagID), + DefaultFile(defaultFile) { } + +SourceMgrAdapter::~SourceMgrAdapter() { } + +SourceLocation SourceMgrAdapter::mapLocation(const llvm::SourceMgr &llvmSrcMgr, + llvm::SMLoc loc) { + // Map invalid locations. + if (!loc.isValid()) + return SourceLocation(); + + // Find the buffer containing the location. + unsigned bufferID = llvmSrcMgr.FindBufferContainingLoc(loc); + if (!bufferID) + return SourceLocation(); + + + // If we haven't seen this buffer before, copy it over. + auto buffer = llvmSrcMgr.getMemoryBuffer(bufferID); + auto knownBuffer = FileIDMapping.find(std::make_pair(&llvmSrcMgr, bufferID)); + if (knownBuffer == FileIDMapping.end()) { + FileID fileID; + if (DefaultFile) { + // Map to the default file. + fileID = SrcMgr.createFileID(DefaultFile, SourceLocation(), + SrcMgr::C_User); + + // Only do this once. + DefaultFile = nullptr; + } else { + // Make a copy of the memory buffer. + StringRef bufferName = buffer->getBufferIdentifier(); + auto bufferCopy + = std::unique_ptr<llvm::MemoryBuffer>( + llvm::MemoryBuffer::getMemBufferCopy(buffer->getBuffer(), + bufferName)); + + // Add this memory buffer to the Clang source manager. + fileID = SrcMgr.createFileID(std::move(bufferCopy)); + } + + // Save the mapping. + knownBuffer = FileIDMapping.insert( + std::make_pair(std::make_pair(&llvmSrcMgr, bufferID), + fileID)).first; + } + + // Translate the offset into the file. + unsigned offset = loc.getPointer() - buffer->getBufferStart(); + return SrcMgr.getLocForStartOfFile(knownBuffer->second) + .getLocWithOffset(offset); +} + +SourceRange SourceMgrAdapter::mapRange(const llvm::SourceMgr &llvmSrcMgr, + llvm::SMRange range) { + if (!range.isValid()) + return SourceRange(); + + SourceLocation start = mapLocation(llvmSrcMgr, range.Start); + SourceLocation end = mapLocation(llvmSrcMgr, range.End); + return SourceRange(start, end); +} + +void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &diag) { + // Map the location. + SourceLocation loc; + if (auto *llvmSrcMgr = diag.getSourceMgr()) + loc = mapLocation(*llvmSrcMgr, diag.getLoc()); + + // Extract the message. + StringRef message = diag.getMessage(); + + // Map the diagnostic kind. + unsigned diagID; + switch (diag.getKind()) { + case llvm::SourceMgr::DK_Error: + diagID = ErrorDiagID; + break; + + case llvm::SourceMgr::DK_Warning: + diagID = WarningDiagID; + break; + + case llvm::SourceMgr::DK_Note: + diagID = NoteDiagID; + break; + } + + // Report the diagnostic. + DiagnosticBuilder builder = Diag.Report(loc, diagID) << message; + + if (auto *llvmSrcMgr = diag.getSourceMgr()) { + // Translate ranges. + SourceLocation startOfLine = loc.getLocWithOffset(-diag.getColumnNo()); + for (auto range : diag.getRanges()) { + builder << SourceRange(startOfLine.getLocWithOffset(range.first), + startOfLine.getLocWithOffset(range.second)); + } + + // Translate Fix-Its. + for (const llvm::SMFixIt &fixIt : diag.getFixIts()) { + CharSourceRange range(mapRange(*llvmSrcMgr, fixIt.getRange()), false); + builder << FixItHint::CreateReplacement(range, fixIt.getText()); + } + } +} diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index dfd819a407e98..574c511747f40 100644 --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(Headers) add_subdirectory(Basic) +add_subdirectory(APINotes) add_subdirectory(Lex) add_subdirectory(Parse) add_subdirectory(AST) diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 1e5db24042987..c9588e679ef88 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -569,8 +569,9 @@ class CGObjCGNU : public CGObjCRuntime { return NULLPtr; } - llvm::GlobalVariable *GetClassGlobal(const std::string &Name, - bool Weak = false) override { + llvm::Constant *GetClassGlobal(const std::string &Name, + bool ForDefinition, + bool Weak) override { return nullptr; } }; diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 4eaa2e51f25db..cff3a77207f30 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -1255,8 +1255,9 @@ class CGObjCMac : public CGObjCCommonMac { /// GetClassGlobal - Return the global variable for the Objective-C /// class of the given name. - llvm::GlobalVariable *GetClassGlobal(const std::string &Name, - bool Weak = false) override { + llvm::Constant *GetClassGlobal(const std::string &Name, + bool ForDefinition, + bool Weak) override { llvm_unreachable("CGObjCMac::GetClassGlobal"); } }; @@ -1357,8 +1358,9 @@ class CGObjCNonFragileABIMac : public CGObjCCommonMac { /// GetClassGlobal - Return the global variable for the Objective-C /// class of the given name. - llvm::GlobalVariable *GetClassGlobal(const std::string &Name, - bool Weak = false) override; + llvm::Constant *GetClassGlobal(const std::string &Name, + bool ForDefinition, + bool Weak) override; /// EmitClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given class reference. @@ -5857,7 +5859,12 @@ llvm::GlobalVariable *CGObjCNonFragileABIMac::BuildClassMetaData( llvm::PointerType::getUnqual(ObjCTypes.ImpnfABITy)); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassnfABITy, Values); - llvm::GlobalVariable *GV = GetClassGlobal(ClassName, Weak); + llvm::GlobalVariable *GV = cast<llvm::GlobalVariable>( + GetClassGlobal(ClassName, + /*ForDefinition=*/true, + Weak)); + if (Init->getType() != GV->getValueType()) + Init = llvm::ConstantExpr::getBitCast(Init, GV->getValueType()); GV->setInitializer(Init); GV->setSection("__DATA, __objc_data"); GV->setAlignment( @@ -5926,7 +5933,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { llvm::SmallString<64> ObjCClassName(getClassSymbolPrefix()); llvm::SmallString<64> TClassName; - llvm::GlobalVariable *SuperClassGV, *IsAGV; + llvm::Constant *SuperClassGV, *IsAGV; // Build the flags for the metaclass. bool classIsHidden = @@ -5948,10 +5955,12 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { TClassName = ObjCClassName; TClassName += ClassName; SuperClassGV = GetClassGlobal(TClassName.str(), + /*ForDefinition=*/false, ID->getClassInterface()->isWeakImported()); TClassName = ObjCMetaClassName; TClassName += ClassName; IsAGV = GetClassGlobal(TClassName.str(), + /*ForDefinition=*/false, ID->getClassInterface()->isWeakImported()); } else { // Has a root. Current class is not a root. @@ -5961,6 +5970,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { TClassName = ObjCMetaClassName ; TClassName += Root->getObjCRuntimeNameAsString(); IsAGV = GetClassGlobal(TClassName.str(), + /*ForDefinition=*/false, Root->isWeakImported()); // work on super class metadata symbol. @@ -5968,6 +5978,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { TClassName += ID->getClassInterface()->getSuperClass()->getObjCRuntimeNameAsString(); SuperClassGV = GetClassGlobal( TClassName.str(), + /*ForDefinition=*/false, ID->getClassInterface()->getSuperClass()->isWeakImported()); } llvm::GlobalVariable *CLASS_RO_GV = BuildClassRoTInitializer(flags, @@ -6010,6 +6021,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { TClassName += ID->getClassInterface()->getSuperClass()->getObjCRuntimeNameAsString(); SuperClassGV = GetClassGlobal( TClassName.str(), + /*ForDefinition=*/false, ID->getClassInterface()->getSuperClass()->isWeakImported()); } GetClassSizeInfo(ID, InstanceStart, InstanceSize); @@ -6102,8 +6114,9 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { llvm::Constant *Values[6]; Values[0] = GetClassName(OCD->getIdentifier()->getName()); // meta-class entry symbol - llvm::GlobalVariable *ClassGV = - GetClassGlobal(ExtClassName.str(), Interface->isWeakImported()); + llvm::Constant *ClassGV = GetClassGlobal(ExtClassName.str(), + /*ForDefinition=*/false, + Interface->isWeakImported()); Values[1] = ClassGV; std::vector<llvm::Constant*> Methods; @@ -6774,8 +6787,10 @@ CGObjCNonFragileABIMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, false, CallArgs, Method, Class, ObjCTypes); } -llvm::GlobalVariable * -CGObjCNonFragileABIMac::GetClassGlobal(const std::string &Name, bool Weak) { +llvm::Constant * +CGObjCNonFragileABIMac::GetClassGlobal(const std::string &Name, + bool ForDefinition, + bool Weak) { llvm::GlobalValue::LinkageTypes L = Weak ? llvm::GlobalValue::ExternalWeakLinkage : llvm::GlobalValue::ExternalLinkage; @@ -6787,7 +6802,12 @@ CGObjCNonFragileABIMac::GetClassGlobal(const std::string &Name, bool Weak) { false, L, nullptr, Name); assert(GV->getLinkage() == L); - return GV; + + if (ForDefinition || + GV->getValueType() == ObjCTypes.ClassnfABITy) + return GV; + + return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ClassnfABIPtrTy); } llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, @@ -6801,7 +6821,9 @@ llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, std::string ClassName( getClassSymbolPrefix() + (ID ? ID->getObjCRuntimeNameAsString() : II->getName()).str()); - llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName, Weak); + llvm::Constant *ClassGV = GetClassGlobal(ClassName, + /*ForDefinition=*/false, + Weak); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, llvm::GlobalValue::PrivateLinkage, ClassGV, "OBJC_CLASSLIST_REFERENCES_$_"); @@ -6832,8 +6854,9 @@ CGObjCNonFragileABIMac::EmitSuperClassRef(CodeGenFunction &CGF, if (!Entry) { llvm::SmallString<64> ClassName(getClassSymbolPrefix()); ClassName += ID->getObjCRuntimeNameAsString(); - llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName.str(), - ID->isWeakImported()); + llvm::Constant *ClassGV = GetClassGlobal(ClassName.str(), + /*ForDefinition=*/false, + ID->isWeakImported()); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, llvm::GlobalValue::PrivateLinkage, ClassGV, "OBJC_CLASSLIST_SUP_REFS_$_"); @@ -6855,8 +6878,9 @@ llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CodeGenFunction &CGF, if (!Entry) { llvm::SmallString<64> MetaClassName(getMetaclassSymbolPrefix()); MetaClassName += ID->getObjCRuntimeNameAsString(); - llvm::GlobalVariable *MetaClassGV = - GetClassGlobal(MetaClassName.str(), Weak); + llvm::Constant *MetaClassGV = GetClassGlobal(MetaClassName.str(), + /*ForDefinition=*/false, + Weak); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, llvm::GlobalValue::PrivateLinkage, @@ -6877,7 +6901,10 @@ llvm::Value *CGObjCNonFragileABIMac::GetClass(CodeGenFunction &CGF, if (ID->isWeakImported()) { llvm::SmallString<64> ClassName(getClassSymbolPrefix()); ClassName += ID->getObjCRuntimeNameAsString(); - llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName.str(), true); + llvm::GlobalVariable *ClassGV = cast<llvm::GlobalVariable>( + GetClassGlobal(ClassName.str(), + /*ForDefinition=*/true, + /*Weak=*/true)); (void)ClassGV; assert(ClassGV->hasExternalWeakLinkage()); } @@ -7179,11 +7206,15 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, llvm::Value *VTableIdx = llvm::ConstantInt::get(CGM.Int32Ty, 2); + llvm::Constant *ClassGV = GetClassGlobal(ClassName.str(), + /*ForDefinition=*/false, + /*Weak=*/false); + llvm::Constant *Values[] = { llvm::ConstantExpr::getGetElementPtr(VTableGV->getValueType(), VTableGV, VTableIdx), GetClassName(ID->getObjCRuntimeNameAsString()), - GetClassGlobal(ClassName.str())}; + ClassGV}; llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.EHTypeTy, Values); diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h index 9b0706770aa90..2c81f49ecefd3 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.h +++ b/clang/lib/CodeGen/CGObjCRuntime.h @@ -277,8 +277,9 @@ class CGObjCRuntime { const CodeGen::CGBlockInfo &blockInfo) = 0; virtual llvm::Constant *BuildByrefLayout(CodeGen::CodeGenModule &CGM, QualType T) = 0; - virtual llvm::GlobalVariable *GetClassGlobal(const std::string &Name, - bool Weak = false) = 0; + virtual llvm::Constant *GetClassGlobal(const std::string &Name, + bool ForDefinition, + bool Weak) = 0; struct MessageSendInfo { const CGFunctionInfo &CallInfo; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 911953437bd15..a607a4ce63f93 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2904,7 +2904,9 @@ CodeGenModule::GetAddrOfConstantString(const StringLiteral *Literal) { std::string str = StringClass.empty() ? "OBJC_CLASS_$_NSConstantString" : "OBJC_CLASS_$_" + StringClass; - GV = getObjCRuntime().GetClassGlobal(str); + GV = getObjCRuntime().GetClassGlobal(str, + /*ForDefinition=*/false, + /*Weak=*/false); // Make sure the result is of the correct type. llvm::Type *PTy = llvm::PointerType::getUnqual(Ty); V = llvm::ConstantExpr::getBitCast(GV, PTy); diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 22904e5398a0b..25ce1a82e35f9 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -72,7 +72,8 @@ static int skipArgs(const char *Flag, bool HaveCrashVFS) { // These flags are treated as a single argument (e.g., -F<Dir>). StringRef FlagRef(Flag); if (FlagRef.startswith("-F") || FlagRef.startswith("-I") || - FlagRef.startswith("-fmodules-cache-path=")) + FlagRef.startswith("-fmodules-cache-path=") || + FlagRef.startswith("-fapinotes-cache-path=")) return 1; return 0; diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index 03d4c6be61206..98904bec70e06 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -4479,6 +4479,33 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_assume_sane_operator_new)) CmdArgs.push_back("-fno-assume-sane-operator-new"); + if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, + false)) { + CmdArgs.push_back("-fapinotes"); + + SmallString<128> APINotesCachePath; + if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) { + APINotesCachePath = A->getValue(); + } + + if (C.isForDiagnostics()) { + // When generating crash reports, we want to emit the API notes along with + // the reproduction sources, so we ignore any provided API notes path. + APINotesCachePath = Output.getFilename(); + llvm::sys::path::replace_extension(APINotesCachePath, ".cache"); + llvm::sys::path::append(APINotesCachePath, "apinotes"); + } else if (APINotesCachePath.empty()) { + // No API notes path was provided: use the default. + llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, + APINotesCachePath); + llvm::sys::path::append(APINotesCachePath, "org.llvm.clang"); + llvm::sys::path::append(APINotesCachePath, "APINotesCache"); + } + const char Arg[] = "-fapinotes-cache-path="; + APINotesCachePath.insert(APINotesCachePath.begin(), Arg, Arg + strlen(Arg)); + CmdArgs.push_back(Args.MakeArgString(APINotesCachePath)); + } + // -fblocks=0 is default. if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks, getToolChain().IsBlocksDefault()) || diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 7844b100d9160..d78d99da708c7 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -832,6 +832,7 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) { Opts.WorkingDir = Args.getLastArgValue(OPT_working_directory); + Opts.APINotesCachePath = Args.getLastArgValue(OPT_fapinotes_cache_path); } /// Parse the argument to the -ftest-module-file-extension @@ -1682,6 +1683,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, std::sort(Opts.ModuleFeatures.begin(), Opts.ModuleFeatures.end()); Opts.NativeHalfType |= Args.hasArg(OPT_fnative_half_type); Opts.HalfArgsAndReturns = Args.hasArg(OPT_fallow_half_arguments_and_returns); + Opts.APINotes = Args.hasArg(OPT_fapinotes); Opts.GNUAsm = !Args.hasArg(OPT_fno_gnu_inline_asm); // __declspec is enabled by default for the PS4 by the driver, and also @@ -1999,6 +2001,13 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ParseLangArgs(*Res.getLangOpts(), Args, DashX, Diags); if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) Res.getLangOpts()->ObjCExceptions = 1; + + // -fapinotes requires -fapinotes-cache-path=<directory>. + if (Res.getLangOpts()->APINotes && + Res.getFileSystemOpts().APINotesCachePath.empty()) { + Diags.Report(diag::err_no_apinotes_cache_path); + Success = false; + } } // FIXME: ParsePreprocessorArgs uses the FileManager to read the contents of // PCH file and find the original header name. Remove the need to do that in diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 2497e5ec38bca..a1b99545dd074 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1064,6 +1064,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("attribute_availability_with_version_underscores", true) .Case("attribute_availability_tvos", true) .Case("attribute_availability_watchos", true) + .Case("attribute_availability_swift", true) .Case("attribute_cf_returns_not_retained", true) .Case("attribute_cf_returns_retained", true) .Case("attribute_cf_returns_on_parameters", true) diff --git a/clang/lib/Makefile b/clang/lib/Makefile index acf8089629e41..e627d5a3a8101 100755 --- a/clang/lib/Makefile +++ b/clang/lib/Makefile @@ -9,9 +9,9 @@ CLANG_LEVEL := .. # ARCMigrate and Rewrite are always needed because of libclang. -PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis Frontend \ - FrontendTool Tooling Driver Format Edit Rewrite Serialization \ - Index ASTMatchers +PARALLEL_DIRS = Headers Basic APINotes Lex Parse AST Sema CodeGen Analysis \ + Frontend FrontendTool Tooling Driver Format Edit Rewrite \ + Serialization Index ASTMatchers include $(CLANG_LEVEL)/../../Makefile.config diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index ab1f97d31a690..5084aee0b7510 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -47,6 +47,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, VS, ICIS_NoInit); if (FnD) { Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs); + Actions.ProcessAPINotes(FnD); if (PureSpecLoc.isValid()) Actions.ActOnPureSpecifier(FnD, PureSpecLoc); } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 1ef53b36d7be7..c24d6a59f3085 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2583,8 +2583,10 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // initialize it. ThisDecl = VT->getTemplatedDecl(); - if (ThisDecl && AccessAttrs) + if (ThisDecl) { Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs); + Actions.ProcessAPINotes(ThisDecl); + } } // Error recovery might have converted a non-static member into a static diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 4ac8f42f1361a..cfbde0900a7d7 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -207,6 +207,8 @@ void Parser::CheckNestedObjCContexts(SourceLocation AtLoc) /// __attribute__((unavailable)) /// __attribute__((objc_exception)) - used by NSException on 64-bit /// __attribute__((objc_root_class)) +/// __attribute__((objc_subclassing_restricted)) +/// __attribute__((objc_complete_definition)) /// Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParsedAttributes &attrs) { diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 8aa005102fe4b..1f49e8411917e 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangSema Sema.cpp SemaAccess.cpp SemaAttr.cpp + SemaAPINotes.cpp SemaCXXScopeSpec.cpp SemaCast.cpp SemaChecking.cpp @@ -57,4 +58,5 @@ add_clang_library(clangSema clangBasic clangEdit clangLex + clangAPINotes ) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index f96430f1ecc98..54622923f7a36 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -77,7 +77,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), - CollectStats(false), CodeCompleter(CodeCompleter), + APINotes(SourceMgr), CollectStats(false), CodeCompleter(CodeCompleter), CurContext(nullptr), OriginalLexicalContext(nullptr), PackContext(nullptr), MSStructPragmaOn(false), MSPointerToMemberRepresentationMethod( diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp new file mode 100644 index 0000000000000..3114805da208b --- /dev/null +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -0,0 +1,359 @@ +//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the mapping from API notes to declaration attributes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Sema/SemaInternal.h" +#include "clang/AST/DeclObjC.h" +#include "clang/APINotes/APINotesReader.h" +using namespace clang; + +/// Determine whether this is a multi-level pointer type. +static bool isMultiLevelPointerType(QualType type) { + QualType pointee = type->getPointeeType(); + if (pointee.isNull()) + return false; + + return pointee->isAnyPointerType() || pointee->isObjCObjectPointerType() || + pointee->isMemberPointerType(); +} + +// Apply nullability to the given declaration. +static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability) { + QualType type; + + // Nullability for a function/method appertains to the retain type. + if (auto function = dyn_cast<FunctionDecl>(decl)) { + type = function->getReturnType(); + } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) { + type = method->getReturnType(); + } else if (auto value = dyn_cast<ValueDecl>(decl)) { + type = value->getType(); + } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) { + type = property->getType(); + } else { + return; + } + + // Check the nullability specifier on this type. + QualType origType = type; + S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(), + /*isContextSensitive=*/false, + /*implicit=*/true); + if (type.getTypePtr() == origType.getTypePtr()) + return; + + if (auto function = dyn_cast<FunctionDecl>(decl)) { + const FunctionType *fnType = function->getType()->castAs<FunctionType>(); + if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) { + function->setType(S.Context.getFunctionType(type, proto->getParamTypes(), + proto->getExtProtoInfo())); + } else { + function->setType(S.Context.getFunctionNoProtoType(type, + fnType->getExtInfo())); + } + } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) { + method->setReturnType(type); + + // Make it a context-sensitive keyword if we can. + if (!isMultiLevelPointerType(type)) { + method->setObjCDeclQualifier( + Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() | + Decl::OBJC_TQ_CSNullability)); + } + } else if (auto value = dyn_cast<ValueDecl>(decl)) { + value->setType(type); + + // Make it a context-sensitive keyword if we can. + if (auto parm = dyn_cast<ParmVarDecl>(decl)) { + if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) { + parm->setObjCDeclQualifier( + Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() | + Decl::OBJC_TQ_CSNullability)); + } + } + } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) { + property->setType(type, property->getTypeSourceInfo()); + + // Make it a property attribute if we can. + if (!isMultiLevelPointerType(type)) { + property->setPropertyAttributes( + ObjCPropertyDecl::OBJC_PR_null_resettable); + } + } else { + llvm_unreachable("cannot handle nullability here"); + } +} + +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonEntityInfo &Info) { + // Availability + if (Info.Unavailable && !D->hasAttr<UnavailableAttr>()) { + D->addAttr(UnavailableAttr::CreateImplicit(S.Context, Info.UnavailableMsg)); + } +} + +/// Process API notes for a variable or property. +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::VariableInfo &Info) { + // Nullability. + if (auto Nullability = Info.getNullability()) { + applyNullability(S, D, *Nullability); + } + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); +} + +/// Process API notes for a global variable. +static void ProcessAPINotes(Sema &S, VarDecl *D, + const api_notes::GlobalVariableInfo &Info) { + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); +} + +/// Process API notes for an Objective-C property. +static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, + const api_notes::ObjCPropertyInfo &Info) { + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); +} + +namespace { + typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod; +} + +/// Process API notes for a function or method. +static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, + const api_notes::FunctionInfo &Info) { + // Find the declaration itself. + FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>(); + Decl *D = FD; + ObjCMethodDecl *MD = 0; + if (!D) { + MD = AnyFunc.get<ObjCMethodDecl *>(); + D = MD; + } + + // Nullability. + if (Info.NullabilityAudited) { + // Return type. + applyNullability(S, D, Info.getReturnTypeInfo()); + + // Parameters. + unsigned NumParams; + if (FD) + NumParams = FD->getNumParams(); + else + NumParams = MD->param_size(); + + for (unsigned I = 0; I != NumParams; ++I) { + ParmVarDecl *Param; + if (FD) + Param = FD->getParamDecl(I); + else + Param = MD->param_begin()[I]; + + applyNullability(S, Param, Info.getParamTypeInfo(I)); + } + } + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); +} + +/// Process API notes for a global function. +static void ProcessAPINotes(Sema &S, FunctionDecl *D, + const api_notes::GlobalFunctionInfo &Info) { + + // Handle common function information. + ProcessAPINotes(S, FunctionOrMethod(D), + static_cast<const api_notes::FunctionInfo &>(Info)); +} + +/// Process API notes for an Objective-C method. +static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, + const api_notes::ObjCMethodInfo &Info) { + // Designated initializers. + if (Info.DesignatedInit && !D->getAttr<ObjCDesignatedInitializerAttr>()) { + if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { + D->addAttr(ObjCDesignatedInitializerAttr::CreateImplicit(S.Context)); + IFace->setHasDesignatedInitializers(); + } + } + + // Handle common function information. + ProcessAPINotes(S, FunctionOrMethod(D), + static_cast<const api_notes::FunctionInfo &>(Info)); +} + +/// Process API notes for an Objective-C class or protocol. +static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, + const api_notes::ObjCContextInfo &Info) { + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); +} + +/// Process API notes that are associated with this declaration, mapping them +/// to attributes as appropriate. +void Sema::ProcessAPINotes(Decl *D) { + if (!Context.getLangOpts().APINotes) + return; + + if (!D || D->getLocation().isInvalid()) + return; + + // Globals. + if (D->getDeclContext()->isFileContext()) { + // Global variables. + if (auto VD = dyn_cast<VarDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupGlobalVariable(VD->getName())) { + ::ProcessAPINotes(*this, VD, *Info); + } + } + + return; + } + + // Global functions. + if (auto FD = dyn_cast<FunctionDecl>(D)) { + if (FD->getDeclName().isIdentifier()) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupGlobalFunction(FD->getName())) { + ::ProcessAPINotes(*this, FD, *Info); + } + } + } + + return; + } + + // Objective-C classes. + if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupObjCClass(Class->getName())) { + ::ProcessAPINotes(*this, Class, Info->second); + } + } + + return; + } + + // Objective-C protocols. + if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupObjCProtocol(Protocol->getName())) { + ::ProcessAPINotes(*this, Protocol, Info->second); + } + } + + return; + } + + return; + } + + if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) { + // Location function that looks up an Objective-C context. + auto GetContext = [&](api_notes::APINotesReader *Reader) + -> Optional<api_notes::ContextID> { + if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) { + if (auto Found = Reader->lookupObjCProtocol(Protocol->getName())) + return Found->first; + + return None; + } + + if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(ObjCContainer)) { + if (auto Cat = Impl->getCategoryDecl()) + ObjCContainer = Cat; + else + return None; + } + + if (auto Category = dyn_cast<ObjCCategoryDecl>(ObjCContainer)) { + if (Category->getClassInterface()) + ObjCContainer = Category->getClassInterface(); + else + return None; + } + + if (auto Impl = dyn_cast<ObjCImplDecl>(ObjCContainer)) { + if (Impl->getClassInterface()) + ObjCContainer = Impl->getClassInterface(); + else + return None; + } + + if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) { + if (auto Found = Reader->lookupObjCClass(Class->getName())) + return Found->first; + + return None; + + } + + return None; + }; + + // Objective-C methods. + if (auto Method = dyn_cast<ObjCMethodDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetContext(Reader)) { + // Map the selector. + Selector Sel = Method->getSelector(); + SmallVector<StringRef, 2> SelPieces; + if (Sel.isUnarySelector()) + SelPieces.push_back(Sel.getNameForSlot(0)); + else { + for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i) + SelPieces.push_back(Sel.getNameForSlot(i)); + } + + api_notes::ObjCSelectorRef SelectorRef; + SelectorRef.NumPieces = Sel.getNumArgs(); + SelectorRef.Identifiers = SelPieces; + + if (auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, + Method->isInstanceMethod())){ + ::ProcessAPINotes(*this, Method, *Info); + } + } + } + } + + // Objective-C properties. + if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetContext(Reader)) { + if (auto Info = Reader->lookupObjCProperty(*Context, + Property->getName())) { + ::ProcessAPINotes(*this, Property, *Info); + } + } + } + + return; + } + + return; + } +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 6297efb73a20b..20beddffaacf2 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2216,6 +2216,10 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.mergeMinSizeAttr(D, MA->getRange(), AttrSpellingListIndex); else if (const auto *OA = dyn_cast<OptimizeNoneAttr>(Attr)) NewAttr = S.mergeOptimizeNoneAttr(D, OA->getRange(), AttrSpellingListIndex); + else if (const auto *SNA = dyn_cast<SwiftNameAttr>(Attr)) + NewAttr = S.mergeSwiftNameAttr(D, SNA->getRange(), SNA->getName(), + AMK == Sema::AMK_Override, + AttrSpellingListIndex); else if (isa<AlignedAttr>(Attr)) // AlignedAttrs are handled separately, because we need to handle all // such attributes on a declaration at the same time. @@ -2224,6 +2228,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, (AMK == Sema::AMK_Override || AMK == Sema::AMK_ProtocolImplementation)) NewAttr = nullptr; + else if (isa<SwiftPrivateAttr>(Attr) && AMK == Sema::AMK_Override) + NewAttr = nullptr; else if (Attr->duplicatesAllowed() || !DeclHasAttr(D, Attr)) NewAttr = cast<InheritableAttr>(Attr->clone(S.Context)); @@ -11185,6 +11191,7 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) D = TD->getTemplatedDecl(); ProcessDeclAttributeList(S, D, Attrs.getList()); + ProcessAPINotes(D); if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D)) if (Method->isStatic()) @@ -12469,6 +12476,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, if (Attr) ProcessDeclAttributeList(S, New, Attr); + ProcessAPINotes(New); // Set the lexical context. If the tag has a C++ scope specifier, the // lexical context will be different from the semantic context. @@ -13674,6 +13682,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (Attr) ProcessDeclAttributeList(S, Record, Attr); + ProcessAPINotes(Record); } /// \brief Determine whether the given integral value is representable within @@ -13967,6 +13976,7 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, if (New) { // Process attributes. if (Attr) ProcessDeclAttributeList(S, New, Attr); + ProcessAPINotes(New); // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); @@ -14192,6 +14202,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, if (Attr) ProcessDeclAttributeList(S, Enum, Attr); + ProcessAPINotes(Enum); if (Enum->isDependentType()) { for (unsigned i = 0, e = Elements.size(); i != e; ++i) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 6d9d7d4aaab72..5dee760fe356f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1249,6 +1249,26 @@ static void handleReturnsNonNullAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } +static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D); + if (!PD) + return; + + // noescape only applies to pointer types. + QualType T = PD->getType(); + if (!T->isAnyPointerType() && !T->isBlockPointerType() && + !T->isReferenceType() && !T->isArrayType() && + !T->isMemberPointerType()) { + S.Diag(Attr.getLoc(), diag::warn_attribute_noescape_non_pointer) + << T; + return; + } + + D->addAttr(::new (S.Context) NoEscapeAttr( + Attr.getRange(), S.Context, + Attr.getAttributeSpellingListIndex())); +} + static void handleAssumeAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { Expr *E = Attr.getArgAsExpr(0), @@ -2007,6 +2027,14 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, dyn_cast_or_null<StringLiteral>(Attr.getMessageExpr())) Str = SE->getString(); + if (II->getName() == "swift") { + if (Introduced.isValid() || Deprecated.isValid() || Obsoleted.isValid() || + !IsUnavailable) { + S.Diag(Attr.getLoc(), diag::warn_availability_swift_unavailable_only); + return; + } + } + AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, Attr.getRange(), II, Introduced.Version, Deprecated.Version, @@ -3417,6 +3445,23 @@ OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, SourceRange Range, AttrSpellingListIndex); } +SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, SourceRange Range, + StringRef Name, bool Override, + unsigned AttrSpellingListIndex) { + if (SwiftNameAttr *Inline = D->getAttr<SwiftNameAttr>()) { + if (Override) { + // FIXME: Warn about an incompatible override. + return nullptr; + } + Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; + Diag(Range.getBegin(), diag::note_conflicting_attribute); + D->dropAttr<SwiftNameAttr>(); + } + + return ::new (Context) SwiftNameAttr(Range, Context, Name, + AttrSpellingListIndex); +} + static void handleAlwaysInlineAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (AlwaysInlineAttr *Inline = S.mergeAlwaysInlineAttr( @@ -4188,6 +4233,219 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } +/// Do a very rough check to make sure \p Name looks like a Swift function name, +/// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>, +/// and return the number of parameter names. +static bool validateSwiftFunctionName(StringRef Name, + unsigned &ParamCount, + bool &IsSingleParamInit) { + ParamCount = 0; + if (Name.back() != ')') + return false; + + StringRef BaseName, Parameters; + std::tie(BaseName, Parameters) = Name.split('('); + if (!isValidIdentifier(BaseName) || BaseName == "_") + return false; + + if (Parameters.empty()) + return false; + Parameters = Parameters.drop_back(); // ')' + if (Parameters.empty()) + return true; + + if (Parameters.back() != ':') + return false; + + StringRef NextParam; + do { + std::tie(NextParam, Parameters) = Parameters.split(':'); + + if (!isValidIdentifier(NextParam)) + return false; + ++ParamCount; + } while (!Parameters.empty()); + + IsSingleParamInit = + (ParamCount == 1 && BaseName == "init" && NextParam != "_"); + + return true; +} + +static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { + StringRef Name; + SourceLocation ArgLoc; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc)) + return; + + if (isa<ObjCMethodDecl>(D) || isa<FunctionDecl>(D)) { + ArrayRef<ParmVarDecl*> Params; + unsigned ParamCount; + + if (const auto *Method = dyn_cast<ObjCMethodDecl>(D)) { + ParamCount = Method->getSelector().getNumArgs(); + Params = Method->parameters().slice(0, ParamCount); + } else { + const auto *Function = cast<FunctionDecl>(D); + ParamCount = Function->getNumParams(); + Params = Function->parameters(); + } + + bool IsSingleParamInit; + unsigned SwiftParamCount; + if (!validateSwiftFunctionName(Name, SwiftParamCount, IsSingleParamInit)) { + S.Diag(ArgLoc, diag::err_attr_swift_name_function) << Attr.getName(); + return; + } + + bool ParamsOK; + if (SwiftParamCount == ParamCount) { + ParamsOK = true; + } else if (SwiftParamCount > ParamCount) { + ParamsOK = IsSingleParamInit && ParamCount == 0; + } else { + // We have fewer Swift parameters than Objective-C parameters, but that + // might be because we've transformed some of them. Check for potential + // "out" parameters and err on the side of not warning. + unsigned MaybeOutParamCount = + std::count_if(Params.begin(), Params.end(), + [](const ParmVarDecl *Param) -> bool { + QualType ParamTy = Param->getType(); + if (ParamTy->isReferenceType() || ParamTy->isPointerType()) + return !ParamTy->getPointeeType().isConstQualified(); + return false; + }); + ParamsOK = (SwiftParamCount + MaybeOutParamCount >= ParamCount); + } + + if (!ParamsOK) { + S.Diag(ArgLoc, diag::warn_attr_swift_name_num_params) + << (SwiftParamCount > ParamCount) << Attr.getName() + << ParamCount << SwiftParamCount; + return; + } + + } else if (isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) || + isa<ObjCInterfaceDecl>(D) || isa<ObjCPropertyDecl>(D) || + isa<VarDecl>(D) || isa<TypedefNameDecl>(D) || isa<TagDecl>(D) || + isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) { + if (!isValidIdentifier(Name)) { + S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); + return; + } + + } else { + S.Diag(Attr.getLoc(), diag::err_attr_swift_name_decl_kind) + << Attr.getName(); + return; + } + + D->addAttr(::new (S.Context) + SwiftNameAttr(Attr.getRange(), S.Context, Name, + Attr.getAttributeSpellingListIndex())); +} + +static bool isErrorParameter(Sema &S, QualType paramType) { + if (auto ptr = paramType->getAs<PointerType>()) { + auto outerPointee = ptr->getPointeeType(); + + // NSError**. + if (auto objcPtr = outerPointee->getAs<ObjCObjectPointerType>()) { + if (auto iface = objcPtr->getInterfaceDecl()) + if (iface->getIdentifier() == S.getNSErrorIdent()) + return true; + } + + // CFErrorRef*. + if (auto cPtr = outerPointee->getAs<PointerType>()) { + auto innerPointee = cPtr->getPointeeType(); + if (auto recordType = innerPointee->getAs<RecordType>()) { + if (S.isCFError(recordType->getDecl())) + return true; + } + } + } + + return false; +} + +static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) { + SwiftErrorAttr::ConventionKind convention; + IdentifierLoc *conventionLoc = attr.getArgAsIdent(0); + StringRef conventionStr = conventionLoc->Ident->getName(); + if (!SwiftErrorAttr::ConvertStrToConventionKind(conventionStr, convention)) { + S.Diag(attr.getLoc(), diag::warn_attribute_type_not_supported) + << attr.getName() << conventionLoc->Ident; + return; + } + + auto requireErrorParameter = [&]() -> bool { + if (D->isInvalidDecl()) return true; + + for (unsigned i = 0, e = getFunctionOrMethodNumParams(D); i != e; ++i) { + if (isErrorParameter(S, getFunctionOrMethodParamType(D, i))) + return true; + } + + S.Diag(attr.getLoc(), diag::err_attr_swift_error_no_error_parameter) + << attr.getName() << isa<ObjCMethodDecl>(D); + return false; + }; + + auto requirePointerResult = [&] { + if (D->isInvalidDecl()) return true; + + // C, ObjC, and block pointers are definitely okay. + // References are definitely not okay. + // nullptr_t is weird but acceptable. + QualType returnType = getFunctionOrMethodResultType(D); + if (returnType->hasPointerRepresentation() && + !returnType->isReferenceType()) return true; + + S.Diag(attr.getLoc(), diag::err_attr_swift_error_return_type) + << attr.getName() << conventionStr + << isa<ObjCMethodDecl>(D) << /*pointer*/ 1; + return false; + }; + + auto requireIntegerResult = [&] { + if (D->isInvalidDecl()) return true; + + QualType returnType = getFunctionOrMethodResultType(D); + if (returnType->isIntegralType(S.Context)) return true; + + S.Diag(attr.getLoc(), diag::err_attr_swift_error_return_type) + << attr.getName() << conventionStr + << isa<ObjCMethodDecl>(D) << /*integral*/ 0; + return false; + }; + + switch (convention) { + case SwiftErrorAttr::None: + // No additional validation required. + break; + + case SwiftErrorAttr::NonNullError: + if (!requireErrorParameter()) return; + break; + + case SwiftErrorAttr::NullResult: + if (!requireErrorParameter()) return; + if (!requirePointerResult()) return; + break; + + case SwiftErrorAttr::NonZeroResult: + case SwiftErrorAttr::ZeroResult: + if (!requireErrorParameter()) return; + if (!requireIntegerResult()) return; + break; + } + + D->addAttr(::new (S.Context) + SwiftErrorAttr(attr.getRange(), S.Context, convention, + attr.getAttributeSpellingListIndex())); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -4855,6 +5113,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ReturnsNonNull: handleReturnsNonNullAttr(S, D, Attr); break; + case AttributeList::AT_NoEscape: + handleNoEscapeAttr(S, D, Attr); + break; case AttributeList::AT_AssumeAligned: handleAssumeAlignedAttr(S, D, Attr); break; @@ -4979,6 +5240,12 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCRootClass: handleSimpleAttribute<ObjCRootClassAttr>(S, D, Attr); break; + case AttributeList::AT_ObjCSubclassingRestricted: + handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, Attr); + break; + case AttributeList::AT_ObjCCompleteDefinition: + handleSimpleAttribute<ObjCCompleteDefinitionAttr>(S, D, Attr); + break; case AttributeList::AT_ObjCExplicitProtocolImpl: handleObjCSuppresProtocolAttr(S, D, Attr); break; @@ -5201,6 +5468,17 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_TypeTagForDatatype: handleTypeTagForDatatypeAttr(S, D, Attr); break; + + // Swift attributes. + case AttributeList::AT_SwiftPrivate: + handleSimpleAttribute<SwiftPrivateAttr>(S, D, Attr); + break; + case AttributeList::AT_SwiftName: + handleSwiftName(S, D, Attr); + break; + case AttributeList::AT_SwiftError: + handleSwiftError(S, D, Attr); + break; } } @@ -5413,6 +5691,9 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { // Finally, apply any attributes on the decl itself. if (const AttributeList *Attrs = PD.getAttributes()) ProcessDeclAttributeList(S, D, Attrs); + + // Look for API notes that map to attributes. + ProcessAPINotes(D); } /// Is the given declaration allowed to use a forbidden type? diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6c308ef9e6a0c..42f4fcee48cea 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7269,6 +7269,7 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope, Namespc->setInvalidDecl(); ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList); + ProcessAPINotes(Namespc); // FIXME: Should we be merging attributes? if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>()) @@ -7642,6 +7643,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, if (UDir) ProcessDeclAttributeList(S, UDir, AttrList); + ProcessAPINotes(UDir); return UDir; } @@ -8576,6 +8578,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, NewTD->setInvalidDecl(); ProcessDeclAttributeList(S, NewTD, AttrList); + ProcessAPINotes(NewTD); CheckTypedefForVariablyModifiedType(S, NewTD); Invalid |= NewTD->isInvalidDecl(); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 527ffa0fece85..c52558d69e8f6 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -982,6 +982,10 @@ ActOnStartClassInterface(Scope *S, SourceLocation AtInterfaceLoc, ObjCInterfaceDecl *IDecl = ObjCInterfaceDecl::Create(Context, CurContext, AtInterfaceLoc, ClassName, typeParamList, PrevIDecl, ClassLoc); + if (AttrList) + ProcessDeclAttributeList(TUScope, IDecl, AttrList); + ProcessAPINotes(IDecl); + if (PrevIDecl) { // Class already seen. Was it a definition? if (ObjCInterfaceDecl *Def = PrevIDecl->getDefinition()) { @@ -992,8 +996,6 @@ ActOnStartClassInterface(Scope *S, SourceLocation AtInterfaceLoc, } } - if (AttrList) - ProcessDeclAttributeList(TUScope, IDecl, AttrList); PushOnScopeChains(IDecl, TUScope); // Start the definition of this class. If we're in a redefinition case, there @@ -1170,6 +1172,7 @@ Sema::ActOnStartProtocolInterface(SourceLocation AtProtoInterfaceLoc, if (AttrList) ProcessDeclAttributeList(TUScope, PDecl, AttrList); + ProcessAPINotes(PDecl); // Merge attributes from previous declarations. if (PrevDecl) @@ -1691,6 +1694,7 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, if (attrList) ProcessDeclAttributeList(TUScope, PDecl, attrList); + ProcessAPINotes(PDecl); if (PrevDecl) mergeDeclAttributes(PDecl, PrevDecl); @@ -3680,7 +3684,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, if (IDecl->getSuperClass() == nullptr) { // This class has no superclass, so check that it has been marked with // __attribute((objc_root_class)). - if (!HasRootClassAttr) { + if (!HasRootClassAttr && !IDecl->hasAttr<ObjCCompleteDefinitionAttr>()) { SourceLocation DeclLoc(IDecl->getLocation()); SourceLocation SuperClassLoc(getLocForEndOfToken(DeclLoc)); Diag(DeclLoc, diag::warn_objc_root_class_missing) @@ -3723,6 +3727,15 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, ImplMethodsVsClassMethods(S, CatImplClass, Cat); } } + } else if (const ObjCInterfaceDecl *IntfDecl = + dyn_cast<ObjCInterfaceDecl>(ClassDecl)) { + if (const ObjCInterfaceDecl *Super = IntfDecl->getSuperClass()) { + if (!IntfDecl->hasAttr<ObjCSubclassingRestrictedAttr>() && + Super->hasAttr<ObjCSubclassingRestrictedAttr>()) { + Diag(IntfDecl->getLocation(), diag::err_restricted_superclass_mismatch); + Diag(Super->getLocation(), diag::note_class_declared); + } + } } if (isInterfaceDeclKind) { // Reject invalid vardecls. @@ -3755,12 +3768,10 @@ CvtQTToAstBitMask(ObjCDeclSpec::ObjCDeclQualifier PQTVal) { return (Decl::ObjCDeclQualifier) (unsigned) PQTVal; } -/// \brief Check whether the declared result type of the given Objective-C -/// method declaration is compatible with the method's class. -/// -static Sema::ResultTypeCompatibilityKind -CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method, - ObjCInterfaceDecl *CurrentClass) { +Sema::ResultTypeCompatibilityKind +Sema::checkRelatedResultTypeCompatibility( + const ObjCMethodDecl *Method, + const ObjCInterfaceDecl *CurrentClass) { QualType ResultType = Method->getReturnType(); // If an Objective-C method inherits its related result type, then its @@ -4216,6 +4227,7 @@ Decl *Sema::ActOnMethodDeclaration( // Apply the attributes to the parameter. ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs); + ProcessAPINotes(Param); if (Param->hasAttr<BlocksAttr>()) { Diag(Param->getLocation(), diag::err_block_on_nonlocal); @@ -4246,6 +4258,7 @@ Decl *Sema::ActOnMethodDeclaration( if (AttrList) ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList); + ProcessAPINotes(ObjCMethod); // Add the method now. const ObjCMethodDecl *PrevMethod = nullptr; @@ -4301,7 +4314,7 @@ Decl *Sema::ActOnMethodDeclaration( } ResultTypeCompatibilityKind RTC - = CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass); + = checkRelatedResultTypeCompatibility(ObjCMethod, CurrentClass); CheckObjCMethodOverrides(ObjCMethod, CurrentClass, RTC); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 8821088baac17..ce1aa4c4448f0 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1136,6 +1136,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, if (Attr) ProcessDeclAttributeList(S, NewClass, Attr); + ProcessAPINotes(NewClass); if (PrevClassTemplate) mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); @@ -6465,6 +6466,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. @@ -7390,6 +7392,7 @@ Sema::ActOnExplicitInstantiation(Scope *S, if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); // Add the explicit instantiation into its lexical context. However, // since explicit instantiations are never found by name lookup, we @@ -7773,6 +7776,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, // Merge attributes. if (AttributeList *Attr = D.getDeclSpec().getAttributes().getList()) ProcessDeclAttributeList(S, Prev, Attr); + ProcessAPINotes(Prev); } if (TSK == TSK_ExplicitInstantiationDefinition) InstantiateVariableDefinition(D.getIdentifierLoc(), Prev); @@ -7916,6 +7920,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, AttributeList *Attr = D.getDeclSpec().getAttributes().getList(); if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); if (Specialization->isDefined()) { // Let the ASTConsumer know that this function has been explicitly diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index e53c7792e50d1..81c8b7f19c6c0 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3125,25 +3125,9 @@ static PointerDeclaratorKind classifyPointerDeclarator(Sema &S, if (auto recordType = type->getAs<RecordType>()) { RecordDecl *recordDecl = recordType->getDecl(); - bool isCFError = false; - if (S.CFError) { - // If we already know about CFError, test it directly. - isCFError = (S.CFError == recordDecl); - } else { - // Check whether this is CFError, which we identify based on its bridge - // to NSError. - if (recordDecl->getTagKind() == TTK_Struct && numNormalPointers > 0) { - if (auto bridgeAttr = recordDecl->getAttr<ObjCBridgeAttr>()) { - if (bridgeAttr->getBridgedType() == S.getNSErrorIdent()) { - S.CFError = recordDecl; - isCFError = true; - } - } - } - } - // If this is CFErrorRef*, report it as such. - if (isCFError && numNormalPointers == 2 && numTypeSpecifierPointers < 2) { + if (numNormalPointers == 2 && numTypeSpecifierPointers < 2 && + S.isCFError(recordDecl)) { return PointerDeclaratorKind::CFErrorRefPointer; } break; @@ -3168,6 +3152,26 @@ static PointerDeclaratorKind classifyPointerDeclarator(Sema &S, } } +bool Sema::isCFError(RecordDecl *recordDecl) { + // If we already know about CFError, test it directly. + if (CFError) { + return (CFError == recordDecl); + } + + // Check whether this is CFError, which we identify based on being + // bridged to NSError. + if (recordDecl->getTagKind() == TTK_Struct) { + if (auto bridgeAttr = recordDecl->getAttr<ObjCBridgeAttr>()) { + if (bridgeAttr->getBridgedType() == getNSErrorIdent()) { + CFError = recordDecl; + return true; + } + } + } + + return false; +} + static FileID getNullabilityCompletenessCheckFileID(Sema &S, SourceLocation loc) { // If we're anywhere in a function, method, or closure context, don't perform @@ -5470,21 +5474,24 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State, bool Sema::checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability, SourceLocation nullabilityLoc, - bool isContextSensitive) { - // We saw a nullability type specifier. If this is the first one for - // this file, note that. - FileID file = getNullabilityCompletenessCheckFileID(*this, nullabilityLoc); - if (!file.isInvalid()) { - FileNullability &fileNullability = NullabilityMap[file]; - if (!fileNullability.SawTypeNullability) { - // If we have already seen a pointer declarator without a nullability - // annotation, complain about it. - if (fileNullability.PointerLoc.isValid()) { - Diag(fileNullability.PointerLoc, diag::warn_nullability_missing) - << static_cast<unsigned>(fileNullability.PointerKind); - } + bool isContextSensitive, + bool implicit) { + if (!implicit) { + // We saw a nullability type specifier. If this is the first one for + // this file, note that. + FileID file = getNullabilityCompletenessCheckFileID(*this, nullabilityLoc); + if (!file.isInvalid()) { + FileNullability &fileNullability = NullabilityMap[file]; + if (!fileNullability.SawTypeNullability) { + // If we have already seen a pointer declarator without a nullability + // annotation, complain about it. + if (fileNullability.PointerLoc.isValid()) { + Diag(fileNullability.PointerLoc, diag::warn_nullability_missing) + << static_cast<unsigned>(fileNullability.PointerKind); + } - fileNullability.SawTypeNullability = true; + fileNullability.SawTypeNullability = true; + } } } @@ -5495,6 +5502,9 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, if (auto existingNullability = attributed->getImmediateNullability()) { // Duplicated nullability. if (nullability == *existingNullability) { + if (implicit) + break; + Diag(nullabilityLoc, diag::warn_nullability_duplicate) << DiagNullabilityKind(nullability, isContextSensitive) << FixItHint::CreateRemoval(nullabilityLoc); @@ -5517,7 +5527,7 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, // have nullability specifiers on them, which means we cannot // provide a useful Fix-It. if (auto existingNullability = desugared->getNullability(Context)) { - if (nullability != *existingNullability) { + if (nullability != *existingNullability && !implicit) { Diag(nullabilityLoc, diag::err_nullability_conflicting) << DiagNullabilityKind(nullability, isContextSensitive) << DiagNullabilityKind(*existingNullability, false); @@ -5541,8 +5551,10 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, // If this definitely isn't a pointer type, reject the specifier. if (!desugared->canHaveNullability()) { - Diag(nullabilityLoc, diag::err_nullability_nonpointer) - << DiagNullabilityKind(nullability, isContextSensitive) << type; + if (!implicit) { + Diag(nullabilityLoc, diag::err_nullability_nonpointer) + << DiagNullabilityKind(nullability, isContextSensitive) << type; + } return true; } @@ -6247,7 +6259,8 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, type, mapNullabilityAttrKind(attr.getKind()), attr.getLoc(), - attr.isContextSensitiveKeywordAttribute())) { + attr.isContextSensitiveKeywordAttribute(), + /*implicit=*/false)) { attr.setInvalid(); } diff --git a/clang/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes b/clang/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes new file mode 100644 index 0000000000000..d5473175ecf8e --- /dev/null +++ b/clang/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes @@ -0,0 +1,4 @@ +Name: SomeBrokenLib +Functions: + - Name: do_something_with_pointers + Nu llabilityOfRet: O diff --git a/clang/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h b/clang/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h new file mode 100644 index 0000000000000..b09c6f63eae02 --- /dev/null +++ b/clang/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h @@ -0,0 +1,6 @@ +#ifndef SOME_BROKEN_LIB_H +#define SOME_BROKEN_LIB_H + +void do_something_with_pointers(int *ptr1, int *ptr2); + +#endif // SOME_BROKEN_LIB_H diff --git a/clang/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes b/clang/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes new file mode 100644 index 0000000000000..33eeaaada999d --- /dev/null +++ b/clang/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes @@ -0,0 +1,7 @@ +Name: SomeBrokenLib +Functions: + - Name: do_something_with_pointers + NullabilityOfRet: O + - Name: do_something_with_pointers + NullabilityOfRet: O + diff --git a/clang/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h b/clang/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h new file mode 100644 index 0000000000000..b09c6f63eae02 --- /dev/null +++ b/clang/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h @@ -0,0 +1,6 @@ +#ifndef SOME_BROKEN_LIB_H +#define SOME_BROKEN_LIB_H + +void do_something_with_pointers(int *ptr1, int *ptr2); + +#endif // SOME_BROKEN_LIB_H diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes new file mode 100644 index 0000000000000..a585ca5f4df3b --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -0,0 +1,24 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "transform:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: intValue + Availability: none + AvailabilityMsg: "wouldn't work anyway" + - Name: B + Availability: none + AvailabilityMsg: "just don't" + - Name: C + Methods: + - Selector: "initWithA:" + MethodKind: Instance + DesignatedInit: true diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes new file mode 100644 index 0000000000000..28ede9dfa25c0 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes @@ -0,0 +1,15 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "privateTransform:input:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: internalProperty + Nullability: N +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h new file mode 100644 index 0000000000000..01b003d1eeef0 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -0,0 +1,23 @@ +#ifndef SOMEKIT_H +#define SOMEKIT_H + +__attribute__((objc_root_class)) +@interface A +-(A*)transform:(A*)input; +-(A*)transform:(A*)input integer:(int)integer; + +@property (nonatomic, readonly, retain) A* someA; +@property (nonatomic, retain) A* someOtherA; + +@property (nonatomic) int intValue; +@end + +@interface B : A +@end + +@interface C : A +- (instancetype)init; +- (instancetype)initWithA:(A*)a; +@end + +#endif diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h new file mode 100644 index 0000000000000..d1eeb61991b48 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h @@ -0,0 +1,55 @@ +#ifndef SOMEKIT_H +#define SOMEKIT_H + +#define ROOT_CLASS __attribute__((objc_root_class)) + +ROOT_CLASS +@interface A +-(A*)transform:(A*)input; +-(A*)transform:(A*)input integer:(int)integer; + +@property (nonatomic, readonly, retain) A* someA; +@property (nonatomic, retain) A* someOtherA; + +@property (nonatomic) int intValue; +@end + +@interface B : A +@end + +@interface C : A +- (instancetype)init; +- (instancetype)initWithA:(A*)a; +@end + + +@interface MyClass : A +- Inst; ++ Clas; +@end + +struct CGRect { + float origin; + float size; +}; +typedef struct CGRect NSRect; + +@interface I +- (void) Meth : (NSRect[4])exposedRects; +- (void) Meth1 : (const I*)exposedRects; +- (void) Meth2 : (const I*)exposedRects; +- (void) Meth3 : (I*)exposedRects; +- (const I*) Meth4; +- (const I*) Meth5 : (int) Arg1 : (const I*)Arg2 : (double)Arg3 : (const I*) Arg4 :(const volatile id) Arg5; +- (volatile const I*) Meth6 : (const char *)Arg1 : (const char *)Arg2 : (double)Arg3 : (const I*) Arg4 :(const volatile id) Arg5; +@end + +@class NSURL, NSArray, NSError; +@interface INTF_BLOCKS + + (void)getNonLocalVersionsOfItemAtURL:(NSURL *)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler; + + (void *)getNonLocalVersionsOfItemAtURL2:(NSURL *)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler; + + (NSError **)getNonLocalVersionsOfItemAtURL3:(int)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler; + + (id)getNonLocalVersionsOfItemAtURL4:(NSURL *)url completionHandler:(void (^)(int nonLocalFileVersions, NSError *error, NSURL*))completionHandler; +@end + +#endif diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h new file mode 100644 index 0000000000000..c7611123e4ad2 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h @@ -0,0 +1,16 @@ +#ifndef SOMEKIT_PRIVATE_H +#define SOMEKIT_PRIVATE_H + +#import <SomeKit/SomeKit.h> + +@interface A(Private) +-(A*)privateTransform:(A*)input; + +@property (nonatomic) A* internalProperty; +@end + +@protocol InternalProtocol +@end + +#endif + diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h new file mode 100644 index 0000000000000..bae4456b40809 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h @@ -0,0 +1,17 @@ +#ifndef SOMEKIT_PRIVATE_H +#define SOMEKIT_PRIVATE_H + +#import <SomeKit/SomeKitForNullAnnotation.h> + +@interface A(Private) +-(A*)privateTransform:(A*)input; + +@property (nonatomic) A* internalProperty; +@end + +@protocol InternalProtocol +- (id) MomeMethod; +@end + +#endif + diff --git a/clang/test/APINotes/Inputs/Headers/APINotes.apinotes b/clang/test/APINotes/Inputs/Headers/APINotes.apinotes new file mode 100644 index 0000000000000..a4ddafe2892ec --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/APINotes.apinotes @@ -0,0 +1,17 @@ +Name: HeaderLib +Functions: + - Name: custom_realloc + NullabilityOfRet: N + Nullability: [ N, S ] + - Name: unavailable_function + Availability: none + AvailabilityMsg: "I beg you not to use this" + - Name: do_something_with_pointers + NullabilityOfRet: O + Nullability: [ N, O ] + +Globals: + - Name: global_int + Nullability: N + - Name: unavailable_global_int + Availability: none diff --git a/clang/test/APINotes/Inputs/Headers/HeaderLib.h b/clang/test/APINotes/Inputs/Headers/HeaderLib.h new file mode 100644 index 0000000000000..1cf199cd49a02 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.h @@ -0,0 +1,13 @@ +#ifndef HEADER_LIB_H +#define HEADER_LIB_H + +void *custom_realloc(void *member, unsigned size); + +int *global_int; + +int unavailable_function(void); +int unavailable_global_int; + +void do_something_with_pointers(int *ptr1, int *ptr2); + +#endif diff --git a/clang/test/APINotes/Inputs/os-availability.apinotes b/clang/test/APINotes/Inputs/os-availability.apinotes new file mode 100644 index 0000000000000..d59e79ce9fc0d --- /dev/null +++ b/clang/test/APINotes/Inputs/os-availability.apinotes @@ -0,0 +1,53 @@ +Name: Foundation +Classes: + - Name: NSCountedSet + Availability: iOS + Methods: + - Selector: 'initWithCapacity:' + MethodKind: Instance + DesignatedInit: true + - Name: NSArray + Methods: + - Selector: 'init' + MethodKind: Instance + DesignatedInit: true + - Selector: 'initWithObjects:' + MethodKind: Instance + DesignatedInit: true + Availability: iOS + - Selector: 'initWithObjects:count:' + MethodKind: Instance + DesignatedInit: true + Availability: iOS + Properties: + - Name: 'familyNameios' + Nullability: N + Availability: iOS + - Name: 'fontName' + Nullability: N +Protocols: + - Name: UIApplicationDelegate + AuditedForNullability: true + Methods: + - Selector: 'application:willFinishLaunchingWithOptions:' + MethodKind: Instance + Nullability: [ N, U ] + - Name: UIApplicationDelegateIOS + Availability: iOS + AuditedForNullability: true + Methods: + - Selector: 'application:willFinishLaunchingWithOptions:' + MethodKind: Instance + Nullability: [ N, U ] +Functions: + - Name: NSAvailableWindowDepthsiOS + NullabilityOfRet: N + Availability: iOS + - Name: NSAvailableWindowDepths + NullabilityOfRet: N +Globals: + - Name: NSCalibratedWhiteColorSpace + Nullability: N + Availability: OSX + AvailabilityMsg: '' + diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes new file mode 100644 index 0000000000000..b1722406663eb --- /dev/null +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -0,0 +1,79 @@ +--- +Name: AppKit +Availability: available +AvailabilityMsg: '' +Classes: + - Name: NSCell + Availability: available + AvailabilityMsg: '' + Methods: + - Selector: init + MethodKind: Instance + NullabilityOfRet: U + Availability: available + AvailabilityMsg: '' + DesignatedInit: true + - Selector: 'initImageCell:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: U + Availability: available + AvailabilityMsg: '' + DesignatedInit: true + - Selector: 'initTextCell:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: U + Availability: available + AvailabilityMsg: '' + DesignatedInit: true + - Selector: 'initWithCoder:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: U + Availability: available + AvailabilityMsg: '' + DesignatedInit: true + Required: true + - Name: NSView + AuditedForNullability: true + Availability: available + AvailabilityMsg: '' + Methods: + - Selector: 'addSubview:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: N + Availability: available + AvailabilityMsg: '' + - Selector: 'addSubview:positioned:relativeTo:' + MethodKind: Instance + Nullability: [ N, N, O ] + NullabilityOfRet: N + Availability: available + AvailabilityMsg: '' + - Selector: 'beginDraggingSessionWithItems:event:source:' + MethodKind: Instance + Nullability: [ U, U, N ] + NullabilityOfRet: N + Availability: available + AvailabilityMsg: '' + Properties: + - Name: enclosingScrollView + Nullability: O + Availability: available + AvailabilityMsg: '' + - Name: makeBackingLayer + Nullability: N + Availability: available + AvailabilityMsg: '' +Functions: + - Name: NSAvailableWindowDepths + NullabilityOfRet: N + Availability: available + AvailabilityMsg: '' +Globals: + - Name: NSCalibratedWhiteColorSpace + Nullability: N + Availability: available + AvailabilityMsg: '' diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m new file mode 100644 index 0000000000000..177700d63111d --- /dev/null +++ b/clang/test/APINotes/availability.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +#include "HeaderLib.h" +#import <SomeKit/SomeKit.h> +#import <SomeKit/SomeKit_Private.h> + +int main() { + int i; + i = unavailable_function(); // expected-error{{'unavailable_function' is unavailable: I beg you not to use this}} + // expected-note@HeaderLib.h:8{{'unavailable_function' has been explicitly marked unavailable here}} + i = unavailable_global_int; // expected-error{{'unavailable_global_int' is unavailable}} + // expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}} + + B *b = 0; // expected-error{{'B' is unavailable: just don't}} + // expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}} + + id<InternalProtocol> proto = 0; // expected-error{{'InternalProtocol' is unavailable: not for you}} + // expected-note@SomeKit/SomeKit_Private.h:12{{'InternalProtocol' has been explicitly marked unavailable here}} + + A *a = 0; + i = a.intValue; // expected-error{{intValue' is unavailable: wouldn't work anyway}} + // expected-note@SomeKit/SomeKit.h:12{{'intValue' has been explicitly marked unavailable here}} + + [a transform:a]; // expected-error{{'transform:' is unavailable: anything but this}} + // expected-note@SomeKit/SomeKit.h:6{{'transform:' has been explicitly marked unavailable here}} + + return 0; +} + diff --git a/clang/test/APINotes/cache.m b/clang/test/APINotes/cache.m new file mode 100644 index 0000000000000..6a2c2f5d17a4d --- /dev/null +++ b/clang/test/APINotes/cache.m @@ -0,0 +1,33 @@ +// RUN: rm -rf %t/APINotesCache +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +// Check for the presence of the cached compiled form. +// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" + +// Run test again to ensure that caching doesn't cause problems. +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +// Check that the driver provides a default -fapinotes-cache-path= +// RUN: %clang -fsyntax-only -fapinotes -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s +// CHECK-DEFAULT-PATH: -fapinotes-cache-path={{.*}}org.llvm.clang/APINotesCache + +// Check that the driver passes through a provided -fapinotes-cache-path= +// RUN: %clang -fsyntax-only -fapinotes -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s +// CHECK-PATH: -fapinotes-cache-path=/wobble + +#include "HeaderLib.h" +#import <SomeKit/SomeKit.h> + +int main() { + int i; + i = unavailable_function(); // expected-error{{'unavailable_function' is unavailable: I beg you not to use this}} + // expected-note@HeaderLib.h:8{{'unavailable_function' has been explicitly marked unavailable here}} + + A *a = 0; + [a transform:a]; // expected-error{{'transform:' is unavailable: anything but this}} + // expected-note@SomeKit/SomeKit.h:6{{'transform:' has been explicitly marked unavailable here}} + + return 0; +} + diff --git a/clang/test/APINotes/cache_pruning.m b/clang/test/APINotes/cache_pruning.m new file mode 100644 index 0000000000000..54b0c647aef3e --- /dev/null +++ b/clang/test/APINotes/cache_pruning.m @@ -0,0 +1,49 @@ +// We need 'touch' and 'find' for this test to work. +// REQUIRES: shell + +// RUN: rm -rf %t/APINotesCache + +// Run Clang. This should generated the cached versions of both and a timestamp. +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB +// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" + +// Set the timestamp back a very long time. We should try to prune, +// but nothing gets pruned because the API Notes files are new enough. +// RUN: touch -m -a -t 201101010000 %t/APINotes.timestamp +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" + +// Set the HeaderLib access time back a very long time. +// This shouldn't prune anything, because the timestamp has been updated, so +// the pruning mechanism won't fire. +// RUN: find %t/APINotesCache -name APINotes-*.apinotesc | xargs touch -a -t 201101010000 +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" + +// Set the timestack back a very long time. This should prune the +// HeaderLib file, because the pruning mechanism should fire and +// HeaderLib is both old and not used. +// RUN: touch -m -a -t 201101010000 %t/APINotesCache/APINotes.timestamp +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: ls %t/APINotesCache | not grep "APINotes-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" + +// Run Clang. This should generated the cached versions of both and a timestamp. +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB +// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" +// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" + +#ifdef INCLUDE_HEADERLIB +#include "HeaderLib.h" +#endif +#include <SomeKit/SomeKit.h> + +int main() { return 0; } diff --git a/clang/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c new file mode 100644 index 0000000000000..940587e8e0a12 --- /dev/null +++ b/clang/test/APINotes/nullability.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +#include "HeaderLib.h" + +int main() { + custom_realloc(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}} + int i = 0; + do_something_with_pointers(&i, 0); + do_something_with_pointers(0, &i); // expected-warning{{null passed to a callee that requires a non-null argument}} + + float *fp = global_int; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * _Nonnull'}} + return 0; +} + diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m new file mode 100644 index 0000000000000..40901f2c4fd76 --- /dev/null +++ b/clang/test/APINotes/nullability.m @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +#import <SomeKit/SomeKit.h> + + +int main() { + A *a; + + [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + + return 0; +} + diff --git a/clang/test/APINotes/objc_designated_inits.m b/clang/test/APINotes/objc_designated_inits.m new file mode 100644 index 0000000000000..194d135b6c0da --- /dev/null +++ b/clang/test/APINotes/objc_designated_inits.m @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +#include "HeaderLib.h" +#import <SomeKit/SomeKit.h> + +@interface CSub : C +-(instancetype)initWithA:(A*)a; +@end + +@implementation CSub +-(instancetype)initWithA:(A*)a { // expected-warning{{designated initializer missing a 'super' call to a designated initializer of the super class}} + // expected-note@SomeKit/SomeKit.h:20 2{{method marked as designated initializer of the class here}} + self = [super init]; // expected-warning{{designated initializer invoked a non-designated initializer}} + return self; +} +@end diff --git a/clang/test/APINotes/yaml-convert-diags.c b/clang/test/APINotes/yaml-convert-diags.c new file mode 100644 index 0000000000000..e8767f228e5e9 --- /dev/null +++ b/clang/test/APINotes/yaml-convert-diags.c @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: not %clang_cc1 -fsyntax-only -fapinotes -fapinotes-cache-path=%t %s -I %S/Inputs/BrokenHeaders2 2>&1 | FileCheck %s + +#include "SomeBrokenLib.h" + +// CHECK: error: multiple definitions of global function 'do_something_with_pointers' diff --git a/clang/test/APINotes/yaml-os-availability.c b/clang/test/APINotes/yaml-os-availability.c new file mode 100644 index 0000000000000..62301af3a1ea4 --- /dev/null +++ b/clang/test/APINotes/yaml-os-availability.c @@ -0,0 +1,31 @@ +# RUN: %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t-ios.apinotesc %S/Inputs/os-availability.apinotes +# RUN: %clang -cc1apinotes -binary-to-yaml %t-ios.apinotesc -o %t.os-availability-ios.apinotes +# RUN: FileCheck %s -check-prefix=IOS < %t.os-availability-ios.apinotes + +# RUN: %clang -cc1apinotes -yaml-to-binary -target x86_64-apple-macosx10.9 -o %t-osx.apinotesc %S/Inputs/os-availability.apinotes +# RUN: %clang -cc1apinotes -binary-to-yaml %t-osx.apinotesc -o %t.os-availability-osx.apinotes +# RUN: FileCheck %s -check-prefix=OSX < %t.os-availability-osx.apinotes + +# IOS: Foundation +# IOS: NSArray +# IOS: initWithObjects +# IOS: familyNameios +# IOS: fontName +# IOS: NSCountedSet +# IOS: UIApplicationDelegate +# IOS: UIApplicationDelegateIOS +# IOS: NSAvailableWindowDepths +# IOS: NSAvailableWindowDepthsiOS +# IOS-NOT: NSCalibratedWhiteColorSpace + +# OSX: Foundation +# qqOSX: NSArray +# OSX-NOT: initWithObjects +# OSX-NOT: familyNameios +# OSX: fontName +# OSX-NOT: NSCountedSet +# OSX: UIApplicationDelegate +# OSX-NOT: UIApplicationDelegateIOS +# OSX: NSAvailableWindowDepths +# OSX-NOT: NSAvailableWindowDepthsiOS +# OSX: NSCalibratedWhiteColorSpace diff --git a/clang/test/APINotes/yaml-parse-diags.c b/clang/test/APINotes/yaml-parse-diags.c new file mode 100644 index 0000000000000..4505e293ef898 --- /dev/null +++ b/clang/test/APINotes/yaml-parse-diags.c @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fapinotes -fapinotes-cache-path=%t %s -I %S/Inputs/BrokenHeaders -verify + +#include "SomeBrokenLib.h" + +// expected-error@APINotes.apinotes:4{{unknown key 'Nu llabilityOfRet'}} diff --git a/clang/test/APINotes/yaml-reader-errors.c b/clang/test/APINotes/yaml-reader-errors.c new file mode 100644 index 0000000000000..51dfe3aed841f --- /dev/null +++ b/clang/test/APINotes/yaml-reader-errors.c @@ -0,0 +1,65 @@ +# RUN: not %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t.apinotesc %s > %t.err 2>&1 +# RUN: FileCheck %s < %t.err + +--- +Name: UIKit +Availability: iOS +AvailabilityMsg: iOSOnly +Classes: + - Name: UIFont + Availability: iOS + AvailabilityMsg: iOSOnly + Methods: + - Selector: 'fontWithName:size:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: O + Availability: iOS + AvailabilityMsg: iOSOnly + DesignatedInit: true +# CHECK: duplicate definition of method '-[UIFont fontWithName:size:]' + - Selector: 'fontWithName:size:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: O + Availability: iOS + AvailabilityMsg: iOSOnly + DesignatedInit: true + Properties: + - Name: familyName + Nullability: N + Availability: iOS + AvailabilityMsg: iOSOnly + - Name: fontName + Nullability: N + Availability: iOS + AvailabilityMsg: iOSOnly +# CHECK: duplicate definition of property 'UIFont.familyName' + - Name: familyName + Nullability: N + Availability: iOS + AvailabilityMsg: iOSOnly +# CHECK: multiple definitions of class 'UIFont' + - Name: UIFont +Protocols: + - Name: MyProto + AuditedForNullability: true +# CHECK: multiple definitions of protocol 'MyProto' + - Name: MyProto + AuditedForNullability: true +Functions: + - Name: 'globalFoo' + Nullability: [ N, N, O, S ] + NullabilityOfRet: O + Availability: iOS + AvailabilityMsg: iOSOnly + - Name: 'globalFoo2' + Nullability: [ N, N, O, S ] + NullabilityOfRet: O +Globals: + - Name: globalVar + Nullability: O + Availability: iOS + AvailabilityMsg: iOSOnly + - Name: globalVar2 + Nullability: O diff --git a/clang/test/APINotes/yaml-reader-test.c b/clang/test/APINotes/yaml-reader-test.c new file mode 100644 index 0000000000000..01ad90a5a2292 --- /dev/null +++ b/clang/test/APINotes/yaml-reader-test.c @@ -0,0 +1,102 @@ +# RUN: %clang -cc1apinotes -dump %s | FileCheck %s +--- +Name: UIKit +Availability: iOS +AvailabilityMsg: iOSOnly +Classes: + - Name: UIFont + Availability: iOS + AvailabilityMsg: iOSOnly + Methods: + - Selector: 'fontWithName:size:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: O + Availability: iOS + AvailabilityMsg: iOSOnly + DesignatedInit: true + Properties: + - Name: familyName + Nullability: N + Availability: iOS + AvailabilityMsg: iOSOnly + - Name: fontName + Nullability: N + Availability: iOS + AvailabilityMsg: iOSOnly +Protocols: + - Name: MyProto + AuditedForNullability: true + - Name: MyProto2 + AuditedForNullability: true +Functions: + - Name: 'globalFoo' + Nullability: [ N, N, O, S ] + NullabilityOfRet: O + Availability: iOS + AvailabilityMsg: iOSOnly + - Name: 'globalFoo2' + Nullability: [ N, N, O, S ] + NullabilityOfRet: O +Globals: + - Name: globalVar + Nullability: O + Availability: iOS + AvailabilityMsg: iOSOnly + - Name: globalVar2 + Nullability: O + + +# CHECK: Name: UIKit +# CHECK: Availability: iOS +# CHECK: AvailabilityMsg: iOSOnly +# CHECK: Classes: +# CHECK: - Name: UIFont +# CHECK: Availability: iOS +# CHECK: AvailabilityMsg: iOSOnly +# CHECK: Methods: +# CHECK: - Selector: 'fontWithName:size:' +# CHECK: MethodKind: Instance +# CHECK: Nullability: [ N ] +# CHECK: NullabilityOfRet: O +# CHECK: Availability: iOS +# CHECK: AvailabilityMsg: iOSOnly +# CHECK: DesignatedInit: true +# CHECK: Properties: +# CHECK: - Name: familyName +# CHECK: Nullability: N +# CHECK: Availability: iOS +# CHECK: AvailabilityMsg: iOSOnly +# CHECK: - Name: fontName +# CHECK: Nullability: N +# CHECK: Availability: iOS +# CHECK: AvailabilityMsg: iOSOnly +# CHECK:Protocols: +# CHECK: - Name: MyProto +# CHECK: AuditedForNullability: true +# CHECK: Availability: available +# CHECK: AvailabilityMsg: '' +# CHECK: - Name: MyProto2 +# CHECK: AuditedForNullability: true +# CHECK: Availability: available +# CHECK: AvailabilityMsg: '' +# CHECK:Functions: +# CHECK: - Name: globalFoo +# CHECK: Nullability: [ N, N, O, U ] +# CHECK: NullabilityOfRet: O +# CHECK: Availability: iOS +# CHECK: AvailabilityMsg: iOSOnly +# CHECK: - Name: globalFoo2 +# CHECK: Nullability: [ N, N, O, U ] +# CHECK: NullabilityOfRet: O +# CHECK: Availability: available +# CHECK: AvailabilityMsg: '' +# CHECK:Globals: +# CHECK: - Name: globalVar +# CHECK: Nullability: O +# CHECK: Availability: iOS +# CHECK: AvailabilityMsg: iOSOnly +# CHECK: - Name: globalVar2 +# CHECK: Nullability: O +# CHECK: Availability: available +# CHECK: AvailabilityMsg: diff --git a/clang/test/APINotes/yaml-roundtrip.c b/clang/test/APINotes/yaml-roundtrip.c new file mode 100644 index 0000000000000..b721b6a6e0167 --- /dev/null +++ b/clang/test/APINotes/yaml-roundtrip.c @@ -0,0 +1,10 @@ +# RUN: %clang -cc1apinotes -yaml-to-binary -o %t.apinotesc %S/Inputs/roundtrip.apinotes +# RUN: %clang -cc1apinotes -binary-to-yaml -o %t.apinotes %t.apinotesc + +# Handle the infurating '...' the YAML writer adds but the parser +# can't read. + +# RUN: cp %S/Inputs/roundtrip.apinotes %t-reference.apinotes +# RUN: echo "..." >> %t-reference.apinotes +# RUN: diff %t-reference.apinotes %t.apinotes + diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c index 5cc769faad975..9abfb51955d91 100644 --- a/clang/test/Misc/warning-flags.c +++ b/clang/test/Misc/warning-flags.c @@ -18,7 +18,7 @@ This test serves two purposes: The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (85): +CHECK: Warnings without flags (86): CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list @@ -40,6 +40,7 @@ CHECK-NEXT: pp_out_of_date_dependency CHECK-NEXT: pp_poisoning_existing_macro CHECK-NEXT: w_asm_qualifier_ignored CHECK-NEXT: warn_accessor_property_type_mismatch +CHECK-NEXT: warn_apinotes_message CHECK-NEXT: warn_arcmt_nsalloc_realloc CHECK-NEXT: warn_asm_label_on_auto_decl CHECK-NEXT: warn_c_kext diff --git a/clang/test/Sema/attr-availability.c b/clang/test/Sema/attr-availability.c index d003e1e2e363b..e9ebf7fd54dbe 100644 --- a/clang/test/Sema/attr-availability.c +++ b/clang/test/Sema/attr-availability.c @@ -81,6 +81,21 @@ extern int x2 __attribute__((availability(macosx,introduced=10.2))); // expected extern int x2 __attribute__((availability(macosx,introduced=10.5))); // expected-warning {{availability does not match previous declaration}} + +#if __has_feature(attribute_availability_swift) +# warning "okay" +// expected-warning@-1{{okay}} +#else +# error "Missing __has_feature" +#endif + + +extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable))); +extern int noSwiftGlobal1 __attribute__((availability(macosx, introduced=10.1))); // okay +extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable, message="and this one has a message"))); // okay + +extern int noSwiftGlobal2 __attribute__((availability(swift, introduced=5))); // expected-warning{{only 'unavailable' is supported for Swift availability}} + enum Original { OriginalDeprecated __attribute__((availability(macosx, deprecated=10.2))), // expected-note + {{'OriginalDeprecated' has been explicitly marked deprecated here}} OriginalUnavailable __attribute__((availability(macosx, unavailable))) // expected-note + {{'OriginalUnavailable' has been explicitly marked unavailable here}} diff --git a/clang/test/Sema/attr-noescape.c b/clang/test/Sema/attr-noescape.c new file mode 100644 index 0000000000000..ec367b652543c --- /dev/null +++ b/clang/test/Sema/attr-noescape.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 %s -fblocks -verify -fsyntax-only + +#if !__has_attribute(noescape) +# error "missing noescape attribute" +#endif + +int *global_var __attribute((noescape)); // expected-warning{{'noescape' attribute only applies to parameters}} + +void foo(__attribute__((noescape)) int *int_ptr, + __attribute__((noescape)) int (^block)(int), + __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute ignored on parameter of non-pointer type 'int'}} +} diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m new file mode 100644 index 0000000000000..98c705423f120 --- /dev/null +++ b/clang/test/SemaObjC/attr-swift.m @@ -0,0 +1,158 @@ +// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s + +// --- swift_private --- + +__attribute__((swift_private)) +@protocol FooProto +@end + +__attribute__((swift_private)) +@interface Foo +@end + +@interface Bar +@property id prop __attribute__((swift_private)); +- (void)instMethod __attribute__((swift_private)); ++ (instancetype)bar __attribute__((swift_private)); +@end + +void function(id) __attribute__((swift_private)); + +struct __attribute__((swift_private)) Point { + int x; + int y; +}; + +enum __attribute__((swift_private)) Colors { + Red, Green, Blue +}; + +typedef struct { + float x, y, z; +} Point3D __attribute__((swift_private)); + + +// --- swift_name --- + +__attribute__((swift_name("SNFooType"))) +@protocol SNFoo +@end + +__attribute__((swift_name("SNFooClass"))) +@interface SNFoo <SNFoo> +- (instancetype)init __attribute__((swift_name("init()"))); +- (instancetype)initWithValue:(int)value __attribute__((swift_name("fooWithValue(_:)"))); + ++ (void)refresh __attribute__((swift_name("refresh()"))); + ++ (instancetype)foo __attribute__((swift_name("foo()"))); ++ (SNFoo *)fooWithValue:(int)value __attribute__((swift_name("foo(value:)"))); ++ (SNFoo *)fooWithValue:(int)value value:(int)value2 __attribute__((swift_name("foo(value:extra:)"))); ++ (SNFoo *)fooWithConvertingValue:(int)value value:(int)value2 __attribute__((swift_name("init(_:extra:)"))); + ++ (SNFoo *)fooWithOtherValue:(int)value __attribute__((swift_name("init"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (SNFoo *)fooWithAnotherValue:(int)value __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}} ++ (SNFoo *)fooWithYetAnotherValue:(int)value __attribute__((swift_name("foo(value:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 1; got 2)}} + ++ (SNFoo *)fooAndReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning ++ (SNFoo *)fooWithValue:(int)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(value:)"))); // no-warning ++ (SNFoo *)fooFromErrorCode:(const int *)errorCode __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}} ++ (SNFoo *)fooWithValue:(int)value fromErrorCode:(const int *)errorCode __attribute__((swift_name("foo(value:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} ++ (SNFoo *)fooWithPointerA:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning ++ (SNFoo *)fooWithPointerB:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:)"))); // no-warning ++ (SNFoo *)fooWithPointerC:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:errorCode:)"))); // no-warning ++ (SNFoo *)fooWithOtherFoo:(SNFoo *)other __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}} + ++ (instancetype)specialFoo __attribute__((swift_name("init(options:)"))); ++ (instancetype)specialBar __attribute__((swift_name("init(options:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 2)}} ++ (instancetype)specialBaz __attribute__((swift_name("init(_:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}} ++ (instancetype)specialGarply __attribute__((swift_name("foo(options:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}} + ++ (instancetype)trailingParen __attribute__((swift_name("foo("))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (instancetype)trailingColon:(int)value __attribute__((swift_name("foo(value)"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (instancetype)initialIgnore:(int)value __attribute__((swift_name("_(value:)"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (instancetype)middleOmitted:(int)value __attribute__((swift_name("foo(:)"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} + +@property(strong) id someProp __attribute__((swift_name("prop"))); +@end + +enum __attribute__((swift_name("MoreColors"))) MoreColors { + Cyan, + Magenta, + Yellow __attribute__((swift_name("RoseGold"))), + Black __attribute__((swift_name("SpaceGrey()"))) // expected-error {{parameter of 'swift_name' attribute must be an ASCII identifier string}} +}; + +struct __attribute__((swift_name("FooStruct"))) BarStruct { + int x, y, z __attribute__((swift_name("zed"))); +}; + +int global_int __attribute__((swift_name("GlobalInt"))); + +void foo1(int i) __attribute__((swift_name("foo"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +void foo2(int i) __attribute__((swift_name("foo()"))); // expected-warning{{too few parameters in 'swift_name' attribute (expected 1; got 0)}} +void foo2(int i) __attribute__((swift_name("foo(a:b:)"))); // expected-warning{{too many parameters in 'swift_name' attribute (expected 1; got 2)}} +void foo3(int i, int j) __attribute__((swift_name("fooWithX(_:y:)"))); // okay +void foo4(int i, int *error) __attribute__((swift_name("fooWithA(_:)"))); // okay + +typedef int some_int_type __attribute__((swift_name("SomeInt"))); + +// --- swift_error --- + +@class NSError; + +typedef struct __attribute__((objc_bridge(NSError))) __CFError *CFErrorRef; + +@interface Erroneous +- (_Bool) tom0: (NSError**) err __attribute__((swift_error(none))); +- (_Bool) tom1: (NSError**) err __attribute__((swift_error(nonnull_error))); +- (_Bool) tom2: (NSError**) err __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute with 'null_result' convention can only be applied to a method returning a pointer}} +- (_Bool) tom3: (NSError**) err __attribute__((swift_error(nonzero_result))); +- (_Bool) tom4: (NSError**) err __attribute__((swift_error(zero_result))); + +- (Undeclared) richard0: (NSError**) err __attribute__((swift_error(none))); // expected-error {{expected a type}} +- (Undeclared) richard1: (NSError**) err __attribute__((swift_error(nonnull_error))); // expected-error {{expected a type}} +- (Undeclared) richard2: (NSError**) err __attribute__((swift_error(null_result))); // expected-error {{expected a type}} +// FIXME: the follow-on warnings should really be suppressed, but apparently having an ill-formed return type doesn't mark anything as invalid +- (Undeclared) richard3: (NSError**) err __attribute__((swift_error(nonzero_result))); // expected-error {{expected a type}} expected-error {{can only be applied}} +- (Undeclared) richard4: (NSError**) err __attribute__((swift_error(zero_result))); // expected-error {{expected a type}} expected-error {{can only be applied}} + +- (instancetype) harry0: (NSError**) err __attribute__((swift_error(none))); +- (instancetype) harry1: (NSError**) err __attribute__((swift_error(nonnull_error))); +- (instancetype) harry2: (NSError**) err __attribute__((swift_error(null_result))); +- (instancetype) harry3: (NSError**) err __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute with 'nonzero_result' convention can only be applied to a method returning an integral type}} +- (instancetype) harry4: (NSError**) err __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute with 'zero_result' convention can only be applied to a method returning an integral type}} + +- (instancetype) harry0 __attribute__((swift_error(none))); +- (instancetype) harry1 __attribute__((swift_error(nonnull_error))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}} +- (instancetype) harry2 __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}} +- (instancetype) harry3 __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}} +- (instancetype) harry4 __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}} +@end + +extern _Bool tom0(CFErrorRef *) __attribute__((swift_error(none))); +extern _Bool tom1(CFErrorRef *) __attribute__((swift_error(nonnull_error))); +extern _Bool tom2(CFErrorRef *) __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute with 'null_result' convention can only be applied to a function returning a pointer}} +extern _Bool tom3(CFErrorRef *) __attribute__((swift_error(nonzero_result))); +extern _Bool tom4(CFErrorRef *) __attribute__((swift_error(zero_result))); + +extern Undeclared richard0(CFErrorRef *) __attribute__((swift_error(none))); // expected-error {{unknown type name 'Undeclared'}} +extern Undeclared richard1(CFErrorRef *) __attribute__((swift_error(nonnull_error))); // expected-error {{unknown type name 'Undeclared'}} +extern Undeclared richard2(CFErrorRef *) __attribute__((swift_error(null_result))); // expected-error {{unknown type name 'Undeclared'}} +extern Undeclared richard3(CFErrorRef *) __attribute__((swift_error(nonzero_result))); // expected-error {{unknown type name 'Undeclared'}} +extern Undeclared richard4(CFErrorRef *) __attribute__((swift_error(zero_result))); // expected-error {{unknown type name 'Undeclared'}} + +extern void *harry0(CFErrorRef *) __attribute__((swift_error(none))); +extern void *harry1(CFErrorRef *) __attribute__((swift_error(nonnull_error))); +extern void *harry2(CFErrorRef *) __attribute__((swift_error(null_result))); +extern void *harry3(CFErrorRef *) __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute with 'nonzero_result' convention can only be applied to a function returning an integral type}} +extern void *harry4(CFErrorRef *) __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute with 'zero_result' convention can only be applied to a function returning an integral type}} + +extern void *wilma0(void) __attribute__((swift_error(none))); +extern void *wilma1(void) __attribute__((swift_error(nonnull_error))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}} +extern void *wilma2(void) __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}} +extern void *wilma3(void) __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}} +extern void *wilma4(void) __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}} + + +extern _Bool suzanne __attribute__((swift_error(none))); // expected-error {{'swift_error' attribute only applies to functions and methods}} diff --git a/clang/test/SemaObjC/subclassing-restricted-attr.m b/clang/test/SemaObjC/subclassing-restricted-attr.m new file mode 100644 index 0000000000000..2e77df8763aab --- /dev/null +++ b/clang/test/SemaObjC/subclassing-restricted-attr.m @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -verify -Wno-objc-root-class %s +// rdar://16560476 + +__attribute__((objc_subclassing_restricted)) +@interface Leaf // okay +@end + +__attribute__((objc_subclassing_restricted)) +@interface SubClassOfLeaf : Leaf // expected-note {{class is declared here}} +@end + + +@interface SubClass : SubClassOfLeaf // expected-error {{cannot subclass a class with objc_subclassing_restricted attribute}} +@end + +__attribute__((objc_root_class)) +@interface PlainRoot +@end + +__attribute__((objc_subclassing_restricted)) +@interface Sub2Class : PlainRoot // okay +@end diff --git a/clang/tools/arcmt-test/Makefile b/clang/tools/arcmt-test/Makefile index d9d44bb05bd26..ec7683b368221 100644 --- a/clang/tools/arcmt-test/Makefile +++ b/clang/tools/arcmt-test/Makefile @@ -21,6 +21,6 @@ LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangARCMigrate.a clangRewrite.a \ clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a \ - clangBasic.a + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/c-arcmt-test/Makefile b/clang/tools/c-arcmt-test/Makefile index 03e0c9e58c4d5..ec5e122d0d1a5 100644 --- a/clang/tools/c-arcmt-test/Makefile +++ b/clang/tools/c-arcmt-test/Makefile @@ -45,6 +45,7 @@ USEDLIBS = clang.a \ clangFrontend.a clangDriver.a \ clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ clangSerialization.a clangParse.a clangSema.a \ - clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a + clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangAPINotes.a \ + clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/c-index-test/Makefile b/clang/tools/c-index-test/Makefile index b757b477958e3..7e133770b1c77 100644 --- a/clang/tools/c-index-test/Makefile +++ b/clang/tools/c-index-test/Makefile @@ -42,7 +42,7 @@ USEDLIBS = clang.a \ clangToolingCore.a \ clangSerialization.a clangParse.a clangSema.a \ clangAnalysis.a clangEdit.a clangAST.a clangLex.a \ - clangBasic.a + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/clang-check/CMakeLists.txt b/clang/tools/clang-check/CMakeLists.txt index 04151a8e0331d..233f9818fc963 100644 --- a/clang/tools/clang-check/CMakeLists.txt +++ b/clang/tools/clang-check/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_executable(clang-check ) target_link_libraries(clang-check + clangAPINotes clangAST clangBasic clangDriver diff --git a/clang/tools/clang-check/Makefile b/clang/tools/clang-check/Makefile index da010ab1f32a5..f2e280d920b45 100644 --- a/clang/tools/clang-check/Makefile +++ b/clang/tools/clang-check/Makefile @@ -23,6 +23,6 @@ USEDLIBS = clangFrontend.a clangCodeGen.a clangIndex.a \ clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ clangStaticAnalyzerCore.a clangAnalysis.a clangRewriteFrontend.a \ clangRewrite.a clangEdit.a clangAST.a clangLex.a \ - clangBasic.a + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/clang-format/Makefile b/clang/tools/clang-format/Makefile index 76e31cc1a0725..58642f140b920 100644 --- a/clang/tools/clang-format/Makefile +++ b/clang/tools/clang-format/Makefile @@ -17,6 +17,6 @@ TOOL_NO_EXPORTS = 1 include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangFormat.a clangToolingCore.a clangDriver.a clangRewrite.a \ - clangLex.a clangBasic.a + clangLex.a clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/diagtool/Makefile b/clang/tools/diagtool/Makefile index d49e976e6428b..b5026210433d0 100644 --- a/clang/tools/diagtool/Makefile +++ b/clang/tools/diagtool/Makefile @@ -20,7 +20,7 @@ include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ clangSema.a clangAnalysis.a clangEdit.a clangAST.a clangLex.a \ - clangBasic.a + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/driver/CMakeLists.txt b/clang/tools/driver/CMakeLists.txt index a57b22e0a26d4..73bebb6c73689 100644 --- a/clang/tools/driver/CMakeLists.txt +++ b/clang/tools/driver/CMakeLists.txt @@ -28,10 +28,12 @@ add_clang_executable(clang driver.cpp cc1_main.cpp cc1as_main.cpp + apinotes_main.cpp ) target_link_libraries(clang clangBasic + clangAPINotes clangCodeGen clangDriver clangFrontend diff --git a/clang/tools/driver/Makefile b/clang/tools/driver/Makefile index 347702eb9611e..d2626a1154fa8 100644 --- a/clang/tools/driver/Makefile +++ b/clang/tools/driver/Makefile @@ -47,7 +47,8 @@ ifeq ($(ENABLE_CLANG_ARCMT),1) USEDLIBS += clangARCMigrate.a endif -USEDLIBS += clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a +USEDLIBS += clangAnalysis.a clangEdit.a clangAST.a clangLex.a \ + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/tools/driver/apinotes_main.cpp b/clang/tools/driver/apinotes_main.cpp new file mode 100644 index 0000000000000..76892b69ba4d7 --- /dev/null +++ b/clang/tools/driver/apinotes_main.cpp @@ -0,0 +1,149 @@ +//===-- api_notes.cpp - API Notes Driver ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides conversion between the YAML (source) and binary forms +/// of API notes. +/// +//===----------------------------------------------------------------------===// +#include "clang/APINotes/APINotesYAMLCompiler.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Triple.h" + +using namespace llvm; +namespace api_notes = clang::api_notes; + +int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0, + void *MainAddr) { + + // Mark all our options with this category, everything else (except for + // -version and -help) will be hidden. + static cl::OptionCategory APINotesCategory("API Notes options"); + + static cl::opt<api_notes::ActionType> + Action(cl::desc("Mode:"), cl::init(api_notes::ActionType::None), + cl::values( + clEnumValN(api_notes::ActionType::YAMLToBinary, + "yaml-to-binary", + "Convert YAML to binary format"), + clEnumValN(api_notes::ActionType::BinaryToYAML, + "binary-to-yaml", + "Convert binary format to YAML"), + clEnumValN(api_notes::ActionType::Dump, + "dump", + "Parse and dump the output"), + clEnumValEnd), + cl::cat(APINotesCategory)); + + static cl::opt<std::string> + InputFilename(cl::Positional, cl::desc("<input file>"), + cl::Required, cl::cat(APINotesCategory)); + + static cl::opt<std::string> + Target("target", cl::desc("Generate binary format for the given target"), + cl::cat(APINotesCategory)); + + static cl::opt<std::string> + OutputFilename("o", cl::desc("Output file name"), cl::cat(APINotesCategory)); + + cl::HideUnrelatedOptions(APINotesCategory); + + SmallVector<const char *, 4> Args; + Args.push_back(Argv0); + Args.append(Argv.begin(), Argv.end()); + cl::ParseCommandLineOptions(Args.size(), + Args.data(), + "Clang API Notes Tool\n"); + + if (Action == clang::api_notes::ActionType::None) { + errs() << "action required\n"; + cl::PrintHelpMessage(); + return 1; + } + + auto fileBufOrErr = MemoryBuffer::getFile(InputFilename); + if (std::error_code EC = fileBufOrErr.getError()) { + llvm::errs() << "\n Could not open input file: " + EC.message() << '\n'; + return true; + } + StringRef input = fileBufOrErr.get()->getBuffer(); + + switch (Action) { + case api_notes::ActionType::None: + llvm_unreachable("handled above"); + + case api_notes::ActionType::YAMLToBinary: { + if (OutputFilename.empty()) { + errs() << "output file is required\n"; + cl::PrintHelpMessage(); + return 1; + } + + api_notes::OSType targetOS = api_notes::OSType::Absent; + // TODO: Check that we've specified the target. + if (!Target.empty()) { + llvm::Triple target(llvm::Triple::normalize(Target)); + switch (target.getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + targetOS = api_notes::OSType::OSX; + break; + case llvm::Triple::IOS: + targetOS = api_notes::OSType::IOS; + break; + default: + errs() << "target is not supported\n"; + return 1; + } + } + std::error_code EC; + llvm::raw_fd_ostream os(OutputFilename, EC, + llvm::sys::fs::OpenFlags::F_None); + + if (api_notes::compileAPINotes(input, os, targetOS)) + return 1; + + os.flush(); + + return os.has_error(); + } + + case api_notes::ActionType::BinaryToYAML: { + if (OutputFilename.empty()) { + errs() << "output file required\n"; + cl::PrintHelpMessage(); + return 1; + } + + std::error_code EC; + llvm::raw_fd_ostream os(OutputFilename, EC, + llvm::sys::fs::OpenFlags::F_None); + + if (api_notes::decompileAPINotes(std::move(fileBufOrErr.get()), os)) + return 1; + + os.flush(); + + return os.has_error(); + } + + case api_notes::ActionType::Dump: + return api_notes::parseAndDumpAPINotes(input); + } + + return 1; +} + diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index ea218d5403d83..00ba309edaf31 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -200,6 +200,8 @@ extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr); extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr); +extern int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0, + void *MainAddr); static void insertTargetAndModeArgs(StringRef Target, StringRef Mode, SmallVectorImpl<const char *> &ArgVector, @@ -301,6 +303,8 @@ static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) { return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); if (Tool == "as") return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); + if (Tool == "apinotes") + return cc1apinotes_main(argv.slice(2), argv[0], GetExecutablePathVP); // Reject unknown tools. llvm::errs() << "error: unknown integrated tool '" << Tool << "'\n"; diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt index e8c247bf4264d..07fc17e0a4e1d 100644 --- a/clang/tools/libclang/CMakeLists.txt +++ b/clang/tools/libclang/CMakeLists.txt @@ -39,6 +39,7 @@ set(SOURCES set(LIBS clangAST + clangAPINotes clangBasic clangFrontend clangIndex diff --git a/clang/tools/libclang/Makefile b/clang/tools/libclang/Makefile index 84914e0f4609f..1b8f5f924f757 100644 --- a/clang/tools/libclang/Makefile +++ b/clang/tools/libclang/Makefile @@ -30,7 +30,7 @@ USEDLIBS = clangIndex.a clangARCMigrate.a \ clangRewrite.a \ clangAnalysis.a clangEdit.a \ clangASTMatchers.a \ - clangAST.a clangLex.a clangBasic.a + clangAST.a clangLex.a clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp index 87aeef47c61fb..67b80acf46bbf 100644 --- a/clang/unittests/AST/DeclTest.cpp +++ b/clang/unittests/AST/DeclTest.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "MatchVerifier.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" @@ -57,3 +58,53 @@ TEST(Decl, CleansUpAPValues) { "constexpr _Complex __uint128_t c = 0xffffffffffffffff;", Args)); } + +TEST(Decl, Availability) { + const char *CodeStr = "int x __attribute__((availability(macosx, " + "introduced=10.2, deprecated=10.8, obsoleted=10.10)));"; + auto Matcher = varDecl(hasName("x")); + std::vector<std::string> Args = {"-target", "x86_64-apple-macosx10.9"}; + + class AvailabilityVerifier : public MatchVerifier<clang::VarDecl> { + public: + void verify(const MatchFinder::MatchResult &Result, + const clang::VarDecl &Node) override { + if (Node.getAvailability(nullptr, clang::VersionTuple(10, 1)) != + clang::AR_NotYetIntroduced) { + setFailure("failed introduced"); + } + if (Node.getAvailability(nullptr, clang::VersionTuple(10, 2)) != + clang::AR_Available) { + setFailure("failed available (exact)"); + } + if (Node.getAvailability(nullptr, clang::VersionTuple(10, 3)) != + clang::AR_Available) { + setFailure("failed available"); + } + if (Node.getAvailability(nullptr, clang::VersionTuple(10, 8)) != + clang::AR_Deprecated) { + setFailure("failed deprecated (exact)"); + } + if (Node.getAvailability(nullptr, clang::VersionTuple(10, 9)) != + clang::AR_Deprecated) { + setFailure("failed deprecated"); + } + if (Node.getAvailability(nullptr, clang::VersionTuple(10, 10)) != + clang::AR_Unavailable) { + setFailure("failed obsoleted (exact)"); + } + if (Node.getAvailability(nullptr, clang::VersionTuple(10, 11)) != + clang::AR_Unavailable) { + setFailure("failed obsoleted"); + } + + if (Node.getAvailability() != clang::AR_Deprecated) + setFailure("did not default to target OS version"); + + setSuccess(); + } + }; + + AvailabilityVerifier Verifier; + EXPECT_TRUE(Verifier.match(CodeStr, Matcher, Args, Lang_C)); +} diff --git a/clang/unittests/AST/Makefile b/clang/unittests/AST/Makefile index e3b3d7dc33e8c..a306ac94568d4 100644 --- a/clang/unittests/AST/Makefile +++ b/clang/unittests/AST/Makefile @@ -14,6 +14,7 @@ LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ clangRewrite.a clangRewriteFrontend.a \ clangParse.a clangSema.a clangAnalysis.a \ - clangEdit.a clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + clangEdit.a clangAST.a clangASTMatchers.a clangLex.a \ + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/ASTMatchers/Dynamic/Makefile b/clang/unittests/ASTMatchers/Dynamic/Makefile index df253b8a1d32a..d0c48528f05ee 100644 --- a/clang/unittests/ASTMatchers/Dynamic/Makefile +++ b/clang/unittests/ASTMatchers/Dynamic/Makefile @@ -17,6 +17,7 @@ USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ clangDynamicASTMatchers.a \ clangAnalysis.a clangEdit.a clangAST.a clangASTMatchers.a \ clangLex.a \ - clangBasic.a + clangBasic.a \ + clangAPINotes.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/ASTMatchers/Makefile b/clang/unittests/ASTMatchers/Makefile index 92f2fa0e5d179..accbca0552e49 100644 --- a/clang/unittests/ASTMatchers/Makefile +++ b/clang/unittests/ASTMatchers/Makefile @@ -17,6 +17,7 @@ LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ clangRewrite.a clangRewriteFrontend.a \ clangParse.a clangSema.a clangAnalysis.a \ - clangEdit.a clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + clangEdit.a clangAST.a clangASTMatchers.a clangLex.a \ + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/CodeGen/Makefile b/clang/unittests/CodeGen/Makefile index de347e1afddc9..01bdf76789676 100644 --- a/clang/unittests/CodeGen/Makefile +++ b/clang/unittests/CodeGen/Makefile @@ -15,6 +15,6 @@ LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader mc option \ USEDLIBS = clangCodeGen.a clangFrontend.a clangSerialization.a \ clangDriver.a \ clangParse.a clangSema.a clangAnalysis.a \ - clangEdit.a clangAST.a clangLex.a clangBasic.a + clangEdit.a clangAST.a clangLex.a clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Format/Makefile b/clang/unittests/Format/Makefile index f95d6d34127bc..7029ea7002834 100644 --- a/clang/unittests/Format/Makefile +++ b/clang/unittests/Format/Makefile @@ -14,6 +14,6 @@ LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangFormat.a clangTooling.a clangToolingCore.a clangFrontend.a \ clangSerialization.a clangDriver.a clangParse.a clangRewrite.a \ clangRewriteFrontend.a clangSema.a clangAnalysis.a clangEdit.a \ - clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + clangAST.a clangASTMatchers.a clangLex.a clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Frontend/Makefile b/clang/unittests/Frontend/Makefile index a63ae18245349..a6b6091a6dda8 100644 --- a/clang/unittests/Frontend/Makefile +++ b/clang/unittests/Frontend/Makefile @@ -16,6 +16,6 @@ USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ clangARCMigrate.a clangRewrite.a \ clangRewriteFrontend.a clangEdit.a \ - clangAnalysis.a clangAST.a clangLex.a clangBasic.a + clangAnalysis.a clangAST.a clangLex.a clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Lex/CMakeLists.txt b/clang/unittests/Lex/CMakeLists.txt index 461e0d95fc87e..68730b40d7ca0 100644 --- a/clang/unittests/Lex/CMakeLists.txt +++ b/clang/unittests/Lex/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries(LexTests clangAST clangBasic clangLex + clangAPINotes clangParse clangSema ) diff --git a/clang/unittests/Lex/Makefile b/clang/unittests/Lex/Makefile index 071d01c8b567b..c34ef76de7bb5 100644 --- a/clang/unittests/Lex/Makefile +++ b/clang/unittests/Lex/Makefile @@ -11,6 +11,6 @@ CLANG_LEVEL = ../.. TESTNAME = Lex LINK_COMPONENTS := mcparser support mc bitreader USEDLIBS = clangParse.a clangSema.a clangAnalysis.a clangEdit.a \ - clangSerialization.a clangAST.a clangLex.a clangBasic.a + clangSerialization.a clangAST.a clangLex.a clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Sema/Makefile b/clang/unittests/Sema/Makefile index 7fd5c27ad607b..ef8852dd90860 100644 --- a/clang/unittests/Sema/Makefile +++ b/clang/unittests/Sema/Makefile @@ -14,6 +14,7 @@ LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ clangRewrite.a clangRewriteFrontend.a \ clangParse.a clangSema.a clangAnalysis.a \ - clangEdit.a clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + clangEdit.a clangAST.a clangASTMatchers.a clangLex.a \ + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Tooling/Makefile b/clang/unittests/Tooling/Makefile index 514e80bd03098..93483d93cadd6 100644 --- a/clang/unittests/Tooling/Makefile +++ b/clang/unittests/Tooling/Makefile @@ -15,6 +15,6 @@ USEDLIBS = clangTooling.a clangToolingCore.a clangFrontend.a \ clangSerialization.a clangDriver.a \ clangParse.a clangRewrite.a clangRewriteFrontend.a \ clangSema.a clangAnalysis.a clangEdit.a \ - clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + clangAST.a clangASTMatchers.a clangLex.a clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/libclang/Makefile b/clang/unittests/libclang/Makefile index 037dea34d299f..4dbafb7a2d075 100644 --- a/clang/unittests/libclang/Makefile +++ b/clang/unittests/libclang/Makefile @@ -32,6 +32,6 @@ USEDLIBS = clang.a \ clangToolingCore.a \ clangSerialization.a clangParse.a clangSema.a \ clangAnalysis.a clangEdit.a clangAST.a clangLex.a \ - clangBasic.a + clangAPINotes.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile From 03530f9358d206e57435e6906a74447c69ab9936 Mon Sep 17 00:00:00 2001 From: Frederic Riss <friss@apple.com> Date: Thu, 5 Nov 2015 17:53:42 -0800 Subject: [PATCH 002/582] Apply Swift-related changes to the swift-llvm repo apple-llvm-split-commit: 3e2331faf2911f1ac0ca73c6ec15b4cf34393174 apple-llvm-split-dir: llvm/ --- llvm/docs/LangRef.rst | 11 + llvm/include/llvm-c/Core.h | 2 + llvm/include/llvm/ADT/DenseMapInfo.h | 81 ++- llvm/include/llvm/ADT/Fixnum.h | 190 +++++++ llvm/include/llvm/Analysis/LoopInfo.h | 2 + llvm/include/llvm/Bitcode/RecordLayout.h | 537 ++++++++++++++++++ llvm/include/llvm/CodeGen/FastISel.h | 5 +- .../llvm/CodeGen/FunctionLoweringInfo.h | 29 + llvm/include/llvm/IR/Argument.h | 6 + llvm/include/llvm/IR/Attributes.h | 2 + llvm/include/llvm/IR/Instructions.h | 12 + llvm/include/llvm/MC/MCAsmInfo.h | 5 + llvm/include/llvm/MC/MCDirectives.h | 1 + llvm/include/llvm/MC/MCSymbolMachO.h | 5 + llvm/include/llvm/Target/TargetCallingConv.h | 10 + llvm/include/llvm/Target/TargetCallingConv.td | 10 + .../include/llvm/Target/TargetFrameLowering.h | 5 +- llvm/include/llvm/Target/TargetLowering.h | 11 +- .../llvm/Transforms/Utils/SSAUpdaterImpl.h | 3 +- llvm/lib/AsmParser/LLLexer.cpp | 3 + llvm/lib/AsmParser/LLParser.cpp | 13 +- llvm/lib/AsmParser/LLToken.h | 3 + llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 29 +- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 7 +- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 7 +- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 12 +- llvm/lib/CodeGen/MachineSSAUpdater.cpp | 5 + llvm/lib/CodeGen/PrologEpilogInserter.cpp | 3 +- llvm/lib/CodeGen/SelectionDAG/FastISel.cpp | 20 +- .../SelectionDAG/FunctionLoweringInfo.cpp | 28 + .../SelectionDAG/SelectionDAGBuilder.cpp | 195 ++++++- .../SelectionDAG/SelectionDAGBuilder.h | 2 + .../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 119 ++++ .../CodeGen/SelectionDAG/TargetLowering.cpp | 2 + llvm/lib/IR/AsmWriter.cpp | 3 + llvm/lib/IR/Attributes.cpp | 6 + llvm/lib/IR/Function.cpp | 10 + llvm/lib/IR/Instructions.cpp | 1 + llvm/lib/IR/Verifier.cpp | 35 +- llvm/lib/MC/MCAsmInfo.cpp | 1 + llvm/lib/MC/MCAsmInfoDarwin.cpp | 1 + llvm/lib/MC/MCAsmStreamer.cpp | 1 + llvm/lib/MC/MCELFStreamer.cpp | 3 + llvm/lib/MC/MCMachOStreamer.cpp | 4 + llvm/lib/MC/MCParser/AsmParser.cpp | 7 +- .../AArch64/AArch64CallingConvention.td | 13 + llvm/lib/Target/AArch64/AArch64FastISel.cpp | 31 +- .../Target/AArch64/AArch64FrameLowering.cpp | 170 ++++-- .../lib/Target/AArch64/AArch64FrameLowering.h | 8 + llvm/lib/Target/AArch64/AArch64ISelLowering.h | 4 + .../Target/AArch64/AArch64RegisterInfo.cpp | 19 + llvm/lib/Target/AArch64/AArch64RegisterInfo.h | 5 + llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp | 9 + llvm/lib/Target/ARM/ARMCallingConv.td | 30 + llvm/lib/Target/ARM/ARMFastISel.cpp | 32 ++ llvm/lib/Target/ARM/ARMISelLowering.cpp | 1 + llvm/lib/Target/ARM/ARMISelLowering.h | 4 + .../Target/Hexagon/HexagonFrameLowering.cpp | 3 +- .../lib/Target/Hexagon/HexagonFrameLowering.h | 3 +- llvm/lib/Target/X86/X86CallingConv.td | 28 + llvm/lib/Target/X86/X86FastISel.cpp | 33 ++ llvm/lib/Target/X86/X86FrameLowering.cpp | 3 +- llvm/lib/Target/X86/X86FrameLowering.h | 4 +- llvm/lib/Target/X86/X86ISelLowering.cpp | 2 +- llvm/lib/Target/X86/X86ISelLowering.h | 4 + llvm/lib/Target/X86/X86RegisterInfo.cpp | 6 + llvm/lib/Transforms/Utils/SSAUpdater.cpp | 5 + llvm/test/Bitcode/swifterror.ll | 8 + llvm/test/Bitcode/swiftself.ll | 8 + llvm/test/CodeGen/AArch64/swifterror.ll | 371 ++++++++++++ llvm/test/CodeGen/AArch64/swiftself.ll | 29 + llvm/test/CodeGen/ARM/swift-ios.ll | 73 +++ llvm/test/CodeGen/ARM/swift-return.ll | 130 +++++ llvm/test/CodeGen/ARM/swifterror.ll | 368 ++++++++++++ llvm/test/CodeGen/ARM/swiftself.ll | 32 ++ llvm/test/CodeGen/X86/alias-gep.ll | 22 + llvm/test/CodeGen/X86/swift-return.ll | 135 +++++ llvm/test/CodeGen/X86/swifterror.ll | 276 +++++++++ llvm/test/CodeGen/X86/swiftself.ll | 29 + llvm/test/Transforms/GlobalOpt/alias-used.ll | 19 + llvm/test/Verifier/swifterror.ll | 4 + llvm/test/Verifier/swiftself.ll | 4 + 82 files changed, 3300 insertions(+), 72 deletions(-) create mode 100644 llvm/include/llvm/ADT/Fixnum.h create mode 100644 llvm/include/llvm/Bitcode/RecordLayout.h create mode 100644 llvm/test/Bitcode/swifterror.ll create mode 100644 llvm/test/Bitcode/swiftself.ll create mode 100644 llvm/test/CodeGen/AArch64/swifterror.ll create mode 100644 llvm/test/CodeGen/AArch64/swiftself.ll create mode 100644 llvm/test/CodeGen/ARM/swift-ios.ll create mode 100644 llvm/test/CodeGen/ARM/swift-return.ll create mode 100644 llvm/test/CodeGen/ARM/swifterror.ll create mode 100644 llvm/test/CodeGen/ARM/swiftself.ll create mode 100644 llvm/test/CodeGen/X86/alias-gep.ll create mode 100644 llvm/test/CodeGen/X86/swift-return.ll create mode 100644 llvm/test/CodeGen/X86/swifterror.ll create mode 100644 llvm/test/CodeGen/X86/swiftself.ll create mode 100644 llvm/test/Verifier/swifterror.ll create mode 100644 llvm/test/Verifier/swiftself.ll diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 388bb6b164203..1062778ac5742 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1037,6 +1037,17 @@ Currently, only the following parameter attributes are defined: ``dereferenceable(<n>)``). This attribute may only be applied to pointer typed parameters. +``swiftself`` + This indicates that the parameter is the self/context parameter. This is not + a valid attribute for return values and can only be applied to one + parameter. + +``swifterror`` + This indicates that the parameter is a pointer type. That pointer holds a + pointer to the error object. We can only load and store from the parameter + to get the pointer to the error object. This is not a valid attribute for + return values and can only be applied to one parameter. + .. _gc: Garbage Collector Strategy Names diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index 519363271ad57..91ae239afc59b 100644 --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -170,6 +170,8 @@ typedef enum { LLVMJumpTableAttribute = 1ULL << 45, LLVMConvergentAttribute = 1ULL << 46, LLVMSafeStackAttribute = 1ULL << 47, + LLVMSwiftSelfAttribute = 1ULL << 48, + LLVMSwiftErrorAttribute = 1ULL << 49, */ } LLVMAttribute; diff --git a/llvm/include/llvm/ADT/DenseMapInfo.h b/llvm/include/llvm/ADT/DenseMapInfo.h index a844ebcccf5b8..d7c34876be71a 100644 --- a/llvm/include/llvm/ADT/DenseMapInfo.h +++ b/llvm/include/llvm/ADT/DenseMapInfo.h @@ -19,6 +19,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/type_traits.h" +#include <tuple> namespace llvm { @@ -132,6 +133,20 @@ template<> struct DenseMapInfo<long long> { } }; +/// Simplistic combination of 32-bit hash values into 32-bit hash values. +static inline unsigned combineHashValue(unsigned a, unsigned b) { + uint64_t key = (uint64_t)a << 32 | (uint64_t)b; + key += ~(key << 32); + key ^= (key >> 22); + key += ~(key << 13); + key ^= (key >> 8); + key += (key << 3); + key ^= (key >> 15); + key += ~(key << 27); + key ^= (key >> 31); + return (unsigned)key; +} + // Provide DenseMapInfo for all pairs whose members have info. template<typename T, typename U> struct DenseMapInfo<std::pair<T, U> > { @@ -148,17 +163,8 @@ struct DenseMapInfo<std::pair<T, U> > { SecondInfo::getTombstoneKey()); } static unsigned getHashValue(const Pair& PairVal) { - uint64_t key = (uint64_t)FirstInfo::getHashValue(PairVal.first) << 32 - | (uint64_t)SecondInfo::getHashValue(PairVal.second); - key += ~(key << 32); - key ^= (key >> 22); - key += ~(key << 13); - key ^= (key >> 8); - key += (key << 3); - key ^= (key >> 15); - key += ~(key << 27); - key ^= (key >> 31); - return (unsigned)key; + return combineHashValue(FirstInfo::getHashValue(PairVal.first), + SecondInfo::getHashValue(PairVal.second)); } static bool isEqual(const Pair &LHS, const Pair &RHS) { return FirstInfo::isEqual(LHS.first, RHS.first) && @@ -166,6 +172,59 @@ struct DenseMapInfo<std::pair<T, U> > { } }; +template<typename ...Ts> +struct DenseMapInfo<std::tuple<Ts...> > { + typedef std::tuple<Ts...> Tuple; + + /// Helper class + template<unsigned N> struct UnsignedC { }; + + static inline Tuple getEmptyKey() { + return Tuple(DenseMapInfo<Ts>::getEmptyKey()...); + } + + static inline Tuple getTombstoneKey() { + return Tuple(DenseMapInfo<Ts>::getTombstoneKey()...); + } + + template<unsigned I> + static unsigned getHashValueImpl(const Tuple& values, std::false_type) { + typedef typename std::tuple_element<I, Tuple>::type EltType; + std::integral_constant<bool, I+1 == sizeof...(Ts)> atEnd; + return combineHashValue( + DenseMapInfo<EltType>::getHashValue(std::get<I>(values)), + getHashValueImpl<I+1>(values, atEnd)); + } + + template<unsigned I> + static unsigned getHashValueImpl(const Tuple& values, std::true_type) { + return 0; + } + + static unsigned getHashValue(const std::tuple<Ts...>& values) { + std::integral_constant<bool, 0 == sizeof...(Ts)> atEnd; + return getHashValueImpl<0>(values, atEnd); + } + + template<unsigned I> + static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs, std::false_type) { + typedef typename std::tuple_element<I, Tuple>::type EltType; + std::integral_constant<bool, I+1 == sizeof...(Ts)> atEnd; + return DenseMapInfo<EltType>::isEqual(std::get<I>(lhs), std::get<I>(rhs)) + && isEqualImpl<I+1>(lhs, rhs, atEnd); + } + + template<unsigned I> + static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs, std::true_type) { + return true; + } + + static bool isEqual(const Tuple &lhs, const Tuple &rhs) { + std::integral_constant<bool, 0 == sizeof...(Ts)> atEnd; + return isEqualImpl<0>(lhs, rhs, atEnd); + } +}; + // Provide DenseMapInfo for StringRefs. template <> struct DenseMapInfo<StringRef> { static inline StringRef getEmptyKey() { diff --git a/llvm/include/llvm/ADT/Fixnum.h b/llvm/include/llvm/ADT/Fixnum.h new file mode 100644 index 0000000000000..033cebe6f7574 --- /dev/null +++ b/llvm/include/llvm/ADT/Fixnum.h @@ -0,0 +1,190 @@ +//===- Fixnum.h - An integer type with an explicit bit width ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file Declares Fixnum, an integer type with an explicit bit width, +/// and utilities for working with bit widths of integers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_FIXNUM_H +#define LLVM_ADT_FIXNUM_H + +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/PointerLikeTypeTraits.h" +#include <cassert> +#include <limits> + +namespace llvm { + +/// Defines a member #type that is the smallest signed integer that can hold +/// a value with the given bit width. +template <unsigned Bits> +struct int_least { + static_assert(Bits <= 64, "too many bits"); + using type = typename std::conditional<(Bits <= 8), int_least8_t, + typename std::conditional<(Bits <= 16), int_least16_t, + typename std::conditional<(Bits <= 32), int_least32_t, + int_least64_t>::type>::type>::type; +}; + +/// Defines a member #type that is the smallest unsigned integer that can hold +/// a value with the given bit width. +template <unsigned Bits> +struct uint_least { + using type = + typename std::make_unsigned<typename int_least<Bits>::type>::type; +}; + +/// A wrapper for an integer type that is guaranteed to only use a certain +/// number of bits. +/// +/// This can be used to treat an integer like a pointer with low bits free. +/// +/// Note that if the integer type is signed, \p Bits must include the sign +/// bit, just like a bitfield. +template <unsigned Bits, typename IntType = typename uint_least<Bits>::type> +class Fixnum { + static_assert(Bits <= (std::numeric_limits<IntType>::digits + + std::numeric_limits<IntType>::is_signed), + "too many bits for integer type"); + + IntType Value; + + void assertValid() const { + assert((std::is_signed<IntType>::value ? llvm::isInt<Bits>(Value) + : llvm::isUInt<Bits>(Value)) && + "value exceeds limited bit width"); + } + +public: + using value_type = IntType; + + Fixnum() : Value(0) {} + + /*implicit*/ Fixnum(IntType val) : Value(val) { + assertValid(); + } + + /// Initialize a Fixnum from another, smaller Fixnum. + /// + /// This is always safe and thus permitted as an implicit coercion. + template <unsigned OtherBits, typename OtherIntType> + /*implicit*/ Fixnum( + const typename std::enable_if<(OtherBits < Bits), + Fixnum<OtherBits, + OtherIntType>>::type &other) + : Value(static_cast<IntType>(other)) {} + + /// Initialize a Fixnum from another of a different width. + /// + /// This is permitted, but checked with assertions. It must be explicitly + /// requested -- it is not a valid implicit conversion. + template <unsigned OtherBits, typename OtherIntType> + explicit Fixnum(const Fixnum<OtherBits, OtherIntType> &other) { + operator=(other); + } + + /// Assign to a Fixnum from another of a different width. + /// + /// This is permitted, but checked with assertions. + template <unsigned OtherBits, typename OtherIntType> + Fixnum &operator=(const Fixnum<OtherBits, OtherIntType> &other) { + Value = static_cast<IntType>(other); + assert(static_cast<OtherIntType>(Value) == other && + "cannot represent the same value"); + assert(((Value < 0) == (other < 0)) && "signedness mismatch"); + assertValid(); + return *this; + } + + /*implicit*/ operator IntType() const { + return Value; + } + + Fixnum &operator++() { + assert((Value != std::numeric_limits<IntType>::max()) && + "increment would cause wraparound"); + ++Value; + assertValid(); + return *this; + } + + Fixnum operator++(int) { + assert((Value != std::numeric_limits<IntType>::max()) && + "increment would cause wraparound"); + Fixnum result = *this; + ++Value; + assertValid(); + return result; + } + + Fixnum &operator--() { + assert((Value != std::numeric_limits<IntType>::min()) && + "decrement would cause wraparound"); + --Value; + assertValid(); + return *this; + } + + Fixnum operator--(int) { + assert((Value != std::numeric_limits<IntType>::min()) && + "decrement would cause wraparound"); + Fixnum result = *this; + --Value; + assertValid(); + return result; + } + + bool operator==(const Fixnum &RHS) const { + return Value == RHS.Value; + } + bool operator!=(const Fixnum &RHS) const { + return !operator==(RHS); + } + + bool operator==(int RHS) const { + return Value == IntType(RHS); + } + bool operator!=(int RHS) const { + return !operator==(RHS); + } +}; + +// Fixnum can be treated like a pointer with low bits free if it is no +// larger than a pointer. +template<unsigned IntBits, typename IntType> +class PointerLikeTypeTraits<Fixnum<IntBits, IntType>> { + using IntPointerType = + typename std::conditional<std::is_signed<IntType>::value, + intptr_t, uintptr_t>::type; + +public: + static_assert(sizeof(IntType) <= sizeof(IntPointerType), + "Fixnum is too big to fit in a pointer"); + + static inline void * + getAsVoidPointer(const Fixnum<IntBits, IntType> &I) { + auto opaqueValue = static_cast<IntPointerType>(I) << NumLowBitsAvailable; + return reinterpret_cast<void *>(opaqueValue); + } + + static inline Fixnum<IntBits, IntType> + getFromVoidPointer(const void *P) { + auto opaqueValue = reinterpret_cast<IntPointerType>(P); + return static_cast<IntType>(opaqueValue >> NumLowBitsAvailable); + } + + enum { + NumLowBitsAvailable = std::numeric_limits<uintptr_t>::digits - IntBits + }; +}; + +} // end namespace llvm + +#endif diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h index ac0a4b02f4458..19ebabf6d4003 100644 --- a/llvm/include/llvm/Analysis/LoopInfo.h +++ b/llvm/include/llvm/Analysis/LoopInfo.h @@ -542,6 +542,8 @@ class LoopInfoBase { /// LoopT *getLoopFor(const BlockT *BB) const { return BBMap.lookup(BB); } + const DenseMap<const BlockT *, LoopT *> &getBlockMap() const { return BBMap; } + /// operator[] - same as getLoopFor... /// const LoopT *operator[](const BlockT *BB) const { diff --git a/llvm/include/llvm/Bitcode/RecordLayout.h b/llvm/include/llvm/Bitcode/RecordLayout.h new file mode 100644 index 0000000000000..c3cbf13b2239f --- /dev/null +++ b/llvm/include/llvm/Bitcode/RecordLayout.h @@ -0,0 +1,537 @@ +//===--- BCRecordLayout.h - Convenience wrappers for bitcode ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file Convenience wrappers for the LLVM bitcode format and bitstream APIs. +/// +/// This allows you to use a sort of DSL to declare and use bitcode abbrevs +/// and records. Example: +/// +/// \code +/// using Metadata = BCRecordLayout< +/// METADATA_ID, // ID +/// BCFixed<16>, // Module format major version +/// BCFixed<16>, // Module format minor version +/// BCBlob // misc. version information +/// >; +/// unsigned MetadataAbbrevCode = Metadata::emitAbbrev(Out); +/// Metadata::emitRecord(Out, ScratchRecord, MetadataAbbrevCode, +/// VERSION_MAJOR, VERSION_MINOR, extraData); +/// \endcode +/// +/// For details on the bitcode format, see +/// http://llvm.org/docs/BitCodeFormat.html +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BITCODE_RECORDLAYOUT_H +#define LLVM_BITCODE_RECORDLAYOUT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Fixnum.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Bitcode/BitCodes.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MathExtras.h" + +namespace llvm { + +namespace impl { + /// Convenience base for all kinds of bitcode abbreviation fields. + /// + /// This just defines common properties queried by the metaprogramming. + template<bool COMPOUND = false> + class BCField { + public: + static const bool IS_COMPOUND = COMPOUND; + + /// Asserts that the given data is a valid value for this field. + template<typename T> + static void assertValid(const T &data) {} + + /// Converts a raw numeric representation of this value to its preferred + /// type. + template<typename T> + static T convert(T rawValue) { + return rawValue; + } + }; +} // end namespace impl + + +/// Represents a literal operand in a bitcode record. +/// +/// The value of a literal operand is the same for all instances of the record, +/// so it is only emitted in the abbreviation definition. +/// +/// Note that because this uses a compile-time template, you cannot have a +/// literal operand that is fixed at run-time without dropping down to the +/// raw LLVM APIs. +template<uint64_t Value> +class BCLiteral : public impl::BCField<> { +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(Value)); + } + + template<typename T> + static void assertValid(const T &data) { + assert(data == Value && "data value does not match declared literal value"); + } +}; + +/// Represents a fixed-width value in a bitcode record. +/// +/// Note that the LLVM bitcode format only supports unsigned values. +template<unsigned Width> +class BCFixed : public impl::BCField<> { +public: + static_assert(Width <= 64, "fixed-width field is too large"); + + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, Width)); + } + + template<typename T> + static void assertValid(const T &data) { + assert(data >= 0 && "cannot encode signed integers"); + assert(llvm::isUInt<Width>(data) && + "data value does not fit in the given bit width"); + } + + using value_type = Fixnum<Width>; + + template<typename T> + static value_type convert(T rawValue) { + return static_cast<value_type>(rawValue); + } +}; + +/// Represents a variable-width value in a bitcode record. +/// +/// The \p Width parameter should include the continuation bit. +/// +/// Note that the LLVM bitcode format only supports unsigned values. +template<unsigned Width> +class BCVBR : public impl::BCField<> { + static_assert(Width >= 2, "width does not have room for continuation bit"); + +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, Width)); + } + + template<typename T> + static void assertValid(const T &data) { + assert(data >= 0 && "cannot encode signed integers"); + } +}; + +/// Represents a character encoded in LLVM's Char6 encoding. +/// +/// This format is suitable for encoding decimal numbers (without signs or +/// exponents) and C identifiers (without dollar signs), but not much else. +/// +/// \sa http://llvm.org/docs/BitCodeFormat.html#char6-encoded-value +class BCChar6 : public impl::BCField<> { +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Char6)); + } + + template<typename T> + static void assertValid(const T &data) { + assert(llvm::BitCodeAbbrevOp::isChar6(data) && "invalid Char6 data"); + } + + template<typename T> + char convert(T rawValue) { + return static_cast<char>(rawValue); + } +}; + +/// Represents an untyped blob of bytes. +/// +/// If present, this must be the last field in a record. +class BCBlob : public impl::BCField<true> { +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); + } +}; + +/// Represents an array of some other type. +/// +/// If present, this must be the last field in a record. +template<typename Element> +class BCArray : public impl::BCField<true> { + static_assert(!Element::IS_COMPOUND, "arrays can only contain scalar types"); +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Array)); + Element::emitOp(abbrev); + } +}; + + +namespace impl { + /// Attaches the last field to an abbreviation. + /// + /// This is the base case for \c emitOps. + /// + /// \sa BCRecordLayout::emitAbbrev + template<typename Last> + static void emitOps(llvm::BitCodeAbbrev &abbrev) { + Last::emitOp(abbrev); + } + + /// Attaches fields to an abbreviation. + /// + /// This is the recursive case for \c emitOps. + /// + /// \sa BCRecordLayout::emitAbbrev + template<typename First, typename Next, typename ...Rest> + static void emitOps(llvm::BitCodeAbbrev &abbrev) { + static_assert(!First::IS_COMPOUND, + "arrays and blobs may not appear in the middle of a record"); + First::emitOp(abbrev); + emitOps<Next, Rest...>(abbrev); + } + + + /// Helper class for dealing with a scalar element in the middle of a record. + /// + /// \sa BCRecordLayout + template<typename First, typename... Fields> + class BCRecordCoding { + public: + template <typename BufferTy, typename FirstData, typename... Data> + static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, FirstData data, Data... rest) { + static_assert(!First::IS_COMPOUND, + "arrays and blobs may not appear in the middle of a record"); + First::assertValid(data); + buffer.push_back(data); + BCRecordCoding<Fields...>::emit(out, buffer, abbrCode, rest...); + } + + template <typename ElementTy, typename FirstData, typename... Data> + static void read(ArrayRef<ElementTy> buffer, + FirstData &data, Data &&...rest) { + assert(!buffer.empty() && "too few elements in buffer"); + data = First::convert(buffer.front()); + BCRecordCoding<Fields...>::read(buffer.slice(1), + std::forward<Data>(rest)...); + } + + template <typename ElementTy, typename... Data> + static void read(ArrayRef<ElementTy> buffer, + NoneType, Data &&...rest) { + assert(!buffer.empty() && "too few elements in buffer"); + BCRecordCoding<Fields...>::read(buffer.slice(1), + std::forward<Data>(rest)...); + } + }; + + /// Helper class for dealing with a scalar element at the end of a record. + /// + /// This has a separate implementation because up until now we've only been + /// \em building the record (into a data buffer), and now we need to hand it + /// off to the BitstreamWriter to be emitted. + /// + /// \sa BCRecordLayout + template<typename Last> + class BCRecordCoding<Last> { + public: + template <typename BufferTy, typename LastData> + static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, LastData data) { + static_assert(!Last::IS_COMPOUND, + "arrays and blobs need special handling"); + Last::assertValid(data); + buffer.push_back(data); + out.EmitRecordWithAbbrev(abbrCode, buffer); + } + + template <typename ElementTy, typename LastData> + static void read(ArrayRef<ElementTy> buffer, LastData &data) { + assert(buffer.size() == 1 && "record data does not match layout"); + data = Last::convert(buffer.front()); + } + + template <typename ElementTy> + static void read(ArrayRef<ElementTy> buffer, NoneType) { + assert(buffer.size() == 1 && "record data does not match layout"); + (void)buffer; + } + + template <typename ElementTy> + static void read(ArrayRef<ElementTy> buffer) = delete; + }; + + /// Helper class for dealing with an array at the end of a record. + /// + /// \sa BCRecordLayout::emitRecord + template<typename EleTy> + class BCRecordCoding<BCArray<EleTy>> { + public: + template <typename BufferTy> + static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, StringRef arrayData) { + // FIXME: validate array data. + out.EmitRecordWithArray(abbrCode, buffer, arrayData); + } + + template <typename BufferTy, typename ArrayTy> + static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, const ArrayTy &arrayData) { +#ifndef NDEBUG + for (auto &item : arrayData) + EleTy::assertValid(item); +#endif + buffer.reserve(buffer.size() + arrayData.size()); + std::copy(arrayData.begin(), arrayData.end(), + std::back_inserter(buffer)); + out.EmitRecordWithAbbrev(abbrCode, buffer); + } + + template <typename BufferTy, typename FirstData, typename ...RestData> + static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, FirstData firstData, + RestData... restData) { + std::array<FirstData, 1+sizeof...(restData)> arrayData{ { + firstData, + restData... + } }; + emit(out, buffer, abbrCode, arrayData); + } + + template <typename BufferTy> + static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, NoneType) { + out.EmitRecordWithAbbrev(abbrCode, buffer); + } + + template <typename ElementTy> + static void read(ArrayRef<ElementTy> buffer, ArrayRef<ElementTy> &rawData) { + rawData = buffer; + } + + template <typename ElementTy, typename ArrayTy> + static void read(ArrayRef<ElementTy> buffer, ArrayTy &array) { + array.append(llvm::map_iterator(buffer.begin(), ElementTy::convert), + llvm::map_iterator(buffer.end(), ElementTy::convert)); + } + + template <typename ElementTy> + static void read(ArrayRef<ElementTy> buffer, NoneType) { + (void)buffer; + } + + template <typename ElementTy> + static void read(ArrayRef<ElementTy> buffer) = delete; + }; + + /// Helper class for dealing with a blob at the end of a record. + /// + /// \sa BCRecordLayout + template<> + class BCRecordCoding<BCBlob> { + public: + template <typename BufferTy> + static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, StringRef blobData) { + out.EmitRecordWithBlob(abbrCode, buffer, blobData); + } + + template <typename ElementTy> + static void read(ArrayRef<ElementTy> buffer) { + (void)buffer; + } + + /// Blob data is not stored in the buffer if you are using the correct + /// accessor; this method should not be used. + template <typename ElementTy, typename DataTy> + static void read(ArrayRef<ElementTy> buffer, DataTy &data) = delete; + }; + + /// A type trait whose \c type field is the last of its template parameters. + template<typename First, typename ...Rest> + struct last_type { + using type = typename last_type<Rest...>::type; + }; + + template<typename Last> + struct last_type<Last> { + using type = Last; + }; + + /// A type trait whose \c value field is \c true if the last type is BCBlob. + template<typename ...Types> + using has_blob = std::is_same<BCBlob, typename last_type<int, Types...>::type>; + + /// A type trait whose \c value field is \c true if the given type is a + /// BCArray (of any element kind). + template <typename T> + struct is_array { + private: + template <typename E> + static bool check(BCArray<E> *); + static int check(...); + + public: + typedef bool value_type; + static constexpr bool value = + !std::is_same<decltype(check((T*)nullptr)), + decltype(check(false))>::value; + }; + + /// A type trait whose \c value field is \c true if the last type is a + /// BCArray (of any element kind). + template<typename ...Types> + using has_array = is_array<typename last_type<int, Types...>::type>; +} // end namespace impl + +/// Represents a single bitcode record type. +/// +/// This class template is meant to be instantiated and then given a name, +/// so that from then on that name can be used +template<typename IDField, typename... Fields> +class BCGenericRecordLayout { + llvm::BitstreamWriter &Out; + +public: + /// The abbreviation code used for this record in the current block. + /// + /// Note that this is not the same as the semantic record code, which is the + /// first field of the record. + const unsigned AbbrevCode; + + /// Create a layout and register it with the given bitstream writer. + explicit BCGenericRecordLayout(llvm::BitstreamWriter &out) + : Out(out), AbbrevCode(emitAbbrev(out)) {} + + /// Emit a record to the bitstream writer, using the given buffer for scratch + /// space. + /// + /// Note that even fixed arguments must be specified here. + template <typename BufferTy, typename... Data> + void emit(BufferTy &buffer, unsigned recordID, Data... data) const { + emitRecord(Out, buffer, AbbrevCode, recordID, data...); + } + + /// Registers this record's layout with the bitstream reader. + /// + /// \returns The abbreviation code for the newly-registered record type. + static unsigned emitAbbrev(llvm::BitstreamWriter &out) { + auto *abbrev = new llvm::BitCodeAbbrev(); + impl::emitOps<IDField, Fields...>(*abbrev); + return out.EmitAbbrev(abbrev); + } + + /// Emit a record identified by \p abbrCode to bitstream reader \p out, using + /// \p buffer for scratch space. + /// + /// Note that even fixed arguments must be specified here. Blobs are passed + /// as StringRefs, while arrays can be passed inline, as aggregates, or as + /// pre-encoded StringRef data. Skipped values and empty arrays should use + /// the special Nothing value. + template <typename BufferTy, typename... Data> + static void emitRecord(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, unsigned recordID, Data... data) { + static_assert(sizeof...(data) <= sizeof...(Fields) || + impl::has_array<Fields...>::value, + "Too many record elements"); + static_assert(sizeof...(data) >= sizeof...(Fields), + "Too few record elements"); + buffer.clear(); + impl::BCRecordCoding<IDField, Fields...>::emit(out, buffer, abbrCode, + recordID, data...); + } + + /// Extract record data from \p buffer into the given data fields. + /// + /// Note that even fixed arguments must be specified here. Pass \c Nothing + /// if you don't care about a particular parameter. Blob data is not included + /// in the buffer and should be handled separately by the caller. + template <typename ElementTy, typename... Data> + static void readRecord(ArrayRef<ElementTy> buffer, Data &&... data) { + static_assert(sizeof...(data) <= sizeof...(Fields), + "Too many record elements"); + static_assert(sizeof...(Fields) <= + sizeof...(data) + impl::has_blob<Fields...>::value, + "Too few record elements"); + return impl::BCRecordCoding<Fields...>::read(buffer, + std::forward<Data>(data)...); + } + + /// Extract record data from \p buffer into the given data fields. + /// + /// Note that even fixed arguments must be specified here. Pass \c Nothing + /// if you don't care about a particular parameter. Blob data is not included + /// in the buffer and should be handled separately by the caller. + template <typename BufferTy, typename... Data> + static void readRecord(BufferTy &buffer, Data &&... data) { + return readRecord(llvm::makeArrayRef(buffer), std::forward<Data>(data)...); + } +}; + +/// A record with a fixed record code. +template<unsigned RecordCode, typename... Fields> +class BCRecordLayout : public BCGenericRecordLayout<BCLiteral<RecordCode>, + Fields...> { + using Base = BCGenericRecordLayout<BCLiteral<RecordCode>, Fields...>; +public: + enum : unsigned { + /// The record code associated with this layout. + Code = RecordCode + }; + + /// Create a layout and register it with the given bitstream writer. + explicit BCRecordLayout(llvm::BitstreamWriter &out) : Base(out) {} + + /// Emit a record to the bitstream writer, using the given buffer for scratch + /// space. + /// + /// Note that even fixed arguments must be specified here. + template <typename BufferTy, typename... Data> + void emit(BufferTy &buffer, Data... data) const { + Base::emit(buffer, RecordCode, data...); + } + + /// Emit a record identified by \p abbrCode to bitstream reader \p out, using + /// \p buffer for scratch space. + /// + /// Note that even fixed arguments must be specified here. Currently, arrays + /// and blobs can only be passed as StringRefs. + template <typename BufferTy, typename... Data> + static void emitRecord(llvm::BitstreamWriter &out, BufferTy &buffer, + unsigned abbrCode, Data... data) { + Base::emitRecord(out, buffer, abbrCode, RecordCode, data...); + } +}; + +/// RAII object to pair entering and exiting a sub-block. +class BCBlockRAII { + llvm::BitstreamWriter &Writer; +public: + BCBlockRAII(llvm::BitstreamWriter &writer, unsigned blockID, + unsigned abbrevLen) + : Writer(writer) { + writer.EnterSubblock(blockID, abbrevLen); + } + + ~BCBlockRAII() { + Writer.ExitBlock(); + } +}; + +} // end namespace llvm + +#endif diff --git a/llvm/include/llvm/CodeGen/FastISel.h b/llvm/include/llvm/CodeGen/FastISel.h index b2bb8566aadf1..4f594a9c00df2 100644 --- a/llvm/include/llvm/CodeGen/FastISel.h +++ b/llvm/include/llvm/CodeGen/FastISel.h @@ -40,12 +40,15 @@ class FastISel { bool IsByVal : 1; bool IsInAlloca : 1; bool IsReturned : 1; + bool IsSwiftSelf : 1; + bool IsSwiftError : 1; uint16_t Alignment; ArgListEntry() : Val(nullptr), Ty(nullptr), IsSExt(false), IsZExt(false), IsInReg(false), IsSRet(false), IsNest(false), IsByVal(false), - IsInAlloca(false), IsReturned(false), Alignment(0) {} + IsInAlloca(false), IsReturned(false), IsSwiftSelf(false), + IsSwiftError(false), Alignment(0) {} /// \brief Set CallLoweringInfo attribute flags based on a call instruction /// and called function attributes. diff --git a/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h b/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h index bd8da736c16b9..7125b69179290 100644 --- a/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h +++ b/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h @@ -69,6 +69,35 @@ class FunctionLoweringInfo { /// MBBMap - A mapping from LLVM basic blocks to their machine code entry. DenseMap<const BasicBlock*, MachineBasicBlock *> MBBMap; + typedef SmallVector<unsigned, 1> SwiftErrorVRegs; + typedef SmallVector<const Value*, 1> SwiftErrorValues; + /// A function can only have a single swifterror argument. And if it does + /// have a swifterror argument, it must be the first entry in + /// SwiftErrorVals. + SwiftErrorValues SwiftErrorVals; + + /// Track the virtual register for each swifterror value in a given basic + /// block. Entries in SwiftErrorVRegs have the same ordering as entries + /// in SwiftErrorValues. + /// Note that another choice that is more straight-forward is to use + /// Map<const MachineBasicBlock*, Map<Value*, unsigned/*VReg*/>>. It + /// maintains a map from swifterror values to virtual registers for each + /// machine basic block. This choice does not require a one-to-one + /// corresponse between SwiftErrorValues and SwiftErrorVRegs. But because of + /// efficiency concern, we do not choose it. + llvm::DenseMap<const MachineBasicBlock*, SwiftErrorVRegs> SwiftErrorMap; + + /// Track the virtual register for a swifterror value at the end of a basic + /// block when the basic block is not yet visited. + llvm::DenseMap<const MachineBasicBlock*, SwiftErrorVRegs> + SwiftErrorWorklist; + + /// Find the swifterror virtual register in SwiftErrorMap. We will assert + /// failure when the value does not exist in swifterror map. + unsigned findSwiftErrorVReg(const MachineBasicBlock*, const Value*) const; + /// Set the swifterror virtual register in SwiftErrorMap. + void setSwiftErrorVReg(const MachineBasicBlock *MBB, const Value*, unsigned); + /// ValueMap - Since we emit code for the function a basic block at a time, /// we must remember which virtual registers hold the values for /// cross-basic-block values. diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h index 0092f49e49ad6..22973753e123f 100644 --- a/llvm/include/llvm/IR/Argument.h +++ b/llvm/include/llvm/IR/Argument.h @@ -73,6 +73,12 @@ class Argument : public Value, public ilist_node<Argument> { /// containing function. bool hasByValAttr() const; + /// \brief Return true if this argument has the swiftself attribute. + bool hasSwiftSelfAttr() const; + + /// \brief Return true if this argument has the swifterror attribute. + bool hasSwiftErrorAttr() const; + /// \brief Return true if this argument has the byval attribute or inalloca /// attribute on it in its containing function. These attributes both /// represent arguments being passed by value. diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index 15f48fa38d93c..2eac273bd43d9 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -115,6 +115,8 @@ class Attribute { SanitizeAddress, ///< AddressSanitizer is on. SanitizeThread, ///< ThreadSanitizer is on. SanitizeMemory, ///< MemorySanitizer is on. + SwiftError, ///< Argument is swift error. + SwiftSelf, ///< Argument is swift self/context. UWTable, ///< Function must be in a unwind table ZExt, ///< Zero extended before/after call diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index c2ade1994f5dc..efb0038e24e75 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -151,6 +151,18 @@ class AllocaInst : public UnaryInstruction { (V ? 32 : 0)); } + /// \brief Return true if this alloca is used as a swifterror argument to a + /// call. + bool isSwiftError() const { + return getSubclassDataFromInstruction() & 64; + } + + /// \brief Specify whether this alloca is used to represent a swifterror. + void setSwiftError(bool V) { + setInstructionSubclassData((getSubclassDataFromInstruction() & ~64) | + (V ? 64 : 0)); + } + // Methods for support type inquiry through isa, cast, and dyn_cast: static inline bool classof(const Instruction *I) { return (I->getOpcode() == Instruction::Alloca); diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h index 384584ef4ef0e..3106c8c501f31 100644 --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -280,6 +280,10 @@ class MCAsmInfo { /// to false. bool HasNoDeadStrip; + /// True if this target supports the MachO .alt_entry directive. Defaults to + /// false. + bool HasAltEntry; + /// Used to declare a global as being a weak symbol. Defaults to ".weak". const char *WeakDirective; @@ -498,6 +502,7 @@ class MCAsmInfo { bool hasSingleParameterDotFile() const { return HasSingleParameterDotFile; } bool hasIdentDirective() const { return HasIdentDirective; } bool hasNoDeadStrip() const { return HasNoDeadStrip; } + bool hasAltEntry() const { return HasAltEntry; } const char *getWeakDirective() const { return WeakDirective; } const char *getWeakRefDirective() const { return WeakRefDirective; } bool hasWeakDefDirective() const { return HasWeakDefDirective; } diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h index 326b2a1ac061b..8c74b169135b1 100644 --- a/llvm/include/llvm/MC/MCDirectives.h +++ b/llvm/include/llvm/MC/MCDirectives.h @@ -35,6 +35,7 @@ enum MCSymbolAttr { MCSA_Local, ///< .local (ELF) MCSA_NoDeadStrip, ///< .no_dead_strip (MachO) MCSA_SymbolResolver, ///< .symbol_resolver (MachO) + MCSA_AltEntry, ///< .alt_entry (MachO) MCSA_PrivateExtern, ///< .private_extern (MachO) MCSA_Protected, ///< .protected (ELF) MCSA_Reference, ///< .reference (MachO) diff --git a/llvm/include/llvm/MC/MCSymbolMachO.h b/llvm/include/llvm/MC/MCSymbolMachO.h index 5b0321fe9f732..8b63c43466a35 100644 --- a/llvm/include/llvm/MC/MCSymbolMachO.h +++ b/llvm/include/llvm/MC/MCSymbolMachO.h @@ -33,6 +33,7 @@ class MCSymbolMachO : public MCSymbol { SF_WeakReference = 0x0040, SF_WeakDefinition = 0x0080, SF_SymbolResolver = 0x0100, + SF_AltEntry = 0x0200, // Common alignment SF_CommonAlignmentMask = 0xF0FF, @@ -88,6 +89,10 @@ class MCSymbolMachO : public MCSymbol { modifyFlags(SF_SymbolResolver, SF_SymbolResolver); } + void setAltEntry() const { + modifyFlags(SF_AltEntry, SF_AltEntry); + } + void setDesc(unsigned Value) const { assert(Value == (Value & SF_DescFlagsMask) && "Invalid .desc value!"); diff --git a/llvm/include/llvm/Target/TargetCallingConv.h b/llvm/include/llvm/Target/TargetCallingConv.h index 9d4e7a04d905a..35088627c4c9b 100644 --- a/llvm/include/llvm/Target/TargetCallingConv.h +++ b/llvm/include/llvm/Target/TargetCallingConv.h @@ -46,6 +46,10 @@ namespace ISD { static const uint64_t SplitOffs = 11; static const uint64_t InAlloca = 1ULL<<12; ///< Passed with inalloca static const uint64_t InAllocaOffs = 12; + static const uint64_t SwiftSelf = 1ULL<<13; ///< Swift self parameter + static const uint64_t SwiftSelfOffs = 13; + static const uint64_t SwiftError = 1ULL<<14; ///< Swift error parameter + static const uint64_t SwiftErrorOffs = 14; static const uint64_t OrigAlign = 0x1FULL<<27; static const uint64_t OrigAlignOffs = 27; static const uint64_t ByValSize = 0x3fffffffULL<<32; ///< Struct size @@ -79,6 +83,12 @@ namespace ISD { bool isInAlloca() const { return Flags & InAlloca; } void setInAlloca() { Flags |= One << InAllocaOffs; } + bool isSwiftSelf() const { return Flags & SwiftSelf; } + void setSwiftSelf() { Flags = One << SwiftSelfOffs; } + + bool isSwiftError() const { return Flags & SwiftError; } + void setSwiftError() { Flags = One << SwiftErrorOffs; } + bool isNest() const { return Flags & Nest; } void setNest() { Flags |= One << NestOffs; } diff --git a/llvm/include/llvm/Target/TargetCallingConv.td b/llvm/include/llvm/Target/TargetCallingConv.td index 2e766c448b34a..3d8639dfe1daa 100644 --- a/llvm/include/llvm/Target/TargetCallingConv.td +++ b/llvm/include/llvm/Target/TargetCallingConv.td @@ -42,6 +42,16 @@ class CCIf<string predicate, CCAction A> : CCPredicateAction<A> { class CCIfByVal<CCAction A> : CCIf<"ArgFlags.isByVal()", A> { } +/// CCIfSwiftSelf - If the current argument has swiftself parameter attribute, +/// apply Action A. +class CCIfSwiftSelf<CCAction A> : CCIf<"ArgFlags.isSwiftSelf()", A> { +} + +/// CCIfSwiftError - If the current argument has swifterror parameter attribute, +/// apply Action A. +class CCIfSwiftError<CCAction A> : CCIf<"ArgFlags.isSwiftError()", A> { +} + /// CCIfConsecutiveRegs - If the current argument has InConsecutiveRegs /// parameter attribute, apply Action A. class CCIfConsecutiveRegs<CCAction A> : CCIf<"ArgFlags.isInConsecutiveRegs()", A> { diff --git a/llvm/include/llvm/Target/TargetFrameLowering.h b/llvm/include/llvm/Target/TargetFrameLowering.h index 398c91ef56249..61c10c7436a38 100644 --- a/llvm/include/llvm/Target/TargetFrameLowering.h +++ b/llvm/include/llvm/Target/TargetFrameLowering.h @@ -120,7 +120,10 @@ class TargetFrameLowering { virtual bool assignCalleeSavedSpillSlots(MachineFunction &MF, const TargetRegisterInfo *TRI, - std::vector<CalleeSavedInfo> &CSI) const { + std::vector<CalleeSavedInfo> &CSI, + unsigned &MinCSFrameIndex, + unsigned &MaxCSFrameIndex) const { + return false; } diff --git a/llvm/include/llvm/Target/TargetLowering.h b/llvm/include/llvm/Target/TargetLowering.h index e17682f704e4d..bd6fa4b0678ba 100644 --- a/llvm/include/llvm/Target/TargetLowering.h +++ b/llvm/include/llvm/Target/TargetLowering.h @@ -2266,6 +2266,11 @@ class TargetLowering : public TargetLoweringBase { return false; } + /// Return true if the target supports swifterror attribute. + virtual bool supportSwiftError() const { + return false; + } + //===--------------------------------------------------------------------===// // Lowering methods - These methods must be implemented by targets so that // the SelectionDAGBuilder code knows how to lower these. @@ -2296,11 +2301,14 @@ class TargetLowering : public TargetLoweringBase { bool isByVal : 1; bool isInAlloca : 1; bool isReturned : 1; + bool isSwiftSelf : 1; + bool isSwiftError : 1; uint16_t Alignment; ArgListEntry() : isSExt(false), isZExt(false), isInReg(false), isSRet(false), isNest(false), isByVal(false), isInAlloca(false), - isReturned(false), Alignment(0) { } + isReturned(false), isSwiftSelf(false), isSwiftError(false), + Alignment(0) { } void setAttributes(ImmutableCallSite *CS, unsigned AttrIdx); }; @@ -2335,6 +2343,7 @@ class TargetLowering : public TargetLoweringBase { SmallVector<ISD::OutputArg, 32> Outs; SmallVector<SDValue, 32> OutVals; SmallVector<ISD::InputArg, 32> Ins; + SmallVector<SDValue, 4> InVals; CallLoweringInfo(SelectionDAG &DAG) : RetTy(nullptr), RetSExt(false), RetZExt(false), IsVarArg(false), diff --git a/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h b/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h index 425ecd3cfb5e0..acd371e0d9dc3 100644 --- a/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h +++ b/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h @@ -38,6 +38,7 @@ class SSAUpdaterImpl { typedef typename Traits::BlkT BlkT; typedef typename Traits::ValT ValT; typedef typename Traits::PhiT PhiT; + typedef typename Traits::PhiItT PhiItT; /// BBInfo - Per-basic block information used internally by SSAUpdaterImpl. /// The predecessors of each block are cached here since pred_iterator is @@ -376,7 +377,7 @@ class SSAUpdaterImpl { /// FindExistingPHI - Look through the PHI nodes in a block to see if any of /// them match what is needed. void FindExistingPHI(BlkT *BB, BlockListTy *BlockList) { - for (typename BlkT::iterator BBI = BB->begin(), BBE = BB->end(); + for (PhiItT BBI = Traits::PhiItT_begin(BB), BBE = Traits::PhiItT_end(BB); BBI != BBE; ++BBI) { PhiT *SomePHI = Traits::InstrIsPHI(&*BBI); if (!SomePHI) diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 2eb5f0bf45d5b..8e302b2538a93 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -583,6 +583,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(x86_64_sysvcc); KEYWORD(x86_64_win64cc); KEYWORD(webkit_jscc); + KEYWORD(swiftcc); KEYWORD(anyregcc); KEYWORD(preserve_mostcc); KEYWORD(preserve_allcc); @@ -636,6 +637,8 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(sanitize_address); KEYWORD(sanitize_thread); KEYWORD(sanitize_memory); + KEYWORD(swifterror); + KEYWORD(swiftself); KEYWORD(uwtable); KEYWORD(zeroext); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 7774a70f5f44c..190612a64d5e4 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1036,6 +1036,8 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B, case lltok::kw_nonnull: case lltok::kw_returned: case lltok::kw_sret: + case lltok::kw_swifterror: + case lltok::kw_swiftself: HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute on a function"); @@ -1309,6 +1311,8 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) { case lltok::kw_returned: B.addAttribute(Attribute::Returned); break; case lltok::kw_signext: B.addAttribute(Attribute::SExt); break; case lltok::kw_sret: B.addAttribute(Attribute::StructRet); break; + case lltok::kw_swifterror: B.addAttribute(Attribute::SwiftError); break; + case lltok::kw_swiftself: B.addAttribute(Attribute::SwiftSelf); break; case lltok::kw_zeroext: B.addAttribute(Attribute::ZExt); break; case lltok::kw_alignstack: @@ -1396,6 +1400,8 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) { case lltok::kw_nocapture: case lltok::kw_returned: case lltok::kw_sret: + case lltok::kw_swifterror: + case lltok::kw_swiftself: HaveError |= Error(Lex.getLoc(), "invalid use of parameter-only attribute"); break; @@ -1532,6 +1538,7 @@ bool LLParser::ParseOptionalDLLStorageClass(unsigned &Res) { /// ::= 'preserve_mostcc' /// ::= 'preserve_allcc' /// ::= 'ghccc' +/// ::= 'swiftcc' /// ::= 'hhvmcc' /// ::= 'hhvm_ccc' /// ::= 'cc' UINT @@ -1562,6 +1569,7 @@ bool LLParser::ParseOptionalCallingConv(unsigned &CC) { case lltok::kw_preserve_mostcc:CC = CallingConv::PreserveMost; break; case lltok::kw_preserve_allcc: CC = CallingConv::PreserveAll; break; case lltok::kw_ghccc: CC = CallingConv::GHC; break; + case lltok::kw_swiftcc: CC = CallingConv::Swift; break; case lltok::kw_hhvmcc: CC = CallingConv::HHVM; break; case lltok::kw_hhvm_ccc: CC = CallingConv::HHVM_C; break; case lltok::kw_cc: { @@ -5744,7 +5752,8 @@ bool LLParser::ParseCall(Instruction *&Inst, PerFunctionState &PFS, //===----------------------------------------------------------------------===// /// ParseAlloc -/// ::= 'alloca' 'inalloca'? Type (',' TypeAndValue)? (',' 'align' i32)? +/// ::= 'alloca' 'inalloca'? 'swifterror'? Type (',' TypeAndValue)? +/// (',' 'align' i32)? int LLParser::ParseAlloc(Instruction *&Inst, PerFunctionState &PFS) { Value *Size = nullptr; LocTy SizeLoc, TyLoc; @@ -5752,6 +5761,7 @@ int LLParser::ParseAlloc(Instruction *&Inst, PerFunctionState &PFS) { Type *Ty = nullptr; bool IsInAlloca = EatIfPresent(lltok::kw_inalloca); + bool IsSwiftError = EatIfPresent(lltok::kw_swifterror); if (ParseType(Ty, TyLoc)) return true; @@ -5776,6 +5786,7 @@ int LLParser::ParseAlloc(Instruction *&Inst, PerFunctionState &PFS) { AllocaInst *AI = new AllocaInst(Ty, Size, Alignment); AI->setUsedWithInAlloca(IsInAlloca); + AI->setSwiftError(IsSwiftError); Inst = AI; return AteExtraComma ? InstExtraComma : InstNormal; } diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h index 06b5f9b0800f9..b6f7f02fa7bb1 100644 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -95,6 +95,7 @@ namespace lltok { kw_spir_kernel, kw_spir_func, kw_x86_64_sysvcc, kw_x86_64_win64cc, kw_webkit_jscc, kw_anyregcc, + kw_swiftcc, kw_preserve_mostcc, kw_preserve_allcc, kw_ghccc, kw_hhvmcc, kw_hhvm_ccc, @@ -142,6 +143,8 @@ namespace lltok { kw_sret, kw_sanitize_thread, kw_sanitize_memory, + kw_swifterror, + kw_swiftself, kw_uwtable, kw_zeroext, diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 7c1b208c62610..142f73a86e4c8 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1351,6 +1351,10 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::SanitizeThread; case bitc::ATTR_KIND_SANITIZE_MEMORY: return Attribute::SanitizeMemory; + case bitc::ATTR_KIND_SWIFT_ERROR: + return Attribute::SwiftError; + case bitc::ATTR_KIND_SWIFT_SELF: + return Attribute::SwiftSelf; case bitc::ATTR_KIND_UW_TABLE: return Attribute::UWTable; case bitc::ATTR_KIND_Z_EXT: @@ -2063,6 +2067,23 @@ std::error_code BitcodeReader::parseMetadata() { if (Tag >= 1u << 16 || Version != 0) return error("Invalid record"); + // Deprecated internal hack to support serializing MDModule. + // This node has since been deleted. + // Upgrading this node is not officially supported. This code + // may be removed in the future. + if (Tag == dwarf::DW_TAG_module) { + if (Record.size() != 6) + return error("Invalid record"); + + MDValueList.assignValue( + GET_OR_DISTINCT(DIModule, Record[0], + (Context, getMDOrNull(Record[4]), + getMDString(Record[5]), nullptr, + nullptr, nullptr)), + NextMDValueNo++); + break; + } + auto *Header = getMDString(Record[3]); SmallVector<Metadata *, 8> DwarfOps; for (unsigned I = 4, E = Record.size(); I != E; ++I) @@ -4715,10 +4736,11 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) { uint64_t AlignRecord = Record[3]; const uint64_t InAllocaMask = uint64_t(1) << 5; const uint64_t ExplicitTypeMask = uint64_t(1) << 6; - // Reserve bit 7 for SwiftError flag. - // const uint64_t SwiftErrorMask = uint64_t(1) << 7; - const uint64_t FlagMask = InAllocaMask | ExplicitTypeMask; + const uint64_t SwiftErrorMask = uint64_t(1) << 7; + const uint64_t FlagMask = InAllocaMask | ExplicitTypeMask | + SwiftErrorMask; bool InAlloca = AlignRecord & InAllocaMask; + bool SwiftError = AlignRecord & SwiftErrorMask; Type *Ty = getTypeByID(Record[0]); if ((AlignRecord & ExplicitTypeMask) == 0) { auto *PTy = dyn_cast_or_null<PointerType>(Ty); @@ -4737,6 +4759,7 @@ std::error_code BitcodeReader::parseFunctionBody(Function *F) { return error("Invalid record"); AllocaInst *AI = new AllocaInst(Ty, Size, Align); AI->setUsedWithInAlloca(InAlloca); + AI->setSwiftError(SwiftError); I = AI; InstructionList.push_back(I); break; diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index c73b099e27f92..90fb55700cd32 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -248,6 +248,10 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_SANITIZE_THREAD; case Attribute::SanitizeMemory: return bitc::ATTR_KIND_SANITIZE_MEMORY; + case Attribute::SwiftError: + return bitc::ATTR_KIND_SWIFT_ERROR; + case Attribute::SwiftSelf: + return bitc::ATTR_KIND_SWIFT_SELF; case Attribute::UWTable: return bitc::ATTR_KIND_UW_TABLE; case Attribute::ZExt: @@ -2051,8 +2055,7 @@ static void WriteInstruction(const Instruction &I, unsigned InstID, assert(AlignRecord < 1 << 5 && "alignment greater than 1 << 64"); AlignRecord |= AI.isUsedWithInAlloca() << 5; AlignRecord |= 1 << 6; - // Reserve bit 7 for SwiftError flag. - // AlignRecord |= AI.isSwiftError() << 7; + AlignRecord |= AI.isSwiftError() << 7; Vals.push_back(AlignRecord); break; } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 8fe5094ba689e..1002fc3f6a1c4 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1187,8 +1187,13 @@ bool AsmPrinter::doFinalization(Module &M) { EmitVisibility(Name, Alias.getVisibility()); + const MCExpr *Expr = lowerConstant(Alias.getAliasee()); + + if (MAI->hasAltEntry() && isa<MCBinaryExpr>(Expr)) + OutStreamer->EmitSymbolAttribute(Name, MCSA_AltEntry); + // Emit the directives as assignments aka .set: - OutStreamer->EmitAssignment(Name, lowerConstant(Alias.getAliasee())); + OutStreamer->EmitAssignment(Name, Expr); // If the aliasee does not correspond to a symbol in the output, i.e. the // alias is not of an object or the aliased object is private, then set the diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index c441a67a5d70b..ef0ea4301d057 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -691,6 +691,8 @@ DIE *DwarfUnit::getOrCreateContextDIE(const DIScope *Context) { return getOrCreateTypeDIE(T); if (auto *NS = dyn_cast<DINamespace>(Context)) return getOrCreateNameSpace(NS); + if (auto *M = dyn_cast<DIModule>(Context)) + return getOrCreateModule(M); if (auto *SP = dyn_cast<DISubprogram>(Context)) return getOrCreateSubprogramDIE(SP); if (auto *M = dyn_cast<DIModule>(Context)) @@ -996,6 +998,13 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) { if (!Name.empty()) addString(Buffer, dwarf::DW_AT_name, Name); + // For Swift, mangled names are put into DW_AT_linkage_name; human-readable + // names are emitted put into DW_AT_name and the accelerator table. + if ((CTy->getRuntimeLang() == dwarf::DW_LANG_Swift || + CTy->getRuntimeLang() == dwarf::DW_LANG_PLI) && + CTy->getRawIdentifier()) + addString(Buffer, dwarf::DW_AT_linkage_name, CTy->getIdentifier()); + if (Tag == dwarf::DW_TAG_enumeration_type || Tag == dwarf::DW_TAG_class_type || Tag == dwarf::DW_TAG_structure_type || Tag == dwarf::DW_TAG_union_type) { @@ -1018,8 +1027,7 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) { // No harm in adding the runtime language to the declaration. unsigned RLang = CTy->getRuntimeLang(); if (RLang) - addUInt(Buffer, dwarf::DW_AT_APPLE_runtime_class, dwarf::DW_FORM_data1, - RLang); + addUInt(Buffer, dwarf::DW_AT_APPLE_runtime_class, None, RLang); } } diff --git a/llvm/lib/CodeGen/MachineSSAUpdater.cpp b/llvm/lib/CodeGen/MachineSSAUpdater.cpp index 71a6ebaba2431..57597a68a46f7 100644 --- a/llvm/lib/CodeGen/MachineSSAUpdater.cpp +++ b/llvm/lib/CodeGen/MachineSSAUpdater.cpp @@ -246,6 +246,11 @@ class SSAUpdaterTraits<MachineSSAUpdater> { static BlkSucc_iterator BlkSucc_begin(BlkT *BB) { return BB->succ_begin(); } static BlkSucc_iterator BlkSucc_end(BlkT *BB) { return BB->succ_end(); } + /// Iterator over phis in a block. + typedef BlkT::iterator PhiItT; + static PhiItT PhiItT_begin(BlkT *BB) { return BB->begin(); } + static PhiItT PhiItT_end(BlkT *BB) { return BB->end(); } + /// Iterator for PHI operands. class PHI_iterator { private: diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index 13e753692de41..76e4e4330af80 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -311,7 +311,8 @@ void PEI::assignCalleeSavedSpillSlots(MachineFunction &F, const TargetFrameLowering *TFI = F.getSubtarget().getFrameLowering(); MachineFrameInfo *MFI = F.getFrameInfo(); - if (!TFI->assignCalleeSavedSpillSlots(F, RegInfo, CSI)) { + if (!TFI->assignCalleeSavedSpillSlots(F, RegInfo, CSI, MinCSFrameIndex, + MaxCSFrameIndex)) { // If target doesn't implement this, use generic code. if (CSI.empty()) diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp index adc51c555649b..b7af6868a67c9 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -88,6 +88,8 @@ void FastISel::ArgListEntry::setAttributes(ImmutableCallSite *CS, IsByVal = CS->paramHasAttr(AttrIdx, Attribute::ByVal); IsInAlloca = CS->paramHasAttr(AttrIdx, Attribute::InAlloca); IsReturned = CS->paramHasAttr(AttrIdx, Attribute::Returned); + IsSwiftSelf = CS->paramHasAttr(AttrIdx, Attribute::SwiftSelf); + IsSwiftError = CS->paramHasAttr(AttrIdx, Attribute::SwiftError); Alignment = CS->getParamAlignment(AttrIdx); } @@ -960,6 +962,10 @@ bool FastISel::lowerCallTo(CallLoweringInfo &CLI) { Flags.setInReg(); if (Arg.IsSRet) Flags.setSRet(); + if (Arg.IsSwiftSelf) + Flags.setSwiftSelf(); + if (Arg.IsSwiftError) + Flags.setSwiftError(); if (Arg.IsByVal) Flags.setByVal(); if (Arg.IsInAlloca) { @@ -1322,12 +1328,24 @@ bool FastISel::selectBitCast(const User *I) { return true; } +// Return true if we should copy from swift error to the final vreg as specified +// by SwiftErrorWorklist. +static bool shouldCopySwiftErrorsToFinalVRegs(const TargetLowering &TLI, + FunctionLoweringInfo &FuncInfo) { + if (!TLI.supportSwiftError()) + return false; + return FuncInfo.SwiftErrorWorklist.count(FuncInfo.MBB); +} + bool FastISel::selectInstruction(const Instruction *I) { // Just before the terminator instruction, insert instructions to // feed PHI nodes in successor blocks. - if (isa<TerminatorInst>(I)) + if (isa<TerminatorInst>(I)) { + if (shouldCopySwiftErrorsToFinalVRegs(TLI, FuncInfo)) + return false; if (!handlePHINodesInSuccessorBlocks(I->getParent())) return false; + } DbgLoc = I->getDebugLoc(); diff --git a/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp b/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp index 52216c2c1e822..7b4c0e8f9537b 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp @@ -593,3 +593,31 @@ void llvm::AddLandingPadInfo(const LandingPadInst &I, MachineModuleInfo &MMI, } } } + +unsigned FunctionLoweringInfo::findSwiftErrorVReg(const MachineBasicBlock *MBB, + const Value* Val) const { + // Find the index in SwiftErrorVals. + unsigned End = SwiftErrorVals.size(); + unsigned Index = End; + for (unsigned I = 0; I < End; I++) + if (SwiftErrorVals[I] == Val) { + Index = I; + break; + } + assert(Index < End && "Can't find value in SwiftErrorVals"); + return SwiftErrorMap.lookup(MBB)[Index]; +} + +void FunctionLoweringInfo::setSwiftErrorVReg(const MachineBasicBlock *MBB, + const Value* Val, unsigned VReg) { + // Find the index in SwiftErrorVals. + unsigned End = SwiftErrorVals.size(); + unsigned Index = End; + for (unsigned I = 0, E = SwiftErrorVals.size(); I < E; I++) + if (SwiftErrorVals[I] == Val) { + Index = I; + break; + } + assert(Index < End && "Can't find value in SwiftErrorVals"); + SwiftErrorMap[MBB][Index] = VReg; +} diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 75b2960bb37e7..a8c4c1b3c1edc 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -888,10 +888,46 @@ SDValue SelectionDAGBuilder::getControlRoot() { return Root; } +/// Copy swift error to the final virtual register at end of a basic block, as +/// specified by SwiftErrorWorklist, if necessary. +static void copySwiftErrorsToFinalVRegs(SelectionDAGBuilder &SDB) { + const TargetLowering &TLI = SDB.DAG.getTargetLoweringInfo(); + if (!TLI.supportSwiftError()) + return; + + if (!SDB.FuncInfo.SwiftErrorWorklist.count(SDB.FuncInfo.MBB)) + return; + + // Go through entries in SwiftErrorWorklist, and create copy as necessary. + auto &WorklistEntry = SDB.FuncInfo.SwiftErrorWorklist[SDB.FuncInfo.MBB]; + auto &MapEntry = SDB.FuncInfo.SwiftErrorMap[SDB.FuncInfo.MBB]; + for (unsigned I = 0, E = WorklistEntry.size(); I < E; I++) { + unsigned WorkReg = WorklistEntry[I]; + + // Find the swifterror virtual register for the value in SwiftErrorMap. + unsigned MapReg = MapEntry[I]; + assert(TargetRegisterInfo::isVirtualRegister(MapReg) && + "Entries in SwiftErrorMap should be virtual registers"); + + if (WorkReg == MapReg) + continue; + + // Create copy from SwiftErrorMap to SwiftWorklist. + auto &DL = SDB.DAG.getDataLayout(); + SDValue CopyNode = SDB.DAG.getCopyToReg( + SDB.getRoot(), SDB.getCurSDLoc(), WorkReg, + SDB.DAG.getRegister(MapReg, EVT(TLI.getPointerTy(DL)))); + MapEntry[I] = WorkReg; + SDB.DAG.setRoot(CopyNode); + } +} + void SelectionDAGBuilder::visit(const Instruction &I) { // Set up outgoing PHI node register values before emitting the terminator. - if (isa<TerminatorInst>(&I)) + if (isa<TerminatorInst>(&I)) { + copySwiftErrorsToFinalVRegs(*this); HandlePHINodesInSuccessorBlocks(I.getParent()); + } ++SDNodeOrder; @@ -1416,6 +1452,23 @@ void SelectionDAGBuilder::visitRet(const ReturnInst &I) { } } + // Push in swifterror virtual register as the last element of Outs. This makes + // sure swifterror virtual register will be returned in the swifterror + // physical register. + const Function *F = I.getParent()->getParent(); + if (F->getAttributes().hasAttrSomewhere(Attribute::SwiftError) && + TLI.supportSwiftError()) { + ISD::ArgFlagsTy Flags = ISD::ArgFlagsTy(); + Flags.setSwiftError(); + Outs.push_back(ISD::OutputArg(Flags, EVT(TLI.getPointerTy(DL)) /*vt*/, + EVT(TLI.getPointerTy(DL)) /*argvt*/, + true /*isfixed*/, 1 /*origidx*/, + 0 /*partOffs*/)); + // Create SDNode for the swifterror virtual register. + OutVals.push_back(DAG.getRegister(FuncInfo.SwiftErrorMap[FuncInfo.MBB][0], + EVT(TLI.getPointerTy(DL)))); + } + bool isVarArg = DAG.getMachineFunction().getFunction()->isVarArg(); CallingConv::ID CallConv = DAG.getMachineFunction().getFunction()->getCallingConv(); @@ -3098,6 +3151,18 @@ void SelectionDAGBuilder::visitLoad(const LoadInst &I) { return visitAtomicLoad(I); const Value *SV = I.getOperand(0); + if (const Argument *Arg = dyn_cast<Argument>(SV)) { + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return visitLoadFromSwiftError(I); + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return visitLoadFromSwiftError(I); + } + SDValue Ptr = getValue(SV); Type *Ty = I.getType(); @@ -3192,6 +3257,62 @@ void SelectionDAGBuilder::visitLoad(const LoadInst &I) { DAG.getVTList(ValueVTs), Values)); } +void SelectionDAGBuilder::visitStoreToSwiftError(const StoreInst &I) { + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + assert(TLI.supportSwiftError() && + "call visitStoreToSwiftError when backend supports swifterror"); + + SmallVector<EVT, 4> ValueVTs; + SmallVector<uint64_t, 4> Offsets; + const Value *SrcV = I.getOperand(0); + ComputeValueVTs(DAG.getTargetLoweringInfo(), DAG.getDataLayout(), + SrcV->getType(), ValueVTs, &Offsets); + assert(ValueVTs.size() == 1 && "expect a single EVT for swifterror"); + + SDValue Src = getValue(SrcV); + // Create a virtual register, then update the virtual register. + auto &DL = DAG.getDataLayout(); + const TargetRegisterClass *RC = TLI.getRegClassFor(TLI.getPointerTy(DL)); + unsigned VReg = FuncInfo.MF->getRegInfo().createVirtualRegister(RC); + // Chain, DL, Reg, N or Chain, DL, Reg, N, Glue + // Chain can be getRoot or getControlRoot. + SDValue CopyNode = DAG.getCopyToReg(getRoot(), getCurSDLoc(), VReg, + SDValue(Src.getNode(), Src.getResNo())); + DAG.setRoot(CopyNode); + FuncInfo.setSwiftErrorVReg(FuncInfo.MBB, I.getOperand(1), VReg); +} + +void SelectionDAGBuilder::visitLoadFromSwiftError(const LoadInst &I) { + assert(DAG.getTargetLoweringInfo().supportSwiftError() && + "call visitLoadFromSwiftError when backend supports swifterror"); + + assert(!I.isVolatile() && + I.getMetadata(LLVMContext::MD_nontemporal) == nullptr && + I.getMetadata(LLVMContext::MD_invariant_load) == nullptr && + "Support volatile, non temporal, invariant for load_from_swift_error"); + + const Value *SV = I.getOperand(0); + Type *Ty = I.getType(); + AAMDNodes AAInfo; + I.getAAMetadata(AAInfo); + assert(!AA->pointsToConstantMemory(MemoryLocation( + SV, DAG.getDataLayout().getTypeStoreSize(Ty), AAInfo)) && + "load_from_swift_error should not be constant memory"); + + SmallVector<EVT, 4> ValueVTs; + SmallVector<uint64_t, 4> Offsets; + ComputeValueVTs(DAG.getTargetLoweringInfo(), DAG.getDataLayout(), Ty, + ValueVTs, &Offsets); + assert(ValueVTs.size() == 1 && "expect a single EVT for swifterror"); + + // Chain, DL, Reg, VT, Glue or Chain, DL, Reg, VT + SDValue L = DAG.getCopyFromReg(getRoot(), getCurSDLoc(), + FuncInfo.findSwiftErrorVReg(FuncInfo.MBB, SV), + ValueVTs[0]); + + setValue(&I, L); +} + void SelectionDAGBuilder::visitStore(const StoreInst &I) { if (I.isAtomic()) return visitAtomicStore(I); @@ -3199,6 +3320,18 @@ void SelectionDAGBuilder::visitStore(const StoreInst &I) { const Value *SrcV = I.getOperand(0); const Value *PtrV = I.getOperand(1); + if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return visitStoreToSwiftError(I); + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return visitStoreToSwiftError(I); + } + SmallVector<EVT, 4> ValueVTs; SmallVector<uint64_t, 4> Offsets; ComputeValueVTs(DAG.getTargetLoweringInfo(), DAG.getDataLayout(), @@ -5345,11 +5478,14 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, PointerType *PT = cast<PointerType>(CS.getCalledValue()->getType()); FunctionType *FTy = cast<FunctionType>(PT->getElementType()); Type *RetTy = FTy->getReturnType(); + auto &DL = DAG.getDataLayout(); TargetLowering::ArgListTy Args; TargetLowering::ArgListEntry Entry; Args.reserve(CS.arg_size()); + bool HasSwiftError = false; + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); for (ImmutableCallSite::arg_iterator i = CS.arg_begin(), e = CS.arg_end(); i != e; ++i) { const Value *V = *i; @@ -5363,6 +5499,14 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, // Skip the first return-type Attribute to get to params. Entry.setAttributes(&CS, i - CS.arg_begin() + 1); + + // Use swifterror virtual register as input to the call. + if (Entry.isSwiftError && TLI.supportSwiftError()) { + HasSwiftError = true; + Entry.Node = DAG.getRegister(FuncInfo.SwiftErrorMap[FuncInfo.MBB][0], + EVT(TLI.getPointerTy(DL))); + } + Args.push_back(Entry); // If we have an explicit sret argument that is an Instruction, (i.e., it @@ -5384,6 +5528,19 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, if (Result.first.getNode()) setValue(CS.getInstruction(), Result.first); + + // The last element of CLI.InVals has the SDValue for swifterror return. + // Here we copy it to a virtual register and update SwiftErrorMap for + // book-keeping. + if (HasSwiftError && TLI.supportSwiftError()) { + // Get the last element of InVals. + SDValue Src = CLI.InVals.back(); + const TargetRegisterClass *RC = TLI.getRegClassFor(TLI.getPointerTy(DL)); + unsigned VReg = FuncInfo.MF->getRegInfo().createVirtualRegister(RC); + SDValue CopyNode = CLI.DAG.getCopyToReg(Result.second, CLI.DL, VReg, Src); + FuncInfo.SwiftErrorMap[FuncInfo.MBB][0] = VReg; + DAG.setRoot(CopyNode); + } } /// IsOnlyUsedInZeroEqualityComparison - Return true if it only matters that the @@ -7021,6 +7178,8 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const { Entry.isNest = false; Entry.isByVal = false; Entry.isReturned = false; + Entry.isSwiftSelf = false; + Entry.isSwiftError = false; Entry.Alignment = Align; CLI.getArgs().insert(CLI.getArgs().begin(), Entry); CLI.RetTy = Type::getVoidTy(CLI.RetTy->getContext()); @@ -7049,10 +7208,23 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const { } } + // We push in swifterror return as the last element of CLI.Ins. + ArgListTy &Args = CLI.getArgs(); + if (supportSwiftError()) { + for (unsigned i = 0, e = Args.size(); i != e; ++i) { + if (Args[i].isSwiftError) { + ISD::InputArg MyFlags; + MyFlags.VT = getPointerTy(DL); + MyFlags.ArgVT = EVT(getPointerTy(DL)); + MyFlags.Flags.setSwiftError(); + CLI.Ins.push_back(MyFlags); + } + } + } + // Handle all of the outgoing arguments. CLI.Outs.clear(); CLI.OutVals.clear(); - ArgListTy &Args = CLI.getArgs(); for (unsigned i = 0, e = Args.size(); i != e; ++i) { SmallVector<EVT, 4> ValueVTs; ComputeValueVTs(*this, DL, Args[i].Ty, ValueVTs); @@ -7078,6 +7250,10 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const { Flags.setInReg(); if (Args[i].isSRet) Flags.setSRet(); + if (Args[i].isSwiftSelf) + Flags.setSwiftSelf(); + if (Args[i].isSwiftError) + Flags.setSwiftError(); if (Args[i].isByVal) Flags.setByVal(); if (Args[i].isInAlloca) { @@ -7163,6 +7339,9 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const { SmallVector<SDValue, 4> InVals; CLI.Chain = LowerCall(CLI, InVals); + // Update CLI.InVals to use outside of this function. + CLI.InVals = InVals; + // Verify that the target's LowerCall behaved as expected. assert(CLI.Chain.getNode() && CLI.Chain.getValueType() == MVT::Other && "LowerCall didn't return a valid chain!"); @@ -7350,6 +7529,10 @@ void SelectionDAGISel::LowerArguments(const Function &F) { Flags.setInReg(); if (F.getAttributes().hasAttribute(Idx, Attribute::StructRet)) Flags.setSRet(); + if (F.getAttributes().hasAttribute(Idx, Attribute::SwiftSelf)) + Flags.setSwiftSelf(); + if (F.getAttributes().hasAttribute(Idx, Attribute::SwiftError)) + Flags.setSwiftError(); if (F.getAttributes().hasAttribute(Idx, Attribute::ByVal)) Flags.setByVal(); if (F.getAttributes().hasAttribute(Idx, Attribute::InAlloca)) { @@ -7507,6 +7690,14 @@ void SelectionDAGISel::LowerArguments(const Function &F) { FuncInfo->setArgumentFrameIndex(&*I, FI->getIndex()); } + // Update SwiftErrorMap. + if (Res.getOpcode() == ISD::CopyFromReg && TLI->supportSwiftError() && + F.getAttributes().hasAttribute(Idx, Attribute::SwiftError)) { + unsigned Reg = cast<RegisterSDNode>(Res.getOperand(1))->getReg(); + if (TargetRegisterInfo::isVirtualRegister(Reg)) + FuncInfo->SwiftErrorMap[FuncInfo->MBB][0] = Reg; + } + // If this argument is live outside of the entry block, insert a copy from // wherever we got it to the vreg that other BB's will reference it as. if (!TM.Options.EnableFastISel && Res.getOpcode() == ISD::CopyFromReg) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h index 321e18d682a4c..2696d89eb856b 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -840,6 +840,8 @@ class SelectionDAGBuilder { bool visitBinaryFloatCall(const CallInst &I, unsigned Opcode); void visitAtomicLoad(const LoadInst &I); void visitAtomicStore(const StoreInst &I); + void visitLoadFromSwiftError(const LoadInst &I); + void visitStoreToSwiftError(const StoreInst &I); void visitInlineAsm(ImmutableCallSite CS); const char *visitIntrinsicCall(const CallInst &I, unsigned Intrinsic); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index a9113252abb7d..29313e04a1b6b 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -1106,12 +1106,130 @@ static void collectFailStats(const Instruction *I) { } #endif +/// Set up SwiftErrorVals by going through the function. If the function has +/// swifterror argument, it will be the first entry. +static void setupSwiftErrorVals(const Function &Fn, const TargetLowering *TLI, + FunctionLoweringInfo *FuncInfo) { + if (!TLI->supportSwiftError()) + return; + + FuncInfo->SwiftErrorVals.clear(); + FuncInfo->SwiftErrorMap.clear(); + FuncInfo->SwiftErrorWorklist.clear(); + + // Check if function has a swifterror argument. + for (Function::const_arg_iterator AI = Fn.arg_begin(), AE = Fn.arg_end(); + AI != AE; ++AI) + if (AI->hasSwiftErrorAttr()) + FuncInfo->SwiftErrorVals.push_back(AI); + + for (const auto &LLVMBB : Fn) + for (const auto &Inst : LLVMBB) { + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(&Inst)) + if (Alloca->isSwiftError()) + FuncInfo->SwiftErrorVals.push_back(Alloca); + } +} + +/// For each basic block, merge incoming swifterror values or simply propagate +/// them. The merged results will be saved in SwiftErrorMap. For predecessors +/// that are not yet visited, we create virtual registers to hold the swifterror +/// values and save them in SwiftErrorWorklist. +static void mergeIncomingSwiftErrors(FunctionLoweringInfo *FuncInfo, + const TargetLowering *TLI, + const TargetInstrInfo *TII, + const BasicBlock *LLVMBB, + SelectionDAGBuilder *SDB) { + if (!TLI->supportSwiftError()) + return; + + // We should only do this when we have swifterror parameter or swifterror + // alloc. + if (FuncInfo->SwiftErrorVals.empty()) + return; + + // At beginning of a basic block, insert PHI nodes or get the virtual + // register from the only predecessor, and update SwiftErrorMap; if one + // of the predecessors is not visited, update SwiftErrorWorklist. + // At end of a basic block, if a block is in SwiftErrorWorklist, insert copy + // to sync up the virtual register assignment. + + // Always create a virtual register for each swifterror value in entry block. + auto &DL = SDB->DAG.getDataLayout(); + const TargetRegisterClass *RC = TLI->getRegClassFor(TLI->getPointerTy(DL)); + if (pred_begin(LLVMBB) == pred_end(LLVMBB)) { + for (unsigned I = 0, E = FuncInfo->SwiftErrorVals.size(); I < E; I++) { + unsigned VReg = FuncInfo->MF->getRegInfo().createVirtualRegister(RC); + // Assign Undef to Vreg. We construct MI directly to make sure it works + // with FastISel. + BuildMI(*FuncInfo->MBB, FuncInfo->InsertPt, SDB->getCurDebugLoc(), + TII->get(TargetOpcode::IMPLICIT_DEF), VReg); + FuncInfo->SwiftErrorMap[FuncInfo->MBB].push_back(VReg); + } + return; + } + + if (auto *UniquePred = LLVMBB->getUniquePredecessor()) { + auto *UniquePredMBB = FuncInfo->MBBMap[UniquePred]; + if (!FuncInfo->SwiftErrorMap.count(UniquePredMBB)) { + // Update SwiftErrorWorklist with a new virtual register. + for (unsigned I = 0, E = FuncInfo->SwiftErrorVals.size(); I < E; I++) { + unsigned VReg = FuncInfo->MF->getRegInfo().createVirtualRegister(RC); + FuncInfo->SwiftErrorWorklist[UniquePredMBB].push_back(VReg); + // Propagate the information from the single predecessor. + FuncInfo->SwiftErrorMap[FuncInfo->MBB].push_back(VReg); + } + return; + } + // Propagate the information from the single predecessor. + FuncInfo->SwiftErrorMap[FuncInfo->MBB] = + FuncInfo->SwiftErrorMap[UniquePredMBB]; + return; + } + + // For the case of multiple predecessors, update SwiftErrorWorklist. + // Handle the case where we have two or more predecessors being the same. + for (const_pred_iterator PI = pred_begin(LLVMBB), PE = pred_end(LLVMBB); + PI != PE; ++PI) { + auto *PredMBB = FuncInfo->MBBMap[*PI]; + if (!FuncInfo->SwiftErrorMap.count(PredMBB) && + !FuncInfo->SwiftErrorWorklist.count(PredMBB)) { + for (unsigned I = 0, E = FuncInfo->SwiftErrorVals.size(); I < E; I++) { + unsigned VReg = FuncInfo->MF->getRegInfo().createVirtualRegister(RC); + FuncInfo->SwiftErrorWorklist[PredMBB].push_back(VReg); + } + } + } + + // For the case of multiple predecessors, create a virtual register for + // each swifterror value and generate Phi node. + for (unsigned I = 0, E = FuncInfo->SwiftErrorVals.size(); I < E; I++) { + unsigned VReg = FuncInfo->MF->getRegInfo().createVirtualRegister(RC); + FuncInfo->SwiftErrorMap[FuncInfo->MBB].push_back(VReg); + + MachineInstrBuilder SwiftErrorPHI = BuildMI(*FuncInfo->MBB, + FuncInfo->MBB->begin(), SDB->getCurDebugLoc(), + TII->get(TargetOpcode::PHI), VReg); + for (const_pred_iterator PI = pred_begin(LLVMBB), PE = pred_end(LLVMBB); + PI != PE; ++PI) { + auto *PredMBB = FuncInfo->MBBMap[*PI]; + unsigned SwiftErrorReg = FuncInfo->SwiftErrorMap.count(PredMBB) ? + FuncInfo->SwiftErrorMap[PredMBB][I] : + FuncInfo->SwiftErrorWorklist[PredMBB][I]; + SwiftErrorPHI.addReg(SwiftErrorReg) + .addMBB(PredMBB); + } + } +} + void SelectionDAGISel::SelectAllBasicBlocks(const Function &Fn) { // Initialize the Fast-ISel state, if needed. FastISel *FastIS = nullptr; if (TM.Options.EnableFastISel) FastIS = TLI->createFastISel(*FuncInfo, LibInfo); + setupSwiftErrorVals(Fn, TLI, FuncInfo); + // Iterate over all basic blocks in the function. ReversePostOrderTraversal<const Function*> RPOT(&Fn); for (ReversePostOrderTraversal<const Function*>::rpo_iterator @@ -1150,6 +1268,7 @@ void SelectionDAGISel::SelectAllBasicBlocks(const Function &Fn) { if (!FuncInfo->MBB) continue; // Some blocks like catchpads have no code or MBB. FuncInfo->InsertPt = FuncInfo->MBB->getFirstNonPHI(); + mergeIncomingSwiftErrors(FuncInfo, TLI, TII, LLVMBB, SDB); // Setup an EH landing-pad block. FuncInfo->ExceptionPointerVirtReg = 0; diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 21935cdd46993..81e990e6c1ca1 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -77,6 +77,8 @@ void TargetLowering::ArgListEntry::setAttributes(ImmutableCallSite *CS, isByVal = CS->paramHasAttr(AttrIdx, Attribute::ByVal); isInAlloca = CS->paramHasAttr(AttrIdx, Attribute::InAlloca); isReturned = CS->paramHasAttr(AttrIdx, Attribute::Returned); + isSwiftSelf = CS->paramHasAttr(AttrIdx, Attribute::SwiftSelf); + isSwiftError = CS->paramHasAttr(AttrIdx, Attribute::SwiftError); Alignment = CS->getParamAlignment(AttrIdx); } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index db757508594a2..8987c213ff5c6 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -319,6 +319,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) { case CallingConv::X86_64_Win64: Out << "x86_64_win64cc"; break; case CallingConv::SPIR_FUNC: Out << "spir_func"; break; case CallingConv::SPIR_KERNEL: Out << "spir_kernel"; break; + case CallingConv::Swift: Out << "swiftcc"; break; case CallingConv::HHVM: Out << "hhvmcc"; break; case CallingConv::HHVM_C: Out << "hhvm_ccc"; break; } @@ -3033,6 +3034,8 @@ void AssemblyWriter::printInstruction(const Instruction &I) { Out << ' '; if (AI->isUsedWithInAlloca()) Out << "inalloca "; + if (AI->isSwiftError()) + Out << "swifterror "; TypePrinter.print(AI->getAllocatedType(), Out); // Explicitly write the array size if the code is broken, if it's an array diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 2586cb54c3ee8..f437aa08f5b60 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -198,6 +198,10 @@ std::string Attribute::getAsString(bool InAttrGrp) const { return "byval"; if (hasAttribute(Attribute::Convergent)) return "convergent"; + if (hasAttribute(Attribute::SwiftError)) + return "swifterror"; + if (hasAttribute(Attribute::SwiftSelf)) + return "swiftself"; if (hasAttribute(Attribute::InAlloca)) return "inalloca"; if (hasAttribute(Attribute::InlineHint)) @@ -442,6 +446,8 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) { case Attribute::JumpTable: return 1ULL << 45; case Attribute::Convergent: return 1ULL << 46; case Attribute::SafeStack: return 1ULL << 47; + case Attribute::SwiftSelf: return 1ULL << 48; + case Attribute::SwiftError: return 1ULL << 49; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index 3a1c7a4ca3616..5a0c3cb1e39a5 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -93,6 +93,16 @@ bool Argument::hasByValAttr() const { hasAttribute(getArgNo()+1, Attribute::ByVal); } +bool Argument::hasSwiftSelfAttr() const { + return getParent()->getAttributes(). + hasAttribute(getArgNo()+1, Attribute::SwiftSelf); +} + +bool Argument::hasSwiftErrorAttr() const { + return getParent()->getAttributes(). + hasAttribute(getArgNo()+1, Attribute::SwiftError); +} + /// \brief Return true if this argument has the inalloca attribute on it in /// its containing function. bool Argument::hasInAllocaAttr() const { diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index dfd711f5c23aa..1db39497a925d 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -3900,6 +3900,7 @@ AllocaInst *AllocaInst::cloneImpl() const { AllocaInst *Result = new AllocaInst(getAllocatedType(), (Value *)getOperand(0), getAlignment()); Result->setUsedWithInAlloca(isUsedWithInAlloca()); + Result->setSwiftError(isSwiftError()); return Result; } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 813f9ca674485..ffe3307a0c55f 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1269,9 +1269,12 @@ void Verifier::VerifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty, !Attrs.hasAttribute(Idx, Attribute::StructRet) && !Attrs.hasAttribute(Idx, Attribute::NoCapture) && !Attrs.hasAttribute(Idx, Attribute::Returned) && - !Attrs.hasAttribute(Idx, Attribute::InAlloca), - "Attributes 'byval', 'inalloca', 'nest', 'sret', 'nocapture', and " - "'returned' do not apply to return values!", + !Attrs.hasAttribute(Idx, Attribute::InAlloca) && + !Attrs.hasAttribute(Idx, Attribute::SwiftSelf) && + !Attrs.hasAttribute(Idx, Attribute::SwiftError), + "Attributes 'byval', 'inalloca', 'nest', 'sret', 'nocapture', " + "'returned', 'swiftself', and 'swifterror' do not apply to return " + "values!", V); // Check for mutually incompatible attributes. Only inreg is compatible with @@ -1348,6 +1351,8 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs, bool SawNest = false; bool SawReturned = false; bool SawSRet = false; + bool SawSwiftSelf = false; + bool SawSwiftError = false; for (unsigned i = 0, e = Attrs.getNumSlots(); i != e; ++i) { unsigned Idx = Attrs.getSlotIndex(i); @@ -1387,6 +1392,17 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs, SawSRet = true; } + if (Attrs.hasAttribute(Idx, Attribute::SwiftSelf)) { + Assert(!SawSwiftSelf, "Cannot have multiple 'swiftself' parameters!", V); + SawSwiftSelf = true; + } + + if (Attrs.hasAttribute(Idx, Attribute::SwiftError)) { + Assert(!SawSwiftError, "Cannot have multiple 'swifterror' parameters!", + V); + SawSwiftError = true; + } + if (Attrs.hasAttribute(Idx, Attribute::InAlloca)) { Assert(Idx == FT->getNumParams(), "inalloca isn't on the last parameter!", V); @@ -2263,6 +2279,16 @@ void Verifier::VerifyCallSite(CallSite CS) { "inalloca argument for call has mismatched alloca", AI, I); } + // For each argument of the callsite, if it has the swifterror argument, + // make sure the underlying alloca has swifterror as well. + for (unsigned i = 0, e = FTy->getNumParams(); i != e; ++i) + if (CS.paramHasAttr(i+1, Attribute::SwiftError)) { + Value *SwiftErrorArg = CS.getArgument(i); + if (auto AI = dyn_cast<AllocaInst>(SwiftErrorArg->stripInBoundsOffsets())) + Assert(AI->isSwiftError(), + "swifterror argument for call has mismatched alloca", AI, I); + } + if (FTy->isVarArg()) { // FIXME? is 'nest' even legal here? bool SawNest = false; @@ -2341,7 +2367,8 @@ static bool isTypeCongruent(Type *L, Type *R) { static AttrBuilder getParameterABIAttributes(int I, AttributeSet Attrs) { static const Attribute::AttrKind ABIAttrs[] = { Attribute::StructRet, Attribute::ByVal, Attribute::InAlloca, - Attribute::InReg, Attribute::Returned}; + Attribute::InReg, Attribute::Returned, Attribute::SwiftSelf, + Attribute::SwiftError}; AttrBuilder Copy; for (auto AK : ABIAttrs) { if (Attrs.hasAttribute(I + 1, AK)) diff --git a/llvm/lib/MC/MCAsmInfo.cpp b/llvm/lib/MC/MCAsmInfo.cpp index 36e10b3c6a073..fa7d438d15918 100644 --- a/llvm/lib/MC/MCAsmInfo.cpp +++ b/llvm/lib/MC/MCAsmInfo.cpp @@ -75,6 +75,7 @@ MCAsmInfo::MCAsmInfo() { HasSingleParameterDotFile = true; HasIdentDirective = false; HasNoDeadStrip = false; + HasAltEntry = false; WeakDirective = "\t.weak\t"; WeakRefDirective = nullptr; HasWeakDefDirective = false; diff --git a/llvm/lib/MC/MCAsmInfoDarwin.cpp b/llvm/lib/MC/MCAsmInfoDarwin.cpp index bb90ff2c350ae..200f5312a73bf 100644 --- a/llvm/lib/MC/MCAsmInfoDarwin.cpp +++ b/llvm/lib/MC/MCAsmInfoDarwin.cpp @@ -88,6 +88,7 @@ MCAsmInfoDarwin::MCAsmInfoDarwin() { HasDotTypeDotSizeDirective = false; HasNoDeadStrip = true; + HasAltEntry = true; DwarfUsesRelocationsAcrossSections = false; diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 4f2d1d2229d92..e35c062c9acca 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -453,6 +453,7 @@ bool MCAsmStreamer::EmitSymbolAttribute(MCSymbol *Symbol, OS << "\t.no_dead_strip\t"; break; case MCSA_SymbolResolver: OS << "\t.symbol_resolver\t"; break; + case MCSA_AltEntry: OS << "\t.alt_entry\t"; break; case MCSA_PrivateExtern: OS << "\t.private_extern\t"; break; diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 06d161bccab48..5e680a8c788f5 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -283,6 +283,9 @@ bool MCELFStreamer::EmitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) { case MCSA_Internal: Symbol->setVisibility(ELF::STV_INTERNAL); break; + + case MCSA_AltEntry: + llvm_unreachable("ELF doesn't support this attribute"); } return true; diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp index 52ecf9fcfbf3d..634265819b0da 100644 --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -345,6 +345,10 @@ bool MCMachOStreamer::EmitSymbolAttribute(MCSymbol *Sym, Symbol->setSymbolResolver(); break; + case MCSA_AltEntry: + Symbol->setAltEntry(); + break; + case MCSA_PrivateExtern: Symbol->setExternal(true); Symbol->setPrivateExtern(true); diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 8903637678061..7df7d514d96fa 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -347,8 +347,8 @@ class AsmParser : public MCAsmParser { DK_BALIGNL, DK_P2ALIGN, DK_P2ALIGNW, DK_P2ALIGNL, DK_ORG, DK_FILL, DK_ENDR, DK_BUNDLE_ALIGN_MODE, DK_BUNDLE_LOCK, DK_BUNDLE_UNLOCK, DK_ZERO, DK_EXTERN, DK_GLOBL, DK_GLOBAL, - DK_LAZY_REFERENCE, DK_NO_DEAD_STRIP, DK_SYMBOL_RESOLVER, DK_PRIVATE_EXTERN, - DK_REFERENCE, DK_WEAK_DEFINITION, DK_WEAK_REFERENCE, + DK_LAZY_REFERENCE, DK_NO_DEAD_STRIP, DK_SYMBOL_RESOLVER, DK_ALT_ENTRY, + DK_PRIVATE_EXTERN, DK_REFERENCE, DK_WEAK_DEFINITION, DK_WEAK_REFERENCE, DK_WEAK_DEF_CAN_BE_HIDDEN, DK_COMM, DK_COMMON, DK_LCOMM, DK_ABORT, DK_INCLUDE, DK_INCBIN, DK_CODE16, DK_CODE16GCC, DK_REPT, DK_IRP, DK_IRPC, DK_IF, DK_IFEQ, DK_IFGE, DK_IFGT, DK_IFLE, DK_IFLT, DK_IFNE, DK_IFB, @@ -1569,6 +1569,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info, return parseDirectiveSymbolAttribute(MCSA_NoDeadStrip); case DK_SYMBOL_RESOLVER: return parseDirectiveSymbolAttribute(MCSA_SymbolResolver); + case DK_ALT_ENTRY: + return parseDirectiveSymbolAttribute(MCSA_AltEntry); case DK_PRIVATE_EXTERN: return parseDirectiveSymbolAttribute(MCSA_PrivateExtern); case DK_REFERENCE: @@ -4266,6 +4268,7 @@ void AsmParser::initializeDirectiveKindMap() { DirectiveKindMap[".lazy_reference"] = DK_LAZY_REFERENCE; DirectiveKindMap[".no_dead_strip"] = DK_NO_DEAD_STRIP; DirectiveKindMap[".symbol_resolver"] = DK_SYMBOL_RESOLVER; + DirectiveKindMap[".alt_entry"] = DK_ALT_ENTRY; DirectiveKindMap[".private_extern"] = DK_PRIVATE_EXTERN; DirectiveKindMap[".reference"] = DK_REFERENCE; DirectiveKindMap[".weak_definition"] = DK_WEAK_DEFINITION; diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.td b/llvm/lib/Target/AArch64/AArch64CallingConvention.td index 948b9ddb5df6d..1def59ce76516 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -86,6 +86,8 @@ def RetCC_AArch64_AAPCS : CallingConv<[ CCIfType<[v2f32], CCBitConvertToType<v2i32>>, CCIfType<[v2f64, v4f32], CCBitConvertToType<v2i64>>, + CCIfSwiftError<CCIfType<[i64], CCAssignToRegWithShadow<[X19], [W19]>>>, + // Big endian vectors must be passed as if they were 1-element vectors so that // their lanes are in a consistent order. CCIfBigEndian<CCIfType<[v2i32, v2f32, v4i16, v4f16, v8i8], @@ -126,6 +128,12 @@ def CC_AArch64_DarwinPCS : CallingConv<[ // slot is 64-bit. CCIfByVal<CCPassByVal<8, 8>>, + // An SwiftSelf is passed in X9. + CCIfSwiftSelf<CCIfType<[i64], CCAssignToRegWithShadow<[X9], [W9]>>>, + + // A SwiftError is passed in X19. + CCIfSwiftError<CCIfType<[i64], CCAssignToRegWithShadow<[X19], [W19]>>>, + CCIfConsecutiveRegs<CCCustom<"CC_AArch64_Custom_Block">>, // Handle i1, i8, i16, i32, i64, f32, f64 and v2f64 by passing in registers, @@ -270,6 +278,11 @@ def CSR_AArch64_AAPCS : CalleeSavedRegs<(add LR, FP, X19, X20, X21, X22, // case) def CSR_AArch64_AAPCS_ThisReturn : CalleeSavedRegs<(add CSR_AArch64_AAPCS, X0)>; +def CSR_AArch64_AAPCS_SwiftError : CalleeSavedRegs<(add LR, FP, X20, X21, X22, + X23, X24, X25, X26, X27, X28, + D8, D9, D10, D11, + D12, D13, D14, D15)>; + // The function used by Darwin to obtain the address of a thread-local variable // guarantees more than a normal AAPCS function. x16 and x17 are used on the // fast path for calculation, but other registers except X0 (argument/return) diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index 2f50480efbe23..ddae47a6cf956 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -1904,6 +1904,17 @@ bool AArch64FastISel::selectLoad(const Instruction *I) { cast<LoadInst>(I)->isAtomic()) return false; + const Value *SV = I->getOperand(0); + if (const Argument *Arg = dyn_cast<Argument>(SV)) { + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return false; + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return false; + } + // See if we can handle this address. Address Addr; if (!computeAddress(I->getOperand(0), Addr, I->getType())) @@ -2068,6 +2079,17 @@ bool AArch64FastISel::selectStore(const Instruction *I) { cast<StoreInst>(I)->isAtomic()) return false; + const Value *PtrV = I->getOperand(1); + if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return false; + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return false; + } + // Get the value to be stored into a register. Use the zero register directly // when possible to avoid an unnecessary copy and a wasted register. unsigned SrcReg = 0; @@ -2845,6 +2867,8 @@ bool AArch64FastISel::fastLowerArguments() { if (F->getAttributes().hasAttribute(Idx, Attribute::ByVal) || F->getAttributes().hasAttribute(Idx, Attribute::InReg) || F->getAttributes().hasAttribute(Idx, Attribute::StructRet) || + F->getAttributes().hasAttribute(Idx, Attribute::SwiftSelf) || + F->getAttributes().hasAttribute(Idx, Attribute::SwiftError) || F->getAttributes().hasAttribute(Idx, Attribute::Nest)) return false; @@ -3096,7 +3120,8 @@ bool AArch64FastISel::fastLowerCall(CallLoweringInfo &CLI) { return false; for (auto Flag : CLI.OutFlags) - if (Flag.isInReg() || Flag.isSRet() || Flag.isNest() || Flag.isByVal()) + if (Flag.isInReg() || Flag.isSRet() || Flag.isNest() || Flag.isByVal() || + Flag.isSwiftSelf() || Flag.isSwiftError()) return false; // Set up the argument vectors. @@ -3678,6 +3703,10 @@ bool AArch64FastISel::selectRet(const Instruction *I) { if (F.isVarArg()) return false; + if (F.getAttributes().hasAttrSomewhere(Attribute::SwiftError) && + TLI.supportSwiftError()) + return false; + // Build a list of return value registers. SmallVector<unsigned, 4> RetRegs; diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index c5b7d0c168e00..edf3e7d0ef529 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -723,31 +723,80 @@ static unsigned getPrologueDeath(MachineFunction &MF, unsigned Reg) { return getKillRegState(LRKill); } +/// The register order of CSI list is controlled by getCalleeSavedRegs, and the +/// frame index is controlled by getCalleeSavedRegsForLayout. We produce a +/// vector of register pairs that need storing in the RegPairs argument. The +/// value 0 indicates no register needs storing in a paired slot. +static void +getCSRPairs(const std::vector<CalleeSavedInfo> &CSI, + const AArch64RegisterInfo *RegInfo, const MachineFunction *MF, + SmallVectorImpl<std::pair<unsigned, unsigned>> &RegPairs) { + const MCPhysReg *CSRegs = RegInfo->getCalleeSavedRegsForLayout(MF); + + auto CSIPos = CSI.begin(); + auto CSIEnd = CSI.end(); + // We assume that getCalleeSavedRegsForLayout has the same ordering as + // getCalleeSavedRegs. So we can advance CSI vector while advancing + // getCalleeSavedRegsForLayout. + for (unsigned i = 0; CSRegs[i]; i += 2) { + assert(CSRegs[i + 1] && "Odd number of callee-saved registers!"); + auto FirstPos = + std::find_if(CSIPos, CSIEnd, [&](const CalleeSavedInfo &I) { + return I.getReg() == CSRegs[i]; + }); + auto SecondPos = + std::find_if(CSIPos, CSIEnd, [&](const CalleeSavedInfo &I) { + return I.getReg() == CSRegs[i + 1]; + }); + if (FirstPos == CSIEnd && SecondPos == CSIEnd) + // Neither of these regs need storing. + continue; + + unsigned FirstReg = FirstPos == CSIEnd ? AArch64::XZR : CSRegs[i]; + unsigned SecondReg = SecondPos == CSIEnd ? AArch64::XZR : CSRegs[i + 1]; + + if (FirstPos != CSIEnd && SecondPos != CSIEnd) { + // GPRs and FPRs are saved in pairs of 64-bit regs. We expect the CSI + // list to come in sorted by frame index so that we can issue the store + // pair instructions directly. Assert if we see anything otherwise. + assert(FirstPos->getFrameIdx() + 1 == SecondPos->getFrameIdx() && + "Out of order callee saved regs!"); + } + + DEBUG(dbgs() << "CSR pair: (" << RegInfo->getName(FirstReg) << ", " + << RegInfo->getName(SecondReg) << ") -> fi#(" + << (FirstPos == CSIEnd ? SecondPos->getFrameIdx() + : FirstPos->getFrameIdx()) + << ")\n"); + + RegPairs.push_back(std::make_pair(FirstReg, SecondReg)); + CSIPos = SecondPos == CSIEnd ? FirstPos : SecondPos; + } +} + bool AArch64FrameLowering::spillCalleeSavedRegisters( MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const std::vector<CalleeSavedInfo> &CSI, const TargetRegisterInfo *TRI) const { MachineFunction &MF = *MBB.getParent(); const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); - unsigned Count = CSI.size(); DebugLoc DL; - assert((Count & 1) == 0 && "Odd number of callee-saved regs to spill!"); - - for (unsigned i = 0; i < Count; i += 2) { - unsigned idx = Count - i - 2; - unsigned Reg1 = CSI[idx].getReg(); - unsigned Reg2 = CSI[idx + 1].getReg(); - // GPRs and FPRs are saved in pairs of 64-bit regs. We expect the CSI - // list to come in sorted by frame index so that we can issue the store - // pair instructions directly. Assert if we see anything otherwise. - // + + const AArch64RegisterInfo *RegInfo = static_cast<const AArch64RegisterInfo *>( + MF.getSubtarget().getRegisterInfo()); + SmallVector<std::pair<unsigned, unsigned>, 4> RegPairs; + + // First gather information about the paris we'd like to store. + getCSRPairs(CSI, RegInfo, &MF, RegPairs); + unsigned Count = RegPairs.size(); + + for (unsigned i = 0; i < Count; ++i) { + unsigned idx = Count - i - 1; + unsigned Reg1 = RegPairs[idx].first; + unsigned Reg2 = RegPairs[idx].second; // The order of the registers in the list is controlled by // getCalleeSavedRegs(), so they will always be in-order, as well. - assert(CSI[idx].getFrameIdx() + 1 == CSI[idx + 1].getFrameIdx() && - "Out of order callee saved regs!"); unsigned StrOpc; - assert((Count & 1) == 0 && "Odd number of callee-saved regs to spill!"); - assert((i & 1) == 0 && "Odd index for callee-saved reg spill!"); // Issue sequence of non-sp increment and pi sp spills for cs regs. The // first spill is a pre-increment that allocates the stack. // For example: @@ -780,7 +829,7 @@ bool AArch64FrameLowering::spillCalleeSavedRegisters( << ", " << CSI[idx + 1].getFrameIdx() << ")\n"); // Compute offset: i = 0 => offset = -Count; // i = 2 => offset = -(Count - 2) + Count = 2 = i; etc. - const int Offset = (i == 0) ? -Count : i; + const int Offset = (i == 0) ? -2 * Count : 2 * i; assert((Offset >= -64 && Offset <= 63) && "Offset out of bounds for STP immediate"); MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(StrOpc)); @@ -804,21 +853,22 @@ bool AArch64FrameLowering::restoreCalleeSavedRegisters( const TargetRegisterInfo *TRI) const { MachineFunction &MF = *MBB.getParent(); const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); - unsigned Count = CSI.size(); DebugLoc DL; - assert((Count & 1) == 0 && "Odd number of callee-saved regs to spill!"); if (MI != MBB.end()) DL = MI->getDebugLoc(); - for (unsigned i = 0; i < Count; i += 2) { - unsigned Reg1 = CSI[i].getReg(); - unsigned Reg2 = CSI[i + 1].getReg(); - // GPRs and FPRs are saved in pairs of 64-bit regs. We expect the CSI - // list to come in sorted by frame index so that we can issue the store - // pair instructions directly. Assert if we see anything otherwise. - assert(CSI[i].getFrameIdx() + 1 == CSI[i + 1].getFrameIdx() && - "Out of order callee saved regs!"); + const AArch64RegisterInfo *RegInfo = static_cast<const AArch64RegisterInfo *>( + MF.getSubtarget().getRegisterInfo()); + SmallVector<std::pair<unsigned, unsigned>, 4> RegPairs; + + // First gather information about the paris we'd like to store. + getCSRPairs(CSI, RegInfo, &MF, RegPairs); + unsigned Count = RegPairs.size(); + + for (unsigned i = 0; i < Count; ++i) { + unsigned Reg1 = RegPairs[i].first; + unsigned Reg2 = RegPairs[i].second; // Issue sequence of non-sp increment and sp-pi restores for cs regs. Only // the last load is sp-pi post-increment and de-allocates the stack: // For example: @@ -828,19 +878,17 @@ bool AArch64FrameLowering::restoreCalleeSavedRegisters( // Note: see comment in spillCalleeSavedRegisters() unsigned LdrOpc; - assert((Count & 1) == 0 && "Odd number of callee-saved regs to spill!"); - assert((i & 1) == 0 && "Odd index for callee-saved reg spill!"); if (AArch64::GPR64RegClass.contains(Reg1)) { assert(AArch64::GPR64RegClass.contains(Reg2) && "Expected GPR64 callee-saved register pair!"); - if (i == Count - 2) + if (i == Count - 1) LdrOpc = AArch64::LDPXpost; else LdrOpc = AArch64::LDPXi; } else if (AArch64::FPR64RegClass.contains(Reg1)) { assert(AArch64::FPR64RegClass.contains(Reg2) && "Expected FPR64 callee-saved register pair!"); - if (i == Count - 2) + if (i == Count - 1) LdrOpc = AArch64::LDPDpost; else LdrOpc = AArch64::LDPDi; @@ -852,7 +900,7 @@ bool AArch64FrameLowering::restoreCalleeSavedRegisters( // Compute offset: i = 0 => offset = Count - 2; i = 2 => offset = Count - 4; // etc. - const int Offset = (i == Count - 2) ? Count : Count - i - 2; + const int Offset = (i == Count - 1) ? 2 * Count : 2 * (Count - i - 1); assert((Offset >= -64 && Offset <= 63) && "Offset out of bounds for LDP immediate"); MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(LdrOpc)); @@ -903,7 +951,7 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, bool ExtraCSSpill = false; bool CanEliminateFrame = true; DEBUG(dbgs() << "*** determineCalleeSaves\nUsed CSRs:"); - const MCPhysReg *CSRegs = RegInfo->getCalleeSavedRegs(&MF); + const MCPhysReg *CSRegs = RegInfo->getCalleeSavedRegsForLayout(&MF); // Check pairs of consecutive callee-saved registers. for (unsigned i = 0; CSRegs[i]; i += 2) { @@ -1010,3 +1058,61 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, } } } + +bool AArch64FrameLowering::assignCalleeSavedSpillSlots( + MachineFunction &MF, const TargetRegisterInfo *TRI, + std::vector<CalleeSavedInfo> &CSI, + unsigned &MinCSFrameIndex, unsigned &MaxCSFrameIndex) const { + const AArch64RegisterInfo *RegInfo = static_cast<const AArch64RegisterInfo *>( + MF.getSubtarget().getRegisterInfo()); + + if (!MF.getFunction()->getAttributes().hasAttrSomewhere( + Attribute::SwiftError)) + return false; + + if (CSI.empty()) + return true; // Early exit if no callee saved registers are modified! + + MachineFrameInfo *MFI = MF.getFrameInfo(); + + // Simplify the logic here since AArch64 does not have fixed or reserved + // spill slots. + + // Now that we know which registers need to be saved and restored, allocate + // stack slots according to getCalleeSavedRegsForLayout. + const MCPhysReg *CSRegs = RegInfo->getCalleeSavedRegsForLayout(&MF); + + for (unsigned i = 0; CSRegs[i]; ++i) { + unsigned Reg = CSRegs[i]; + + // Find the corresponding entry in CSI for Reg. + std::vector<CalleeSavedInfo>::iterator CSInfo, E; + for (CSInfo = CSI.begin(), E = CSI.end(); CSInfo != E; ++CSInfo) + if (CSInfo->getReg() == Reg) + break; + // Assign a slot for functions which call __builtin_unwind_init. + if (CSInfo == CSI.end() && !MF.getMMI().callsUnwindInit()) + continue; + + DEBUG(dbgs() << "try to allocate stack slot for reg " << Reg << '\n'); + const TargetRegisterClass *RC = RegInfo->getMinimalPhysRegClass(Reg); + + // Just spill it anywhere convenient. + unsigned Align = RC->getAlignment(); + unsigned StackAlign = getStackAlignment(); + + // We may not be able to satisfy the desired alignment specification of + // the TargetRegisterClass if the stack alignment is smaller. Use the + // min. + Align = std::min(Align, StackAlign); + int FrameIdx = MFI->CreateStackObject(RC->getSize(), Align, true); + if ((unsigned)FrameIdx < MinCSFrameIndex) MinCSFrameIndex = FrameIdx; + if ((unsigned)FrameIdx > MaxCSFrameIndex) MaxCSFrameIndex = FrameIdx; + + // Find the corresponding entry in CSI for Reg. + if (CSInfo != CSI.end()) + CSInfo->setFrameIdx(FrameIdx); + } + + return true; +} diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.h b/llvm/lib/Target/AArch64/AArch64FrameLowering.h index 16dab2f5754f3..1a847d22e51f4 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.h +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.h @@ -60,6 +60,14 @@ class AArch64FrameLowering : public TargetFrameLowering { void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, RegScavenger *RS) const override; + + + bool + assignCalleeSavedSpillSlots(MachineFunction &MF, + const TargetRegisterInfo *TRI, + std::vector<CalleeSavedInfo> &CSI, + unsigned &MinCSFrameIndex, + unsigned &MaxCSFrameIndex) const override; }; } // End llvm namespace diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 64a2934d1d52d..c7d3d7ff67b3f 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -377,6 +377,10 @@ class AArch64TargetLowering : public TargetLowering { void addDRTypeForNEON(MVT VT); void addQRTypeForNEON(MVT VT); + bool supportSwiftError() const override { + return true; + } + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl<ISD::InputArg> &Ins, SDLoc DL, diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index 1aef31baad203..97dbbdcb82051 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -40,6 +40,23 @@ AArch64RegisterInfo::AArch64RegisterInfo(const Triple &TT) const MCPhysReg * AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { assert(MF && "Invalid MachineFunction pointer."); + if (MF->getFunction()->getCallingConv() == CallingConv::GHC) + // GHC set of callee saved regs is empty as all those regs are + // used for passing STG regs around + return CSR_AArch64_NoRegs_SaveList; + if (MF->getFunction()->getCallingConv() == CallingConv::AnyReg) + return CSR_AArch64_AllRegs_SaveList; + if (MF->getFunction()->getAttributes().hasAttrSomewhere( + Attribute::SwiftError)) + return CSR_AArch64_AAPCS_SwiftError_SaveList; + else + return CSR_AArch64_AAPCS_SaveList; +} + +const MCPhysReg * +AArch64RegisterInfo::getCalleeSavedRegsForLayout( + const MachineFunction *MF) const { + assert(MF && "Invalid MachineFunction pointer."); if (MF->getFunction()->getCallingConv() == CallingConv::GHC) // GHC set of callee saved regs is empty as all those regs are // used for passing STG regs around @@ -58,6 +75,8 @@ AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF, return CSR_AArch64_NoRegs_RegMask; if (CC == CallingConv::AnyReg) return CSR_AArch64_AllRegs_RegMask; + if (MF.getFunction()->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) + return CSR_AArch64_AAPCS_SwiftError_RegMask; else return CSR_AArch64_AAPCS_RegMask; } diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h index c01bfa5ea70b2..8b95e6c812fc3 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h @@ -60,6 +60,11 @@ struct AArch64RegisterInfo : public AArch64GenRegisterInfo { const uint32_t *getThisReturnPreservedMask(const MachineFunction &MF, CallingConv::ID) const; + /// Return callee-saved registers for stack layout purpose. When we use + /// SwiftError CSR, we still need to use the standard CSR for layout purpose, + /// since compact unwinding expects the layout according to standard CSR. + const MCPhysReg *getCalleeSavedRegsForLayout(const MachineFunction *MF) const; + BitVector getReservedRegs(const MachineFunction &MF) const override; const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp index 419717c85a79d..9a53d92cf90a4 100644 --- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -87,6 +87,10 @@ ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { } } + if (STI.isTargetDarwin() && + F->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) + return CSR_iOS_SwiftError_SaveList; + return RegList; } @@ -97,6 +101,11 @@ ARMBaseRegisterInfo::getCallPreservedMask(const MachineFunction &MF, if (CC == CallingConv::GHC) // This is academic becase all GHC calls are (supposed to be) tail calls return CSR_NoRegs_RegMask; + + if (STI.isTargetDarwin() && + MF.getFunction()->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) + return CSR_iOS_SwiftError_RegMask; + return STI.isTargetDarwin() ? CSR_iOS_RegMask : CSR_AAPCS_RegMask; } diff --git a/llvm/lib/Target/ARM/ARMCallingConv.td b/llvm/lib/Target/ARM/ARMCallingConv.td index 233516415149d..8a30a4aaf8ea9 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.td +++ b/llvm/lib/Target/ARM/ARMCallingConv.td @@ -23,6 +23,12 @@ def CC_ARM_APCS : CallingConv<[ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>, + // An SwiftSelf is passed in R9. + CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, + + // An SwiftError is passed in R6. + CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, + // Handle all vector types as either f64 or v2f64. CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, @@ -42,6 +48,9 @@ def RetCC_ARM_APCS : CallingConv<[ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>, CCIfType<[f32], CCBitConvertToType<i32>>, + // An SwiftError is returned in R6. + CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, + // Handle all vector types as either f64 or v2f64. CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, @@ -151,6 +160,12 @@ def CC_ARM_AAPCS : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + // An SwiftSelf is passed in R9. + CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, + + // An SwiftError is passed in R6. + CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, + CCIfType<[f64, v2f64], CCCustom<"CC_ARM_AAPCS_Custom_f64">>, CCIfType<[f32], CCBitConvertToType<i32>>, CCDelegateTo<CC_ARM_AAPCS_Common> @@ -161,6 +176,9 @@ def RetCC_ARM_AAPCS : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + // An SwiftError is returned in R6. + CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, + CCIfType<[f64, v2f64], CCCustom<"RetCC_ARM_AAPCS_Custom_f64">>, CCIfType<[f32], CCBitConvertToType<i32>>, CCDelegateTo<RetCC_ARM_AAPCS_Common> @@ -179,6 +197,12 @@ def CC_ARM_AAPCS_VFP : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + // An SwiftSelf is passed in R9. + CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, + + // An SwiftError is passed in R6. + CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, + // HFAs are passed in a contiguous block of registers, or on the stack CCIfConsecutiveRegs<CCCustom<"CC_ARM_AAPCS_Custom_Aggregate">>, @@ -194,6 +218,9 @@ def RetCC_ARM_AAPCS_VFP : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, + // An SwiftError is returned in R6. + CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, + CCIfType<[v2f64], CCAssignToReg<[Q0, Q1, Q2, Q3]>>, CCIfType<[f64], CCAssignToReg<[D0, D1, D2, D3, D4, D5, D6, D7]>>, CCIfType<[f32], CCAssignToReg<[S0, S1, S2, S3, S4, S5, S6, S7, S8, @@ -222,6 +249,9 @@ def CSR_AAPCS_ThisReturn : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6, // Also save R7-R4 first to match the stack frame fixed spill areas. def CSR_iOS : CalleeSavedRegs<(add LR, R7, R6, R5, R4, (sub CSR_AAPCS, R9))>; +// R6 is used to pass swifterror, remove it from CSR. +def CSR_iOS_SwiftError : CalleeSavedRegs<(sub CSR_iOS, R6)>; + def CSR_iOS_ThisReturn : CalleeSavedRegs<(add LR, R7, R6, R5, R4, (sub CSR_AAPCS_ThisReturn, R9))>; diff --git a/llvm/lib/Target/ARM/ARMFastISel.cpp b/llvm/lib/Target/ARM/ARMFastISel.cpp index 175107450fc0a..48fe89edb108e 100644 --- a/llvm/lib/Target/ARM/ARMFastISel.cpp +++ b/llvm/lib/Target/ARM/ARMFastISel.cpp @@ -1062,6 +1062,17 @@ bool ARMFastISel::SelectLoad(const Instruction *I) { if (cast<LoadInst>(I)->isAtomic()) return false; + const Value *SV = I->getOperand(0); + if (const Argument *Arg = dyn_cast<Argument>(SV)) { + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return false; + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return false; + } + // Verify we have a legal type before going any further. MVT VT; if (!isLoadTypeLegal(I->getType(), VT)) @@ -1177,6 +1188,17 @@ bool ARMFastISel::SelectStore(const Instruction *I) { if (cast<StoreInst>(I)->isAtomic()) return false; + const Value *PtrV = I->getOperand(1); + if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return false; + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return false; + } + // Verify we have a legal type before going any further. MVT VT; if (!isLoadTypeLegal(I->getOperand(0)->getType(), VT)) @@ -1839,6 +1861,7 @@ CCAssignFn *ARMFastISel::CCAssignFnForCall(CallingConv::ID CC, default: llvm_unreachable("Unsupported calling convention"); case CallingConv::Fast: + case CallingConv::Swift: if (Subtarget->hasVFP2() && !isVarArg) { if (!Subtarget->isAAPCS_ABI()) return (Return ? RetFastCC_ARM_APCS : FastCC_ARM_APCS); @@ -2083,6 +2106,10 @@ bool ARMFastISel::SelectRet(const Instruction *I) { if (!FuncInfo.CanLowerReturn) return false; + if (F.getAttributes().hasAttrSomewhere(Attribute::SwiftError) && + TLI.supportSwiftError()) + return false; + // Build a list of return value registers. SmallVector<unsigned, 4> RetRegs; @@ -2342,6 +2369,8 @@ bool ARMFastISel::SelectCall(const Instruction *I, // FIXME: Only handle *easy* calls for now. if (CS.paramHasAttr(AttrInd, Attribute::InReg) || CS.paramHasAttr(AttrInd, Attribute::StructRet) || + CS.paramHasAttr(AttrInd, Attribute::SwiftSelf) || + CS.paramHasAttr(AttrInd, Attribute::SwiftError) || CS.paramHasAttr(AttrInd, Attribute::Nest) || CS.paramHasAttr(AttrInd, Attribute::ByVal)) return false; @@ -3003,6 +3032,7 @@ bool ARMFastISel::fastLowerArguments() { case CallingConv::ARM_AAPCS_VFP: case CallingConv::ARM_AAPCS: case CallingConv::ARM_APCS: + case CallingConv::Swift: break; } @@ -3016,6 +3046,8 @@ bool ARMFastISel::fastLowerArguments() { if (F->getAttributes().hasAttribute(Idx, Attribute::InReg) || F->getAttributes().hasAttribute(Idx, Attribute::StructRet) || + F->getAttributes().hasAttribute(Idx, Attribute::SwiftSelf) || + F->getAttributes().hasAttribute(Idx, Attribute::SwiftError) || F->getAttributes().hasAttribute(Idx, Attribute::ByVal)) return false; diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp index 992902d6f6eb1..5254e6183e628 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -1380,6 +1380,7 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC, else return CallingConv::ARM_AAPCS; case CallingConv::Fast: + case CallingConv::Swift: if (!Subtarget->isAAPCS_ABI()) { if (Subtarget->hasVFP2() && !Subtarget->isThumb1Only() && !isVarArg) return CallingConv::Fast; diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h index 852a36b0c1212..4112b2a14c8d1 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.h +++ b/llvm/lib/Target/ARM/ARMISelLowering.h @@ -567,6 +567,10 @@ namespace llvm { SmallVectorImpl<SDValue> &InVals, bool isThisReturn, SDValue ThisVal) const; + bool supportSwiftError() const override { + return true; + } + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, diff --git a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp index 7a52a1c9eaecb..c6c5e6f2b5465 100644 --- a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp +++ b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp @@ -1193,7 +1193,8 @@ static void dump_registers(BitVector &Regs, const TargetRegisterInfo &TRI) { bool HexagonFrameLowering::assignCalleeSavedSpillSlots(MachineFunction &MF, - const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI) const { + const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI, + unsigned &MinCSFrameIndex, unsigned &MaxCSFrameIndex) const { DEBUG(dbgs() << LLVM_FUNCTION_NAME << " on " << MF.getFunction()->getName() << '\n'); MachineFrameInfo *MFI = MF.getFrameInfo(); diff --git a/llvm/lib/Target/Hexagon/HexagonFrameLowering.h b/llvm/lib/Target/Hexagon/HexagonFrameLowering.h index 683b303d43ead..d9725c0240f0f 100644 --- a/llvm/lib/Target/Hexagon/HexagonFrameLowering.h +++ b/llvm/lib/Target/Hexagon/HexagonFrameLowering.h @@ -70,7 +70,8 @@ class HexagonFrameLowering : public TargetFrameLowering { } bool assignCalleeSavedSpillSlots(MachineFunction &MF, - const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI) + const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI, + unsigned &MinCSFrameIndex, unsigned &MaxCSFrameIndex) const override; bool needsAligna(const MachineFunction &MF) const; diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td index aaabf7a59f5c1..4e9c771c23be0 100644 --- a/llvm/lib/Target/X86/X86CallingConv.td +++ b/llvm/lib/Target/X86/X86CallingConv.td @@ -161,6 +161,9 @@ def RetCC_X86_64_C : CallingConv<[ // MMX vector types are always returned in XMM0. CCIfType<[x86mmx], CCAssignToReg<[XMM0, XMM1]>>, + + CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>, + CCDelegateTo<RetCC_X86Common> ]>; @@ -191,6 +194,20 @@ def RetCC_X86_64_WebKit_JS : CallingConv<[ CCIfType<[i64], CCAssignToReg<[RAX]>> ]>; +def RetCC_X86_64_Swift : CallingConv<[ + CCIfType<[i8] , CCAssignToReg<[AL, DL, CL, R8B]>>, + CCIfType<[i16], CCAssignToReg<[AX, DX, CX, R8W]>>, + CCIfType<[i32], CCAssignToReg<[EAX, EDX, ECX, R8D]>>, + CCIfType<[i64], CCAssignToReg<[RAX, RDX, RCX, R8]>>, + + CCIfType<[f32], CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>, + CCIfType<[f64], CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>, + + // MMX vector types are always returned in XMM0. + CCIfType<[x86mmx], CCAssignToReg<[XMM0, XMM1, XMM2, XMM3]>>, + CCDelegateTo<RetCC_X86Common> +]>; + // X86-64 AnyReg return-value convention. No explicit register is specified for // the return-value. The register allocator is allowed and expected to choose // any free register. @@ -233,6 +250,9 @@ def RetCC_X86_64 : CallingConv<[ CCIfCC<"CallingConv::WebKit_JS", CCDelegateTo<RetCC_X86_64_WebKit_JS>>, CCIfCC<"CallingConv::AnyReg", CCDelegateTo<RetCC_X86_64_AnyReg>>, + // Handle Swift calls. + CCIfCC<"CallingConv::Swift", CCDelegateTo<RetCC_X86_64_Swift>>, + // Handle explicit CC selection CCIfCC<"CallingConv::X86_64_Win64", CCDelegateTo<RetCC_X86_Win64_C>>, CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<RetCC_X86_64_C>>, @@ -272,6 +292,12 @@ def CC_X86_64_C : CallingConv<[ CCIfNest<CCIfSubtarget<"isTarget64BitILP32()", CCAssignToReg<[R10D]>>>, CCIfNest<CCAssignToReg<[R10]>>, + // An SwiftSelf is passed in R10. + CCIfSwiftSelf<CCIfType<[i64], CCAssignToReg<[R10]>>>, + + // An SwiftError is passed in R12. + CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>, + // The first 6 integer arguments are passed in integer registers. CCIfType<[i32], CCAssignToReg<[EDI, ESI, EDX, ECX, R8D, R9D]>>, CCIfType<[i64], CCAssignToReg<[RDI, RSI, RDX, RCX, R8 , R9 ]>>, @@ -790,6 +816,8 @@ def CSR_NoRegs : CalleeSavedRegs<(add)>; def CSR_32 : CalleeSavedRegs<(add ESI, EDI, EBX, EBP)>; def CSR_64 : CalleeSavedRegs<(add RBX, R12, R13, R14, R15, RBP)>; +def CSR_64_SwiftError : CalleeSavedRegs<(add RBX, R13, R14, R15, RBP)>; + def CSR_32EHRet : CalleeSavedRegs<(add EAX, EDX, CSR_32)>; def CSR_64EHRet : CalleeSavedRegs<(add RAX, RDX, CSR_64)>; diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index 914fd04ad6b7f..efcd18fbf1d4f 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -972,6 +972,17 @@ bool X86FastISel::X86SelectStore(const Instruction *I) { if (S->isAtomic()) return false; + const Value *PtrV = I->getOperand(1); + if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return false; + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return false; + } + const Value *Val = S->getValueOperand(); const Value *Ptr = S->getPointerOperand(); @@ -1002,6 +1013,10 @@ bool X86FastISel::X86SelectRet(const Instruction *I) { if (!FuncInfo.CanLowerReturn) return false; + if (F.getAttributes().hasAttrSomewhere(Attribute::SwiftError) && + TLI.supportSwiftError()) + return false; + CallingConv::ID CC = F.getCallingConv(); if (CC != CallingConv::C && CC != CallingConv::Fast && @@ -1131,6 +1146,17 @@ bool X86FastISel::X86SelectLoad(const Instruction *I) { if (LI->isAtomic()) return false; + const Value *SV = I->getOperand(0); + if (const Argument *Arg = dyn_cast<Argument>(SV)) { + if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) + return false; + } + + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { + if (Alloca->isSwiftError() && TLI.supportSwiftError()) + return false; + } + MVT VT; if (!isTypeLegal(LI->getType(), VT, /*AllowI1=*/true)) return false; @@ -2740,6 +2766,8 @@ bool X86FastISel::fastLowerArguments() { if (F->getAttributes().hasAttribute(Idx, Attribute::ByVal) || F->getAttributes().hasAttribute(Idx, Attribute::InReg) || F->getAttributes().hasAttribute(Idx, Attribute::StructRet) || + F->getAttributes().hasAttribute(Idx, Attribute::SwiftSelf) || + F->getAttributes().hasAttribute(Idx, Attribute::SwiftError) || F->getAttributes().hasAttribute(Idx, Attribute::Nest)) return false; @@ -2847,6 +2875,7 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) { case CallingConv::C: case CallingConv::Fast: case CallingConv::WebKit_JS: + case CallingConv::Swift: case CallingConv::X86_FastCall: case CallingConv::X86_64_Win64: case CallingConv::X86_64_SysV: @@ -2871,6 +2900,10 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) { if (CLI.CS && CLI.CS->hasInAllocaArgument()) return false; + for (auto Flag : CLI.OutFlags) + if (Flag.isSwiftError()) + return false; + // Fast-isel doesn't know about callee-pop yet. if (X86::isCalleePop(CC, Subtarget->is64Bit(), IsVarArg, TM.Options.GuaranteedTailCallOpt)) diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index ad83344b32762..60e8f41099ede 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -1441,7 +1441,8 @@ int X86FrameLowering::getFrameIndexReferenceFromSP(const MachineFunction &MF, bool X86FrameLowering::assignCalleeSavedSpillSlots( MachineFunction &MF, const TargetRegisterInfo *TRI, - std::vector<CalleeSavedInfo> &CSI) const { + std::vector<CalleeSavedInfo> &CSI, + unsigned &MinCSFrameIndex, unsigned &MaxCSFrameIndex) const { MachineFrameInfo *MFI = MF.getFrameInfo(); X86MachineFunctionInfo *X86FI = MF.getInfo<X86MachineFunctionInfo>(); diff --git a/llvm/lib/Target/X86/X86FrameLowering.h b/llvm/lib/Target/X86/X86FrameLowering.h index 35bafb532b41a..ec3e5ed6e0cb0 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.h +++ b/llvm/lib/Target/X86/X86FrameLowering.h @@ -74,7 +74,9 @@ class X86FrameLowering : public TargetFrameLowering { bool assignCalleeSavedSpillSlots(MachineFunction &MF, const TargetRegisterInfo *TRI, - std::vector<CalleeSavedInfo> &CSI) const override; + std::vector<CalleeSavedInfo> &CSI, + unsigned &MinCSFrameIndex, + unsigned &MaxCSFrameIndex) const override; bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index d13d0510a0c9a..27c6b50b9bdae 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -2248,7 +2248,7 @@ X86TargetLowering::LowerReturn(SDValue Chain, // false, then an sret argument may be implicitly inserted in the SelDAG. In // either case FuncInfo->setSRetReturnReg() will have been called. if (unsigned SRetReg = FuncInfo->getSRetReturnReg()) { - SDValue Val = DAG.getCopyFromReg(Chain, dl, SRetReg, + SDValue Val = DAG.getCopyFromReg(RetOps[0], dl, SRetReg, getPointerTy(MF.getDataLayout())); unsigned RetValReg diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h index 714e6b1bd345b..bfc3c4b9abd33 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.h +++ b/llvm/lib/Target/X86/X86ISelLowering.h @@ -1029,6 +1029,10 @@ namespace llvm { SDValue LowerGC_TRANSITION_START(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGC_TRANSITION_END(SDValue Op, SelectionDAG &DAG) const; + bool supportSwiftError() const override { + return true; + } + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index b1147877753d6..3f4e8464227b6 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -277,6 +277,9 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { return CSR_Win64_SaveList; if (CallsEHReturn) return CSR_64EHRet_SaveList; + if (MF->getFunction()->getAttributes().hasAttrSomewhere( + Attribute::SwiftError)) + return CSR_64_SwiftError_SaveList; return CSR_64_SaveList; } if (CallsEHReturn) @@ -337,6 +340,9 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF, if (Is64Bit) { if (IsWin64) return CSR_Win64_RegMask; + if (MF.getFunction()->getAttributes().hasAttrSomewhere( + Attribute::SwiftError)) + return CSR_64_SwiftError_RegMask; return CSR_64_RegMask; } return CSR_32_RegMask; diff --git a/llvm/lib/Transforms/Utils/SSAUpdater.cpp b/llvm/lib/Transforms/Utils/SSAUpdater.cpp index 88b39dd7f6640..8eea38e11fb6b 100644 --- a/llvm/lib/Transforms/Utils/SSAUpdater.cpp +++ b/llvm/lib/Transforms/Utils/SSAUpdater.cpp @@ -216,6 +216,11 @@ class SSAUpdaterTraits<SSAUpdater> { static BlkSucc_iterator BlkSucc_begin(BlkT *BB) { return succ_begin(BB); } static BlkSucc_iterator BlkSucc_end(BlkT *BB) { return succ_end(BB); } + /// Iterator over phis in a block. + typedef BlkT::iterator PhiItT; + static PhiItT PhiItT_begin(BlkT *BB) { return BB->begin(); } + static PhiItT PhiItT_end(BlkT *BB) { return BB->end(); } + class PHI_iterator { private: PHINode *PHI; diff --git a/llvm/test/Bitcode/swifterror.ll b/llvm/test/Bitcode/swifterror.ll new file mode 100644 index 0000000000000..ac2a657368bd8 --- /dev/null +++ b/llvm/test/Bitcode/swifterror.ll @@ -0,0 +1,8 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; RUN: verify-uselistorder < %s + +define i32 @test(i8** swifterror) +; CHECK: define i32 @test(i8** swifterror) +{ + ret i32 0 +} diff --git a/llvm/test/Bitcode/swiftself.ll b/llvm/test/Bitcode/swiftself.ll new file mode 100644 index 0000000000000..e9293ec98b3dd --- /dev/null +++ b/llvm/test/Bitcode/swiftself.ll @@ -0,0 +1,8 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; RUN: verify-uselistorder < %s + +define void @test(i8* swiftself) +; CHECK: define void @test(i8* swiftself) +{ + ret void; +} diff --git a/llvm/test/CodeGen/AArch64/swifterror.ll b/llvm/test/CodeGen/AArch64/swifterror.ll new file mode 100644 index 0000000000000..0106bb30a3d0b --- /dev/null +++ b/llvm/test/CodeGen/AArch64/swifterror.ll @@ -0,0 +1,371 @@ +; RUN: llc -verify-machineinstrs < %s -mtriple=aarch64-apple-ios | FileCheck --check-prefix=CHECK-APPLE %s +; RUN: llc -verify-machineinstrs -O0 < %s -mtriple=aarch64-apple-ios | FileCheck --check-prefix=CHECK-O0 %s + +declare i8* @malloc(i64) +declare void @free(i8*) +%swift_error = type {i64, i8} + +define float @foo(%swift_error** swifterror %error_ptr_ref) { +; CHECK-APPLE-LABEL: foo: +; CHECK-APPLE: orr w0, wzr, #0x10 +; CHECK-APPLE: malloc +; CHECK-APPLE: orr [[ID:w[0-9]+]], wzr, #0x1 +; CHECK-APPLE: strb [[ID]], [x0, #8] +; CHECK-APPLE: mov x19, x0 +; CHECK-APPLE-NOT: x19 + +; CHECK-O0-LABEL: foo: +; CHECK-O0: orr w{{.*}}, wzr, #0x10 +; CHECK-O0: malloc +; CHECK-O0: mov [[ID2:x[0-9]+]], x0 +; CHECK-O0: orr [[ID:w[0-9]+]], wzr, #0x1 +; CHECK-O0: strb [[ID]], [x0, #8] +; CHECK-O0: mov x19, [[ID2]] +; CHECK-O0-NOT: x19 +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + ret float 1.0 +} + +define float @caller(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller: +; CHECK-APPLE: mov [[ID:x[0-9]+]], x0 +; CHECK-APPLE: mov x19, xzr +; CHECK-APPLE: bl {{.*}}foo +; CHECK-APPLE: cbnz x19 +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrb [[CODE:w[0-9]+]], [x19, #8] +; CHECK-APPLE: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov x0, x19 +; CHECK_APPLE: bl {{.*}}free + +; CHECK-O0-LABEL: caller: +; CHECK-O0: mov x19 +; CHECK-O0: bl {{.*}}foo +; CHECK-O0: mov [[ID:x[0-9]+]], x19 +; CHECK-O0: cbnz [[ID]] +entry: + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + %call = call float @foo(%swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +define float @caller2(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller2: +; CHECK-APPLE: mov [[ID:x[0-9]+]], x0 +; CHECK-APPLE: fmov [[CMP:s[0-9]+]], #1.0 +; CHECK-APPLE: mov x19, xzr +; CHECK-APPLE: bl {{.*}}foo +; CHECK-APPLE: cbnz x19 +; CHECK-APPLE: fcmp s0, [[CMP]] +; CHECK-APPLE: b.le +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrb [[CODE:w[0-9]+]], [x19, #8] +; CHECK-APPLE: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov x0, x19 +; CHECK_APPLE: bl {{.*}}free + +; CHECK-O0-LABEL: caller2: +; CHECK-O0: mov x19 +; CHECK-O0: bl {{.*}}foo +; CHECK-O0: mov [[ID:x[0-9]+]], x19 +; CHECK-O0: cbnz [[ID]] +entry: + %error_ptr_ref = alloca swifterror %swift_error* + br label %bb_loop +bb_loop: + store %swift_error* null, %swift_error** %error_ptr_ref + %call = call float @foo(%swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %cmp = fcmp ogt float %call, 1.000000e+00 + br i1 %cmp, label %bb_end, label %bb_loop +bb_end: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) { +; CHECK-APPLE-LABEL: foo_if: +; CHECK-APPLE: cbz w0 +; CHECK-APPLE: orr w0, wzr, #0x10 +; CHECK-APPLE: malloc +; CHECK-APPLE: orr [[ID:w[0-9]+]], wzr, #0x1 +; CHECK-APPLE: strb [[ID]], [x0, #8] +; CHECK-APPLE: mov x19, x0 +; CHECK-APPLE-NOT: x19 +; CHECK-APPLE: ret + +; CHECK-O0-LABEL: foo_if: +; spill x19 +; CHECK-O0: str x19 +; CHECK-O0: cbz w0 +; CHECK-O0: orr w{{.*}}, wzr, #0x10 +; CHECK-O0: malloc +; CHECK-O0: mov [[ID:x[0-9]+]], x0 +; CHECK-O0: orr [[ID2:w[0-9]+]], wzr, #0x1 +; CHECK-O0: strb [[ID2]], [x0, #8] +; CHECK-O0: mov x19, [[ID]] +; CHECK-O0: ret +; reload from stack +; CHECK-O0: ldr x19 +; CHECK-O0: ret +entry: + %cond = icmp ne i32 %cc, 0 + br i1 %cond, label %gen_error, label %normal + +gen_error: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + ret float 1.0 + +normal: + ret float 0.0 +} + +define float @foo_loop(%swift_error** swifterror %error_ptr_ref, i32 %cc, float %cc2) { +; CHECK-APPLE-LABEL: foo_loop: +; CHECK-APPLE: mov x0, x19 +; CHECK-APPLE: cbz +; CHECK-APPLE: orr w0, wzr, #0x10 +; CHECK-APPLE: malloc +; CHECK-APPLE: strb w{{.*}}, [x0, #8] +; CHECK-APPLE: fcmp +; CHECK-APPLE: b.le +; CHECK-APPLE: mov x19, x0 +; CHECK-APPLE: ret + +; CHECK-O0-LABEL: foo_loop: +; spill x19 +; CHECK-O0: str x19 +; CHECk-O0: cbz +; CHECK-O0: orr w{{.*}}, wzr, #0x10 +; CHECK-O0: malloc +; CHECK-O0: mov [[ID:x[0-9]+]], x0 +; CHECK-O0: strb w{{.*}}, [{{.*}}[[ID]], #8] +; spill x0 +; CHECK-O0: str x0 +; CHECK-O0: fcmp +; CHECK-O0: b.le +; reload from stack +; CHECK-O0: ldr x19 +; CHECK-O0: ret +entry: + br label %bb_loop + +bb_loop: + %cond = icmp ne i32 %cc, 0 + br i1 %cond, label %gen_error, label %bb_cont + +gen_error: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + br label %bb_cont + +bb_cont: + %cmp = fcmp ogt float %cc2, 1.000000e+00 + br i1 %cmp, label %bb_end, label %bb_loop +bb_end: + ret float 0.0 +} + +%struct.S = type { i32, i32, i32, i32, i32, i32 } + +define void @foo_sret(%struct.S* sret %agg.result, i32 %val1, %swift_error** swifterror %error_ptr_ref) { +; CHECK-APPLE-LABEL: foo_sret: +; CHECK-APPLE: mov [[SRET:x[0-9]+]], x8 +; CHECK-APPLE: orr w0, wzr, #0x10 +; CHECK-APPLE: malloc +; CHECK-APPLE: orr [[ID:w[0-9]+]], wzr, #0x1 +; CHECK-APPLE: strb [[ID]], [x0, #8] +; CHECK-APPLE: str w{{.*}}, [{{.*}}[[SRET]], #4] +; CHECK-APPLE: mov x19, x0 +; CHECK-APPLE-NOT: x19 + +; CHECK-O0-LABEL: foo_sret: +; CHECK-O0: orr w{{.*}}, wzr, #0x10 +; spill x8 +; CHECK-O0-DAG: str x8 +; spill x19 +; CHECK-O0-DAG: str x19 +; CHECK-O0: malloc +; CHECK-O0: orr [[ID:w[0-9]+]], wzr, #0x1 +; CHECK-O0: strb [[ID]], [x0, #8] +; reload from stack +; CHECK-O0: ldr [[SRET:x[0-9]+]] +; CHECK-O0: str w{{.*}}, [{{.*}}[[SRET]], #4] +; CHECK-O0: mov x19 +; CHECK-O0-NOT: x19 +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + %v2 = getelementptr inbounds %struct.S, %struct.S* %agg.result, i32 0, i32 1 + store i32 %val1, i32* %v2 + ret void +} + +define float @caller3(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller3: +; CHECK-APPLE: mov [[ID:x[0-9]+]], x0 +; CHECK-APPLE: mov x19, xzr +; CHECK-APPLE: bl {{.*}}foo_sret +; CHECK-APPLE: cbnz x19 +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrb [[CODE:w[0-9]+]], [x19, #8] +; CHECK-APPLE: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov x0, x19 +; CHECK_APPLE: bl {{.*}}free + +; CHECK-O0-LABEL: caller3: +; spill x0 +; CHECK-O0: str x0 +; CHECK-O0: mov x19 +; CHECK-O0: bl {{.*}}foo_sret +; CHECK-O0: mov [[ID2:x[0-9]+]], x19 +; CHECK-O0: cbnz [[ID2]] +; Access part of the error object and save it to error_ref +; reload from stack +; CHECK-O0: ldrb [[CODE:w[0-9]+]] +; CHECK-O0: ldr [[ID:x[0-9]+]] +; CHECK-O0: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK_O0: bl {{.*}}free +entry: + %s = alloca %struct.S, align 8 + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + call void @foo_sret(%struct.S* sret %s, i32 1, %swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +declare void @llvm.va_start(i8*) nounwind +define float @foo_vararg(%swift_error** swifterror %error_ptr_ref, ...) { +; CHECK-APPLE-LABEL: foo_vararg: +; CHECK-APPLE: orr w0, wzr, #0x10 +; CHECK-APPLE: malloc +; CHECK-APPLE: orr [[ID:w[0-9]+]], wzr, #0x1 +; CHECK-APPLE: add [[ARGS:x[0-9]+]], [[TMP:x[0-9]+]], #16 +; CHECK-APPLE: strb [[ID]], [x0, #8] + +; First vararg +; CHECK-APPLE-DAG: orr {{x[0-9]+}}, [[ARGS]], #0x8 +; CHECK-APPLE-DAG: ldr {{w[0-9]+}}, [{{.*}}[[TMP]], #16] +; CHECK-APPLE: add {{x[0-9]+}}, {{x[0-9]+}}, #8 +; Second vararg +; CHECK-APPLE: ldr {{w[0-9]+}}, [{{x[0-9]+}}] +; CHECK-APPLE: add {{x[0-9]+}}, {{x[0-9]+}}, #8 +; Third vararg +; CHECK-APPLE: ldr {{w[0-9]+}}, [{{x[0-9]+}}] + +; CHECK-APPLE: mov x19, x0 +; CHECK-APPLE-NOT: x19 +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + + %args = alloca i8*, align 8 + %a10 = alloca i32, align 4 + %a11 = alloca i32, align 4 + %a12 = alloca i32, align 4 + %v10 = bitcast i8** %args to i8* + call void @llvm.va_start(i8* %v10) + %v11 = va_arg i8** %args, i32 + store i32 %v11, i32* %a10, align 4 + %v12 = va_arg i8** %args, i32 + store i32 %v12, i32* %a11, align 4 + %v13 = va_arg i8** %args, i32 + store i32 %v13, i32* %a12, align 4 + + ret float 1.0 +} + +define float @caller4(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller4: + +; CHECK-APPLE: mov [[ID:x[0-9]+]], x0 +; CHECK-APPLE: stp {{x[0-9]+}}, {{x[0-9]+}}, [sp, #8] +; CHECK-APPLE: str {{x[0-9]+}}, [sp] + +; CHECK-APPLE: mov x19, xzr +; CHECK-APPLE: bl {{.*}}foo_vararg +; CHECK-APPLE: cbnz x19 +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrb [[CODE:w[0-9]+]], [x19, #8] +; CHECK-APPLE: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov x0, x19 +; CHECK_APPLE: bl {{.*}}free +entry: + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + + %a10 = alloca i32, align 4 + %a11 = alloca i32, align 4 + %a12 = alloca i32, align 4 + store i32 10, i32* %a10, align 4 + store i32 11, i32* %a11, align 4 + store i32 12, i32* %a12, align 4 + %v10 = load i32, i32* %a10, align 4 + %v11 = load i32, i32* %a11, align 4 + %v12 = load i32, i32* %a12, align 4 + + %call = call float (%swift_error**, ...) @foo_vararg(%swift_error** swifterror %error_ptr_ref, i32 %v10, i32 %v11, i32 %v12) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont + +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} diff --git a/llvm/test/CodeGen/AArch64/swiftself.ll b/llvm/test/CodeGen/AArch64/swiftself.ll new file mode 100644 index 0000000000000..f93f8f3982259 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/swiftself.ll @@ -0,0 +1,29 @@ +; RUN: llc -verify-machineinstrs < %s -mtriple=aarch64-apple-ios | FileCheck --check-prefix=CHECK-APPLE %s +; RUN: llc -O0 -verify-machineinstrs < %s -mtriple=aarch64-apple-ios | FileCheck --check-prefix=CHECK-O0 %s + +; Parameter with swiftself should be allocated to x9. +define void @check_swiftself(i32* swiftself %addr0) { +; CHECK-APPLE-LABEL: check_swiftself: +; CHECK-O0-LABEL: check_swiftself: + + %val0 = load volatile i32, i32* %addr0 +; CHECK-APPLE: ldr w{{.*}}, [x9] +; CHECK-O0: ldr w{{.*}}, [x9] + ret void +} + +@var8_3 = global i8 0 +declare void @take_swiftself(i8* swiftself %addr0) + +define void @simple_args() { +; CHECK-APPLE-LABEL: simple_args: +; CHECK-O0-LABEL: simple_args: + + call void @take_swiftself(i8* @var8_3) +; CHECK-APPLE: add x9, +; CHECK-APPLE: bl {{_?}}take_swiftself +; CHECK-O0: add x9, +; CHECK-O0: bl {{_?}}take_swiftself + + ret void +} diff --git a/llvm/test/CodeGen/ARM/swift-ios.ll b/llvm/test/CodeGen/ARM/swift-ios.ll new file mode 100644 index 0000000000000..8ff75cd52b595 --- /dev/null +++ b/llvm/test/CodeGen/ARM/swift-ios.ll @@ -0,0 +1,73 @@ +; RUN: llc -mtriple=armv7-apple-ios < %s | FileCheck %s + +define swiftcc float @t1(float %a, float %b) { +entry: +; CHECK: t1 +; CHECK-NOT: vmov +; CHECK: vadd.f32 + %a.addr = alloca float, align 4 + %b.addr = alloca float, align 4 + store float %a, float* %a.addr, align 4 + store float %b, float* %b.addr, align 4 + %0 = load float, float* %a.addr, align 4 + %1 = load float, float* %b.addr, align 4 + %add = fadd float %0, %1 + ret float %add +} + +define swiftcc double @t2(double %a, double %b) { +entry: +; CHECK: t2 +; CHECK-NOT: vmov +; CHECK: vadd.f64 + %a.addr = alloca double, align 8 + %b.addr = alloca double, align 8 + store double %a, double* %a.addr, align 8 + store double %b, double* %b.addr, align 8 + %0 = load double, double* %a.addr, align 8 + %1 = load double, double* %b.addr, align 8 + %add = fadd double %0, %1 + ret double %add +} + +define swiftcc double @t9(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, double %d7, float %a, float %b) { +entry: +; CHECK-LABEL: t9: +; CHECK-NOT: vmov +; CHECK: vldr + %add = fadd float %a, %b + %conv = fpext float %add to double + ret double %conv +} + +define swiftcc double @t10(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %a, float %b, double %c) { +entry: +; CHECK-LABEL: t10: +; CHECK-NOT: vmov +; CHECK: vldr + %add = fadd double %a, %c + ret double %add +} + +define swiftcc float @t11(double %d0, double %d1, double %d2, double %d3, double %d4, double %d5, double %d6, float %a, double %b, float %c) { +entry: +; CHECK-LABEL: t11: +; CHECK: vldr + %add = fadd float %a, %c + ret float %add +} + +define swiftcc double @t12(double %a, double %b) { +entry: +; CHECK-LABEL: t12: +; CHECK: vstr + %add = fadd double %a, %b + %sub = fsub double %a, %b + %call = tail call swiftcc double @x(double 0.000000e+00, double 0.000000e+00, double 0.000000e+00, double 0.000000e+00, double 0.000000e+00, double 0.000000e+00, double %add, float 0.000000e+00, double %sub) + ret double %call +} + +declare swiftcc double @x(double, double, double, double, double, double, double, float, double) + +attributes #0 = { readnone } +attributes #1 = { readonly } diff --git a/llvm/test/CodeGen/ARM/swift-return.ll b/llvm/test/CodeGen/ARM/swift-return.ll new file mode 100644 index 0000000000000..c307f0e3f5112 --- /dev/null +++ b/llvm/test/CodeGen/ARM/swift-return.ll @@ -0,0 +1,130 @@ +; RUN: llc -mtriple=armv7k-apple-ios8.0 -mcpu=cortex-a7 -verify-machineinstrs < %s | FileCheck %s +; RUN: llc -mtriple=armv7k-apple-ios8.0 -mcpu=cortex-a7 -verify-machineinstrs < %s -O0 | FileCheck --check-prefix=CHECK-O0 %s + +; RUN: llc -mtriple=armv7-apple-ios -verify-machineinstrs < %s | FileCheck %s +; RUN: llc -mtriple=armv7-apple-ios -verify-machineinstrs < %s -O0 | FileCheck --check-prefix=CHECK-O0 %s + +; Test how llvm handles return type of {i16, i8}. The return value will be passed in %r0 and %r1. +; CHECK-LABEL: test: +; CHECK: bl {{.*}}gen +; CHECK: sxth {{.*}}, r0 +; CHECK: sxtab r0, {{.*}}, r1 +; CHECK-O0-LABEL: test: +; CHECK-O0: bl {{.*}}gen +; CHECK-O0: sxth r0, r0 +; CHECK-O0: sxtb r1, r1 +; CHECK-O0: add r0, r0, r1 +define i16 @test(i32 %key) { +entry: + %key.addr = alloca i32, align 4 + store i32 %key, i32* %key.addr, align 4 + %0 = load i32, i32* %key.addr, align 4 + %call = call swiftcc { i16, i8 } @gen(i32 %0) + %v3 = extractvalue { i16, i8 } %call, 0 + %v1 = sext i16 %v3 to i32 + %v5 = extractvalue { i16, i8 } %call, 1 + %v2 = sext i8 %v5 to i32 + %add = add nsw i32 %v1, %v2 + %conv = trunc i32 %add to i16 + ret i16 %conv +} + +declare swiftcc { i16, i8 } @gen(i32) + +; We can't pass every return value in register, instead, pass everything in memroy. +; The caller provides space for the return value and passes the address in %r0. +; The first input argument will be in %r1. +; CHECK-LABEL: test2: +; CHECK: mov r1, r0 +; CHECK: mov r0, sp +; CHECK: bl {{.*}}gen2 +; CHECK-DAG: add +; CHECK-DAG: ldr {{.*}}, [sp, #16] +; CHECK-DAG: add +; CHECK-DAG: add +; CHECK-DAG: add +; CHECK-O0-LABEL: test2: +; CHECK-O0: str r0 +; CHECK-O0: mov r0, sp +; CHECK-O0: bl {{.*}}gen2 +; CHECK-O0-DAG: ldr {{.*}}, [sp] +; CHECK-O0-DAG: ldr {{.*}}, [sp, #4] +; CHECK-O0-DAG: ldr {{.*}}, [sp, #8] +; CHECK-O0-DAG: ldr {{.*}}, [sp, #12] +; CHECK-O0-DAG: ldr {{.*}}, [sp, #16] +; CHECK-O0-DAG: add +; CHECK-O0-DAG: add +; CHECK-O0-DAG: add +; CHECK-O0-DAG: add +define i32 @test2(i32 %key) #0 { +entry: + %key.addr = alloca i32, align 4 + store i32 %key, i32* %key.addr, align 4 + %0 = load i32, i32* %key.addr, align 4 + %call = call swiftcc { i32, i32, i32, i32, i32 } @gen2(i32 %0) + + %v3 = extractvalue { i32, i32, i32, i32, i32 } %call, 0 + %v5 = extractvalue { i32, i32, i32, i32, i32 } %call, 1 + %v6 = extractvalue { i32, i32, i32, i32, i32 } %call, 2 + %v7 = extractvalue { i32, i32, i32, i32, i32 } %call, 3 + %v8 = extractvalue { i32, i32, i32, i32, i32 } %call, 4 + + %add = add nsw i32 %v3, %v5 + %add1 = add nsw i32 %add, %v6 + %add2 = add nsw i32 %add1, %v7 + %add3 = add nsw i32 %add2, %v8 + ret i32 %add3 +} + +; The address of the return value is passed in %r0. +; CHECK-LABEL: gen2: +; CHECK-DAG: str r1, [r0] +; CHECK-DAG: str r1, [r0, #4] +; CHECK-DAG: str r1, [r0, #8] +; CHECK-DAG: str r1, [r0, #12] +; CHECK-DAG: str r1, [r0, #16] +; CHECK-O0-LABEL: gen2: +; CHECK-O0-DAG: str r1, [r0] +; CHECK-O0-DAG: str r1, [r0, #4] +; CHECK-O0-DAG: str r1, [r0, #8] +; CHECK-O0-DAG: str r1, [r0, #12] +; CHECK-O0-DAG: str r1, [r0, #16] +define swiftcc { i32, i32, i32, i32, i32 } @gen2(i32 %key) { + %Y = insertvalue { i32, i32, i32, i32, i32 } undef, i32 %key, 0 + %Z = insertvalue { i32, i32, i32, i32, i32 } %Y, i32 %key, 1 + %Z2 = insertvalue { i32, i32, i32, i32, i32 } %Z, i32 %key, 2 + %Z3 = insertvalue { i32, i32, i32, i32, i32 } %Z2, i32 %key, 3 + %Z4 = insertvalue { i32, i32, i32, i32, i32 } %Z3, i32 %key, 4 + ret { i32, i32, i32, i32, i32 } %Z4 +} + +; The return value {i32, i32, i32, i32} will be returned via registers %r0, %r1, %r2, %r3. +; CHECK-LABEL: test3: +; CHECK: bl {{.*}}gen3 +; CHECK: add r0, r0, r1 +; CHECK: add r0, r0, r2 +; CHECK: add r0, r0, r3 +; CHECK-O0-LABEL: test3: +; CHECK-O0: bl {{.*}}gen3 +; CHECK-O0: add r0, r0, r1 +; CHECK-O0: add r0, r0, r2 +; CHECK-O0: add r0, r0, r3 +define i32 @test3(i32 %key) #0 { +entry: + %key.addr = alloca i32, align 4 + store i32 %key, i32* %key.addr, align 4 + %0 = load i32, i32* %key.addr, align 4 + %call = call swiftcc { i32, i32, i32, i32 } @gen3(i32 %0) + + %v3 = extractvalue { i32, i32, i32, i32 } %call, 0 + %v5 = extractvalue { i32, i32, i32, i32 } %call, 1 + %v6 = extractvalue { i32, i32, i32, i32 } %call, 2 + %v7 = extractvalue { i32, i32, i32, i32 } %call, 3 + + %add = add nsw i32 %v3, %v5 + %add1 = add nsw i32 %add, %v6 + %add2 = add nsw i32 %add1, %v7 + ret i32 %add2 +} + +declare swiftcc { i32, i32, i32, i32 } @gen3(i32 %key) diff --git a/llvm/test/CodeGen/ARM/swifterror.ll b/llvm/test/CodeGen/ARM/swifterror.ll new file mode 100644 index 0000000000000..99a531cd706c8 --- /dev/null +++ b/llvm/test/CodeGen/ARM/swifterror.ll @@ -0,0 +1,368 @@ +; RUN: llc -verify-machineinstrs < %s -mtriple=armv7-apple-ios | FileCheck --check-prefix=CHECK-APPLE %s +; RUN: llc -verify-machineinstrs -O0 < %s -mtriple=armv7-apple-ios | FileCheck --check-prefix=CHECK-O0 %s + +declare i8* @malloc(i64) +declare void @free(i8*) +%swift_error = type {i64, i8} + +define float @foo(%swift_error** swifterror %error_ptr_ref) { +; CHECK-APPLE-LABEL: foo: +; CHECK-APPLE: mov r0, #16 +; CHECK-APPLE: malloc +; CHECK-APPLE-DAG: mov [[ID:r[0-9]+]], #1 +; CHECK-APPLE-DAG: mov r6, r{{.*}} +; CHECK-APPLE-DAG: strb [[ID]], [r{{.*}}, #8] + +; CHECK-O0-LABEL: foo: +; CHECK-O0: mov r{{.*}}, #16 +; CHECK-O0: malloc +; CHECK-O0: mov [[ID2:r[0-9]+]], r0 +; CHECK-O0: mov [[ID:r[0-9]+]], #1 +; CHECK-O0: strb [[ID]], [r0, #8] +; CHECK-O0: mov r6, [[ID2]] +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + ret float 1.0 +} + +define float @caller(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller: +; CHECK-APPLE-DAG: mov [[ID:r[0-9]+]], r0 +; CHECK-APPLE-DAG: mov r6, #0 +; CHECK-APPLE: bl {{.*}}foo +; CHECK-APPLE: cmp r6, #0 +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrbeq [[CODE:r[0-9]+]], [r6, #8] +; CHECK-APPLE: strbeq [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov r0, r6 +; CHECK_APPLE: bl {{.*}}free + +; CHECK-O0-LABEL: caller: +; spill r0 +; CHECK-O0-DAG: str r0, +; CHECK-O0-DAG: mov r6, #0 +; CHECK-O0: bl {{.*}}foo +; CHECK-O0: mov r{{.*}}, r6 +; CHECK-O0: bne +; CHECK-O0: ldrb [[CODE:r[0-9]+]], [r0, #8] +; reload r0 +; CHECK-O0: ldr [[ID:r[0-9]+]], +; CHECK-O0: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-O0: mov r0, +; CHECK-O0: free +entry: + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + %call = call float @foo(%swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +define float @caller2(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller2: +; CHECK-APPLE-DAG: mov [[ID:r[0-9]+]], r0 +; CHECK-APPLE-DAG: mov r6, #0 +; CHECK-APPLE: bl {{.*}}foo +; CHECK-APPLE: cmp r6, #0 +; CHECK-APPLE: bne +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrb [[CODE:r[0-9]+]], [r6, #8] +; CHECK-APPLE: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov r0, r6 +; CHECK_APPLE: bl {{.*}}free + +; CHECK-O0-LABEL: caller2: +; spill r0 +; CHECK-O0-DAG: str r0, +; CHECK-O0-DAG: mov r6, #0 +; CHECK-O0: bl {{.*}}foo +; CHECK-O0: mov r{{.*}}, r6 +; CHECK-O0: bne +; CHECK-O0: ble +; CHECK-O0: ldrb [[CODE:r[0-9]+]], [r0, #8] +; reload r0 +; CHECK-O0: ldr [[ID:r[0-9]+]], +; CHECK-O0: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-O0: mov r0, +; CHECK-O0: free +entry: + %error_ptr_ref = alloca swifterror %swift_error* + br label %bb_loop +bb_loop: + store %swift_error* null, %swift_error** %error_ptr_ref + %call = call float @foo(%swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %cmp = fcmp ogt float %call, 1.000000e+00 + br i1 %cmp, label %bb_end, label %bb_loop +bb_end: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) { +; CHECK-APPLE-LABEL: foo_if: +; CHECK-APPLE: cmp r0, #0 +; CHECK-APPLE: eq +; CHECK-APPLE: mov r0, #16 +; CHECK-APPLE: malloc +; CHECK-APPLE: mov [[ID:r[0-9]+]], #1 +; CHECK-APPLE-DAG: mov r6, r{{.*}} +; CHECK-APPLE-DAG: strb [[ID]], [r{{.*}}, #8] + +; CHECK-O0-LABEL: foo_if: +; CHECK-O0: cmp r0, #0 +; spill to stack +; CHECK-O0: str r6 +; CHECK-O0: beq +; CHECK-O0: mov r0, #16 +; CHECK-O0: malloc +; CHECK-O0: mov [[ID:r[0-9]+]], r0 +; CHECK-O0: mov [[ID2:r[0-9]+]], #1 +; CHECK-O0: strb [[ID2]], [r0, #8] +; CHECK-O0: mov r6, [[ID]] +; reload from stack +; CHECK-O0: ldr r6 +entry: + %cond = icmp ne i32 %cc, 0 + br i1 %cond, label %gen_error, label %normal + +gen_error: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + ret float 1.0 + +normal: + ret float 0.0 +} + +define float @foo_loop(%swift_error** swifterror %error_ptr_ref, i32 %cc, float %cc2) { +; CHECK-APPLE-LABEL: foo_loop: +; CHECK-APPLE: mov [[CODE:r[0-9]+]], r0 +; swifterror is kept in a register +; CHECK-APPLE: mov [[ID:r[0-9]+]], r6 +; CHECK-APPLE: cmp [[CODE]], #0 +; CHECK-APPLE: beq +; CHECK-APPLE: mov r0, #16 +; CHECK-APPLE: malloc +; CHECK-APPLE: strb r{{.*}}, [{{.*}}[[ID]], #8] +; CHECK-APPLE: ble +; CHECK-APPLE: mov r6, [[ID]] + +; CHECK-O0-LABEL: foo_loop: +; CHECK-O0: mov r{{.*}}, r6 +; CHECK-O0: cmp r{{.*}}, #0 +; CHECK-O0: beq +; CHECK-O0-DAG: movw r{{.*}}, #1 +; CHECK-O0-DAG: mov r{{.*}}, #16 +; CHECK-O0: malloc +; CHECK-O0-DAG: mov [[ID:r[0-9]+]], r0 +; CHECK-O0-DAG: ldr [[ID2:r[0-9]+]], [sp{{.*}}] +; CHECK-O0: strb [[ID2]], [{{.*}}[[ID]], #8] +; spill r0 +; CHECK-O0: str r0, [sp{{.*}}] +; CHECK-O0: vcmpe +; CHECK-O0: ble +; reload from stack +; CHECK-O0: ldr r6 +entry: + br label %bb_loop + +bb_loop: + %cond = icmp ne i32 %cc, 0 + br i1 %cond, label %gen_error, label %bb_cont + +gen_error: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + br label %bb_cont + +bb_cont: + %cmp = fcmp ogt float %cc2, 1.000000e+00 + br i1 %cmp, label %bb_end, label %bb_loop +bb_end: + ret float 0.0 +} + +%struct.S = type { i32, i32, i32, i32, i32, i32 } + +define void @foo_sret(%struct.S* sret %agg.result, i32 %val1, %swift_error** swifterror %error_ptr_ref) { +; CHECK-APPLE-LABEL: foo_sret: +; CHECK-APPLE: mov [[SRET:r[0-9]+]], r0 +; CHECK-APPLE: mov r0, #16 +; CHECK-APPLE: malloc +; CHECK-APPLE: mov [[REG:r[0-9]+]], #1 +; CHECK-APPLE-DAG: mov r6, r0 +; CHECK-APPLE-DAG: strb [[REG]], [r0, #8] +; CHECK-APPLE-DAG: str r{{.*}}, [{{.*}}[[SRET]], #4] + +; CHECK-O0-LABEL: foo_sret: +; CHECK-O0: mov r{{.*}}, #16 +; spill to stack: sret and val1 +; CHECK-O0-DAG: str r0 +; CHECK-O0-DAG: str r1 +; CHECK-O0: malloc +; CHECK-O0: mov [[ID:r[0-9]+]], #1 +; CHECK-O0: strb [[ID]], [r0, #8] +; reload from stack: sret and val1 +; CHECK-O0: ldr +; CHECK-O0: ldr +; CHECK-O0: str r{{.*}}, [{{.*}}, #4] +; CHECK-O0: mov r6 +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + %v2 = getelementptr inbounds %struct.S, %struct.S* %agg.result, i32 0, i32 1 + store i32 %val1, i32* %v2 + ret void +} + +define float @caller3(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller3: +; CHECK-APPLE: mov [[ID:r[0-9]+]], r0 +; CHECK-APPLE: mov r6, #0 +; CHECK-APPLE: bl {{.*}}foo_sret +; CHECK-APPLE: cmp r6, #0 +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrbeq [[CODE:r[0-9]+]], [r6, #8] +; CHECK-APPLE: strbeq [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov r0, r6 +; CHECK_APPLE: bl {{.*}}free + +; CHECK-O0-LABEL: caller3: +; CHECK-O0-DAG: mov r6, #0 +; CHECK-O0-DAG: mov r0 +; CHECK-O0-DAG: mov r1 +; CHECK-O0: bl {{.*}}foo_sret +; CHECK-O0: mov [[ID2:r[0-9]+]], r6 +; CHECK-O0: cmp [[ID2]] +; CHECK-O0: bne +; Access part of the error object and save it to error_ref +; CHECK-O0: ldrb [[CODE:r[0-9]+]] +; CHECK-O0: ldr [[ID:r[0-9]+]] +; CHECK-O0: strb [[CODE]], [{{.*}}[[ID]]] +; CHECK-O0: mov r0, +; CHECK_O0: bl {{.*}}free +entry: + %s = alloca %struct.S, align 8 + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + call void @foo_sret(%struct.S* sret %s, i32 1, %swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +declare void @llvm.va_start(i8*) nounwind +define float @foo_vararg(%swift_error** swifterror %error_ptr_ref, ...) { +; CHECK-APPLE-LABEL: foo_vararg: +; CHECK-APPLE: mov r0, #16 +; CHECK-APPLE: malloc +; CHECK-APPLE: mov [[REG:r[0-9]+]], r0 +; CHECK-APPLE: mov [[ID:r[0-9]+]], #1 +; CHECK-APPLE-DAG: strb [[ID]], [{{.*}}[[REG]], #8] +; CHECK-APPLE-DAG: mov r6, [[REG]] + +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + + %args = alloca i8*, align 8 + %a10 = alloca i32, align 4 + %a11 = alloca i32, align 4 + %a12 = alloca i32, align 4 + %v10 = bitcast i8** %args to i8* + call void @llvm.va_start(i8* %v10) + %v11 = va_arg i8** %args, i32 + store i32 %v11, i32* %a10, align 4 + %v12 = va_arg i8** %args, i32 + store i32 %v12, i32* %a11, align 4 + %v13 = va_arg i8** %args, i32 + store i32 %v13, i32* %a12, align 4 + + ret float 1.0 +} + +define float @caller4(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller4: +; CHECK-APPLE: mov [[ID:r[0-9]+]], r0 +; CHECK-APPLE: mov r6, #0 +; CHECK-APPLE: bl {{.*}}foo_vararg +; CHECK-APPLE: cmp r6, #0 +; Access part of the error object and save it to error_ref +; CHECK-APPLE: ldrbeq [[CODE:r[0-9]+]], [r6, #8] +; CHECK-APPLE: strbeq [[CODE]], [{{.*}}[[ID]]] +; CHECK-APPLE: mov r0, r6 +; CHECK_APPLE: bl {{.*}}free +entry: + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + + %a10 = alloca i32, align 4 + %a11 = alloca i32, align 4 + %a12 = alloca i32, align 4 + store i32 10, i32* %a10, align 4 + store i32 11, i32* %a11, align 4 + store i32 12, i32* %a12, align 4 + %v10 = load i32, i32* %a10, align 4 + %v11 = load i32, i32* %a11, align 4 + %v12 = load i32, i32* %a12, align 4 + + %call = call float (%swift_error**, ...) @foo_vararg(%swift_error** swifterror %error_ptr_ref, i32 %v10, i32 %v11, i32 %v12) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont + +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} diff --git a/llvm/test/CodeGen/ARM/swiftself.ll b/llvm/test/CodeGen/ARM/swiftself.ll new file mode 100644 index 0000000000000..cbeedc410661c --- /dev/null +++ b/llvm/test/CodeGen/ARM/swiftself.ll @@ -0,0 +1,32 @@ +; RUN: llc -verify-machineinstrs < %s -mtriple=armv7k-apple-ios8.0 -mcpu=cortex-a7 | FileCheck --check-prefix=CHECK-APPLE %s +; RUN: llc -O0 -verify-machineinstrs < %s -mtriple=armv7k-apple-ios8.0 -mcpu=cortex-a7 | FileCheck --check-prefix=CHECK-O0 %s + +; RUN: llc -verify-machineinstrs < %s -mtriple=armv7-apple-ios | FileCheck --check-prefix=CHECK-APPLE %s +; RUN: llc -O0 -verify-machineinstrs < %s -mtriple=armv7-apple-ios | FileCheck --check-prefix=CHECK-O0 %s + +; Parameter with swiftself should be allocated to r9. +define void @check_swiftself(i32* swiftself %addr0) { +; CHECK-APPLE-LABEL: check_swiftself: +; CHECK-O0-LABEL: check_swiftself: + + %val0 = load volatile i32, i32* %addr0 +; CHECK-APPLE: ldr r{{.*}}, [r9] +; CHECK-O0: ldr r{{.*}}, [r9] + ret void +} + +@var8_3 = global i8 0 +declare void @take_swiftself(i8* swiftself %addr0) + +define void @simple_args() { +; CHECK-APPLE-LABEL: simple_args: +; CHECK-O0-LABEL: simple_args: + + call void @take_swiftself(i8* @var8_3) +; CHECK-APPLE: add r9, pc +; CHECK-APPLE: bl {{_?}}take_swiftself +; CHECK-O0: add r9, pc +; CHECK-O0: bl {{_?}}take_swiftself + + ret void +} diff --git a/llvm/test/CodeGen/X86/alias-gep.ll b/llvm/test/CodeGen/X86/alias-gep.ll new file mode 100644 index 0000000000000..5ecf20ba78ed5 --- /dev/null +++ b/llvm/test/CodeGen/X86/alias-gep.ll @@ -0,0 +1,22 @@ +; RUN: llc < %s -mtriple=x86_64-apple-darwin | FileCheck --check-prefix=MACHO %s +; RUN: llc < %s -mtriple=x86_64-pc-linux | FileCheck --check-prefix=ELF %s + +;MACHO: .globl _offsetSym0 +;MACHO-NOT: .alt_entry +;MACHO: _offsetSym0 = _s +;MACHO: .globl _offsetSym1 +;MACHO: .alt_entry _offsetSym1 +;MACHO: _offsetSym1 = _s+8 + +;ELF: .globl offsetSym0 +;ELF-NOT: .alt_entry +;ELF: offsetSym0 = s +;ELF: .globl offsetSym1 +;ELF-NOT: .alt_entry +;ELF: offsetSym1 = s+8 + +%struct.S1 = type { i32, i32, i32 } + +@s = global %struct.S1 { i32 31, i32 32, i32 33 }, align 4 +@offsetSym0 = alias i32, i32* getelementptr inbounds (%struct.S1, %struct.S1* @s, i64 0, i32 0) +@offsetSym1 = alias i32, i32* getelementptr inbounds (%struct.S1, %struct.S1* @s, i64 0, i32 2) diff --git a/llvm/test/CodeGen/X86/swift-return.ll b/llvm/test/CodeGen/X86/swift-return.ll new file mode 100644 index 0000000000000..e4e069700c7c9 --- /dev/null +++ b/llvm/test/CodeGen/X86/swift-return.ll @@ -0,0 +1,135 @@ +; RUN: llc -verify-machineinstrs < %s -mtriple=x86_64-unknown-unknown | FileCheck %s +; RUN: llc -verify-machineinstrs < %s -mtriple=x86_64-unknown-unknown -O0 | FileCheck --check-prefix=CHECK-O0 %s + +; Test how llvm handles return type of {i16, i8}. The return value will be passed in %eax and %dl. +; clang actually returns i32 instead of {i16, i8}. +; CHECK-LABEL: test: +; CHECK: movl %edi +; CHECK: callq gen +; CHECK: movsbl %dl +; CHECK: addl %{{.*}}, %eax +; CHECK-O0-LABEL: test +; CHECK-O0: movl %edi +; CHECK-O0: callq gen +; CHECK-O0: movswl %ax +; CHECK-O0: movsbl %dl +; CHECK-O0: addl +; CHECK-O0: movw %{{.*}}, %ax +define i16 @test(i32 %key) { +entry: + %key.addr = alloca i32, align 4 + store i32 %key, i32* %key.addr, align 4 + %0 = load i32, i32* %key.addr, align 4 + %call = call swiftcc { i16, i8 } @gen(i32 %0) + %v3 = extractvalue { i16, i8 } %call, 0 + %v1 = sext i16 %v3 to i32 + %v5 = extractvalue { i16, i8 } %call, 1 + %v2 = sext i8 %v5 to i32 + %add = add nsw i32 %v1, %v2 + %conv = trunc i32 %add to i16 + ret i16 %conv +} + +declare swiftcc { i16, i8 } @gen(i32) + +; We can't pass every return value in register, instead, pass everything in memroy. +; The caller provides space for the return value and passes the address in %rdi. +; The first input argument will be in %rsi. +; CHECK-LABEL: test2: +; CHECK: leaq (%rsp), %rdi +; CHECK: movl %{{.*}}, %esi +; CHECK: callq gen2 +; CHECK: movl (%rsp) +; CHECK-DAG: addl 4(%rsp) +; CHECK-DAG: addl 8(%rsp) +; CHECK-DAG: addl 12(%rsp) +; CHECK-DAG: addl 16(%rsp) +; CHECK-O0-LABEL: test2: +; CHECK-O0-DAG: leaq (%rsp), %rdi +; CHECK-O0-DAG: movl {{.*}}, %esi +; CHECK-O0: callq gen2 +; CHECK-O0-DAG: movl (%rsp) +; CHECK-O0-DAG: movl 4(%rsp) +; CHECK-O0-DAG: movl 8(%rsp) +; CHECK-O0-DAG: movl 12(%rsp) +; CHECK-O0-DAG: movl 16(%rsp) +; CHECK-O0: addl +; CHECK-O0: addl +; CHECK-O0: addl +; CHECK-O0: addl +; CHECK-O0: movl %{{.*}}, %eax +define i32 @test2(i32 %key) #0 { +entry: + %key.addr = alloca i32, align 4 + store i32 %key, i32* %key.addr, align 4 + %0 = load i32, i32* %key.addr, align 4 + %call = call swiftcc { i32, i32, i32, i32, i32 } @gen2(i32 %0) + + %v3 = extractvalue { i32, i32, i32, i32, i32 } %call, 0 + %v5 = extractvalue { i32, i32, i32, i32, i32 } %call, 1 + %v6 = extractvalue { i32, i32, i32, i32, i32 } %call, 2 + %v7 = extractvalue { i32, i32, i32, i32, i32 } %call, 3 + %v8 = extractvalue { i32, i32, i32, i32, i32 } %call, 4 + + %add = add nsw i32 %v3, %v5 + %add1 = add nsw i32 %add, %v6 + %add2 = add nsw i32 %add1, %v7 + %add3 = add nsw i32 %add2, %v8 + ret i32 %add3 +} + +; The address of the return value is passed in %rdi. +; On return, %rax will contain the adddress that has been passed in by the caller in %rdi. +; CHECK-LABEL: gen2: +; CHECK: movl %esi, 16(%rdi) +; CHECK: movl %esi, 12(%rdi) +; CHECK: movl %esi, 8(%rdi) +; CHECK: movl %esi, 4(%rdi) +; CHECK: movl %esi, (%rdi) +; CHECK: movq %rdi, %rax +; CHECK-O0-LABEL: gen2: +; CHECK-O0-DAG: movl %esi, 16(%rdi) +; CHECK-O0-DAG: movl %esi, 12(%rdi) +; CHECK-O0-DAG: movl %esi, 8(%rdi) +; CHECK-O0-DAG: movl %esi, 4(%rdi) +; CHECK-O0-DAG: movl %esi, (%rdi) +; CHECK-O0-DAG: movq %rdi, %rax +define swiftcc { i32, i32, i32, i32, i32 } @gen2(i32 %key) { + %Y = insertvalue { i32, i32, i32, i32, i32 } undef, i32 %key, 0 + %Z = insertvalue { i32, i32, i32, i32, i32 } %Y, i32 %key, 1 + %Z2 = insertvalue { i32, i32, i32, i32, i32 } %Z, i32 %key, 2 + %Z3 = insertvalue { i32, i32, i32, i32, i32 } %Z2, i32 %key, 3 + %Z4 = insertvalue { i32, i32, i32, i32, i32 } %Z3, i32 %key, 4 + ret { i32, i32, i32, i32, i32 } %Z4 +} + +; The return value {i32, i32, i32, i32} will be returned via registers %eax, %edx, %ecx, %r8d. +; CHECK-LABEL: test3: +; CHECK: callq gen3 +; CHECK: addl %edx, %eax +; CHECK: addl %ecx, %eax +; CHECK: addl %r8d, %eax +; CHECK-O0-LABEL: test3: +; CHECK-O0: callq gen3 +; CHECK-O0: addl %edx, %eax +; CHECK-O0: addl %ecx, %eax +; CHECK-O0: addl %r8d, %eax +define i32 @test3(i32 %key) #0 { +entry: + %key.addr = alloca i32, align 4 + store i32 %key, i32* %key.addr, align 4 + %0 = load i32, i32* %key.addr, align 4 + %call = call swiftcc { i32, i32, i32, i32 } @gen3(i32 %0) + + %v3 = extractvalue { i32, i32, i32, i32 } %call, 0 + %v5 = extractvalue { i32, i32, i32, i32 } %call, 1 + %v6 = extractvalue { i32, i32, i32, i32 } %call, 2 + %v7 = extractvalue { i32, i32, i32, i32 } %call, 3 + + %add = add nsw i32 %v3, %v5 + %add1 = add nsw i32 %add, %v6 + %add2 = add nsw i32 %add1, %v7 + ret i32 %add2 +} + +declare swiftcc { i32, i32, i32, i32 } @gen3(i32 %key) diff --git a/llvm/test/CodeGen/X86/swifterror.ll b/llvm/test/CodeGen/X86/swifterror.ll new file mode 100644 index 0000000000000..484a5f29a3b3e --- /dev/null +++ b/llvm/test/CodeGen/X86/swifterror.ll @@ -0,0 +1,276 @@ +; RUN: llc -verify-machineinstrs < %s -mtriple=x86_64-apple-darwin | FileCheck --check-prefix=CHECK-APPLE %s +; RUN: llc -verify-machineinstrs -O0 < %s -mtriple=x86_64-apple-darwin | FileCheck --check-prefix=CHECK-O0 %s + +declare i8* @malloc(i64) +declare void @free(i8*) +%swift_error = type {i64, i8} + +define float @foo(%swift_error** swifterror %error_ptr_ref) { +; CHECK-APPLE-LABEL: foo: +; CHECK-APPLE: movl $16, %edi +; CHECK-APPLE: malloc +; CHECK-APPLE: movb $1, 8(%rax) +; CHECK-APPLE: movq %rax, %r12 + +; CHECK-O0-LABEL: foo: +; CHECK-O0: movl $16 +; CHECK-O0: malloc +; CHECK-O0: movb $1, 8(%rax) +; CHECK-O0: movq %{{.*}}, %r12 +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + ret float 1.0 +} + +define float @caller(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller: +; CHECK-APPLE: xorl %r12d, %r12d +; CHECK-APPLE: callq {{.*}}foo +; CHECK-APPLE: testq %r12, %r12 +; CHECK-APPLE: jne +; Access part of the error object and save it to error_ref +; CHECK-APPLE: movb 8(%r12) +; CHECK-APPLE: movq %r12, %rdi +; CHECK_APPLE: callq {{.*}}free + +; CHECK-O0-LABEL: caller: +; CHECK-O0: xorl +; CHECK-O0: movl %{{.*}}, %r12d +; CHECK-O0: callq {{.*}}foo +; CHECK-O0: jne +entry: + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + %call = call float @foo(%swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +define float @caller2(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller2: +; CHECK-APPLE: xorl %r12d, %r12d +; CHECK-APPLE: callq {{.*}}foo +; CHECK-APPLE: testq %r12, %r12 +; CHECK-APPLE: jne +; CHECK-APPLE: ucomiss +; CHECK-APPLE: jbe +; Access part of the error object and save it to error_ref +; CHECK-APPLE: movb 8(%r12) +; CHECK-APPLE: movq %r12, %rdi +; CHECK_APPLE: callq {{.*}}free + +; CHECK-O0-LABEL: caller2: +; CHECK-O0: xorl +; CHECK-O0: movl %{{.*}}, %r12d +; CHECK-O0: callq {{.*}}foo +; CHECK-O0: movq %r12, [[ID:%[a-z]+]] +; CHECK-O0: cmpq $0, [[ID]] +; CHECK-O0: jne +entry: + %error_ptr_ref = alloca swifterror %swift_error* + br label %bb_loop +bb_loop: + store %swift_error* null, %swift_error** %error_ptr_ref + %call = call float @foo(%swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %cmp = fcmp ogt float %call, 1.000000e+00 + br i1 %cmp, label %bb_end, label %bb_loop +bb_end: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} + +define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) { +; CHECK-APPLE-LABEL: foo_if: +; CHECK-APPLE: testl %edi, %edi +; CHECK-APPLE: je +; CHECK-APPLE: movl $16, %edi +; CHECK-APPLE: malloc +; CHECK-APPLE: movb $1, 8(%rax) +; CHECK-APPLE: movq %rax, %r12 +; CHECK-APPLE-NOT: %r12 +; CHECK-APPLE: ret + +; CHECK-O0-LABEL: foo_if: +; CHECK-O0: cmpl $0 +; spill to stack +; CHECK-O0: movq %r12, {{.*}}(%rsp) +; CHECK-O0: je +; CHECK-O0: movl $16, +; CHECK-O0: malloc +; CHECK-O0: movq %rax, [[ID:%[a-z]+]] +; CHECK-O0-DAG: movb $1, 8(%rax) +; CHECK-O0-DAG: movq [[ID]], %r12 +; CHECK-O0: ret +; reload from stack +; CHECK-O0: movq {{.*}}(%rsp), %r12 +; CHECK-O0: ret +entry: + %cond = icmp ne i32 %cc, 0 + br i1 %cond, label %gen_error, label %normal + +gen_error: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + ret float 1.0 + +normal: + ret float 0.0 +} + +define float @foo_loop(%swift_error** swifterror %error_ptr_ref, i32 %cc, float %cc2) { +; CHECK-APPLE-LABEL: foo_loop: +; CHECK-APPLE: movq %r12, %rax +; CHECK-APPLE: testl +; CHECK-APPLE: je +; CHECK-APPLE: movl $16, %edi +; CHECK-APPLE: malloc +; CHECK-APPLE: movb $1, 8(%rax) +; CHECK-APPLE: ucomiss +; CHECK-APPLE: jbe +; CHECK-APPLE: movq %rax, %r12 +; CHECK-APPLE: ret + +; CHECK-O0-LABEL: foo_loop: +; spill to stack +; CHECK-O0: movq %r12, {{.*}}(%rsp) +; CHECK-O0: cmpl $0 +; CHECK-O0: je +; CHECK-O0: movl $16, +; CHECK-O0: malloc +; CHECK-O0: movq %rax, [[ID:%[a-z]+]] +; CHECK-O0: movb $1, 8([[ID]]) +; CHECK-O0: jbe +; reload from stack +; CHECK-O0: movq {{.*}}(%rsp), %r12 +; CHECK-O0: ret +entry: + br label %bb_loop + +bb_loop: + %cond = icmp ne i32 %cc, 0 + br i1 %cond, label %gen_error, label %bb_cont + +gen_error: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + br label %bb_cont + +bb_cont: + %cmp = fcmp ogt float %cc2, 1.000000e+00 + br i1 %cmp, label %bb_end, label %bb_loop +bb_end: + ret float 0.0 +} + +%struct.S = type { i32, i32, i32, i32, i32, i32 } + +define void @foo_sret(%struct.S* sret %agg.result, i32 %val1, %swift_error** swifterror %error_ptr_ref) { +; CHECK-APPLE-LABEL: foo_sret: +; CHECK-APPLE: movq %rdi, %{{.*}} +; CHECK-APPLE: movl $16, %edi +; CHECK-APPLE: malloc +; CHECK-APPLE: movb $1, 8(%rax) +; CHECK-APPLE: movl %{{.*}}, 4(%{{.*}}) +; CHECK-APPLE: movq %rax, %r12 +; CHECK-APPLE: movq %{{.*}}, %rax +; CHECK-APPLE-NOT: x19 + +; CHECK-O0-LABEL: foo_sret: +; CHECK-O0: movl $16, +; spill sret to stack +; CHECK-O0: movq %rdi, +; CHECK-O0: movq {{.*}}, %rdi +; CHECK-O0: malloc +; CHECK-O0: movb $1, 8(%rax) +; CHECK-O0: movl %{{.*}}, 4(%{{.*}}) +; CHECK-O0: movq %{{.*}}, %r12 +; reload sret from stack +; CHECK-O0: movq {{.*}}(%rsp), %rax +; CHECK-O0: ret +entry: + %call = call i8* @malloc(i64 16) + %call.0 = bitcast i8* %call to %swift_error* + store %swift_error* %call.0, %swift_error** %error_ptr_ref + %0 = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %0 + %v2 = getelementptr inbounds %struct.S, %struct.S* %agg.result, i32 0, i32 1 + store i32 %val1, i32* %v2 + ret void +} + +define float @caller3(i8* %error_ref) { +; CHECK-APPLE-LABEL: caller3: +; CHECK-APPLE: movl $1, %esi +; CHECK-APPLE: xorl %r12d, %r12d +; CHECK-APPLE: callq {{.*}}foo_sret +; CHECK-APPLE: testq %r12, %r12 +; CHECK-APPLE: jne +; Access part of the error object and save it to error_ref +; CHECK-APPLE: movb 8(%r12), +; CHECK-APPLE: movb %{{.*}}, +; CHECK-APPLE: movq %r12, %rdi +; CHECK_APPLE: callq {{.*}}free + +; CHECK-O0-LABEL: caller3: +; CHECK-O0: xorl +; CHECK-O0: movl {{.*}}, %r12d +; CHECK-O0: movl $1, %esi +; CHECK-O0: movq {{.*}}, %rdi +; CHECK-O0: callq {{.*}}foo_sret +; CHECK-O0: movq %r12, +; CHECK-O0: cmpq $0 +; CHECK-O0: jne +; Access part of the error object and save it to error_ref +; CHECK-O0: movb 8(%{{.*}}), +; CHECK-O0: movb %{{.*}}, +; reload from stack +; CHECK-O0: movq {{.*}}(%rsp), %rdi +; CHECK-O0: callq {{.*}}free +entry: + %s = alloca %struct.S, align 8 + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + call void @foo_sret(%struct.S* sret %s, i32 1, %swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %0 = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %0) + ret float 1.0 +} diff --git a/llvm/test/CodeGen/X86/swiftself.ll b/llvm/test/CodeGen/X86/swiftself.ll new file mode 100644 index 0000000000000..4886fe3860aa9 --- /dev/null +++ b/llvm/test/CodeGen/X86/swiftself.ll @@ -0,0 +1,29 @@ +; RUN: llc -verify-machineinstrs < %s -mtriple=x86_64-unknown-unknown | FileCheck %s +; RUN: llc -O0 -verify-machineinstrs < %s -mtriple=x86_64-unknown-unknown | FileCheck --check-prefix=CHECK-O0 %s + +; Parameter with swiftself should be allocated to r10. +define void @check_swiftself(i32* swiftself %addr0) { +; CHECK-LABEL: check_swiftself: +; CHECK-O0-LABEL: check_swiftself: + + %val0 = load volatile i32, i32* %addr0 +; CHECK: movl (%r10), +; CHECK-O0: movl (%r10), + ret void +} + +@var8_3 = global i8 0 +declare void @take_swiftself(i8* swiftself %addr0) + +define void @simple_args() { +; CHECK-LABEL: simple_args: +; CHECK-O0-LABEL: simple_args: + + call void @take_swiftself(i8* @var8_3) +; CHECK: movl {{.*}}, %r10d +; CHECK: callq {{_?}}take_swiftself +; CHECK-O0: movabsq {{.*}}, %r10 +; CHECK-O0: callq {{_?}}take_swiftself + + ret void +} diff --git a/llvm/test/Transforms/GlobalOpt/alias-used.ll b/llvm/test/Transforms/GlobalOpt/alias-used.ll index 9ced3974ee87d..02136a0644718 100644 --- a/llvm/test/Transforms/GlobalOpt/alias-used.ll +++ b/llvm/test/Transforms/GlobalOpt/alias-used.ll @@ -45,3 +45,22 @@ define i8* @g2() { define i8* @h() { ret i8* @ca } + +; Check that GlobalOpt doesn't try to resolve aliases with GEP operands. + +%struct.S = type { i32, i32, i32 } +@s = global %struct.S { i32 1, i32 2, i32 3 }, align 4 + +@alias1 = alias i32, i32* getelementptr inbounds (%struct.S, %struct.S* @s, i64 0, i32 1) +@alias2 = alias i32, i32* getelementptr inbounds (%struct.S, %struct.S* @s, i64 0, i32 2) + +; CHECK: load i32, i32* @alias1, align 4 +; CHECK: load i32, i32* @alias2, align 4 + +define i32 @foo1() { +entry: + %0 = load i32, i32* @alias1, align 4 + %1 = load i32, i32* @alias2, align 4 + %add = add nsw i32 %1, %0 + ret i32 %add +} diff --git a/llvm/test/Verifier/swifterror.ll b/llvm/test/Verifier/swifterror.ll new file mode 100644 index 0000000000000..843ca268c58c2 --- /dev/null +++ b/llvm/test/Verifier/swifterror.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +declare void @a(i32** swifterror %a, i32** swifterror %b) +; CHECK: Cannot have multiple 'swifterror' parameters! diff --git a/llvm/test/Verifier/swiftself.ll b/llvm/test/Verifier/swiftself.ll new file mode 100644 index 0000000000000..18789e11c8d72 --- /dev/null +++ b/llvm/test/Verifier/swiftself.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +declare void @a(i32* swiftself %a, i32* swiftself %b) +; CHECK: Cannot have multiple 'swiftself' parameters! From 87073d987b5bb070c06c4f6ef3c11004f7bf3944 Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Sat, 7 Nov 2015 12:48:19 -0600 Subject: [PATCH 003/582] Make upstream-with-swift build. This is a fix that was not properly propagated into the repo. apple-llvm-split-commit: 5411fec22d0f6a8688e0d2eeeb5f0045e0019a0e apple-llvm-split-dir: llvm/ --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index 17a8ab722ce3e..d69de31146a35 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -1122,7 +1122,7 @@ static void setupSwiftErrorVals(const Function &Fn, const TargetLowering *TLI, for (Function::const_arg_iterator AI = Fn.arg_begin(), AE = Fn.arg_end(); AI != AE; ++AI) if (AI->hasSwiftErrorAttr()) - FuncInfo->SwiftErrorVals.push_back(AI); + FuncInfo->SwiftErrorVals.push_back(&*AI); for (const auto &LLVMBB : Fn) for (const auto &Inst : LLVMBB) { From 94631e6ed2a52463cc2a3f6d6483c867beac82ab Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Sat, 7 Nov 2015 12:49:16 -0600 Subject: [PATCH 004/582] Fix bad merge. apple-llvm-split-commit: 3a38cf86fbd78b94e2f1f843b3c5082fb3b8e5e7 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/AttrDocs.td | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 6b991d05c32d3..cf1811a11e7e2 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1646,6 +1646,7 @@ All of these conventions except ``none`` require the function to have an error p * ``swift_error(nonnull_error)`` means that calls to the function should be considered to have thrown if they leave a non-null error in the error parameter. The return type is left unmodified. }]; +} def NotTailCalledDocs : Documentation { let Category = DocCatFunction; From aeabcd027b92187436bf1408268822fcbfd74e1c Mon Sep 17 00:00:00 2001 From: Frederic Riss <friss@apple.com> Date: Wed, 11 Nov 2015 09:26:01 -0800 Subject: [PATCH 005/582] Simplify expression apple-llvm-split-commit: c351d95e90f493ac3b49f5a21387bae8e7ba7b10 apple-llvm-split-dir: llvm/ --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index d69de31146a35..17a8ab722ce3e 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -1122,7 +1122,7 @@ static void setupSwiftErrorVals(const Function &Fn, const TargetLowering *TLI, for (Function::const_arg_iterator AI = Fn.arg_begin(), AE = Fn.arg_end(); AI != AE; ++AI) if (AI->hasSwiftErrorAttr()) - FuncInfo->SwiftErrorVals.push_back(&*AI); + FuncInfo->SwiftErrorVals.push_back(AI); for (const auto &LLVMBB : Fn) for (const auto &Inst : LLVMBB) { From c1449f84e6493c04a37439f189b99213537a44c0 Mon Sep 17 00:00:00 2001 From: David Farler <dfarler@apple.com> Date: Tue, 17 Mar 2015 12:00:39 -0700 Subject: [PATCH 006/582] Add APINotes cases for TvOS and WatchOS rdar://problem/20192169 apple-llvm-split-commit: a112670eba7ff17002233c9575108a68bc5a8425 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesYAMLCompiler.h | 2 ++ clang/tools/driver/apinotes_main.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h index e897b5cf90496..f459e079d25bd 100644 --- a/clang/include/clang/APINotes/APINotesYAMLCompiler.h +++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h @@ -35,6 +35,8 @@ namespace api_notes { enum class OSType { OSX, IOS, + TvOS, + WatchOS, Absent }; diff --git a/clang/tools/driver/apinotes_main.cpp b/clang/tools/driver/apinotes_main.cpp index 76892b69ba4d7..cbd8045922d2b 100644 --- a/clang/tools/driver/apinotes_main.cpp +++ b/clang/tools/driver/apinotes_main.cpp @@ -104,6 +104,12 @@ int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0, case llvm::Triple::IOS: targetOS = api_notes::OSType::IOS; break; + case llvm::Triple::WatchOS: + targetOS = api_notes::OSType::WatchOS; + break; + case llvm::Triple::TvOS: + targetOS = api_notes::OSType::TvOS; + break; default: errs() << "target is not supported\n"; return 1; From ab7aea0ca0556522dfb5dd743048dbca4198a4fb Mon Sep 17 00:00:00 2001 From: Greg Clayton <gclayton@apple.com> Date: Tue, 26 May 2015 10:02:30 -0700 Subject: [PATCH 007/582] LLDB needs access to RecordDecl::LoadedFieldsFromExternalStorage member variable. Adding accessors to this variable. Needed for module debug info: <rdar://problem/20891698> apple-llvm-split-commit: 70949ee8bfb57a1c546b9d4e4001aed94a1f10c9 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/Decl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index cc778a550a7ba..434b45651ba77 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3284,6 +3284,9 @@ class RecordDecl : public TagDecl { bool hasFlexibleArrayMember() const { return HasFlexibleArrayMember; } void setHasFlexibleArrayMember(bool V) { HasFlexibleArrayMember = V; } + bool hasLoadedFieldsFromExternalStorage() const { return LoadedFieldsFromExternalStorage; } + void setHasLoadedFieldsFromExternalStorage(bool V) { LoadedFieldsFromExternalStorage = V; } + /// isAnonymousStructOrUnion - Whether this is an anonymous struct /// or union. To be an anonymous struct or union, it must have been /// declared without a name and there must be no objects of this From b84dbfff739f5db81fbbdb39c8edf9f5078802be Mon Sep 17 00:00:00 2001 From: Ben Langmuir <blangmuir@apple.com> Date: Tue, 10 Nov 2015 13:31:33 -0800 Subject: [PATCH 008/582] Allow modules specified by -fmodule-map-file to shadow implicitly found ones When modules come from module map files explicitly specified by -fmodule-map-file= arguments, allow those to override/shadow modules with the same name that are found implicitly by header search. If such a module is looked up by name (e.g. @import), we will always find the one from -fmodule-map-file. If we try to use a shadowed module by including one of its headers report an error. This enables developers to force use of a specific copy of their module to be used if there are multiple copies that would otherwise be visible, for example if they develop modules that are installed in the default search paths. rdar://problem/23467372 apple-llvm-split-commit: ead19a9fbe2c8884b9cf292579244e41a997f86a apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticCommonKinds.td | 2 + clang/include/clang/Basic/Module.h | 18 ++++-- clang/include/clang/Lex/ModuleMap.h | 29 +++++++++- clang/lib/Basic/Module.cpp | 7 ++- clang/lib/Frontend/CompilerInstance.cpp | 7 ++- clang/lib/Frontend/FrontendAction.cpp | 7 +++ clang/lib/Frontend/FrontendActions.cpp | 7 ++- clang/lib/Lex/ModuleMap.cpp | 58 ++++++++++++++----- clang/lib/Lex/PPDirectives.cpp | 6 +- clang/test/Modules/Inputs/shadow/A1/A.h | 1 + .../Modules/Inputs/shadow/A1/module.modulemap | 5 ++ clang/test/Modules/Inputs/shadow/A2/A.h | 1 + .../Modules/Inputs/shadow/A2/module.modulemap | 5 ++ clang/test/Modules/shadow.m | 21 +++++++ 14 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 clang/test/Modules/Inputs/shadow/A1/A.h create mode 100644 clang/test/Modules/Inputs/shadow/A1/module.modulemap create mode 100644 clang/test/Modules/Inputs/shadow/A2/A.h create mode 100644 clang/test/Modules/Inputs/shadow/A2/module.modulemap create mode 100644 clang/test/Modules/shadow.m diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index dc28200da94a2..e7d9354ae1d02 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -88,6 +88,8 @@ def err_module_unavailable : Error< "module '%0' %select{is incompatible with|requires}1 feature '%2'">; def err_module_header_missing : Error< "%select{|umbrella }0header '%1' not found">; +def err_module_shadowed : Error< + "import of shadowed module '%0'">; def err_module_lock_failure : Error< "could not acquire lock file for module '%0'">, DefaultFatal; def err_module_lock_timeout : Error< diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index fa032e9eb6695..087320bd2c36d 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -149,6 +149,9 @@ class Module { /// will be false to indicate that this (sub)module is not available. SmallVector<Requirement, 2> Requirements; + /// \brief A module with the same name that shadows this module. + Module *ShadowingModule = nullptr; + /// \brief Whether this module is missing a feature from \c Requirements. unsigned IsMissingRequirement : 1; @@ -323,13 +326,20 @@ class Module { /// /// \param Target The target options used for the current translation unit. /// - /// \param Req If this module is unavailable, this parameter - /// will be set to one of the requirements that is not met for use of - /// this module. + /// \param Req If this module is unavailable because of a missing requirement, + /// this parameter will be set to one of the requirements that is not met for + /// use of this module. + /// + /// \param MissingHeader If this module is unavailable because of a missing + /// header, this parameter will be set to one of the missing headers. + /// + /// \param ShadowingModule If this module is unavailable because it is + /// shadowed, this parameter will be set to the shadowing module. bool isAvailable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, - UnresolvedHeaderDirective &MissingHeader) const; + UnresolvedHeaderDirective &MissingHeader, + Module *&ShadowingModule) const; /// \brief Determine whether this module is a submodule. bool isSubModule() const { return Parent != nullptr; } diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 155943e5453c8..6d171b24f2469 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -79,7 +79,7 @@ class ModuleMap { std::string SourceModuleName; private: - /// \brief The top-level modules that are known. + /// \brief The unshadowed top-level modules that are known. llvm::StringMap<Module *> Modules; /// \brief The number of modules we have created in total. @@ -155,6 +155,15 @@ class ModuleMap { /// header. llvm::DenseMap<const DirectoryEntry *, Module *> UmbrellaDirs; + /// \brief A generation counter that is used to test whether modules of the + /// same name may shadow or are illegal redefintions. + /// + /// Modules from earlier scopes may shadow modules from later ones. + /// Modules from the same scope may not have the same name. + unsigned CurrentModuleScopeID = 0; + + llvm::DenseMap<Module *, unsigned> ModuleScopeIDs; + /// \brief The set of attributes that can be attached to a module. struct Attributes { Attributes() : IsSystem(), IsExternC(), IsExhaustive() {} @@ -388,6 +397,24 @@ class ModuleMap { Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir, bool IsSystem, Module *Parent); + /// \brief Create a new top-level module that is shadowed by + /// \p ShadowingModule. + Module *createShadowedModule(StringRef Name, bool IsFramework, + Module *ShadowingModule); + + /// \brief Creates a new declaration scope for module names, allowing + /// previously defined modules to shadow definitions from the new scope. + /// + /// \note Module names from earlier scopes will shadow names from the new + /// scope, which is the opposite of how shadowing works for variables. + void finishModuleDeclarationScope() { CurrentModuleScopeID += 1; } + + bool mayShadowNewModule(Module *ExistingModule) { + assert(!ExistingModule->Parent && "expected top-level module"); + assert(ModuleScopeIDs.count(ExistingModule) && "unknown module"); + return ModuleScopeIDs[ExistingModule] < CurrentModuleScopeID; + } + /// \brief Retrieve the module map file containing the definition of the given /// module. /// diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 0b78326369439..a8656749907f7 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -79,11 +79,16 @@ static bool hasFeature(StringRef Feature, const LangOptions &LangOpts, bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, - UnresolvedHeaderDirective &MissingHeader) const { + UnresolvedHeaderDirective &MissingHeader, + Module *&ShadowingModule) const { if (IsAvailable) return true; for (const Module *Current = this; Current; Current = Current->Parent) { + if (Current->ShadowingModule) { + ShadowingModule = Current->ShadowingModule; + return false; + } for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) { if (hasFeature(Current->Requirements[I].first, LangOpts, Target) != Current->Requirements[I].second) { diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index c0cf64cecbe80..57ec9c47a0529 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1605,8 +1605,13 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, // Check whether this module is available. clang::Module::Requirement Requirement; clang::Module::UnresolvedHeaderDirective MissingHeader; + clang::Module *ShadowingModule = nullptr; if (!Module->isAvailable(getLangOpts(), getTarget(), Requirement, - MissingHeader)) { + MissingHeader, ShadowingModule)) { + + assert(!ShadowingModule && + "lookup of module by name should never find shadowed module"); + if (MissingHeader.FileNameLoc.isValid()) { getDiagnostics().Report(MissingHeader.FileNameLoc, diag::err_module_header_missing) diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index ecef92e0a7dde..005bbf6e2745a 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -394,6 +394,13 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename; } + // Add a module declaration scope so that modules from -fmodule-map-file + // arguments may shadow modules found implicitly in search paths. + CI.getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .finishModuleDeclarationScope(); + // If we were asked to load any module files, do so now. for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles) if (!CI.loadModuleFile(ModuleFile)) diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 865fb474020f5..8b0eff2d6ed5f 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -321,8 +321,13 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, // Check whether we can build this module at all. clang::Module::Requirement Requirement; clang::Module::UnresolvedHeaderDirective MissingHeader; + clang::Module *ShadowingModule = nullptr; if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Requirement, - MissingHeader)) { + MissingHeader, ShadowingModule)) { + + assert(!ShadowingModule && + "lookup of module by name should never find shadowed module"); + if (MissingHeader.FileNameLoc.isValid()) { CI.getDiagnostics().Report(MissingHeader.FileNameLoc, diag::err_module_header_missing) diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index c66bd70487fd8..d997aedabc00e 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -562,6 +562,7 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, } if (!Parent) { Modules[Name] = Result; + ModuleScopeIDs[Result] = CurrentModuleScopeID; if (!LangOpts.CurrentModule.empty() && !CompilingModule && Name == LangOpts.CurrentModule) { CompilingModule = Result; @@ -570,6 +571,20 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, return std::make_pair(Result, true); } +Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework, + Module *ShadowingModule) { + + // Create a new module with this name. + Module *Result = + new Module(Name, SourceLocation(), /*Parent=*/nullptr, IsFramework, + /*IsExplicit=*/false, NumCreatedModules++); + Result->ShadowingModule = ShadowingModule; + Result->IsAvailable = false; + ModuleScopeIDs[Result] = CurrentModuleScopeID; + + return Result; +} + /// \brief For a framework module, infer the framework against which we /// should link. static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir, @@ -682,6 +697,8 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir, Module *Result = new Module(ModuleName, SourceLocation(), Parent, /*IsFramework=*/true, /*IsExplicit=*/false, NumCreatedModules++); + if (!Parent) + ModuleScopeIDs[Result] = CurrentModuleScopeID; InferredModuleAllowedBy[Result] = ModuleMapFile; Result->IsInferred = true; if (LangOpts.CurrentModule == ModuleName) { @@ -1412,6 +1429,7 @@ void ModuleMapParser::parseModuleDecl() { SourceLocation LBraceLoc = consumeToken(); // Determine whether this (sub)module has already been defined. + Module *ShadowingModule = nullptr; if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) { if (Existing->DefinitionLoc.isInvalid() && !ActiveModule) { // Skip the module definition. @@ -1425,23 +1443,35 @@ void ModuleMapParser::parseModuleDecl() { } return; } - - Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) - << ModuleName; - Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); - - // Skip the module definition. - skipUntil(MMToken::RBrace); - if (Tok.is(MMToken::RBrace)) - consumeToken(); - - HadError = true; - return; + + if (!Existing->Parent && Map.mayShadowNewModule(Existing)) { + ShadowingModule = Existing; + } else { + // This is not a shawdowed module decl, it is an illegal redefinition. + Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) + << ModuleName; + Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); + + // Skip the module definition. + skipUntil(MMToken::RBrace); + if (Tok.is(MMToken::RBrace)) + consumeToken(); + + HadError = true; + return; + } } // Start defining this module. - ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework, - Explicit).first; + if (ShadowingModule) { + ActiveModule = + Map.createShadowedModule(ModuleName, Framework, ShadowingModule); + } else { + ActiveModule = + Map.findOrCreateModule(ModuleName, ActiveModule, Framework, Explicit) + .first; + } + ActiveModule->DefinitionLoc = ModuleNameLoc; if (Attrs.IsSystem || IsSystem) ActiveModule->IsSystem = true; diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index fd16168203a99..01f680c3429fb 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1683,13 +1683,17 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, if (!SuggestedModule.getModule()->isAvailable()) { clang::Module::Requirement Requirement; clang::Module::UnresolvedHeaderDirective MissingHeader; + clang::Module *ShadowingModule = nullptr; Module *M = SuggestedModule.getModule(); // Identify the cause. (void)M->isAvailable(getLangOpts(), getTargetInfo(), Requirement, - MissingHeader); + MissingHeader, ShadowingModule); if (MissingHeader.FileNameLoc.isValid()) { Diag(MissingHeader.FileNameLoc, diag::err_module_header_missing) << MissingHeader.IsUmbrella << MissingHeader.FileName; + } else if (ShadowingModule) { + Diag(M->DefinitionLoc, diag::err_module_shadowed) << M->Name; + Diag(ShadowingModule->DefinitionLoc, diag::note_previous_definition); } else { Diag(M->DefinitionLoc, diag::err_module_unavailable) << M->getFullModuleName() << Requirement.second << Requirement.first; diff --git a/clang/test/Modules/Inputs/shadow/A1/A.h b/clang/test/Modules/Inputs/shadow/A1/A.h new file mode 100644 index 0000000000000..f07c681c2aa5c --- /dev/null +++ b/clang/test/Modules/Inputs/shadow/A1/A.h @@ -0,0 +1 @@ +#define A1_A_h diff --git a/clang/test/Modules/Inputs/shadow/A1/module.modulemap b/clang/test/Modules/Inputs/shadow/A1/module.modulemap new file mode 100644 index 0000000000000..9439a431b1dbe --- /dev/null +++ b/clang/test/Modules/Inputs/shadow/A1/module.modulemap @@ -0,0 +1,5 @@ +module A { + header "A.h" +} + +module A1 {} diff --git a/clang/test/Modules/Inputs/shadow/A2/A.h b/clang/test/Modules/Inputs/shadow/A2/A.h new file mode 100644 index 0000000000000..9880ed010f569 --- /dev/null +++ b/clang/test/Modules/Inputs/shadow/A2/A.h @@ -0,0 +1 @@ +#define A2_A_h diff --git a/clang/test/Modules/Inputs/shadow/A2/module.modulemap b/clang/test/Modules/Inputs/shadow/A2/module.modulemap new file mode 100644 index 0000000000000..935d89bb425e0 --- /dev/null +++ b/clang/test/Modules/Inputs/shadow/A2/module.modulemap @@ -0,0 +1,5 @@ +module A { + header "A.h" +} + +module A2 {} diff --git a/clang/test/Modules/shadow.m b/clang/test/Modules/shadow.m new file mode 100644 index 0000000000000..44320af2b0c66 --- /dev/null +++ b/clang/test/Modules/shadow.m @@ -0,0 +1,21 @@ +// RUN: rm -rf %t +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// REDEFINITION: error: redefinition of module 'A' +// REDEFINITION: note: previously defined + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -I %S/Inputs/shadow %s -verify + +@import A1; +@import A2; +@import A; + +#import "A2/A.h" // expected-note {{implicitly imported}} +// expected-error@A2/module.modulemap:1 {{import of shadowed module 'A'}} +// expected-note@A1/module.modulemap:1 {{previous definition}} + +#if defined(A2_A_h) +#error got the wrong definition of module A +#elif !defined(A1_A_h) +#error missing definition from A1 +#endif From 0d2beade7ce4891eca34428a2a73bc01820c797a Mon Sep 17 00:00:00 2001 From: Matthias Braun <mbraun@apple.com> Date: Tue, 1 Dec 2015 11:30:12 -0800 Subject: [PATCH 009/582] Fix build problem caused by automatic merging apple-llvm-split-commit: 7dccaefc101ea7bd4b415add60cfc98c7c509bbb apple-llvm-split-dir: llvm/ --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index ae6d1cb6a465a..a1dc92e717030 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -1126,7 +1126,7 @@ static void setupSwiftErrorVals(const Function &Fn, const TargetLowering *TLI, for (Function::const_arg_iterator AI = Fn.arg_begin(), AE = Fn.arg_end(); AI != AE; ++AI) if (AI->hasSwiftErrorAttr()) - FuncInfo->SwiftErrorVals.push_back(AI); + FuncInfo->SwiftErrorVals.push_back(&*AI); for (const auto &LLVMBB : Fn) for (const auto &Inst : LLVMBB) { From 002115512a6a6e65e3e9760811d4b8668d2f1701 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Sat, 5 Dec 2015 11:49:46 -0800 Subject: [PATCH 010/582] Add legal notice for pull requests and reference to contribution guidelines. GitHub will look for this file when a new pull request is opened and offer it to the user. apple-llvm-split-commit: c5f76a1c0c8dd8ba4b95b98ca06c140fdf4bef29 apple-llvm-split-dir: llvm/ --- llvm/CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 llvm/CONTRIBUTING.md diff --git a/llvm/CONTRIBUTING.md b/llvm/CONTRIBUTING.md new file mode 100644 index 0000000000000..a0c1644fc66a2 --- /dev/null +++ b/llvm/CONTRIBUTING.md @@ -0,0 +1,14 @@ +By submitting a pull request, you represent that you have the right to license +your contribution to Apple and the community, and agree by submitting the patch +that your contributions are licensed under the [Swift +license](https://swift.org/LICENSE.txt). + +--- + +Changes to this repository follow special considerations as described on +Swift.org under "[LLVM and Swift](https://swift.org/contributing/#llvm-and-swift)". +Please make sure your change is appropriate for this repository. + +Before submitting a pull request, please make sure you have tested your +changes and that they follow the Swift project [guidelines for contributing +code](https://swift.org/contributing/#contributing-code). From 34ce081b6367b4060a2f5608541bcd9d1b86245d Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Sat, 5 Dec 2015 11:51:53 -0800 Subject: [PATCH 011/582] Add legal notice for pull requests and reference to contribution guidelines. GitHub will look for this file when a new pull request is opened and offer it to the user. apple-llvm-split-commit: a855ffb05fe2b1dc0035a06346326feda2f7240c apple-llvm-split-dir: clang/ --- clang/CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 clang/CONTRIBUTING.md diff --git a/clang/CONTRIBUTING.md b/clang/CONTRIBUTING.md new file mode 100644 index 0000000000000..a0c1644fc66a2 --- /dev/null +++ b/clang/CONTRIBUTING.md @@ -0,0 +1,14 @@ +By submitting a pull request, you represent that you have the right to license +your contribution to Apple and the community, and agree by submitting the patch +that your contributions are licensed under the [Swift +license](https://swift.org/LICENSE.txt). + +--- + +Changes to this repository follow special considerations as described on +Swift.org under "[LLVM and Swift](https://swift.org/contributing/#llvm-and-swift)". +Please make sure your change is appropriate for this repository. + +Before submitting a pull request, please make sure you have tested your +changes and that they follow the Swift project [guidelines for contributing +code](https://swift.org/contributing/#contributing-code). From 4f1106e6aaa92dfde2b68e759c3e1b9244e7a90e Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Thu, 10 Dec 2015 18:20:34 -0800 Subject: [PATCH 012/582] Revert "LLDB needs access to RecordDecl::LoadedFieldsFromExternalStorage member variable. Adding accessors to this variable." This reverts commit 70949ee8bfb57a1c546b9d4e4001aed94a1f10c9. This was upstreamed in revision (r254451) into a different part of the file. By reverting this commit, we still have the functionality, no duplicate declarations, and hopefully less merge conflicts since the member variable that is in tree is in the place that it was upstreamed. apple-llvm-split-commit: 56faa3674d501cc8e934fe6aef9d4d8972f86134 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/Decl.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index f70e12edf7dfc..a7203f828e066 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3284,9 +3284,6 @@ class RecordDecl : public TagDecl { bool hasFlexibleArrayMember() const { return HasFlexibleArrayMember; } void setHasFlexibleArrayMember(bool V) { HasFlexibleArrayMember = V; } - bool hasLoadedFieldsFromExternalStorage() const { return LoadedFieldsFromExternalStorage; } - void setHasLoadedFieldsFromExternalStorage(bool V) { LoadedFieldsFromExternalStorage = V; } - /// isAnonymousStructOrUnion - Whether this is an anonymous struct /// or union. To be an anonymous struct or union, it must have been /// declared without a name and there must be no objects of this From cbd2ddc7239f05b2709a059158aa4ac6cb9f22d1 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Sat, 12 Dec 2015 12:54:57 -0800 Subject: [PATCH 013/582] Add __has_feature(generalized_swift_name). apple-llvm-split-commit: cf15d4961a3e7c113eea3dcb37d4a0405220e0b5 apple-llvm-split-dir: clang/ --- clang/lib/Lex/PPMacroExpansion.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index a1b99545dd074..e168d0dea8f6b 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1085,6 +1085,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_exceptions", LangOpts.CXXExceptions) .Case("cxx_rtti", LangOpts.RTTI && LangOpts.RTTIData) .Case("enumerator_attributes", true) + .Case("generalized_swift_name", true) .Case("nullability", true) .Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory)) .Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread)) From 8910c5c786886f17a75bd142fa967932ca3f54c1 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Mon, 21 Dec 2015 13:54:44 -0800 Subject: [PATCH 014/582] [AArch64] Add -disable-post-ra to swifterror tests. This was enabled by default in r256158. Like there, let's avoid test churn and disable it for the old tests. apple-llvm-split-commit: 971e110c2ed8c35356000dddde6bc543fbc6f33d apple-llvm-split-dir: llvm/ --- llvm/test/CodeGen/AArch64/swifterror.ll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/test/CodeGen/AArch64/swifterror.ll b/llvm/test/CodeGen/AArch64/swifterror.ll index 0106bb30a3d0b..ec9d256fb82ee 100644 --- a/llvm/test/CodeGen/AArch64/swifterror.ll +++ b/llvm/test/CodeGen/AArch64/swifterror.ll @@ -1,5 +1,5 @@ -; RUN: llc -verify-machineinstrs < %s -mtriple=aarch64-apple-ios | FileCheck --check-prefix=CHECK-APPLE %s -; RUN: llc -verify-machineinstrs -O0 < %s -mtriple=aarch64-apple-ios | FileCheck --check-prefix=CHECK-O0 %s +; RUN: llc -verify-machineinstrs < %s -mtriple=aarch64-apple-ios -disable-post-ra | FileCheck --check-prefix=CHECK-APPLE %s +; RUN: llc -verify-machineinstrs -O0 < %s -mtriple=aarch64-apple-ios -disable-post-ra | FileCheck --check-prefix=CHECK-O0 %s declare i8* @malloc(i64) declare void @free(i8*) From 6b74c5b3de14bc3877f1b2ed2db6d547c1838a41 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Mon, 4 Jan 2016 09:58:27 -0800 Subject: [PATCH 015/582] [Bitcode] Update swift-related code for upstream (r256593) change. This was added in 3e2331faf. apple-llvm-split-commit: aae18979923e22fb65f23559cd9ebaf3f923420f apple-llvm-split-dir: llvm/ --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 256e56b7df0e2..3bc5bdec85180 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2069,12 +2069,12 @@ std::error_code BitcodeReader::parseMetadata(bool ModuleLevel) { if (Record.size() != 6) return error("Invalid record"); - MDValueList.assignValue( + MetadataList.assignValue( GET_OR_DISTINCT(DIModule, Record[0], (Context, getMDOrNull(Record[4]), getMDString(Record[5]), nullptr, nullptr, nullptr)), - NextMDValueNo++); + NextMetadataNo++); break; } From 2bd6c3c512bf5f22ce2b0fb378b72165c69e30c7 Mon Sep 17 00:00:00 2001 From: Rafael Espindola <rafael.espindola@gmail.com> Date: Wed, 20 Jan 2016 18:57:48 +0000 Subject: [PATCH 016/582] Accept subtractions involving a weak symbol. When a symbol S shows up in an expression in assembly there are two possible interpretations * The expression is referring to the value of S in this file. * The expression is referring to the value after symbol resolution. In the first case the assembler can reason about the value and try to produce a relocation. In the second case, that is only possible if the symbol cannot be preempted. Assemblers are not very consistent about which interpretation gets used. This changes MC to agree with GAS in the case of an expression of the form "Sym - WeakSym". git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@258329 91177308-0d34-0410-b5e6-96231b3b80d8 apple-llvm-split-commit: 46550e64cdfaa341709758f62064c04132336c85 apple-llvm-split-dir: llvm/ --- llvm/lib/MC/ELFObjectWriter.cpp | 6 ------ llvm/test/MC/AArch64/error-location.s | 3 --- llvm/test/MC/ARM/error-location.s | 3 --- llvm/test/MC/ELF/relocation.s | 6 ++++++ 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp index 23d0e28324c0d..20ad41659f48e 100644 --- a/llvm/lib/MC/ELFObjectWriter.cpp +++ b/llvm/lib/MC/ELFObjectWriter.cpp @@ -655,12 +655,6 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm, return; } - if (::isWeak(SymB)) { - Ctx.reportError(Fixup.getLoc(), - "Cannot represent a subtraction with a weak symbol"); - return; - } - uint64_t SymBOffset = Layout.getSymbolOffset(SymB); uint64_t K = SymBOffset - FixupOffset; IsPCRel = true; diff --git a/llvm/test/MC/AArch64/error-location.s b/llvm/test/MC/AArch64/error-location.s index 02504368f004b..c629e0a50de8b 100644 --- a/llvm/test/MC/AArch64/error-location.s +++ b/llvm/test/MC/AArch64/error-location.s @@ -16,9 +16,6 @@ // CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Cannot represent a difference across sections .word x_a - y_a -// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Cannot represent a subtraction with a weak symbol - .word a - w - // CHECK: <unknown>:0: error: expression could not be evaluated .set v1, -undef diff --git a/llvm/test/MC/ARM/error-location.s b/llvm/test/MC/ARM/error-location.s index 112acf318ed33..2f70f294b57d2 100644 --- a/llvm/test/MC/ARM/error-location.s +++ b/llvm/test/MC/ARM/error-location.s @@ -16,9 +16,6 @@ @ CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Cannot represent a difference across sections .word x_a - y_a -@ CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Cannot represent a subtraction with a weak symbol - .word a - w - @ CHECK: <unknown>:0: error: expression could not be evaluated .set v1, -undef diff --git a/llvm/test/MC/ELF/relocation.s b/llvm/test/MC/ELF/relocation.s index 0fec76792818b..e0313904563a4 100644 --- a/llvm/test/MC/ELF/relocation.s +++ b/llvm/test/MC/ELF/relocation.s @@ -63,6 +63,11 @@ pr24486: .code16 call pr23771 + .weak weak_sym +weak_sym: + .long pr23272-weak_sym + + // CHECK: Section { // CHECK: Name: .rela.text // CHECK: Relocations [ @@ -101,5 +106,6 @@ pr24486: // CHECK-NEXT: 0xDC R_X86_64_PLT32 foo 0x0 // CHECK-NEXT: 0xF0 R_X86_64_32 .text 0xF0 // CHECK-NEXT: 0xF5 R_X86_64_PC16 pr23771 0xFFFFFFFFFFFFFFFE +// CHECK-NEXT: 0xF7 R_X86_64_PC32 pr23272 0x0 // CHECK-NEXT: ] // CHECK-NEXT: } From a14779f504b02ad0e4dbc39d6d10cadc7ed4cfd0 Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Fri, 29 Jan 2016 15:53:43 -0800 Subject: [PATCH 017/582] Introduce ns_error_domain attribute. ns_error_domain can be used by, e.g. NS_ERROR_ENUM, in order to identify a global declaration representing the domain constant. Introduces the attribute, Sema handling, diagnostics, and test case. apple-llvm-split-commit: b624ed1eb8de755bf3958612a2aae79d9bcdc4ed apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 6 +++ clang/include/clang/Basic/AttrDocs.td | 7 ++++ .../clang/Basic/DiagnosticSemaKinds.td | 8 ++++ clang/lib/Sema/SemaDeclAttr.cpp | 38 +++++++++++++++++ clang/test/Analysis/ns_error_enum.m | 42 +++++++++++++++++++ 5 files changed, 101 insertions(+) create mode 100644 clang/test/Analysis/ns_error_enum.m diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index e94bb1ae24bbc..8b5eb257aa58d 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1115,6 +1115,12 @@ def ObjCBridgeRelated : InheritableAttr { let Documentation = [Undocumented]; } +def NSErrorDomain : Attr { + let Spellings = [GNU<"ns_error_domain">]; + let Args = [IdentifierArgument<"ErrorDomain">]; + let Documentation = [NSErrorDomainDocs]; +} + def NSReturnsRetained : InheritableAttr { let Spellings = [GNU<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 5c4473d08d91b..08cd99b9867f1 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1758,6 +1758,13 @@ arguments, with arbitrary offsets. }]; } +def NSErrorDomainDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``ns_error_domain`` attribute indicates a global constant representing the error domain. + }]; +} + def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { let Content = [{ Clang supports additional attributes for controlling how APIs are imported into Swift. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ec992c72ae55c..d2f2b5bf611fd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7533,6 +7533,14 @@ def err_nsconsumed_attribute_mismatch : Error< def err_nsreturns_retained_attribute_mismatch : Error< "overriding method has mismatched ns_returns_%select{not_retained|retained}0" " attributes">; + +def err_nserrordomain_not_tagdecl : Error< + "ns_error_domain attribute only valid on " + "%select{enums, structs, and unions|enums, structs, unions, and classes}0">; +def err_nserrordomain_invalid_decl : Error< + "domain argument %0 does not refer to global constant">; +def err_nserrordomain_requires_identifier : Error< + "domain argument must be an identifier">; def note_getter_unavailable : Note< "or because setter is declared here, but no getter method %0 is found">; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index d03686130f9b8..5449bc8184ce8 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4263,6 +4263,40 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, attr.getAttributeSpellingListIndex())); } +static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isa<TagDecl>(D)) { + S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl) + << S.getLangOpts().CPlusPlus; + return; + } + IdentifierLoc *identLoc = + Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr; + if (!identLoc || !identLoc->Ident) { + // Try to locate the argument directly + SourceLocation loc = Attr.getLoc(); + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) + loc = Attr.getArgAsExpr(0)->getLocStart(); + + S.Diag(loc, diag::err_nserrordomain_requires_identifier); + return; + } + + // Verify that the identifier is a valid decl in the C decl namespace + LookupResult lookupResult(S, DeclarationName(identLoc->Ident), + SourceLocation(), + Sema::LookupNameKind::LookupOrdinaryName); + if (!S.LookupName(lookupResult, S.TUScope) || + !lookupResult.getAsSingle<VarDecl>()) { + S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl) + << identLoc->Ident; + return; + } + + D->addAttr(::new (S.Context) + NSErrorDomainAttr(Attr.getRange(), S.Context, identLoc->Ident, + Attr.getAttributeSpellingListIndex())); +} + static void handleCFAuditedTransferAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (checkAttrMutualExclusion<CFUnknownTransferAttr>(S, D, Attr.getRange(), @@ -5560,6 +5594,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; + + case AttributeList::AT_NSErrorDomain: + handleNSErrorDomain(S, D, Attr); + break; case AttributeList::AT_CFAuditedTransfer: handleCFAuditedTransferAttr(S, D, Attr); diff --git a/clang/test/Analysis/ns_error_enum.m b/clang/test/Analysis/ns_error_enum.m new file mode 100644 index 0000000000000..bf616291578bd --- /dev/null +++ b/clang/test/Analysis/ns_error_enum.m @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -verify %s + +#define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#define NS_ENUM(_type, _name) CF_ENUM(_type, _name) + +#define NS_ERROR_ENUM(_type, _name, _domain) \ + enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type + +typedef NS_ENUM(unsigned, MyEnum) { + MyFirst, + MySecond, +}; + +typedef NS_ENUM(invalidType, MyInvalidEnum) { +// expected-error@-1{{unknown type name 'invalidType'}} +// expected-error@-2{{unknown type name 'invalidType'}} + MyFirstInvalid, + MySecondInvalid, +}; + +const char *MyErrorDomain; +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) { + MyErrFirst, + MyErrSecond, +}; +struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructErrorDomain {}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) { + // expected-error@-1{{domain argument 'InvalidDomain' does not refer to global constant}} + MyErrFirstInvalid, + MyErrSecondInvalid, +}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string"); + // expected-error@-1{{domain argument must be an identifier}} + +int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl; + // expected-error@-1{{ns_error_domain attribute only valid on enums, structs, and unions}} + +void foo() {} +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalidFunction, foo); + // expected-error@-1{{domain argument 'foo' does not refer to global constant}} From 96ad82d49e7f20d47c2e393416c7af58201e8ed7 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 12 Feb 2016 11:49:25 -0800 Subject: [PATCH 018/582] [ADT] Fix PointerEmbeddedInt when the underlying type is uintptr_t. ...and when you try to store negative values in it. apple-llvm-split-commit: caeff488e6b6e64bedcdcf2959a4489d617819ab apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/PointerEmbeddedInt.h | 27 ++++++++--- llvm/unittests/ADT/PointerEmbeddedIntTest.cpp | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/llvm/include/llvm/ADT/PointerEmbeddedInt.h b/llvm/include/llvm/ADT/PointerEmbeddedInt.h index 9562819f28351..432d9e1c125ce 100644 --- a/llvm/include/llvm/ADT/PointerEmbeddedInt.h +++ b/llvm/include/llvm/ADT/PointerEmbeddedInt.h @@ -11,6 +11,7 @@ #define LLVM_ADT_POINTEREMBEDDEDINT_H #include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include <climits> @@ -30,6 +31,8 @@ template <typename IntT, int Bits = sizeof(IntT) * CHAR_BIT> class PointerEmbeddedInt { uintptr_t Value; + // Note: This '<' is correct; using '<=' would result in some shifts + // overflowing their storage types. static_assert(Bits < sizeof(uintptr_t) * CHAR_BIT, "Cannot embed more bits than we have in a pointer!"); @@ -42,26 +45,36 @@ class PointerEmbeddedInt { Mask = static_cast<uintptr_t>(-1) << Bits }; + enum RawValue_t { + RawValue + }; + friend class PointerLikeTypeTraits<PointerEmbeddedInt>; - explicit PointerEmbeddedInt(uintptr_t Value) : Value(Value) {} + explicit PointerEmbeddedInt(uintptr_t Value, RawValue_t) : Value(Value) {} public: PointerEmbeddedInt() : Value(0) {} - PointerEmbeddedInt(IntT I) : Value(static_cast<uintptr_t>(I) << Shift) { - assert((I & Mask) == 0 && "Integer has bits outside those preserved!"); + PointerEmbeddedInt(IntT I) { + *this = I; } PointerEmbeddedInt &operator=(IntT I) { - assert((I & Mask) == 0 && "Integer has bits outside those preserved!"); + assert((std::is_signed<IntT>::value ? llvm::isInt<Bits>(I) + : llvm::isUInt<Bits>(I)) && + "Integer has bits outside those preserved!"); Value = static_cast<uintptr_t>(I) << Shift; return *this; } // Note that this implicit conversion additionally allows all of the basic // comparison operators to work transparently, etc. - operator IntT() const { return static_cast<IntT>(Value >> Shift); } + operator IntT() const { + if (std::is_signed<IntT>::value) + return static_cast<IntT>(static_cast<intptr_t>(Value) >> Shift); + return static_cast<IntT>(Value >> Shift); + } }; // Provide pointer like traits to support use with pointer unions and sum @@ -75,10 +88,10 @@ class PointerLikeTypeTraits<PointerEmbeddedInt<IntT, Bits>> { return reinterpret_cast<void *>(P.Value); } static inline T getFromVoidPointer(void *P) { - return T(reinterpret_cast<uintptr_t>(P)); + return T(reinterpret_cast<uintptr_t>(P), T::RawValue); } static inline T getFromVoidPointer(const void *P) { - return T(reinterpret_cast<uintptr_t>(P)); + return T(reinterpret_cast<uintptr_t>(P), T::RawValue); } enum { NumLowBitsAvailable = T::Shift }; diff --git a/llvm/unittests/ADT/PointerEmbeddedIntTest.cpp b/llvm/unittests/ADT/PointerEmbeddedIntTest.cpp index b10365a2f618e..21ed560a8a40a 100644 --- a/llvm/unittests/ADT/PointerEmbeddedIntTest.cpp +++ b/llvm/unittests/ADT/PointerEmbeddedIntTest.cpp @@ -43,4 +43,50 @@ TEST(PointerEmbeddedIntTest, Basic) { EXPECT_FALSE(42 >= J); } +TEST(PointerEmbeddedIntTest, intptr_t) { + { + PointerEmbeddedInt<intptr_t, CHAR_BIT> I = 42, J = -42; + EXPECT_EQ(42, I); + EXPECT_EQ(-42, J); + } + + { + PointerEmbeddedInt<uintptr_t, CHAR_BIT> I = 42, J = 255; + EXPECT_EQ(42U, I); + EXPECT_EQ(255U, J); + } + + { + PointerEmbeddedInt<intptr_t, std::numeric_limits<intptr_t>::digits> + I = std::numeric_limits<intptr_t>::max() >> 1, + J = std::numeric_limits<intptr_t>::min() >> 1; + EXPECT_EQ(std::numeric_limits<intptr_t>::max() >> 1, I); + EXPECT_EQ(std::numeric_limits<intptr_t>::min() >> 1, J); + } + + { + PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1> + I = std::numeric_limits<uintptr_t>::max() >> 1, + J = std::numeric_limits<uintptr_t>::min() >> 1; + EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1, I); + EXPECT_EQ(std::numeric_limits<uintptr_t>::min() >> 1, J); + } +} + +TEST(PointerEmbeddedIntTest, PointerLikeTypeTraits) { + { + PointerEmbeddedInt<int, CHAR_BIT> I = 42; + using Traits = PointerLikeTypeTraits<decltype(I)>; + EXPECT_EQ(42, Traits::getFromVoidPointer(Traits::getAsVoidPointer(I))); + } + + { + PointerEmbeddedInt<uintptr_t, std::numeric_limits<uintptr_t>::digits - 1> + I = std::numeric_limits<uintptr_t>::max() >> 1; + using Traits = PointerLikeTypeTraits<decltype(I)>; + EXPECT_EQ(std::numeric_limits<uintptr_t>::max() >> 1, + Traits::getFromVoidPointer(Traits::getAsVoidPointer(I))); + } +} + } // end anonymous namespace From 163144e01437fd6b7819190fa09a0bc7b911e5b8 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 18 Feb 2016 10:16:29 -0800 Subject: [PATCH 019/582] Fix for compiling with MSVC, from @vitalyster apple-llvm-split-commit: 4fec28d8b60eba6769e5d1d391b7dbd681026e19 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/SourceMgrAdapter.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/SourceMgrAdapter.h b/clang/include/clang/Basic/SourceMgrAdapter.h index 6782aebbeff58..dd7b83f1a5146 100644 --- a/clang/include/clang/Basic/SourceMgrAdapter.h +++ b/clang/include/clang/Basic/SourceMgrAdapter.h @@ -70,7 +70,9 @@ class SourceMgrAdapter { void handleDiag(const llvm::SMDiagnostic &diag); /// Retrieve the diagnostic handler to use with the underlying SourceMgr. - llvm::SourceMgr::DiagHandlerTy getDiagHandler() { return &handleDiag; } + llvm::SourceMgr::DiagHandlerTy getDiagHandler() { + return &SourceMgrAdapter::handleDiag; + } /// Retrieve the context to use with the diagnostic handler produced by /// \c getDiagHandler(). From 7dba415bcfd23ff631bda2f398c8da1eb7b1c9e5 Mon Sep 17 00:00:00 2001 From: Tim Northover <t.p.northover@gmail.com> Date: Thu, 18 Feb 2016 14:46:26 -0800 Subject: [PATCH 020/582] Fix botched merge apple-llvm-split-commit: 8719de3fbcadd7d722baa8dba84b4685251c28ff apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/PointerEmbeddedInt.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/llvm/include/llvm/ADT/PointerEmbeddedInt.h b/llvm/include/llvm/ADT/PointerEmbeddedInt.h index 894106d753005..546597f4577a2 100644 --- a/llvm/include/llvm/ADT/PointerEmbeddedInt.h +++ b/llvm/include/llvm/ADT/PointerEmbeddedInt.h @@ -60,11 +60,7 @@ class PointerEmbeddedInt { PointerEmbeddedInt &operator=(IntT I) { assert((std::is_signed<IntT>::value ? llvm::isInt<Bits>(I) -<<<<<<< HEAD - : llvm::isUInt<Bits>(I)) && -======= : llvm::isUInt<Bits>(I)) && ->>>>>>> upstream/master "Integer has bits outside those preserved!"); Value = static_cast<uintptr_t>(I) << Shift; return *this; From 0ea15e7d7749be6dcbf4c33c3a22a047ddc1ed45 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 18 Feb 2016 16:35:59 -0800 Subject: [PATCH 021/582] Add -iapinotes-modules search path option. This option adds a new module-centric search path to find the API notes file that applies to the current module that needs to be built. It costs us only 2 stats per search path at module construction time to determine if API notes are available, making it far more efficient (and easier to use) than the prior API notes searching mechanism. It also fits much better with the future direction of the Swift Clang importer, which will soon delegate its responsibilities to Clang's API notes infrastructure. This is part of rdar://problem/24447420. apple-llvm-split-commit: 86e45f96e98198758e6fcdca764aaa2821004322 apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesManager.h | 32 ++- .../include/clang/APINotes/APINotesOptions.h | 37 ++++ clang/include/clang/APINotes/APINotesReader.h | 2 +- clang/include/clang/Driver/Options.td | 2 + .../include/clang/Frontend/CompilerInstance.h | 7 + .../clang/Frontend/CompilerInvocation.h | 9 + clang/lib/APINotes/APINotesManager.cpp | 200 +++++++++++------- clang/lib/Driver/Tools.cpp | 6 +- clang/lib/Frontend/CompilerInstance.cpp | 7 + clang/lib/Frontend/CompilerInvocation.cpp | 8 + clang/lib/Sema/Sema.cpp | 3 +- clang/lib/Sema/SemaAPINotes.cpp | 5 +- .../Inputs/APINotes/HeaderLib.apinotes | 17 ++ .../APINotes/Inputs/APINotes/SomeKit.apinotes | 34 +++ .../Modules/module.modulemap | 5 + .../Modules/module.private.modulemap | 8 + .../Modules/module_private.modulemap | 8 + .../APINotes/Inputs/Headers/module.modulemap | 3 + clang/test/APINotes/availability.m | 3 +- clang/test/APINotes/nullability.c | 2 +- clang/test/APINotes/nullability.m | 2 +- clang/test/APINotes/objc_designated_inits.m | 2 +- 22 files changed, 309 insertions(+), 93 deletions(-) create mode 100644 clang/include/clang/APINotes/APINotesOptions.h create mode 100644 clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes create mode 100644 clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap create mode 100644 clang/test/APINotes/Inputs/Headers/module.modulemap diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index 503b36536e621..69752a9c77e90 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines the HeaderSearch interface. +// This file defines the APINotesManager interface. // //===----------------------------------------------------------------------===// @@ -15,15 +15,18 @@ #define LLVM_CLANG_APINOTES_APINOTESMANAGER_H #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include <memory> +#include <string> namespace clang { class DirectoryEntry; class FileEntry; +class LangOptions; class SourceManager; namespace api_notes { @@ -47,6 +50,13 @@ class APINotesManager { SourceManager &SourceMgr; + /// Whether to implicitly search for API notes files based on the + /// source file from which an entity was declared. + bool ImplicitAPINotes; + + /// The API notes reader for the current module. + std::unique_ptr<APINotesReader> CurrentModuleReader; + /// Whether we have already pruned the API notes cache. bool PrunedCache; @@ -56,6 +66,13 @@ class APINotesManager { /// reader for this directory. llvm::DenseMap<const DirectoryEntry *, ReaderEntry> Readers; + /// Load the API notes associated with the given file, whether it is + /// the binary or source form of API notes. + /// + /// \returns the API notes reader for this file, or null if there is + /// a failure. + std::unique_ptr<APINotesReader> loadAPINotes(const FileEntry *apiNotesFile); + /// Load the given API notes file for the given header directory. /// /// \param HeaderDir The directory at which we @@ -78,9 +95,20 @@ class APINotesManager { bool Public); public: - APINotesManager(SourceManager &SourceMgr); + APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts); ~APINotesManager(); + /// Load the API notes for the current module. + /// + /// \param moduleName The name of the current module. + /// \param searchPaths The paths in which we should search for API notes + /// for the current module. + /// + /// \returns the file entry for the API notes file loaded, or nullptr if + /// no API notes were found. + const FileEntry *loadCurrentModuleAPINotes(StringRef moduleName, + ArrayRef<std::string> searchPaths); + /// Find the API notes reader that corresponds to the given source location. APINotesReader *findAPINotes(SourceLocation Loc); }; diff --git a/clang/include/clang/APINotes/APINotesOptions.h b/clang/include/clang/APINotes/APINotesOptions.h new file mode 100644 index 0000000000000..01c7513a1d317 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesOptions.h @@ -0,0 +1,37 @@ +//===--- APINotesOptions.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the APINotesOptions class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H +#define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H + +#include <string> +#include <vector> + +namespace clang { + +/// APINotesOptions - Track various options which control how API +/// notes are found and handled. +class APINotesOptions { +public: + /// The set of search paths where we API notes can be found for + /// particular modules. + /// + /// The API notes in this directory are stored as + /// <ModuleName>.apinotes or <ModuleName>.apinotesc, and are only + /// applied when building the module <ModuleName>. + std::vector<std::string> ModuleSearchPaths; +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 77b5f16bb42f4..4759f85c1ab48 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -38,7 +38,7 @@ class APINotesReader { /// contains the contents of a binary API notes file. /// /// \returns the new API notes reader, or null if an error occurred. - static std::unique_ptr<APINotesReader> + static std::unique_ptr<APINotesReader> get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer); ~APINotesReader(); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index ec8a2c4b80c54..923eaa6d75177 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1216,6 +1216,8 @@ def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption]>, HelpText<"Display available options">; def index_header_map : Flag<["-"], "index-header-map">, Flags<[CC1Option]>, HelpText<"Make the next included directory (-I or -F) an indexer header map">; +def iapinotes_modules : JoinedOrSeparate<["-"], "iapinotes-modules">, Group<clang_i_Group>, Flags<[CC1Option]>, + HelpText<"Add directory to the API notes search path referenced by module name">, MetaVarName<"<directory>">; def idirafter : JoinedOrSeparate<["-"], "idirafter">, Group<clang_i_Group>, Flags<[CC1Option]>, HelpText<"Add directory to AFTER include search path">; def iframework : JoinedOrSeparate<["-"], "iframework">, Group<clang_i_Group>, Flags<[CC1Option]>, diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 83eed2cdc5921..798cc2ddec18f 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -291,6 +291,13 @@ class CompilerInstance : public ModuleLoader { return Invocation->getHeaderSearchOpts(); } + APINotesOptions &getAPINotesOpts() { + return Invocation->getAPINotesOpts(); + } + const APINotesOptions &getAPINotesOpts() const { + return Invocation->getAPINotesOpts(); + } + LangOptions &getLangOpts() { return *Invocation->getLangOpts(); } diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 0b4a1e587e7e4..fa04af2762721 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_ #define LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_ +#include "clang/APINotes/APINotesOptions.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LangOptions.h" @@ -105,6 +106,9 @@ class CompilerInvocation : public CompilerInvocationBase { MigratorOptions MigratorOpts; + /// Options controlling API notes. + APINotesOptions APINotesOpts; + /// Options controlling IRgen and the backend. CodeGenOptions CodeGenOpts; @@ -173,6 +177,11 @@ class CompilerInvocation : public CompilerInvocationBase { const MigratorOptions &getMigratorOpts() const { return MigratorOpts; } + + APINotesOptions &getAPINotesOpts() { return APINotesOpts; } + const APINotesOptions &getAPINotesOpts() const { + return APINotesOpts; + } CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; } const CodeGenOptions &getCodeGenOpts() const { diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 50736939a60c1..3c3d5fdfc4101 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -12,10 +12,12 @@ //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesManager.h" +#include "clang/APINotes/APINotesOptions.h" #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/APINotesYAMLCompiler.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceMgrAdapter.h" #include "clang/Basic/Version.h" @@ -49,9 +51,10 @@ STATISTIC(NumBinaryCacheMisses, STATISTIC(NumBinaryCacheRebuilds, "binary form cache rebuilds"); -APINotesManager::APINotesManager(SourceManager &SourceMgr) - : SourceMgr(SourceMgr), PrunedCache(false) { } - +APINotesManager::APINotesManager(SourceManager &sourceMgr, + const LangOptions &langOpts) + : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes), + PrunedCache(false) { } APINotesManager::~APINotesManager() { // Free the API notes readers. @@ -131,155 +134,145 @@ static void pruneAPINotesCache(StringRef APINotesCachePath) { } } -bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, - const FileEntry *APINotesFile) { - assert(Readers.find(HeaderDir) == Readers.end()); - - FileManager &FileMgr = SourceMgr.getFileManager(); +std::unique_ptr<APINotesReader> +APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { + FileManager &fileMgr = SourceMgr.getFileManager(); // If the API notes file is already in the binary form, load it directly. - StringRef APINotesFileName = APINotesFile->getName(); - StringRef APINotesFileExt = llvm::sys::path::extension(APINotesFileName); - if (!APINotesFileExt.empty() && - APINotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { + StringRef apiNotesFileName = apiNotesFile->getName(); + StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName); + if (!apiNotesFileExt.empty() && + apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { // Load the file. - auto Buffer = FileMgr.getBufferForFile(APINotesFile); - if (!Buffer) { - Readers[HeaderDir] = nullptr; - return true; - } + auto buffer = fileMgr.getBufferForFile(apiNotesFile); + if (!buffer) return nullptr; // Load the binary form. - auto Reader = APINotesReader::get(std::move(Buffer.get())); - if (!Reader) { - Readers[HeaderDir] = nullptr; - return true; - } - - // Record the reader. - Readers[HeaderDir] = Reader.release(); - return false; + return APINotesReader::get(std::move(buffer.get())); } // If we haven't pruned the API notes cache yet during this execution, do // so now. if (!PrunedCache) { - pruneAPINotesCache(FileMgr.getFileSystemOpts().APINotesCachePath); + pruneAPINotesCache(fileMgr.getFileSystemOpts().APINotesCachePath); PrunedCache = true; } // Compute a hash of the API notes file's directory and the Clang version, // to be used as part of the filename for the cached binary copy. - auto code = llvm::hash_value(StringRef(APINotesFile->getDir()->getName())); + auto code = llvm::hash_value(StringRef(apiNotesFile->getDir()->getName())); code = hash_combine(code, getClangFullRepositoryVersion()); // Determine the file name for the cached binary form. - SmallString<128> CompiledFileName; - CompiledFileName += FileMgr.getFileSystemOpts().APINotesCachePath; - assert(!CompiledFileName.empty() && "No API notes cache path provided?"); - llvm::sys::path::append(CompiledFileName, - (llvm::Twine(llvm::sys::path::stem(APINotesFileName)) + "-" + SmallString<128> compiledFileName; + compiledFileName += fileMgr.getFileSystemOpts().APINotesCachePath; + assert(!compiledFileName.empty() && "No API notes cache path provided?"); + llvm::sys::path::append(compiledFileName, + (llvm::Twine(llvm::sys::path::stem(apiNotesFileName)) + "-" + llvm::APInt(64, code).toString(36, /*Signed=*/false) + "." + BINARY_APINOTES_EXTENSION)); // Try to open the cached binary form. - if (const FileEntry *CompiledFile = FileMgr.getFile(CompiledFileName, + if (const FileEntry *compiledFile = fileMgr.getFile(compiledFileName, /*openFile=*/true, /*cacheFailure=*/false)) { // Load the file contents. - if (auto Buffer = FileMgr.getBufferForFile(CompiledFile)) { + if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { // Make sure the file is up-to-date. - if (CompiledFile->getModificationTime() - >= APINotesFile->getModificationTime()) { + if (compiledFile->getModificationTime() + >= apiNotesFile->getModificationTime()) { // Load the file. - if (auto Reader = APINotesReader::get(std::move(Buffer.get()))) { + if (auto reader = APINotesReader::get(std::move(buffer.get()))) { // Success. ++NumBinaryCacheHits; - Readers[HeaderDir] = Reader.release(); - return false; + return reader; } } } // The cache entry was somehow broken; delete this one so we can build a // new one below. - llvm::sys::fs::remove(CompiledFileName.str()); + llvm::sys::fs::remove(compiledFileName.str()); ++NumBinaryCacheRebuilds; } else { ++NumBinaryCacheMisses; } // Open the source file. - auto Buffer = FileMgr.getBufferForFile(APINotesFile); - if (!Buffer) { - Readers[HeaderDir] = nullptr; - return true; - } + auto buffer = fileMgr.getBufferForFile(apiNotesFile); + if (!buffer) return nullptr; // Compile the API notes source into a buffer. // FIXME: Either propagate OSType through or, better yet, improve the binary // APINotes format to maintain complete availability information. - llvm::SmallVector<char, 1024> APINotesBuffer; + llvm::SmallVector<char, 1024> apiNotesBuffer; { SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(), diag::err_apinotes_message, diag::warn_apinotes_message, diag::note_apinotes_message, - APINotesFile); - llvm::raw_svector_ostream OS(APINotesBuffer); - if (api_notes::compileAPINotes(Buffer.get()->getBuffer(), + apiNotesFile); + llvm::raw_svector_ostream OS(apiNotesBuffer); + if (api_notes::compileAPINotes(buffer.get()->getBuffer(), OS, api_notes::OSType::Absent, srcMgrAdapter.getDiagHandler(), - srcMgrAdapter.getDiagContext())) { - Readers[HeaderDir] = nullptr; - return true; - } + srcMgrAdapter.getDiagContext())) + return nullptr; // Make a copy of the compiled form into the buffer. - Buffer = llvm::MemoryBuffer::getMemBufferCopy( - StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + buffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(apiNotesBuffer.data(), apiNotesBuffer.size())); } // Save the binary form into the cache. Perform this operation // atomically. - SmallString<64> TemporaryBinaryFileName = CompiledFileName.str(); - TemporaryBinaryFileName.erase( - TemporaryBinaryFileName.end() - - llvm::sys::path::extension(TemporaryBinaryFileName).size(), - TemporaryBinaryFileName.end()); - TemporaryBinaryFileName += "-%%%%%%."; - TemporaryBinaryFileName += BINARY_APINOTES_EXTENSION; - - int TemporaryFD; + SmallString<64> temporaryBinaryFileName = compiledFileName.str(); + temporaryBinaryFileName.erase( + temporaryBinaryFileName.end() + - llvm::sys::path::extension(temporaryBinaryFileName).size(), + temporaryBinaryFileName.end()); + temporaryBinaryFileName += "-%%%%%%."; + temporaryBinaryFileName += BINARY_APINOTES_EXTENSION; + + int temporaryFD; llvm::sys::fs::create_directories( - FileMgr.getFileSystemOpts().APINotesCachePath); - if (!llvm::sys::fs::createUniqueFile(TemporaryBinaryFileName.str(), - TemporaryFD, TemporaryBinaryFileName)) { + fileMgr.getFileSystemOpts().APINotesCachePath); + if (!llvm::sys::fs::createUniqueFile(temporaryBinaryFileName.str(), + temporaryFD, temporaryBinaryFileName)) { // Write the contents of the buffer. bool hadError; { - llvm::raw_fd_ostream Out(TemporaryFD, /*shouldClose=*/true); - Out.write(Buffer.get()->getBufferStart(), Buffer.get()->getBufferSize()); - Out.flush(); + llvm::raw_fd_ostream out(temporaryFD, /*shouldClose=*/true); + out.write(buffer.get()->getBufferStart(), buffer.get()->getBufferSize()); + out.flush(); - hadError = Out.has_error(); + hadError = out.has_error(); } if (!hadError) { // Rename the temporary file to the actual compiled file. - llvm::sys::fs::rename(TemporaryBinaryFileName.str(), - CompiledFileName.str()); + llvm::sys::fs::rename(temporaryBinaryFileName.str(), + compiledFileName.str()); } } // Load the binary form we just compiled. - auto Reader = APINotesReader::get(std::move(*Buffer)); - assert(Reader && "Could not load the API notes we just generated?"); + auto reader = APINotesReader::get(std::move(*buffer)); + assert(reader && "Could not load the API notes we just generated?"); + return reader; +} - // Record the reader. - Readers[HeaderDir] = Reader.release(); - return false; +bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, + const FileEntry *APINotesFile) { + assert(Readers.find(HeaderDir) == Readers.end()); + if (auto reader = loadAPINotes(APINotesFile)) { + Readers[HeaderDir] = reader.release(); + return false; + } + + Readers[HeaderDir] = nullptr; + return true; } const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( @@ -332,7 +325,56 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( return HeaderDir; } +const FileEntry *APINotesManager::loadCurrentModuleAPINotes( + StringRef moduleName, + ArrayRef<std::string> searchPaths) { + assert(!CurrentModuleReader && + "Already loaded API notes for the current module?"); + + FileManager &fileMgr = SourceMgr.getFileManager(); + + // Look for API notes for this module in the module search paths. + for (const auto &searchPath : searchPaths) { + // First, look for a binary API notes file. + llvm::SmallString<128> apiNotesFilePath; + apiNotesFilePath += searchPath; + llvm::sys::path::append( + apiNotesFilePath, + llvm::Twine(moduleName) + "." + BINARY_APINOTES_EXTENSION); + + // Try to open the binary API Notes file. + if (const FileEntry *binaryAPINotesFile + = fileMgr.getFile(apiNotesFilePath)) { + CurrentModuleReader = loadAPINotes(binaryAPINotesFile); + return CurrentModuleReader ? binaryAPINotesFile : nullptr; + } + + // Try to open the source API Notes file. + apiNotesFilePath = searchPath; + llvm::sys::path::append( + apiNotesFilePath, + llvm::Twine(moduleName) + "." + SOURCE_APINOTES_EXTENSION); + if (const FileEntry *sourceAPINotesFile + = fileMgr.getFile(apiNotesFilePath)) { + CurrentModuleReader = loadAPINotes(sourceAPINotesFile); + return CurrentModuleReader ? sourceAPINotesFile : nullptr; + } + } + + // Didn't find any API notes. + return nullptr; +} + APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { + // If there is a reader for the current module, return it. + if (CurrentModuleReader) return CurrentModuleReader.get(); + + // If we're not allowed to implicitly load API notes files, we're done. + if (!ImplicitAPINotes) return nullptr; + + // If we don't have source location information, we're done. + if (Loc.isInvalid()) return nullptr; + // API notes are associated with the expansion location. Retrieve the // file for this location. SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc); diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index eca5b03a7d562..50e3d7cd08025 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -4855,8 +4855,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fno-assume-sane-operator-new"); if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, - false)) { - CmdArgs.push_back("-fapinotes"); + false) || + Args.hasArg(options::OPT_iapinotes_modules)) { + if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false)) + CmdArgs.push_back("-fapinotes"); SmallString<128> APINotesCachePath; if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) { diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 7cb86cfa9f4b6..cd52cd408cdd1 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -531,6 +531,13 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, CodeCompleteConsumer *CompletionConsumer) { TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); + + // If we're building a module, notify the API notes manager. + if (!getLangOpts().CurrentModule.empty()) { + (void)TheSema->APINotes.loadCurrentModuleAPINotes( + getLangOpts().CurrentModule, + getAPINotesOpts().ModuleSearchPaths); + } } // Output Files diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index b0fe4c7a092f8..3d72900902cd6 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1332,6 +1332,12 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.AddVFSOverlayFile(A->getValue()); } +static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args) { + using namespace options; + for (const Arg *A : Args.filtered(OPT_iapinotes_modules)) + Opts.ModuleSearchPaths.push_back(A->getValue()); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, LangStandard::Kind LangStd) { // Set some properties which depend solely on the input kind; it would be nice @@ -2117,6 +2123,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, Res.getTargetOpts()); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args); + ParseAPINotesArgs(Res.getAPINotesOpts(), Args); + if (DashX == IK_AST || DashX == IK_LLVM_IR) { // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the // PassManager in BackendUtil.cpp. They need to be initializd no matter diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 34261f2556cf7..5b93d73c847b5 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -77,7 +77,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), - APINotes(SourceMgr), CollectStats(false), CodeCompleter(CodeCompleter), + APINotes(SourceMgr, LangOpts), CollectStats(false), + CodeCompleter(CodeCompleter), CurContext(nullptr), OriginalLexicalContext(nullptr), PackContext(nullptr), MSStructPragmaOn(false), MSPointerToMemberRepresentationMethod( diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 3114805da208b..7817754c9f298 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -208,10 +208,7 @@ static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { - if (!Context.getLangOpts().APINotes) - return; - - if (!D || D->getLocation().isInvalid()) + if (!D) return; // Globals. diff --git a/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes new file mode 100644 index 0000000000000..a4ddafe2892ec --- /dev/null +++ b/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -0,0 +1,17 @@ +Name: HeaderLib +Functions: + - Name: custom_realloc + NullabilityOfRet: N + Nullability: [ N, S ] + - Name: unavailable_function + Availability: none + AvailabilityMsg: "I beg you not to use this" + - Name: do_something_with_pointers + NullabilityOfRet: O + Nullability: [ N, O ] + +Globals: + - Name: global_int + Nullability: N + - Name: unavailable_global_int + Availability: none diff --git a/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes new file mode 100644 index 0000000000000..e700412012688 --- /dev/null +++ b/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes @@ -0,0 +1,34 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "transform:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + - Selector: "privateTransform:input:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: intValue + Availability: none + AvailabilityMsg: "wouldn't work anyway" + - Name: internalProperty + Nullability: N + - Name: B + Availability: none + AvailabilityMsg: "just don't" + - Name: C + Methods: + - Selector: "initWithA:" + MethodKind: Instance + DesignatedInit: true +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..3abee2df0be1b --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module SomeKit { + umbrella header "SomeKit.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap new file mode 100644 index 0000000000000..bbda9d08e3993 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap @@ -0,0 +1,8 @@ +module SomeKit.Private { + header "SomeKit_Private.h" + export * + + explicit module NullAnnotation { + header "SomeKit_PrivateForNullAnnotation.h" + } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap new file mode 100644 index 0000000000000..e31034317cb82 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap @@ -0,0 +1,8 @@ +explicit framework module SomeKit.Private { + header "SomeKit_Private.h" + explicit NullAnnotation { header "SomeKit_PrivateForNullAnnotation.h" } + export * + module * { export * } +syntax error + +} diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap new file mode 100644 index 0000000000000..3e59efcf2c482 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -0,0 +1,3 @@ +module HeaderLib { + header "HeaderLib.h" +} diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m index 177700d63111d..5b996ec8c9497 100644 --- a/clang/test/APINotes/availability.m +++ b/clang/test/APINotes/availability.m @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c index 940587e8e0a12..54b0df6cea94c 100644 --- a/clang/test/APINotes/nullability.c +++ b/clang/test/APINotes/nullability.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index 40901f2c4fd76..ba51cd64d825f 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/objc_designated_inits.m b/clang/test/APINotes/objc_designated_inits.m index 194d135b6c0da..5eb5fa103d5d5 100644 --- a/clang/test/APINotes/objc_designated_inits.m +++ b/clang/test/APINotes/objc_designated_inits.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> From 68da16cdc07ee31751b5cae9fba3818766844d21 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 18 Feb 2016 16:36:27 -0800 Subject: [PATCH 022/582] Map FactoryAsInit: C to a hidden attribute. The FactoryAsInit entry in API notes wasn't getting mapped to any Clang attributes. Since there is no use for such an attribute purely in Objective-C, map it to a new unspellable attribute (SwiftSuppressFactoryAsInitAttr) used only to appropriately annotate the declaration for consumption by the Swift Clang importer. Part of rdar://problem/24447420. apple-llvm-split-commit: 726aaee07233449a0ade9c09708eb3141d3af865 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 8 ++++++++ clang/lib/Sema/SemaAPINotes.cpp | 6 ++++++ clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes | 6 ++++++ .../Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h | 4 ++++ 4 files changed, 24 insertions(+) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 50fe90c2fc2e4..b4906b8c941c3 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1334,6 +1334,14 @@ def SwiftPrivate : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftSuppressFactoryAsInit : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + def ReqdWorkGroupSize : InheritableAttr { let Spellings = [GNU<"reqd_work_group_size">]; let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">, diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 7817754c9f298..8ca364eec198d 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -192,6 +192,12 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, } } + if (Info.getFactoryAsInitKind() + == api_notes::FactoryAsInitKind::AsClassMethod && + !D->getAttr<SwiftNameAttr>()) { + D->addAttr(SwiftSuppressFactoryAsInitAttr::CreateImplicit(S.Context)); + } + // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), static_cast<const api_notes::FunctionInfo &>(Info)); diff --git a/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes index e700412012688..d251491ed43db 100644 --- a/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes @@ -28,6 +28,12 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true + - Name: ProcessInfo + Methods: + - Selector: "processInfo" + MethodKind: Class + FactoryAsInit: C + Protocols: - Name: InternalProtocol Availability: none diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h index 01b003d1eeef0..5e0d7e0b7ab26 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -20,4 +20,8 @@ __attribute__((objc_root_class)) - (instancetype)initWithA:(A*)a; @end +@interface ProcessInfo : A ++(instancetype)processInfo; +@end + #endif From 38ca04ce3ef9a72e0add8a693c15a86ffdf77429 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 18 Feb 2016 16:39:30 -0800 Subject: [PATCH 023/582] Add "nonswift" availability mapping to Swift unavailability. The new "nonswift" availability maps to __attribute__((swift,unavailable)). Part of rdar://problem/24447420. apple-llvm-split-commit: c28028f35f1d0a2c573238d62ed3e969cc4aab05 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 16 ++++++++++++++-- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 4 +++- clang/lib/APINotes/APINotesWriter.cpp | 2 +- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 14 +++++++++++--- clang/lib/Sema/SemaAPINotes.cpp | 10 ++++++++++ 6 files changed, 40 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 8173bf4f3a25b..3cf149357bb4a 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -67,12 +67,16 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable. unsigned Unavailable : 1; - CommonEntityInfo() : Unavailable(0) { } + /// Whether this entity is marked unavailable in Swift. + unsigned UnavailableInSwift : 1; + + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && - lhs.Unavailable == rhs.Unavailable; + lhs.Unavailable == rhs.Unavailable && + lhs.UnavailableInSwift == rhs.UnavailableInSwift; } friend bool operator!=(const CommonEntityInfo &lhs, @@ -91,6 +95,14 @@ class CommonEntityInfo { } } + if (rhs.UnavailableInSwift) { + lhs.UnavailableInSwift = true; + if (rhs.UnavailableMsg.length() != 0 && + lhs.UnavailableMsg.length() == 0) { + lhs.UnavailableMsg = rhs.UnavailableMsg; + } + } + return lhs; } diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 5462e5a558625..0801d2ceefd97 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 6; +const uint16_t VERSION_MINOR = 7; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index a84f2d70491fe..c768f3a3df7f7 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -30,7 +30,9 @@ using namespace llvm; namespace { /// Read serialized CommonEntityInfo. void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) { - info.Unavailable = *data++; + uint8_t unavailableBits = *data++; + info.Unavailable = (unavailableBits >> 1) & 0x01; + info.UnavailableInSwift = unavailableBits & 0x01; unsigned msgLength = endian::readNext<uint16_t, little, unaligned>(data); info.UnavailableMsg diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 400bd08e01393..0e498a797a3ea 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -272,7 +272,7 @@ namespace { static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer<little> writer(out); - writer.write<uint8_t>(info.Unavailable); + writer.write<uint8_t>(info.Unavailable << 1 | info.UnavailableInSwift); writer.write<uint16_t>(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 1f299873ce31c..8c27a01e5edde 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -44,7 +44,7 @@ Availability: OSX # Optional: Specifies which platform the API is # available on. [OSX / iOS / none/ - # available] + # available / nonswift] AvailabilityMsg: "" # Optional: Custom availability message to display to # the user, when API is not available. @@ -143,6 +143,7 @@ namespace { OSX, IOS, None, + NonSwift, }; enum class MethodKind { @@ -264,6 +265,7 @@ namespace llvm { io.enumCase(value, "OSX", APIAvailability::OSX); io.enumCase(value, "iOS", APIAvailability::IOS); io.enumCase(value, "none", APIAvailability::None); + io.enumCase(value, "nonswift", APIAvailability::NonSwift); io.enumCase(value, "available", APIAvailability::Available); } }; @@ -410,9 +412,10 @@ static bool compile(const Module &module, bool convertAvailability(const AvailabilityItem &in, CommonEntityInfo &outInfo, llvm::StringRef apiName) { - // Populate the 'Unavailable' information. + // Populate the unavailability information. outInfo.Unavailable = (in.Mode == APIAvailability::None); - if (outInfo.Unavailable) { + outInfo.UnavailableInSwift = (in.Mode == APIAvailability::NonSwift); + if (outInfo.Unavailable || outInfo.UnavailableInSwift) { outInfo.UnavailableMsg = in.Msg; } else { if (!in.Msg.empty()) { @@ -716,6 +719,11 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, availability.Mode = APIAvailability::None; availability.Msg = copyString(info.UnavailableMsg); } + + if (info.UnavailableInSwift) { + availability.Mode = APIAvailability::NonSwift; + availability.Msg = copyString(info.UnavailableMsg); + } } /// Map nullability information for a function. diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 8ca364eec198d..6f08fd4c923dd 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -99,6 +99,16 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (Info.Unavailable && !D->hasAttr<UnavailableAttr>()) { D->addAttr(UnavailableAttr::CreateImplicit(S.Context, Info.UnavailableMsg)); } + + if (Info.UnavailableInSwift) { + D->addAttr(AvailabilityAttr::CreateImplicit(S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), + VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + Info.UnavailableMsg)); + } } /// Process API notes for a variable or property. From 2400d6de15e12c3dc1760d8f3a89a0e9640e5051 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sat, 20 Feb 2016 09:05:43 -0800 Subject: [PATCH 024/582] [SemaAPINotes] Fix compiler error after change of r261163. apple-llvm-split-commit: 1ffcce8fb7b5a90aec255fd1908933c80d5925ad apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 6f08fd4c923dd..2f4356b71b702 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -107,7 +107,8 @@ static void ProcessAPINotes(Sema &S, Decl *D, VersionTuple(), VersionTuple(), /*Unavailable=*/true, - Info.UnavailableMsg)); + Info.UnavailableMsg, + /*Nopartial=*/true)); } } From 91b20c157854dbbf05c77c36614ce8631d44dd33 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Sat, 20 Feb 2016 21:35:48 -0800 Subject: [PATCH 025/582] [API Notes] Add support for SwiftName on anything, which maps to the swift_name attribute. This allows API notes to be used to override the Swift names of Objective-C entities. apple-llvm-split-commit: 89282506936a78eb364ad8239954a666e3dd0d5f apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 12 +++++-- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 7 ++++ clang/lib/APINotes/APINotesWriter.cpp | 4 ++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 21 +++++++++++ clang/lib/Sema/SemaAPINotes.cpp | 35 ++++++++++++++----- clang/test/APINotes/Inputs/roundtrip.apinotes | 13 +++++++ 7 files changed, 81 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 3cf149357bb4a..79f34ccc21898 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -58,7 +58,7 @@ class ContextID { /// Describes API notes data for any entity. /// -/// This is used as the base of +/// This is used as the base of all API notes. class CommonEntityInfo { public: /// Message to use when this entity is unavailable. @@ -70,13 +70,17 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; + /// Swift name of this entity. + std::string SwiftName; + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && - lhs.UnavailableInSwift == rhs.UnavailableInSwift; + lhs.UnavailableInSwift == rhs.UnavailableInSwift && + lhs.SwiftName == rhs.SwiftName; } friend bool operator!=(const CommonEntityInfo &lhs, @@ -103,6 +107,10 @@ class CommonEntityInfo { } } + if (rhs.SwiftName.length() != 0 && + lhs.SwiftName.length() == 0) + lhs.SwiftName = rhs.SwiftName; + return lhs; } diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 0801d2ceefd97..79241042951e2 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 7; +const uint16_t VERSION_MINOR = 8; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index c768f3a3df7f7..25831287c022c 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -39,6 +39,13 @@ namespace { = std::string(reinterpret_cast<const char *>(data), reinterpret_cast<const char *>(data) + msgLength); data += msgLength; + + unsigned swiftNameLength + = endian::readNext<uint16_t, little, unaligned>(data); + info.SwiftName + = std::string(reinterpret_cast<const char *>(data), + reinterpret_cast<const char *>(data) + swiftNameLength); + data += swiftNameLength; } /// Used to deserialize the on-disk identifier table. diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 0e498a797a3ea..a9d83d95fd9fc 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -265,7 +265,7 @@ namespace { /// Retrieve the serialized size of the given CommonEntityInfo, for use in /// on-disk hash tables. static unsigned getCommonEntityInfoSize(const CommonEntityInfo &info) { - return 3 + info.UnavailableMsg.size(); + return 5 + info.UnavailableMsg.size() + info.SwiftName.size(); } /// Emit a serialized representation of the common entity information. @@ -275,6 +275,8 @@ namespace { writer.write<uint8_t>(info.Unavailable << 1 | info.UnavailableInSwift); writer.write<uint16_t>(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); + writer.write<uint16_t>(info.SwiftName.size()); + out.write(info.SwiftName.c_str(), info.SwiftName.size()); } /// Used to serialize the on-disk Objective-C context table. diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 8c27a01e5edde..8896c40cda0a5 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -168,6 +168,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; + StringRef SwiftName; api_notes::FactoryAsInitKind FactoryAsInit = api_notes::FactoryAsInitKind::Infer; bool DesignatedInit = false; @@ -179,6 +180,7 @@ namespace { StringRef Name; llvm::Optional<NullabilityKind> Nullability; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector<Property> PropertiesSeq; @@ -186,6 +188,7 @@ namespace { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; + StringRef SwiftName; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -196,6 +199,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector<Function> FunctionsSeq; @@ -203,6 +207,7 @@ namespace { StringRef Name; llvm::Optional<NullabilityKind> Nullability; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector<GlobalVariable> GlobalVariablesSeq; @@ -278,6 +283,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); io.mapOptional("AvailabilityMsg", p.Availability.Msg); + io.mapOptional("SwiftName", p.SwiftName); } }; @@ -291,6 +297,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, api_notes::FactoryAsInitKind::Infer); io.mapOptional("DesignatedInit", m.DesignatedInit, false); @@ -305,6 +312,7 @@ namespace llvm { io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); + io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -319,6 +327,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", f.Availability.Mode); io.mapOptional("AvailabilityMsg", f.Availability.Msg); + io.mapOptional("SwiftName", f.SwiftName); } }; @@ -330,6 +339,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftName", v.SwiftName); } }; @@ -463,6 +473,7 @@ static bool compile(const Module &module, return; convertAvailability(meth.Availability, mInfo, meth.Selector); + mInfo.SwiftName = meth.SwiftName; // Check if the selector ends with ':' to determine if it takes arguments. bool takesArguments = meth.Selector.endswith(":"); @@ -505,6 +516,7 @@ static bool compile(const Module &module, return; convertAvailability(cl.Availability, cInfo, cl.Name); + cInfo.SwiftName = cl.SwiftName; if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); @@ -545,6 +557,7 @@ static bool compile(const Module &module, if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); + pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); Writer->addObjCProperty(clID, prop.Name, pInfo); @@ -598,6 +611,7 @@ static bool compile(const Module &module, if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); + info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); Writer->addGlobalVariable(global.Name, info); @@ -617,6 +631,7 @@ static bool compile(const Module &module, if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); + info.SwiftName = function.SwiftName; convertNullability(function.Nullability, function.NullabilityOfRet, info, function.Name); @@ -707,6 +722,8 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, // Handle class information. handleAvailability(record.Availability, info); + record.SwiftName = copyString(info.SwiftName); + if (info.getDefaultNullability()) { record.AuditedForNullability = true; } @@ -771,6 +788,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); handleAvailability(method.Availability, info); + method.SwiftName = copyString(info.SwiftName); method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; @@ -787,6 +805,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, Property property; property.Name = name; handleAvailability(property.Availability, info); + property.SwiftName = copyString(info.SwiftName); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -805,6 +824,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, Function function; function.Name = name; handleAvailability(function.Availability, info); + function.SwiftName = copyString(info.SwiftName); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); @@ -817,6 +837,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, GlobalVariable global; global.Name = name; handleAvailability(global.Availability, info); + global.SwiftName = copyString(info.SwiftName); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 2f4356b71b702..649e8c455975b 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -93,22 +93,39 @@ static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability) { } } +/// Copy a string into ASTContext-allocated memory. +static StringRef CopyString(ASTContext &ctx, StringRef string) { + void *mem = ctx.Allocate(string.size(), alignof(char)); + memcpy(mem, string.data(), string.size()); + return StringRef(static_cast<char *>(mem), string.size()); +} + static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonEntityInfo &Info) { // Availability if (Info.Unavailable && !D->hasAttr<UnavailableAttr>()) { - D->addAttr(UnavailableAttr::CreateImplicit(S.Context, Info.UnavailableMsg)); + D->addAttr(UnavailableAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.UnavailableMsg))); } if (Info.UnavailableInSwift) { - D->addAttr(AvailabilityAttr::CreateImplicit(S.Context, - &S.Context.Idents.get("swift"), - VersionTuple(), - VersionTuple(), - VersionTuple(), - /*Unavailable=*/true, - Info.UnavailableMsg, - /*Nopartial=*/true)); + D->addAttr(AvailabilityAttr::CreateImplicit( + S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), + VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + CopyString(S.Context, Info.UnavailableMsg), + /*Nopartial=*/true)); + } + + // swift_name + if (!Info.SwiftName.empty() && !D->hasAttr<SwiftNameAttr>()) { + D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.SwiftName))); } } diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index b1722406663eb..a1b60e5b19f1f 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -6,12 +6,14 @@ Classes: - Name: NSCell Availability: available AvailabilityMsg: '' + SwiftName: '' Methods: - Selector: init MethodKind: Instance NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initImageCell:' MethodKind: Instance @@ -19,6 +21,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initTextCell:' MethodKind: Instance @@ -26,6 +29,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initWithCoder:' MethodKind: Instance @@ -33,12 +37,14 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true Required: true - Name: NSView AuditedForNullability: true Availability: available AvailabilityMsg: '' + SwiftName: '' Methods: - Selector: 'addSubview:' MethodKind: Instance @@ -46,34 +52,41 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance Nullability: [ N, N, O ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance Nullability: [ U, U, N ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView Nullability: O Availability: available AvailabilityMsg: '' + SwiftName: enclosing - Name: makeBackingLayer Nullability: N Availability: available AvailabilityMsg: '' + SwiftName: '' Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: 'availableWindowDepths()' Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N Availability: available AvailabilityMsg: '' + SwiftName: calibratedWhite From d2348adb2ff85d88b151c83dbcc7d160ff80e80b Mon Sep 17 00:00:00 2001 From: Tim Northover <t.p.northover@gmail.com> Date: Mon, 22 Feb 2016 14:17:39 -0800 Subject: [PATCH 026/582] ARM test fixup: allow lr to be used. apple-llvm-split-commit: ad7215d2e218b598f668ebaff2a22d8b8744dd89 apple-llvm-split-dir: llvm/ --- llvm/test/CodeGen/ARM/swifterror.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/CodeGen/ARM/swifterror.ll b/llvm/test/CodeGen/ARM/swifterror.ll index 99a531cd706c8..e48bf82e5731a 100644 --- a/llvm/test/CodeGen/ARM/swifterror.ll +++ b/llvm/test/CodeGen/ARM/swifterror.ll @@ -140,7 +140,7 @@ define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) { ; CHECK-O0: mov r0, #16 ; CHECK-O0: malloc ; CHECK-O0: mov [[ID:r[0-9]+]], r0 -; CHECK-O0: mov [[ID2:r[0-9]+]], #1 +; CHECK-O0: mov [[ID2:[a-z0-9]+]], #1 ; CHECK-O0: strb [[ID2]], [r0, #8] ; CHECK-O0: mov r6, [[ID]] ; reload from stack From 4a4c6955aa8449f00a52d6b740e4523ec92827fa Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Mon, 22 Feb 2016 19:38:28 -0800 Subject: [PATCH 027/582] Replaces uses of Fixnum with llvm::PointerEmbeddedInt. The two types are nearly identical, and Fixnum is only in the Swift branches of LLVM, not in mainline LLVM. The main change here is that PointerEmbeddedInt's representation is always a pointer, which means sizeof and anything depending on it give you the size of a pointer rather than the smallest primitive integral type that can hold the number. But for serialization purposes being explicit is probably a good thing, although it does mean more coordination across files. apple-llvm-split-commit: 6ce47441485dc39046bcca06b6692e80d8c863b7 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesFormat.h | 9 ++++--- clang/lib/APINotes/APINotesReader.cpp | 22 ++++++++-------- clang/lib/APINotes/APINotesWriter.cpp | 37 +++++++++++++-------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 79241042951e2..b49064bbf11e8 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -1,4 +1,4 @@ -//===--- APINotesFormat.h - The internals of API notes files ------*- C++ -*-===// +//===--- APINotesFormat.h - The internals of API notes files ----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -17,6 +17,7 @@ #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/RecordLayout.h" @@ -37,13 +38,13 @@ const uint16_t VERSION_MAJOR = 0; /// When the format changes IN ANY WAY, this number should be incremented. const uint16_t VERSION_MINOR = 8; -using IdentifierID = Fixnum<31>; +using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; -using SelectorID = Fixnum<31>; +using SelectorID = PointerEmbeddedInt<unsigned, 31>; using SelectorIDField = BCVBR<16>; -using StoredContextID = Fixnum<31>; +using StoredContextID = PointerEmbeddedInt<unsigned, 31>; /// The various types of blocks that can occur within a API notes file. /// diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 25831287c022c..8739f03b953e6 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -125,7 +125,7 @@ namespace { static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID - = endian::readNext<IdentifierID, little, unaligned>(data); + = endian::readNext<uint32_t, little, unaligned>(data); auto isProtocol = endian::readNext<uint8_t, little, unaligned>(data); return { nameID, isProtocol }; } @@ -133,7 +133,7 @@ namespace { static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; - result.first = endian::readNext<StoredContextID, little, unaligned>(data); + result.first = endian::readNext<uint32_t, little, unaligned>(data); readCommonEntityInfo(data, result.second); if (*data++) { result.second.setDefaultNullability(static_cast<NullabilityKind>(*data)); @@ -187,8 +187,8 @@ namespace { } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { - auto classID = endian::readNext<IdentifierID, little, unaligned>(data); - auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + auto classID = endian::readNext<uint32_t, little, unaligned>(data); + auto nameID = endian::readNext<uint32_t, little, unaligned>(data); return { classID, nameID }; } @@ -247,8 +247,8 @@ namespace { } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { - auto classID = endian::readNext<IdentifierID, little, unaligned>(data); - auto selectorID = endian::readNext<SelectorID, little, unaligned>(data); + auto classID = endian::readNext<uint32_t, little, unaligned>(data); + auto selectorID = endian::readNext<uint32_t, little, unaligned>(data); auto isInstance = endian::readNext<uint8_t, little, unaligned>(data); return internal_key_type{ classID, selectorID, isInstance }; } @@ -299,17 +299,17 @@ namespace { static internal_key_type ReadKey(const uint8_t *data, unsigned length) { internal_key_type key; key.NumPieces = endian::readNext<uint16_t, little, unaligned>(data); - unsigned numIdents = (length - sizeof(uint16_t)) / sizeof(IdentifierID); + unsigned numIdents = (length - sizeof(uint16_t)) / sizeof(uint32_t); for (unsigned i = 0; i != numIdents; ++i) { key.Identifiers.push_back( - endian::readNext<IdentifierID, little, unaligned>(data)); + endian::readNext<uint32_t, little, unaligned>(data)); } return key; } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { - return endian::readNext<SelectorID, little, unaligned>(data); + return endian::readNext<uint32_t, little, unaligned>(data); } }; @@ -346,7 +346,7 @@ namespace { } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { - auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + auto nameID = endian::readNext<uint32_t, little, unaligned>(data); return nameID; } @@ -391,7 +391,7 @@ namespace { } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { - auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + auto nameID = endian::readNext<uint32_t, little, unaligned>(data); return nameID; } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index a9d83d95fd9fc..430c2a2ad97e1 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -230,7 +230,6 @@ namespace { void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { - static_assert(sizeof(IdentifierID) <= 4, "DeclID too large"); endian::Writer<little> writer(out); writer.write<uint32_t>(data); } @@ -299,8 +298,8 @@ namespace { std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID) + 1; - uint32_t dataLength = sizeof(ContextID) + uint32_t keyLength = sizeof(uint32_t) + 1; + uint32_t dataLength = sizeof(uint32_t) + getCommonEntityInfoSize(data.second) + dataBytes; endian::Writer<little> writer(out); @@ -311,14 +310,14 @@ namespace { void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); - writer.write<IdentifierID>(key.first); + writer.write<uint32_t>(key.first); writer.write<uint8_t>(key.second); } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer<little> writer(out); - writer.write<StoredContextID >(data.first); + writer.write<uint32_t>(data.first); emitCommonEntityInfo(out, data.second); @@ -400,7 +399,7 @@ namespace { std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID) + sizeof(IdentifierID); + uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t); uint32_t dataLength = getVariableInfoSize(data); endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); @@ -410,8 +409,8 @@ namespace { void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); - writer.write<IdentifierID>(key.first); - writer.write<IdentifierID>(key.second); + writer.write<uint32_t>(key.first); + writer.write<uint32_t>(key.second); } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, @@ -482,7 +481,7 @@ namespace { std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID) + sizeof(SelectorID) + 1; + uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + 1; uint32_t dataLength = getFunctionInfoSize(data) + 3; endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); @@ -492,8 +491,8 @@ namespace { void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); - writer.write<IdentifierID>(std::get<0>(key)); - writer.write<SelectorID>(std::get<1>(key)); + writer.write<uint32_t>(std::get<0>(key)); + writer.write<uint32_t>(std::get<1>(key)); writer.write<uint8_t>(std::get<2>(key)); } @@ -555,8 +554,8 @@ namespace { key_type_ref key, data_type_ref data) { uint32_t keyLength = sizeof(uint16_t) - + sizeof(IdentifierID) * key.Identifiers.size(); - uint32_t dataLength = sizeof(SelectorID); + + sizeof(uint32_t) * key.Identifiers.size(); + uint32_t dataLength = sizeof(uint32_t); endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); @@ -567,14 +566,14 @@ namespace { endian::Writer<little> writer(out); writer.write<uint16_t>(key.NumPieces); for (auto piece : key.Identifiers) { - writer.write<IdentifierID>(piece); + writer.write<uint32_t>(piece); } } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer<little> writer(out); - writer.write<SelectorID>(data); + writer.write<uint32_t>(data); } }; } // end anonymous namespace @@ -621,7 +620,7 @@ namespace { std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); + uint32_t keyLength = sizeof(uint32_t); uint32_t dataLength = getVariableInfoSize(data); endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); @@ -631,7 +630,7 @@ namespace { void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); - writer.write<IdentifierID>(key); + writer.write<uint32_t>(key); } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, @@ -683,7 +682,7 @@ namespace { std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); + uint32_t keyLength = sizeof(uint32_t); uint32_t dataLength = getFunctionInfoSize(data); endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); @@ -693,7 +692,7 @@ namespace { void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); - writer.write<IdentifierID>(key); + writer.write<uint32_t>(key); } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, From c3322f80746568134fd54fca83ddda52027dbd1c Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Mon, 22 Feb 2016 19:49:14 -0800 Subject: [PATCH 028/582] Remove llvm::Fixnum. Use llvm::PointerEmbeddedInt from now on. apple-llvm-split-commit: 9a68e4a28a9da0fe9a098147ea139eb8a0e0bc66 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/Fixnum.h | 190 ----------------------- llvm/include/llvm/Bitcode/RecordLayout.h | 10 +- 2 files changed, 1 insertion(+), 199 deletions(-) delete mode 100644 llvm/include/llvm/ADT/Fixnum.h diff --git a/llvm/include/llvm/ADT/Fixnum.h b/llvm/include/llvm/ADT/Fixnum.h deleted file mode 100644 index 033cebe6f7574..0000000000000 --- a/llvm/include/llvm/ADT/Fixnum.h +++ /dev/null @@ -1,190 +0,0 @@ -//===- Fixnum.h - An integer type with an explicit bit width ----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -/// \file Declares Fixnum, an integer type with an explicit bit width, -/// and utilities for working with bit widths of integers. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_ADT_FIXNUM_H -#define LLVM_ADT_FIXNUM_H - -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/PointerLikeTypeTraits.h" -#include <cassert> -#include <limits> - -namespace llvm { - -/// Defines a member #type that is the smallest signed integer that can hold -/// a value with the given bit width. -template <unsigned Bits> -struct int_least { - static_assert(Bits <= 64, "too many bits"); - using type = typename std::conditional<(Bits <= 8), int_least8_t, - typename std::conditional<(Bits <= 16), int_least16_t, - typename std::conditional<(Bits <= 32), int_least32_t, - int_least64_t>::type>::type>::type; -}; - -/// Defines a member #type that is the smallest unsigned integer that can hold -/// a value with the given bit width. -template <unsigned Bits> -struct uint_least { - using type = - typename std::make_unsigned<typename int_least<Bits>::type>::type; -}; - -/// A wrapper for an integer type that is guaranteed to only use a certain -/// number of bits. -/// -/// This can be used to treat an integer like a pointer with low bits free. -/// -/// Note that if the integer type is signed, \p Bits must include the sign -/// bit, just like a bitfield. -template <unsigned Bits, typename IntType = typename uint_least<Bits>::type> -class Fixnum { - static_assert(Bits <= (std::numeric_limits<IntType>::digits + - std::numeric_limits<IntType>::is_signed), - "too many bits for integer type"); - - IntType Value; - - void assertValid() const { - assert((std::is_signed<IntType>::value ? llvm::isInt<Bits>(Value) - : llvm::isUInt<Bits>(Value)) && - "value exceeds limited bit width"); - } - -public: - using value_type = IntType; - - Fixnum() : Value(0) {} - - /*implicit*/ Fixnum(IntType val) : Value(val) { - assertValid(); - } - - /// Initialize a Fixnum from another, smaller Fixnum. - /// - /// This is always safe and thus permitted as an implicit coercion. - template <unsigned OtherBits, typename OtherIntType> - /*implicit*/ Fixnum( - const typename std::enable_if<(OtherBits < Bits), - Fixnum<OtherBits, - OtherIntType>>::type &other) - : Value(static_cast<IntType>(other)) {} - - /// Initialize a Fixnum from another of a different width. - /// - /// This is permitted, but checked with assertions. It must be explicitly - /// requested -- it is not a valid implicit conversion. - template <unsigned OtherBits, typename OtherIntType> - explicit Fixnum(const Fixnum<OtherBits, OtherIntType> &other) { - operator=(other); - } - - /// Assign to a Fixnum from another of a different width. - /// - /// This is permitted, but checked with assertions. - template <unsigned OtherBits, typename OtherIntType> - Fixnum &operator=(const Fixnum<OtherBits, OtherIntType> &other) { - Value = static_cast<IntType>(other); - assert(static_cast<OtherIntType>(Value) == other && - "cannot represent the same value"); - assert(((Value < 0) == (other < 0)) && "signedness mismatch"); - assertValid(); - return *this; - } - - /*implicit*/ operator IntType() const { - return Value; - } - - Fixnum &operator++() { - assert((Value != std::numeric_limits<IntType>::max()) && - "increment would cause wraparound"); - ++Value; - assertValid(); - return *this; - } - - Fixnum operator++(int) { - assert((Value != std::numeric_limits<IntType>::max()) && - "increment would cause wraparound"); - Fixnum result = *this; - ++Value; - assertValid(); - return result; - } - - Fixnum &operator--() { - assert((Value != std::numeric_limits<IntType>::min()) && - "decrement would cause wraparound"); - --Value; - assertValid(); - return *this; - } - - Fixnum operator--(int) { - assert((Value != std::numeric_limits<IntType>::min()) && - "decrement would cause wraparound"); - Fixnum result = *this; - --Value; - assertValid(); - return result; - } - - bool operator==(const Fixnum &RHS) const { - return Value == RHS.Value; - } - bool operator!=(const Fixnum &RHS) const { - return !operator==(RHS); - } - - bool operator==(int RHS) const { - return Value == IntType(RHS); - } - bool operator!=(int RHS) const { - return !operator==(RHS); - } -}; - -// Fixnum can be treated like a pointer with low bits free if it is no -// larger than a pointer. -template<unsigned IntBits, typename IntType> -class PointerLikeTypeTraits<Fixnum<IntBits, IntType>> { - using IntPointerType = - typename std::conditional<std::is_signed<IntType>::value, - intptr_t, uintptr_t>::type; - -public: - static_assert(sizeof(IntType) <= sizeof(IntPointerType), - "Fixnum is too big to fit in a pointer"); - - static inline void * - getAsVoidPointer(const Fixnum<IntBits, IntType> &I) { - auto opaqueValue = static_cast<IntPointerType>(I) << NumLowBitsAvailable; - return reinterpret_cast<void *>(opaqueValue); - } - - static inline Fixnum<IntBits, IntType> - getFromVoidPointer(const void *P) { - auto opaqueValue = reinterpret_cast<IntPointerType>(P); - return static_cast<IntType>(opaqueValue >> NumLowBitsAvailable); - } - - enum { - NumLowBitsAvailable = std::numeric_limits<uintptr_t>::digits - IntBits - }; -}; - -} // end namespace llvm - -#endif diff --git a/llvm/include/llvm/Bitcode/RecordLayout.h b/llvm/include/llvm/Bitcode/RecordLayout.h index c3cbf13b2239f..ce6c7f8bd869d 100644 --- a/llvm/include/llvm/Bitcode/RecordLayout.h +++ b/llvm/include/llvm/Bitcode/RecordLayout.h @@ -1,4 +1,4 @@ -//===--- BCRecordLayout.h - Convenience wrappers for bitcode ----*- C++ -*-===// +//===--- RecordLayout.h - Convenience wrappers for bitcode ------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -33,7 +33,6 @@ #define LLVM_BITCODE_RECORDLAYOUT_H #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Fixnum.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/BitCodes.h" #include "llvm/Bitcode/BitstreamWriter.h" @@ -104,13 +103,6 @@ class BCFixed : public impl::BCField<> { assert(llvm::isUInt<Width>(data) && "data value does not fit in the given bit width"); } - - using value_type = Fixnum<Width>; - - template<typename T> - static value_type convert(T rawValue) { - return static_cast<value_type>(rawValue); - } }; /// Represents a variable-width value in a bitcode record. From 9d786d7571f50979bc660462271f2badf8bbaf23 Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Sun, 28 Feb 2016 16:41:12 -0800 Subject: [PATCH 029/582] Remove a newline from README.txt so that when llvm.org gets merged in in the future, we will get a merge commit. This is necessary since although currently llvm.org/compiler-rt and github/swift-compiler-rt are the same, they may not be in the future. In such a case, we want to cut compiler-rt stable branches from github, not from llvm.org. To be able to do that in an automated fashion, the swift merge tool needs to be able to reason about whether or not our internal branch had a commit coming from llvm.org/compiler-rt or github/swift-compiler-rt. The easiest way to do that is by ensuring that merging llvm.org/compiler-rt into github/swift-compiler-rt does not fast forward. apple-llvm-split-commit: 5e77a62b8d53a0198f65035806a1ee93da847ba4 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/README.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler-rt/README.txt b/compiler-rt/README.txt index fc8843246e255..2d64f00e3ebe3 100644 --- a/compiler-rt/README.txt +++ b/compiler-rt/README.txt @@ -8,4 +8,3 @@ Compiler-RT is open source software. You may freely distribute it under the terms of the license agreement found in LICENSE.txt. ================================ - From 3d7dd666a38e5649b38a38c204831ec91ffece7b Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 1 Mar 2016 09:05:55 -0800 Subject: [PATCH 030/582] [Swift] Extend swift_name attribute to describe members. Extend the syntax of the swift_name string to allow "Foo.bar", which specifies that the entity to which the attribute appertains should be imported as a member named "foo" of the type with the Swift name "Foo". For functions that will become instance methods, the special argument name "self" indicates which parameter of the C function will become the "self" parameter. For example, given: typedef struct __attribute__(((swift_name("Foo"))) AAFoo { /* ... */ } AAFoo; void AAFooDoSomething(AAFoo foo, double x, double y) __attribute__((swift_name("Foo.doSomething(self:x:y:)"))); The C type AAFoo will be imported as the Swift type Foo, and AAFooDoSomething will be imported as an instance method "doSomething(x:y:)" of Foo, where the first parameter is "self". Part of implementing SE-0033. apple-llvm-split-commit: 864a33392056d76af17f5d955b6082a94b8ea99a apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclAttr.cpp | 38 +++++++++++++++++++++++++++++++- clang/test/SemaObjC/attr-swift.m | 6 +++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 1d744b013862b..cd9b6932e6c58 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4500,6 +4500,21 @@ static bool validateSwiftFunctionName(StringRef Name, StringRef BaseName, Parameters; std::tie(BaseName, Parameters) = Name.split('('); + + // Split at the first '.', if it exists, which separates the context + // name from the base name. + StringRef ContextName; + bool IsMember = false; + std::tie(ContextName, BaseName) = BaseName.split('.'); + if (BaseName.empty()) { + BaseName = ContextName; + ContextName = StringRef(); + } else if (ContextName.empty() || !isValidIdentifier(ContextName)) { + return false; + } else { + IsMember = true; + } + if (!isValidIdentifier(BaseName) || BaseName == "_") return false; @@ -4512,12 +4527,23 @@ static bool validateSwiftFunctionName(StringRef Name, if (Parameters.back() != ':') return false; + Optional<unsigned> SelfLocation; StringRef NextParam; do { std::tie(NextParam, Parameters) = Parameters.split(':'); if (!isValidIdentifier(NextParam)) return false; + + // "self" indicates the "self" argument for a member. + if (IsMember && NextParam == "self") { + // More than one "self"? + if (SelfLocation) return false; + + // The "self" location is the current parameter. + SelfLocation = ParamCount; + } + ++ParamCount; } while (!Parameters.empty()); @@ -4584,7 +4610,17 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { isa<ObjCInterfaceDecl>(D) || isa<ObjCPropertyDecl>(D) || isa<VarDecl>(D) || isa<TypedefNameDecl>(D) || isa<TagDecl>(D) || isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) { - if (!isValidIdentifier(Name)) { + StringRef ContextName, BaseName; + std::tie(ContextName, BaseName) = Name.split('.'); + if (BaseName.empty()) { + BaseName = ContextName; + ContextName = StringRef(); + } else if (!isValidIdentifier(ContextName)) { + S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); + return; + } + + if (!isValidIdentifier(BaseName)) { S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); return; } diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index 98c705423f120..80a379f05475e 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -97,6 +97,12 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { typedef int some_int_type __attribute__((swift_name("SomeInt"))); +struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)"))); +struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radius:)"))); +struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radius:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} + +extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); + // --- swift_error --- @class NSError; From 51e294ac7343ae9fe79021ce4f84e27bc2b6bd46 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 1 Mar 2016 12:55:29 -0800 Subject: [PATCH 031/582] [Swift] Extend swift_name to support getters and setters. Extend the string format of swift_name to support labeling a function as a getter or setter for a given property name, so that the getter (and optional setter) function(s) will be imported into Swift as a property. Slightly extended from what SE-0033 actually requires, but useful in its own right. apple-llvm-split-commit: 91e87c28d1accf4b6cb958c46eed2d81bad37f30 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclAttr.cpp | 31 ++++++++++++++++++++++++++++++- clang/test/SemaObjC/attr-swift.m | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index cd9b6932e6c58..2051c51313a80 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4495,6 +4495,19 @@ static bool validateSwiftFunctionName(StringRef Name, unsigned &ParamCount, bool &IsSingleParamInit) { ParamCount = 0; + + // Check whether this will be mapped to a getter or setter of a + // property. + bool isGetter = false; + bool isSetter = false; + if (Name.startswith("getter:")) { + isGetter = true; + Name = Name.substr(7); + } else if (Name.startswith("setter:")) { + isSetter = true; + Name = Name.substr(7); + } + if (Name.back() != ')') return false; @@ -4521,8 +4534,13 @@ static bool validateSwiftFunctionName(StringRef Name, if (Parameters.empty()) return false; Parameters = Parameters.drop_back(); // ')' - if (Parameters.empty()) + + if (Parameters.empty()) { + // Setters must have at least one parameter. + if (isSetter) return false; + return true; + } if (Parameters.back() != ':') return false; @@ -4550,6 +4568,17 @@ static bool validateSwiftFunctionName(StringRef Name, IsSingleParamInit = (ParamCount == 1 && BaseName == "init" && NextParam != "_"); + // Check the number of parameters for a getter/setter. + if (isGetter || isSetter) { + // Setters have one parameter for the new value. + unsigned NumExpectedParams = isSetter ? 1 : 0; + + // Instance methods have one parameter for "self". + if (SelfLocation) ++NumExpectedParams; + + if (ParamCount != NumExpectedParams) return true; + } + return true; } diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index 80a379f05475e..28042635f7542 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -103,6 +103,30 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); +// Getters and setters. +float Point3DGetMagnitude(Point3D point) __attribute__((swift_name("getter:Point3D.magnitude(self:)"))); + +float Point3DGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.radius(self:)"))); +void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:_:)"))); + +Point3D getCurrentPoint3D(void) __attribute__((swift_name("getter:currentPoint3D()"))); + +void setCurrentPoint3D(Point3D point) __attribute__((swift_name("setter:currentPoint3D(_:)"))); + +Point3D getLastPoint3D(void) __attribute__((swift_name("getter:lastPoint3D()"))); + +void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(_:)"))); + +Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D()"))); +void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D(_:)"))); + +Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + +Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + +void badSetter2(Point3D point) __attribute__((swift_name("setter:bad2(self:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + // --- swift_error --- @class NSError; From 59849692055ece2d327f853961dc7f5aecde5c7a Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Fri, 4 Mar 2016 16:06:20 -0800 Subject: [PATCH 032/582] Fixup test typos in the swift_name tests apple-llvm-split-commit: 1459ff57b040b6a46a12366d621e7b8533762961 apple-llvm-split-dir: clang/ --- clang/test/SemaObjC/attr-swift.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index 28042635f7542..effcfefff2f05 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -98,8 +98,8 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { typedef int some_int_type __attribute__((swift_name("SomeInt"))); struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)"))); -struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radius:)"))); -struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radius:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} +struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radians:)"))); +struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radians:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); @@ -117,8 +117,8 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(_:)"))); -Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D()"))); -void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D(_:)"))); +Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D.zero()"))); +void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(_:)"))); Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} From 1da22bceff45387e158a6240456caa5a087392ff Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 10 Mar 2016 23:03:57 -0800 Subject: [PATCH 033/582] [Swift] Add swift_bridge attribute for bridging Objective-C types to Swift. apple-llvm-split-commit: 64b3bf21dccabfc9e966e70b88f2e8394b23613b apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 15 ++++++++++++- clang/include/clang/Basic/Attr.td | 8 +++++++ clang/include/clang/Basic/AttrDocs.td | 12 +++++++++-- clang/lib/APINotes/APINotesReader.cpp | 8 +++++++ clang/lib/APINotes/APINotesWriter.cpp | 7 ++++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 7 +++++++ clang/lib/Sema/SemaAPINotes.cpp | 16 ++++++++++++++ clang/lib/Sema/SemaDeclAttr.cpp | 21 +++++++++++++++++++ clang/lib/Sema/SemaDeclObjC.cpp | 6 ++++-- clang/test/APINotes/Inputs/roundtrip.apinotes | 2 ++ 10 files changed, 96 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 79f34ccc21898..decb137a0f502 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -127,6 +127,11 @@ class ObjCContextInfo : public CommonEntityInfo { /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; + /// The Swift type to which a given Objective-C class is bridged. + /// + /// Reflects the swift_bridge attribute. + std::string SwiftBridge; + public: ObjCContextInfo() : CommonEntityInfo(), @@ -162,11 +167,15 @@ class ObjCContextInfo : public CommonEntityInfo { DefaultNullability = 0; } + const std::string &getSwiftBridge() const { return SwiftBridge; } + void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { return static_cast<const CommonEntityInfo &>(lhs) == rhs && lhs.HasDefaultNullability == rhs.HasDefaultNullability && lhs.DefaultNullability == rhs.DefaultNullability && - lhs.HasDesignatedInits == rhs.HasDesignatedInits; + lhs.HasDesignatedInits == rhs.HasDesignatedInits && + lhs.SwiftBridge == rhs.SwiftBridge; } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -186,6 +195,10 @@ class ObjCContextInfo : public CommonEntityInfo { } lhs.HasDesignatedInits |= rhs.HasDesignatedInits; + + if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) + lhs.SwiftBridge = rhs.SwiftBridge; + return lhs; } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 33c9930e460f8..0f11afc863686 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1333,6 +1333,14 @@ def Regparm : TypeAttr { let Documentation = [RegparmDocs]; } +def SwiftBridge : Attr { + let Spellings = [GNU<"swift_bridge">]; + let Subjects = SubjectList<[Tag, TypedefName, ObjCProtocol], ErrorDiag, + "ExpectedType">; + let Args = [StringArgument<"SwiftType">]; + let Documentation = [SwiftBridgeDocs]; +} + def SwiftError : InheritableAttr { let Spellings = [GCC<"swift_error">]; let Args = [EnumArgument<"Convention", "ConventionKind", diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 64e45379f5f6f..1010545a3d834 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1860,6 +1860,12 @@ arguments, with arbitrary offsets. }]; } +def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + let Content = [{ +Clang supports additional attributes for controlling how APIs are imported into Swift. + }]; +} + def NSErrorDomainDocs : Documentation { let Category = DocCatFunction; let Content = [{ @@ -1867,9 +1873,11 @@ The ``ns_error_domain`` attribute indicates a global constant representing the e }]; } -def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + +def SwiftBridgeDocs : Documentation { + let Category = SwiftDocs; let Content = [{ -Clang supports additional attributes for controlling how APIs are imported into Swift. +The ``swift_bridge`` attribute indicates that the type to which the attribute appertains is bridged to the named Swift type. }]; } diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 8739f03b953e6..c1bb118398b55 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -140,6 +140,14 @@ namespace { } ++data; result.second.setHasDesignatedInits(*data++); + + // swift bridge. + unsigned swiftBridgeLength = + endian::readNext<uint16_t, little, unaligned>(data); + result.second.setSwiftBridge( + StringRef(reinterpret_cast<const char *>(data), swiftBridgeLength)); + data += swiftBridgeLength; + return result; } }; diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 430c2a2ad97e1..f48a75daf056d 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -301,7 +301,8 @@ namespace { uint32_t keyLength = sizeof(uint32_t) + 1; uint32_t dataLength = sizeof(uint32_t) + getCommonEntityInfoSize(data.second) - + dataBytes; + + dataBytes + + 2 + data.second.getSwiftBridge().size(); endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); @@ -332,6 +333,10 @@ namespace { bytes[2] = data.second.hasDesignatedInits(); out.write(reinterpret_cast<const char *>(bytes), dataBytes); + + writer.write<uint16_t>(data.second.getSwiftBridge().size()); + out.write(data.second.getSwiftBridge().data(), + data.second.getSwiftBridge().size()); } }; } // end anonymous namespace diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 8896c40cda0a5..17d8fab3637f7 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -189,6 +189,7 @@ namespace { bool AuditedForNullability = false; AvailabilityItem Availability; StringRef SwiftName; + StringRef SwiftBridge; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -313,6 +314,7 @@ namespace llvm { io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); io.mapOptional("SwiftName", c.SwiftName); + io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -521,6 +523,9 @@ static bool compile(const Module &module, if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); + if (isClass) + cInfo.setSwiftBridge(cl.SwiftBridge); + ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : Writer->addObjCProtocol(cl.Name, cInfo); @@ -727,6 +732,8 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, if (info.getDefaultNullability()) { record.AuditedForNullability = true; } + + record.SwiftBridge = copyString(info.getSwiftBridge()); } /// Map availability information, if present. diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 649e8c455975b..b9dcfb873416a 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -239,6 +239,22 @@ static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); } +/// Process API notes for an Objective-C class. +static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, + const api_notes::ObjCContextInfo &Info) { + // swift_bridge + if (!Info.getSwiftBridge().empty() && + !D->getAttr<SwiftBridgeAttr>()) { + D->addAttr( + SwiftBridgeAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.getSwiftBridge()))); + } + + // Handle information common to Objective-C classes and protocols. + ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), Info); +} + /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 8410145e8ac0f..110a0c18da943 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4910,6 +4910,24 @@ static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) { attr.getAttributeSpellingListIndex())); } +static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Make sure that there is a string literal as the annotation's single + // argument. + StringRef Str; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + return; + + // Don't duplicate annotations that are already set. + if (D->hasAttr<SwiftBridgeAttr>()) { + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName(); + return; + } + + D->addAttr(::new (S.Context) + SwiftBridgeAttr(Attr.getRange(), S.Context, Str, + Attr.getAttributeSpellingListIndex())); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -6184,6 +6202,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SwiftError: handleSwiftError(S, D, Attr); break; + case AttributeList::AT_SwiftBridge: + handleSwiftBridgeAttr(S, D, Attr); + break; } } diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 02a38b78a1473..b7eec4750b5c7 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1687,7 +1687,8 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, = ObjCProtocolDecl::Create(Context, CurContext, Ident, IdentPair.second, AtProtocolLoc, PrevDecl); - + ProcessAPINotes(PDecl); + PushOnScopeChains(PDecl, TUScope); CheckObjCDeclScope(PDecl); @@ -3009,7 +3010,8 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, ClassName, TypeParams, PrevIDecl, IdentLocs[i]); IDecl->setAtEndRange(IdentLocs[i]); - + ProcessAPINotes(IDecl); + PushOnScopeChains(IDecl, TUScope); CheckObjCDeclScope(IDecl); DeclsInGroup.push_back(IDecl); diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index a1b60e5b19f1f..18bf2deeb1934 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -7,6 +7,7 @@ Classes: Availability: available AvailabilityMsg: '' SwiftName: '' + SwiftBridge: '' Methods: - Selector: init MethodKind: Instance @@ -45,6 +46,7 @@ Classes: Availability: available AvailabilityMsg: '' SwiftName: '' + SwiftBridge: View Methods: - Selector: 'addSubview:' MethodKind: Instance From d2239ed7116a4106bcb3466a0dc7c049e6d60794 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 17 Mar 2016 13:39:41 -0700 Subject: [PATCH 034/582] [Swift] swift_bridge can apply to ObjCInterfaceDecls, of course. Clang's architectural failure of ObjCInterfaceDecl not being a TypeDecl continues to surprise us. apple-llvm-split-commit: 1e173f644bb3ee84c70d77b398372743a2e9f3e2 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 57e46f0418b95..1a0756d5ed7fd 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1338,8 +1338,8 @@ def Regparm : TypeAttr { def SwiftBridge : Attr { let Spellings = [GNU<"swift_bridge">]; - let Subjects = SubjectList<[Tag, TypedefName, ObjCProtocol], ErrorDiag, - "ExpectedType">; + let Subjects = SubjectList<[Tag, TypedefName, ObjCInterface, ObjCProtocol], + ErrorDiag, "ExpectedType">; let Args = [StringArgument<"SwiftType">]; let Documentation = [SwiftBridgeDocs]; } From 44deacba2206782454f6110f4b2ce43c5937a5ce Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 17 Mar 2016 14:20:33 -0700 Subject: [PATCH 035/582] [API Notes] Fix compilation error reported in rdar://problem/25219276. apple-llvm-split-commit: 24e2d6e0a65e408125cb2f45101258b6e3d7780c apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index b9dcfb873416a..203424468b2d1 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -118,7 +118,8 @@ static void ProcessAPINotes(Sema &S, Decl *D, VersionTuple(), /*Unavailable=*/true, CopyString(S.Context, Info.UnavailableMsg), - /*Nopartial=*/true)); + /*Strict=*/false, + StringRef())); } // swift_name From c99d9494d31ee6d76b0086431d5f09cc19675fa4 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sat, 19 Mar 2016 08:13:25 -0700 Subject: [PATCH 036/582] [APINotes] Fix build error with creating 'AvailabilityAttr', after upstream changes. apple-llvm-split-commit: d015e679baac7607f63ec9a9f0834b9c088595e7 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 203424468b2d1..4e96ace98f31e 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -118,8 +118,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, VersionTuple(), /*Unavailable=*/true, CopyString(S.Context, Info.UnavailableMsg), - /*Strict=*/false, - StringRef())); + /*Strict=*/false)); } // swift_name From be2cb65cb054c4fb5641b0f7dcc16e3fb0573ee3 Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Mon, 21 Mar 2016 11:44:28 -0700 Subject: [PATCH 037/582] [APINotes] Fix build error with creating 'AvailabilityAttr', after r263958. apple-llvm-split-commit: 7fd255d183c292400f9c4fdb9bcac4e81a7f6c7f apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 4e96ace98f31e..6e06e4cc80a0a 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -118,7 +118,8 @@ static void ProcessAPINotes(Sema &S, Decl *D, VersionTuple(), /*Unavailable=*/true, CopyString(S.Context, Info.UnavailableMsg), - /*Strict=*/false)); + /*Strict=*/false, + /*Replacement=*/StringRef())); } // swift_name From 12023f3bbf7d96ee059b23bc53a05b64a5b8d1e6 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 28 Mar 2016 09:31:19 -0700 Subject: [PATCH 038/582] [API Notes] Add support for tags (struct/union/enum/C++ class) and typedefs. Addresses rdar://problem/25365464. apple-llvm-split-commit: fea40e61dc6210e5b84e4dae09491e085287adc2 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 21 ++ clang/include/clang/APINotes/APINotesWriter.h | 12 + clang/include/clang/APINotes/Types.h | 66 +++- clang/lib/APINotes/APINotesFormat.h | 36 ++- clang/lib/APINotes/APINotesReader.cpp | 303 +++++++++++++++++- clang/lib/APINotes/APINotesWriter.cpp | 175 +++++++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 216 ++++++++++--- clang/lib/Sema/SemaAPINotes.cpp | 65 +++- .../Inputs/APINotes/HeaderLib.apinotes | 8 + .../test/APINotes/Inputs/Headers/HeaderLib.h | 3 + clang/test/APINotes/Inputs/roundtrip.apinotes | 12 + clang/test/APINotes/availability.m | 6 + 12 files changed, 834 insertions(+), 89 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 4759f85c1ab48..16cb5ebe109b4 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -102,6 +102,21 @@ class APINotesReader { /// \returns information about the global function, if known. Optional<GlobalFunctionInfo> lookupGlobalFunction(StringRef name); + /// Look for information regarding the given tag + /// (struct/union/enum/C++ class). + /// + /// \param name The name of the tag. + /// + /// \returns information about the tag, if known. + Optional<TagInfo> lookupTag(StringRef name); + + /// Look for information regarding the given typedef. + /// + /// \param name The name of the typedef. + /// + /// \returns information about the typedef, if known. + Optional<TypedefInfo> lookupTypedef(StringRef name); + /// Visitor used when walking the contents of the API notes file. class Visitor { public: @@ -131,6 +146,12 @@ class APINotesReader { /// Visit a global function. virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + + /// Visit a tag. + virtual void visitTag(StringRef name, const TagInfo &info); + + /// Visit a typedef. + virtual void visitTypedef(StringRef name, const TypedefInfo &info); }; /// Visit the contents of the API notes file, passing each entity to the diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index dca0773aa28f8..38935a6a271c9 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -89,6 +89,18 @@ class APINotesWriter { /// \param name The name of this global function. /// \param info Information about this global function. void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + + /// Add information about a tag (struct/union/enum/C++ class). + /// + /// \param name The name of this tag. + /// \param info Information about this tag. + void addTag(StringRef name, const TagInfo &info); + + /// Add information about a typedef. + /// + /// \param name The name of this typedef. + /// \param info Information about this typedef. + void addTypedef(StringRef name, const TypedefInfo &info); }; } // end namespace api_notes diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index decb137a0f502..5053190f408eb 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -113,11 +113,43 @@ class CommonEntityInfo { return lhs; } +}; + +/// Describes API notes for types. +class CommonTypeInfo : public CommonEntityInfo { + /// The Swift type to which a given type is bridged. + /// + /// Reflects the swift_bridge attribute. + std::string SwiftBridge; + +public: + CommonTypeInfo() : CommonEntityInfo() { } + + const std::string &getSwiftBridge() const { return SwiftBridge; } + void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + static_cast<CommonEntityInfo &>(lhs) |= rhs; + if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) + lhs.SwiftBridge = rhs.SwiftBridge; + return lhs; + } + + friend bool operator==(const CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + return static_cast<const CommonEntityInfo &>(lhs) == rhs && + lhs.SwiftBridge == rhs.SwiftBridge; + } + + friend bool operator!=(const CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + return !(lhs == rhs); + } }; /// Describes API notes data for an Objective-C class or protocol. -class ObjCContextInfo : public CommonEntityInfo { +class ObjCContextInfo : public CommonTypeInfo { /// Whether this class has a default nullability. unsigned HasDefaultNullability : 1; @@ -127,14 +159,9 @@ class ObjCContextInfo : public CommonEntityInfo { /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; - /// The Swift type to which a given Objective-C class is bridged. - /// - /// Reflects the swift_bridge attribute. - std::string SwiftBridge; - public: ObjCContextInfo() - : CommonEntityInfo(), + : CommonTypeInfo(), HasDefaultNullability(0), DefaultNullability(0), HasDesignatedInits(0) @@ -167,15 +194,11 @@ class ObjCContextInfo : public CommonEntityInfo { DefaultNullability = 0; } - const std::string &getSwiftBridge() const { return SwiftBridge; } - void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } - friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { - return static_cast<const CommonEntityInfo &>(lhs) == rhs && + return static_cast<const CommonTypeInfo &>(lhs) == rhs && lhs.HasDefaultNullability == rhs.HasDefaultNullability && lhs.DefaultNullability == rhs.DefaultNullability && - lhs.HasDesignatedInits == rhs.HasDesignatedInits && - lhs.SwiftBridge == rhs.SwiftBridge; + lhs.HasDesignatedInits == rhs.HasDesignatedInits; } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -185,7 +208,7 @@ class ObjCContextInfo : public CommonEntityInfo { friend ObjCContextInfo &operator|=(ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { // Merge inherited info. - static_cast<CommonEntityInfo &>(lhs) |= rhs; + static_cast<CommonTypeInfo &>(lhs) |= rhs; // Merge nullability. if (!lhs.getDefaultNullability()) { @@ -196,9 +219,6 @@ class ObjCContextInfo : public CommonEntityInfo { lhs.HasDesignatedInits |= rhs.HasDesignatedInits; - if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) - lhs.SwiftBridge = rhs.SwiftBridge; - return lhs; } @@ -436,6 +456,18 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() : FunctionInfo() { } }; +/// Describes API notes data for a tag. +class TagInfo : public CommonTypeInfo { +public: + TagInfo() : CommonTypeInfo() { } +}; + +/// Describes API notes data for a typedef. +class TypedefInfo : public CommonTypeInfo { +public: + TypedefInfo() : CommonTypeInfo() { } +}; + } // end namespace api_notes } // end namespace clang diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index b49064bbf11e8..761470ddfa19e 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 8; +const uint16_t VERSION_MINOR = 9; using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; @@ -85,7 +85,15 @@ enum BlockID { /// The (global) functions data block, which maps global function names to /// information about the global function. - GLOBAL_FUNCTION_BLOCK_ID + GLOBAL_FUNCTION_BLOCK_ID, + + /// The tag data block, which maps tag names to information about + /// the tags. + TAG_BLOCK_ID, + + /// The typedef data block, which maps typedef names to information about + /// the typedefs. + TYPEDEF_BLOCK_ID, }; namespace control_block { @@ -194,6 +202,30 @@ namespace global_function_block { >; } +namespace tag_block { + enum { + TAG_DATA = 1 + }; + + using TagDataLayout = BCRecordLayout< + TAG_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to tag information + >; +}; + +namespace typedef_block { + enum { + TYPEDEF_DATA = 1 + }; + + using TypedefDataLayout = BCRecordLayout< + TYPEDEF_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to typedef information + >; +}; + /// A stored Objective-C selector. struct StoredObjCSelector { unsigned NumPieces; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index c1bb118398b55..f400ebf6457b4 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -48,6 +48,17 @@ namespace { data += swiftNameLength; } + /// Read serialized CommonTypeInfo. + void readCommonTypeInfo(const uint8_t *&data, CommonTypeInfo &info) { + readCommonEntityInfo(data, info); + + unsigned swiftBridgeLength = + endian::readNext<uint16_t, little, unaligned>(data); + info.setSwiftBridge( + StringRef(reinterpret_cast<const char *>(data), swiftBridgeLength)); + data += swiftBridgeLength; + } + /// Used to deserialize the on-disk identifier table. class IdentifierTableInfo { public: @@ -134,19 +145,12 @@ namespace { unsigned length) { data_type result; result.first = endian::readNext<uint32_t, little, unaligned>(data); - readCommonEntityInfo(data, result.second); + readCommonTypeInfo(data, result.second); if (*data++) { result.second.setDefaultNullability(static_cast<NullabilityKind>(*data)); } ++data; result.second.setHasDesignatedInits(*data++); - - // swift bridge. - unsigned swiftBridgeLength = - endian::readNext<uint16_t, little, unaligned>(data); - result.second.setSwiftBridge( - StringRef(reinterpret_cast<const char *>(data), swiftBridgeLength)); - data += swiftBridgeLength; return result; } @@ -410,6 +414,96 @@ namespace { return info; } }; + + /// Used to deserialize the on-disk tag table. + class TagTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = TagInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + TagInfo info; + readCommonTypeInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk typedef table. + class TypedefTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = TypedefInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + TypedefInfo info; + readCommonTypeInfo(data, info); + return info; + } + }; } // end anonymous namespace class APINotesReader::Implementation { @@ -465,6 +559,18 @@ class APINotesReader::Implementation { /// The global function table. std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable; + using SerializedTagTable = + llvm::OnDiskIterableChainedHashTable<TagTableInfo>; + + /// The tag table. + std::unique_ptr<SerializedTagTable> TagTable; + + using SerializedTypedefTable = + llvm::OnDiskIterableChainedHashTable<TypedefTableInfo>; + + /// The typedef table. + std::unique_ptr<SerializedTypedefTable> TypedefTable; + /// Retrieve the identifier ID for the given string, or an empty /// optional if the string is unknown. Optional<IdentifierID> getIdentifier(StringRef str); @@ -489,6 +595,10 @@ class APINotesReader::Implementation { SmallVectorImpl<uint64_t> &scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl<uint64_t> &scratch); + bool readTagBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); + bool readTypedefBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); }; Optional<IdentifierID> APINotesReader::Implementation::getIdentifier( @@ -959,6 +1069,112 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( return false; } +bool APINotesReader::Implementation::readTagBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(TAG_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case tag_block::TAG_DATA: { + // Already saw tag table. + if (TagTable) + return true; + + uint32_t tableOffset; + tag_block::TagDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + TagTable.reset( + SerializedTagTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readTypedefBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case typedef_block::TYPEDEF_DATA: { + // Already saw typedef table. + if (TypedefTable) + return true; + + uint32_t tableOffset; + typedef_block::TypedefDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + TypedefTable.reset( + SerializedTypedefTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + APINotesReader::APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, bool &failed) : Impl(*new Implementation) @@ -1057,6 +1273,20 @@ APINotesReader::APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, } break; + case TAG_BLOCK_ID: + if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case TYPEDEF_BLOCK_ID: + if (!hasValidControlBlock || Impl.readTypedefBlock(cursor, scratch)) { + failed = true; + return; + } + break; + default: // Unknown top-level block, possibly for use by a future version of the // module format. @@ -1197,6 +1427,37 @@ Optional<GlobalFunctionInfo> APINotesReader::lookupGlobalFunction( return *known; } + +Optional<TagInfo> APINotesReader::lookupTag(StringRef name) { + if (!Impl.TagTable) + return None; + + Optional<IdentifierID> nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TagTable->find(*nameID); + if (known == Impl.TagTable->end()) + return None; + + return *known; +} + +Optional<TypedefInfo> APINotesReader::lookupTypedef(StringRef name) { + if (!Impl.TypedefTable) + return None; + + Optional<IdentifierID> nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TypedefTable->find(*nameID); + if (known == Impl.TypedefTable->end()) + return None; + + return *known; +} + APINotesReader::Visitor::~Visitor() { } void APINotesReader::Visitor::visitObjCClass(ContextID contextID, @@ -1224,6 +1485,14 @@ void APINotesReader::Visitor::visitGlobalFunction( StringRef name, const GlobalFunctionInfo &info) { } +void APINotesReader::Visitor::visitTag( + StringRef name, + const TagInfo &info) { } + +void APINotesReader::Visitor::visitTypedef( + StringRef name, + const TypedefInfo &info) { } + void APINotesReader::visit(Visitor &visitor) { // FIXME: All of these iterations would be significantly more efficient if we // could get the keys and data together, but OnDiskIterableHashTable doesn't @@ -1310,5 +1579,23 @@ void APINotesReader::visit(Visitor &visitor) { visitor.visitGlobalVariable(name, info); } } + + // Visit tags. + if (Impl.TagTable) { + for (auto key : Impl.TagTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.TagTable->find(key); + visitor.visitTag(name, info); + } + } + + // Visit typedefs. + if (Impl.TypedefTable) { + for (auto key : Impl.TypedefTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.TypedefTable->find(key); + visitor.visitTypedef(name, info); + } + } } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index f48a75daf056d..2d55c927c703f 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -77,6 +77,16 @@ class APINotesWriter::Implementation { /// Indexed by the identifier ID. llvm::DenseMap<unsigned, GlobalFunctionInfo> GlobalFunctions; + /// Information about tags. + /// + /// Indexed by the identifier ID. + llvm::DenseMap<unsigned, TagInfo> Tags; + + /// Information about typedefs. + /// + /// Indexed by the identifier ID. + llvm::DenseMap<unsigned, TypedefInfo> Typedefs; + /// Retrieve the ID for the given identifier. IdentifierID getIdentifier(StringRef identifier) { if (identifier.empty()) @@ -123,6 +133,8 @@ class APINotesWriter::Implementation { void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); + void writeTagBlock(llvm::BitstreamWriter &writer); + void writeTypedefBlock(llvm::BitstreamWriter &writer); }; /// Record the name of a block. @@ -278,6 +290,20 @@ namespace { out.write(info.SwiftName.c_str(), info.SwiftName.size()); } + // Retrieve the serialized size of the given CommonTypeInfo, for use + // in on-disk hash tables. + static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) { + return 2 + info.getSwiftBridge().size() + getCommonEntityInfoSize(info); + } + + /// Emit a serialized representation of the common type information. + static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) { + emitCommonEntityInfo(out, info); + endian::Writer<little> writer(out); + writer.write<uint16_t>(info.getSwiftBridge().size()); + out.write(info.getSwiftBridge().c_str(), info.getSwiftBridge().size()); + } + /// Used to serialize the on-disk Objective-C context table. class ObjCContextTableInfo { public: @@ -300,9 +326,8 @@ namespace { data_type_ref data) { uint32_t keyLength = sizeof(uint32_t) + 1; uint32_t dataLength = sizeof(uint32_t) - + getCommonEntityInfoSize(data.second) - + dataBytes - + 2 + data.second.getSwiftBridge().size(); + + getCommonTypeInfoSize(data.second) + + dataBytes; endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); @@ -320,7 +345,7 @@ namespace { endian::Writer<little> writer(out); writer.write<uint32_t>(data.first); - emitCommonEntityInfo(out, data.second); + emitCommonTypeInfo(out, data.second); // FIXME: Inefficient representation. uint8_t bytes[dataBytes] = { 0, 0, 0 }; @@ -333,10 +358,6 @@ namespace { bytes[2] = data.second.hasDesignatedInits(); out.write(reinterpret_cast<const char *>(bytes), dataBytes); - - writer.write<uint16_t>(data.second.getSwiftBridge().size()); - out.write(data.second.getSwiftBridge().data(), - data.second.getSwiftBridge().size()); } }; } // end anonymous namespace @@ -732,6 +753,130 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( layout.emit(ScratchRecord, tableOffset, hashTableBlob); } +namespace { + /// Used to serialize the on-disk tag table. + class TagTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = TagInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getCommonTypeInfoSize(data); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<IdentifierID>(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonTypeInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeTagBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, TAG_BLOCK_ID, 3); + + if (Tags.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<TagTableInfo> generator; + for (auto &entry : Tags) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + tag_block::TagDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Used to serialize the on-disk typedef table. + class TypedefTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = TypedefInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getCommonTypeInfoSize(data); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<IdentifierID>(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonTypeInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeTypedefBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, TYPEDEF_BLOCK_ID, 3); + + if (Typedefs.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<TypedefTableInfo> generator; + for (auto &entry : Typedefs) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + typedef_block::TypedefDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { // Write the API notes file into a buffer. SmallVector<char, 0> buffer; @@ -752,6 +897,8 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { writeObjCSelectorBlock(writer); writeGlobalVariableBlock(writer); writeGlobalFunctionBlock(writer); + writeTagBlock(writer); + writeTypedefBlock(writer); } // Write the buffer to the stream. @@ -855,3 +1002,15 @@ void APINotesWriter::addGlobalFunction(llvm::StringRef name, assert(!Impl.GlobalFunctions.count(nameID)); Impl.GlobalFunctions[nameID] = info; } + +void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { + IdentifierID tagID = Impl.getIdentifier(name); + assert(!Impl.Tags.count(tagID)); + Impl.Tags[tagID] = info; +} + +void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { + IdentifierID typedefID = Impl.getIdentifier(name); + assert(!Impl.Typedefs.count(typedefID)); + Impl.Typedefs[typedefID] = info; +} diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 17d8fab3637f7..39f6c9d641084 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -57,6 +57,10 @@ ... Globals: # List of globals ... + Tags: # List of tags (struct/union/enum/C++ class) + ... + Typedefs: # List of typedef-names and C++11 type aliases + ... Each class and protocol is defined as following: @@ -212,6 +216,22 @@ namespace { }; typedef std::vector<GlobalVariable> GlobalVariablesSeq; + struct Tag { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + StringRef SwiftBridge; + }; + typedef std::vector<Tag> TagsSeq; + + struct Typedef { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + StringRef SwiftBridge; + }; + typedef std::vector<Typedef> TypedefsSeq; + struct Module { StringRef Name; AvailabilityItem Availability; @@ -219,6 +239,8 @@ namespace { ClassesSeq Protocols; FunctionsSeq Functions; GlobalVariablesSeq Globals; + TagsSeq Tags; + TypedefsSeq Typedefs; LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, @@ -232,6 +254,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Property) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) +LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) +LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) namespace llvm { namespace yaml { @@ -345,6 +369,28 @@ namespace llvm { } }; + template <> + struct MappingTraits<Tag> { + static void mapping(IO &io, Tag& t) { + io.mapRequired("Name", t.Name); + io.mapOptional("Availability", t.Availability.Mode); + io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftName", t.SwiftName); + io.mapOptional("SwiftBridge", t.SwiftBridge); + } + }; + + template <> + struct MappingTraits<Typedef> { + static void mapping(IO &io, Typedef& t) { + io.mapRequired("Name", t.Name); + io.mapOptional("Availability", t.Availability.Mode); + io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftName", t.SwiftName); + io.mapOptional("SwiftBridge", t.SwiftBridge); + } + }; + template <> struct MappingTraits<Module> { static void mapping(IO &io, Module& m) { @@ -355,6 +401,8 @@ namespace llvm { io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); io.mapOptional("Globals", m.Globals); + io.mapOptional("Tags", m.Tags); + io.mapOptional("Typedefs", m.Typedefs); } }; } @@ -377,11 +425,7 @@ static bool parseAPINotes(StringRef yamlInput, Module &module, return static_cast<bool>(yin.error()); } -static bool compile(const Module &module, - llvm::raw_ostream &os, - api_notes::OSType targetOS, - llvm::SourceMgr::DiagHandlerTy diagHandler, - void *diagHandlerCtxt){ +namespace { using namespace api_notes; class YAMLConverter { @@ -431,7 +475,7 @@ static bool compile(const Module &module, outInfo.UnavailableMsg = in.Msg; } else { if (!in.Msg.empty()) { - emitError("availability message for available class '" + + emitError("availability message for available API '" + apiName + "' will not be used"); } } @@ -466,17 +510,37 @@ static bool compile(const Module &module, } } + /// Convert the common parts of an entity from YAML. + template<typename T> + bool convertCommon(const T& common, CommonEntityInfo &info, + StringRef apiName) { + if (!isAvailable(common.Availability)) + return true; + + convertAvailability(common.Availability, info, apiName); + info.SwiftName = common.SwiftName; + return false; + } + + /// Convert the common parts of a type entity from YAML. + template<typename T> + bool convertCommonType(const T& common, CommonTypeInfo &info, + StringRef apiName) { + if (convertCommon(common, info, apiName)) + return true; + + info.setSwiftBridge(common.SwiftBridge); + return false; + } + // Translate from Method into ObjCMethodInfo and write it out. void convertMethod(const Method &meth, ContextID classID, StringRef className) { ObjCMethodInfo mInfo; - if (!isAvailable(meth.Availability)) + if (convertCommon(meth, mInfo, meth.Selector)) return; - convertAvailability(meth.Availability, mInfo, meth.Selector); - mInfo.SwiftName = meth.SwiftName; - // Check if the selector ends with ':' to determine if it takes arguments. bool takesArguments = meth.Selector.endswith(":"); @@ -505,27 +569,20 @@ static bool compile(const Module &module, // Write it. Writer->addObjCMethod(classID, selectorRef, - meth.Kind == MethodKind::Instance, - mInfo); + meth.Kind == MethodKind::Instance, + mInfo); } void convertContext(const Class &cl, bool isClass) { // Write the class. ObjCContextInfo cInfo; - // First, translate and check availability info. - if (!isAvailable(cl.Availability)) + if (convertCommonType(cl, cInfo, cl.Name)) return; - convertAvailability(cl.Availability, cInfo, cl.Name); - cInfo.SwiftName = cl.SwiftName; - if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); - if (isClass) - cInfo.setSwiftBridge(cl.SwiftBridge); - ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : Writer->addObjCProtocol(cl.Name, cInfo); @@ -644,12 +701,52 @@ static bool compile(const Module &module, Writer->addGlobalFunction(function.Name, info); } + // Write all tags. + llvm::StringSet<> knownTags; + for (const auto &t : TheModule.Tags) { + // Check for duplicate tag definitions. + if (!knownTags.insert(t.Name).second) { + emitError("multiple definitions of tag '" + t.Name + "'"); + continue; + } + + TagInfo tagInfo; + if (convertCommonType(t, tagInfo, t.Name)) + continue; + + Writer->addTag(t.Name, tagInfo); + } + + // Write all typedefs. + llvm::StringSet<> knownTypedefs; + for (const auto &t : TheModule.Typedefs) { + // Check for duplicate typedef definitions. + if (!knownTags.insert(t.Name).second) { + emitError("multiple definitions of typedef '" + t.Name + "'"); + continue; + } + + TypedefInfo typedefInfo; + if (convertCommonType(t, typedefInfo, t.Name)) + continue; + + Writer->addTypedef(t.Name, typedefInfo); + } + if (!ErrorOccured) Writer->writeToStream(OS); return ErrorOccured; } }; +} + +static bool compile(const Module &module, + llvm::raw_ostream &os, + api_notes::OSType targetOS, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt){ + using namespace api_notes; YAMLConverter c(module, targetOS, os, diagHandler, diagHandlerCtxt); return c.convertModule(); @@ -689,15 +786,7 @@ bool api_notes::compileAPINotes(StringRef yamlInput, return compile(module, os, targetOS, diagHandler, diagHandlerCtxt); } -bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, - llvm::raw_ostream &os) { - // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); - if (!reader) { - llvm::errs() << "not a well-formed API notes binary file\n"; - return true; - } - +namespace { // Deserialize the API notes file into a module. class DecompileVisitor : public APINotesReader::Visitor { /// Allocator used to clone those strings that need it. @@ -720,20 +809,28 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, return StringRef(reinterpret_cast<const char *>(ptr), string.size()); } + template<typename T> + void handleCommon(T &record, const CommonEntityInfo &info) { + handleAvailability(record.Availability, info); + record.SwiftName = copyString(info.SwiftName); + } + + template<typename T> + void handleCommonType(T &record, const CommonTypeInfo &info) { + handleCommon(record, info); + record.SwiftBridge = copyString(info.getSwiftBridge()); + } + /// Map Objective-C context info. void handleObjCContext(Class &record, StringRef name, const ObjCContextInfo &info) { record.Name = name; - // Handle class information. - handleAvailability(record.Availability, info); - record.SwiftName = copyString(info.SwiftName); + handleCommonType(record, info); if (info.getDefaultNullability()) { record.AuditedForNullability = true; } - - record.SwiftBridge = copyString(info.getSwiftBridge()); } /// Map availability information, if present. @@ -792,10 +889,9 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, method.Selector = copyString(selector); method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; + handleCommon(method, info); handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); - handleAvailability(method.Availability, info); - method.SwiftName = copyString(info.SwiftName); method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; @@ -811,8 +907,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, const ObjCPropertyInfo &info) { Property property; property.Name = name; - handleAvailability(property.Availability, info); - property.SwiftName = copyString(info.SwiftName); + handleCommon(property, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -830,8 +925,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, const GlobalFunctionInfo &info) { Function function; function.Name = name; - handleAvailability(function.Availability, info); - function.SwiftName = copyString(info.SwiftName); + handleCommon(function, info); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); @@ -843,8 +937,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, const GlobalVariableInfo &info) { GlobalVariable global; global.Name = name; - handleAvailability(global.Availability, info); - global.SwiftName = copyString(info.SwiftName); + handleCommon(global, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -854,10 +947,35 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, TheModule.Globals.push_back(global); } + virtual void visitTag(StringRef name, const TagInfo &info) { + Tag tag; + tag.Name = name; + handleCommonType(tag, info); + TheModule.Tags.push_back(tag); + } + + virtual void visitTypedef(StringRef name, const TypedefInfo &info) { + Typedef td; + td.Name = name; + handleCommonType(td, info); + TheModule.Typedefs.push_back(td); + } + /// Retrieve the module. Module &getModule() { return TheModule; } - } decompileVisitor; + }; +} + +bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, + llvm::raw_ostream &os) { + // Try to read the file. + auto reader = APINotesReader::get(std::move(input)); + if (!reader) { + llvm::errs() << "not a well-formed API notes binary file\n"; + return true; + } + DecompileVisitor decompileVisitor; reader->visit(decompileVisitor); // Sort the data in the module, because the API notes reader doesn't preserve @@ -911,6 +1029,18 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, return lhs.Name < rhs.Name; }); + // Sort tags. + std::sort(module.Tags.begin(), module.Tags.end(), + [](const Tag &lhs, const Tag &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Sort typedefs. + std::sort(module.Typedefs.begin(), module.Typedefs.end(), + [](const Typedef &lhs, const Typedef &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + // Output the YAML representation. Output yout(os); yout << module; diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 6e06e4cc80a0a..fd70e94cd32a8 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -130,6 +130,20 @@ static void ProcessAPINotes(Sema &S, Decl *D, } } +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonTypeInfo &Info) { + // swift_bridge + if (!Info.getSwiftBridge().empty() && + !D->getAttr<SwiftBridgeAttr>()) { + D->addAttr( + SwiftBridgeAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.getSwiftBridge()))); + } + + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); +} + /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &Info) { @@ -232,26 +246,31 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, static_cast<const api_notes::FunctionInfo &>(Info)); } +/// Process API notes for a tag. +static void ProcessAPINotes(Sema &S, TagDecl *D, + const api_notes::TagInfo &Info) { + // Handle common type information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info)); +} + +/// Process API notes for a typedef. +static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, + const api_notes::TypedefInfo &Info) { + // Handle common type information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info)); +} + /// Process API notes for an Objective-C class or protocol. static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, const api_notes::ObjCContextInfo &Info) { - // Handle common entity information. - ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); + // Handle common type information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info)); } /// Process API notes for an Objective-C class. static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, const api_notes::ObjCContextInfo &Info) { - // swift_bridge - if (!Info.getSwiftBridge().empty() && - !D->getAttr<SwiftBridgeAttr>()) { - D->addAttr( - SwiftBridgeAttr::CreateImplicit(S.Context, - CopyString(S.Context, - Info.getSwiftBridge()))); - } - // Handle information common to Objective-C classes and protocols. ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), Info); } @@ -314,6 +333,30 @@ void Sema::ProcessAPINotes(Decl *D) { return; } + // Tags + if (auto Tag = dyn_cast<TagDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupTag(Tag->getName())) { + ::ProcessAPINotes(*this, Tag, *Info); + } + } + + return; + } + + // Typedefs + if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupTypedef(Typedef->getName())) { + ::ProcessAPINotes(*this, Typedef, *Info); + } + } + + return; + } + return; } diff --git a/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes index a4ddafe2892ec..8d8ff11f69b57 100644 --- a/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes +++ b/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -15,3 +15,11 @@ Globals: Nullability: N - Name: unavailable_global_int Availability: none + +Tags: + - Name: unavailable_struct + Availability: none + +Typedefs: + - Name: unavailable_typedef + Availability: none diff --git a/clang/test/APINotes/Inputs/Headers/HeaderLib.h b/clang/test/APINotes/Inputs/Headers/HeaderLib.h index 1cf199cd49a02..81a7d63d4684d 100644 --- a/clang/test/APINotes/Inputs/Headers/HeaderLib.h +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.h @@ -10,4 +10,7 @@ int unavailable_global_int; void do_something_with_pointers(int *ptr1, int *ptr2); +typedef int unavailable_typedef; +struct unavailable_struct { int x, y, z; }; + #endif diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 18bf2deeb1934..378c7258d51b7 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -92,3 +92,15 @@ Globals: Availability: available AvailabilityMsg: '' SwiftName: calibratedWhite +Tags: + - Name: NSSomeStruct + Availability: available + AvailabilityMsg: '' + SwiftName: SomeStruct + SwiftBridge: '' +Typedefs: + - Name: NSTypedef + Availability: available + AvailabilityMsg: '' + SwiftName: Typedef + SwiftBridge: '' diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m index 5b996ec8c9497..1cfc65862f46a 100644 --- a/clang/test/APINotes/availability.m +++ b/clang/test/APINotes/availability.m @@ -12,6 +12,12 @@ int main() { i = unavailable_global_int; // expected-error{{'unavailable_global_int' is unavailable}} // expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}} + unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}} + // expected-note@HeaderLib.h:13{{'unavailable_typedef' has been explicitly marked unavailable here}} + + struct unavailable_struct s; // expected-error{{'unavailable_struct' is unavailable}} + // expected-note@HeaderLib.h:14{{'unavailable_struct' has been explicitly marked unavailable here}} + B *b = 0; // expected-error{{'B' is unavailable: just don't}} // expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}} From 28251dbad19792681c683aa3bad59e50e7762181 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 28 Mar 2016 10:45:01 -0700 Subject: [PATCH 039/582] [API Notes] Add support for enumerators. Addresses the rest of rdar://problem/25365464. apple-llvm-split-commit: 8256e301e9018f584560d8f20652974c04b52907 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 11 ++ clang/include/clang/APINotes/APINotesWriter.h | 6 + clang/include/clang/APINotes/Types.h | 6 + clang/lib/APINotes/APINotesFormat.h | 18 ++- clang/lib/APINotes/APINotesReader.cpp | 143 ++++++++++++++++++ clang/lib/APINotes/APINotesWriter.cpp | 76 ++++++++++ clang/lib/APINotes/APINotesYAMLCompiler.cpp | 53 +++++++ clang/lib/Sema/SemaAPINotes.cpp | 23 +++ clang/test/APINotes/Inputs/roundtrip.apinotes | 5 + 9 files changed, 340 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 16cb5ebe109b4..839e1dc5ee942 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -102,6 +102,13 @@ class APINotesReader { /// \returns information about the global function, if known. Optional<GlobalFunctionInfo> lookupGlobalFunction(StringRef name); + /// Look for information regarding the given enumerator. + /// + /// \param name The name of the enumerator. + /// + /// \returns information about the enumerator, if known. + Optional<EnumConstantInfo> lookupEnumConstant(StringRef name); + /// Look for information regarding the given tag /// (struct/union/enum/C++ class). /// @@ -147,6 +154,10 @@ class APINotesReader { virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + /// Visit an enumerator. + virtual void visitEnumConstant(StringRef name, + const EnumConstantInfo &info); + /// Visit a tag. virtual void visitTag(StringRef name, const TagInfo &info); diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index 38935a6a271c9..66dc8ffebbaeb 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -90,6 +90,12 @@ class APINotesWriter { /// \param info Information about this global function. void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + /// Add information about an enumerator. + /// + /// \param name The name of this enumerator. + /// \param info Information about this enumerator. + void addEnumConstant(StringRef name, const EnumConstantInfo &info); + /// Add information about a tag (struct/union/enum/C++ class). /// /// \param name The name of this tag. diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 5053190f408eb..2fc4ee8fe7328 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -456,6 +456,12 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() : FunctionInfo() { } }; +/// Describes API notes data for an enumerator. +class EnumConstantInfo : public CommonEntityInfo { +public: + EnumConstantInfo() : CommonEntityInfo() { } +}; + /// Describes API notes data for a tag. class TagInfo : public CommonTypeInfo { public: diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 761470ddfa19e..2e52a4d7fddf5 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 9; +const uint16_t VERSION_MINOR = 10; // enum constants using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; @@ -94,6 +94,10 @@ enum BlockID { /// The typedef data block, which maps typedef names to information about /// the typedefs. TYPEDEF_BLOCK_ID, + + /// The enum constant data block, which maps enumerator names to + /// information about the enumerators. + ENUM_CONSTANT_BLOCK_ID, }; namespace control_block { @@ -226,6 +230,18 @@ namespace typedef_block { >; }; +namespace enum_constant_block { + enum { + ENUM_CONSTANT_DATA = 1 + }; + + using EnumConstantDataLayout = BCRecordLayout< + ENUM_CONSTANT_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to enumerator information + >; +} + /// A stored Objective-C selector. struct StoredObjCSelector { unsigned NumPieces; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index f400ebf6457b4..40f0f8938857d 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -415,6 +415,51 @@ namespace { } }; + /// Used to deserialize the on-disk enumerator table. + class EnumConstantTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = EnumConstantInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext<uint32_t, little, unaligned>(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + EnumConstantInfo info; + readCommonEntityInfo(data, info); + return info; + } + }; + /// Used to deserialize the on-disk tag table. class TagTableInfo { public: @@ -559,6 +604,12 @@ class APINotesReader::Implementation { /// The global function table. std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable; + using SerializedEnumConstantTable = + llvm::OnDiskIterableChainedHashTable<EnumConstantTableInfo>; + + /// The enumerator table. + std::unique_ptr<SerializedEnumConstantTable> EnumConstantTable; + using SerializedTagTable = llvm::OnDiskIterableChainedHashTable<TagTableInfo>; @@ -595,6 +646,8 @@ class APINotesReader::Implementation { SmallVectorImpl<uint64_t> &scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl<uint64_t> &scratch); + bool readEnumConstantBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch); bool readTagBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl<uint64_t> &scratch); bool readTypedefBlock(llvm::BitstreamCursor &cursor, @@ -1069,6 +1122,60 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( return false; } +bool APINotesReader::Implementation::readEnumConstantBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl<uint64_t> &scratch) { + if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case enum_constant_block::ENUM_CONSTANT_DATA: { + // Already saw enumerator table. + if (EnumConstantTable) + return true; + + uint32_t tableOffset; + enum_constant_block::EnumConstantDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + EnumConstantTable.reset( + SerializedEnumConstantTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + bool APINotesReader::Implementation::readTagBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl<uint64_t> &scratch) { @@ -1273,6 +1380,14 @@ APINotesReader::APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, } break; + case ENUM_CONSTANT_BLOCK_ID: + if (!hasValidControlBlock || + Impl.readEnumConstantBlock(cursor, scratch)) { + failed = true; + return; + } + break; + case TAG_BLOCK_ID: if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) { failed = true; @@ -1428,6 +1543,21 @@ Optional<GlobalFunctionInfo> APINotesReader::lookupGlobalFunction( return *known; } +Optional<EnumConstantInfo> APINotesReader::lookupEnumConstant(StringRef name) { + if (!Impl.EnumConstantTable) + return None; + + Optional<IdentifierID> nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.EnumConstantTable->find(*nameID); + if (known == Impl.EnumConstantTable->end()) + return None; + + return *known; +} + Optional<TagInfo> APINotesReader::lookupTag(StringRef name) { if (!Impl.TagTable) return None; @@ -1485,6 +1615,10 @@ void APINotesReader::Visitor::visitGlobalFunction( StringRef name, const GlobalFunctionInfo &info) { } +void APINotesReader::Visitor::visitEnumConstant( + StringRef name, + const EnumConstantInfo &info) { } + void APINotesReader::Visitor::visitTag( StringRef name, const TagInfo &info) { } @@ -1580,6 +1714,15 @@ void APINotesReader::visit(Visitor &visitor) { } } + // Visit global variables. + if (Impl.EnumConstantTable) { + for (auto key : Impl.EnumConstantTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.EnumConstantTable->find(key); + visitor.visitEnumConstant(name, info); + } + } + // Visit tags. if (Impl.TagTable) { for (auto key : Impl.TagTable->keys()) { diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 2d55c927c703f..dd90ee2e59012 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -77,6 +77,11 @@ class APINotesWriter::Implementation { /// Indexed by the identifier ID. llvm::DenseMap<unsigned, GlobalFunctionInfo> GlobalFunctions; + /// Information about enumerators. + /// + /// Indexed by the identifier ID. + llvm::DenseMap<unsigned, EnumConstantInfo> EnumConstants; + /// Information about tags. /// /// Indexed by the identifier ID. @@ -133,6 +138,7 @@ class APINotesWriter::Implementation { void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); + void writeEnumConstantBlock(llvm::BitstreamWriter &writer); void writeTagBlock(llvm::BitstreamWriter &writer); void writeTypedefBlock(llvm::BitstreamWriter &writer); }; @@ -753,6 +759,68 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( layout.emit(ScratchRecord, tableOffset, hashTableBlob); } +namespace { + /// Used to serialize the on-disk global enum constant. + class EnumConstantTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = EnumConstantInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(uint32_t); + uint32_t dataLength = getCommonEntityInfoSize(data); + endian::Writer<little> writer(out); + writer.write<uint16_t>(keyLength); + writer.write<uint16_t>(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<uint32_t>(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonEntityInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeEnumConstantBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, ENUM_CONSTANT_BLOCK_ID, 3); + + if (EnumConstants.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<EnumConstantTableInfo> generator; + for (auto &entry : EnumConstants) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + enum_constant_block::EnumConstantDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + namespace { /// Used to serialize the on-disk tag table. class TagTableInfo { @@ -897,6 +965,7 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { writeObjCSelectorBlock(writer); writeGlobalVariableBlock(writer); writeGlobalFunctionBlock(writer); + writeEnumConstantBlock(writer); writeTagBlock(writer); writeTypedefBlock(writer); } @@ -1003,6 +1072,13 @@ void APINotesWriter::addGlobalFunction(llvm::StringRef name, Impl.GlobalFunctions[nameID] = info; } +void APINotesWriter::addEnumConstant(llvm::StringRef name, + const EnumConstantInfo &info) { + IdentifierID enumConstantID = Impl.getIdentifier(name); + assert(!Impl.EnumConstants.count(enumConstantID)); + Impl.EnumConstants[enumConstantID] = info; +} + void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { IdentifierID tagID = Impl.getIdentifier(name); assert(!Impl.Tags.count(tagID)); diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 39f6c9d641084..9ad6dd6a08533 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -216,6 +216,13 @@ namespace { }; typedef std::vector<GlobalVariable> GlobalVariablesSeq; + struct EnumConstant { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + }; + typedef std::vector<EnumConstant> EnumConstantsSeq; + struct Tag { StringRef Name; AvailabilityItem Availability; @@ -239,6 +246,7 @@ namespace { ClassesSeq Protocols; FunctionsSeq Functions; GlobalVariablesSeq Globals; + EnumConstantsSeq EnumConstants; TagsSeq Tags; TypedefsSeq Typedefs; @@ -254,6 +262,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Property) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) +LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant) LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) @@ -369,6 +378,16 @@ namespace llvm { } }; + template <> + struct MappingTraits<EnumConstant> { + static void mapping(IO &io, EnumConstant& v) { + io.mapRequired("Name", v.Name); + io.mapOptional("Availability", v.Availability.Mode); + io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftName", v.SwiftName); + } + }; + template <> struct MappingTraits<Tag> { static void mapping(IO &io, Tag& t) { @@ -401,6 +420,7 @@ namespace llvm { io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); io.mapOptional("Globals", m.Globals); + io.mapOptional("Enumerators", m.EnumConstants); io.mapOptional("Tags", m.Tags); io.mapOptional("Typedefs", m.Typedefs); } @@ -701,6 +721,24 @@ namespace { Writer->addGlobalFunction(function.Name, info); } + // Write all enumerators. + llvm::StringSet<> knownEnumConstants; + for (const auto &enumConstant : TheModule.EnumConstants) { + // Check for duplicate enumerators + if (!knownEnumConstants.insert(enumConstant.Name).second) { + emitError("multiple definitions of enumerator '" + + enumConstant.Name + "'"); + continue; + } + + EnumConstantInfo info; + if (!isAvailable(enumConstant.Availability)) + continue; + convertAvailability(enumConstant.Availability, info, enumConstant.Name); + info.SwiftName = enumConstant.SwiftName; + Writer->addEnumConstant(enumConstant.Name, info); + } + // Write all tags. llvm::StringSet<> knownTags; for (const auto &t : TheModule.Tags) { @@ -947,6 +985,15 @@ namespace { TheModule.Globals.push_back(global); } + virtual void visitEnumConstant(StringRef name, + const EnumConstantInfo &info) { + EnumConstant enumConstant; + enumConstant.Name = name; + handleCommon(enumConstant, info); + + TheModule.EnumConstants.push_back(enumConstant); + } + virtual void visitTag(StringRef name, const TagInfo &info) { Tag tag; tag.Name = name; @@ -1029,6 +1076,12 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, return lhs.Name < rhs.Name; }); + // Sort enum constants. + std::sort(module.EnumConstants.begin(), module.EnumConstants.end(), + [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + // Sort tags. std::sort(module.Tags.begin(), module.Tags.end(), [](const Tag &lhs, const Tag &rhs) -> bool { diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index fd70e94cd32a8..bef88e2f87227 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -224,6 +224,15 @@ static void ProcessAPINotes(Sema &S, FunctionDecl *D, static_cast<const api_notes::FunctionInfo &>(Info)); } +/// Process API notes for an enumerator. +static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, + const api_notes::EnumConstantInfo &Info) { + + // Handle common information. + ProcessAPINotes(S, D, + static_cast<const api_notes::CommonEntityInfo &>(Info)); +} + /// Process API notes for an Objective-C method. static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, const api_notes::ObjCMethodInfo &Info) { @@ -360,6 +369,20 @@ void Sema::ProcessAPINotes(Decl *D) { return; } + // Enumerators. + if (D->getDeclContext()->getRedeclContext()->isFileContext()) { + if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { + ::ProcessAPINotes(*this, EnumConstant, *Info); + } + } + + return; + } + } + if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) { // Location function that looks up an Objective-C context. auto GetContext = [&](api_notes::APINotesReader *Reader) diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 378c7258d51b7..c34907ff67dff 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -92,6 +92,11 @@ Globals: Availability: available AvailabilityMsg: '' SwiftName: calibratedWhite +Enumerators: + - Name: NSColorRed + Availability: available + AvailabilityMsg: '' + SwiftName: Red Tags: - Name: NSSomeStruct Availability: available From ee44727d08e96f4beac295b1449fe2380ffaec49 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 28 Mar 2016 13:24:21 -0700 Subject: [PATCH 040/582] [API Notes] Document enumerators. NFC apple-llvm-split-commit: 6655e5c223c943fcb407af0c71d9a85d605c599c apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 9ad6dd6a08533..a92529eacfbd0 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -57,6 +57,8 @@ ... Globals: # List of globals ... + Enumerators: # List of enumerators + ... Tags: # List of tags (struct/union/enum/C++ class) ... Typedefs: # List of typedef-names and C++11 type aliases From c9b36058aa7c7daeeccc4b78e0056082dae33edb Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 28 Mar 2016 14:00:13 -0700 Subject: [PATCH 041/582] [API notes] Allow tags and typedefs to have the same name. apple-llvm-split-commit: 7b1da27e88dba8f7bc3d51b1ff4a2874e65ca89d apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index a92529eacfbd0..4e84b9fbf1a79 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -761,7 +761,7 @@ namespace { llvm::StringSet<> knownTypedefs; for (const auto &t : TheModule.Typedefs) { // Check for duplicate typedef definitions. - if (!knownTags.insert(t.Name).second) { + if (!knownTypedefs.insert(t.Name).second) { emitError("multiple definitions of typedef '" + t.Name + "'"); continue; } From a62f3b9ba8c419e678d7dad7dd47a8c43cbc8c24 Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Tue, 29 Mar 2016 11:21:53 -0700 Subject: [PATCH 042/582] Remove test/Bitcode/swiftself.ll. The content is now in test/Bitcode/attributes.ll. apple-llvm-split-commit: 68893305c9bc4cc4f8250bb259b94d006de75606 apple-llvm-split-dir: llvm/ --- llvm/test/Bitcode/swiftself.ll | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 llvm/test/Bitcode/swiftself.ll diff --git a/llvm/test/Bitcode/swiftself.ll b/llvm/test/Bitcode/swiftself.ll deleted file mode 100644 index e9293ec98b3dd..0000000000000 --- a/llvm/test/Bitcode/swiftself.ll +++ /dev/null @@ -1,8 +0,0 @@ -; RUN: llvm-as < %s | llvm-dis | FileCheck %s -; RUN: verify-uselistorder < %s - -define void @test(i8* swiftself) -; CHECK: define void @test(i8* swiftself) -{ - ret void; -} From cac31c16d04d6b2dceac626eedad60e0ea6416b8 Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Tue, 29 Mar 2016 11:28:19 -0700 Subject: [PATCH 043/582] Fix merge error. apple-llvm-split-commit: 7b81cc42518899f5497b13479da068d8e9a5a4d8 apple-llvm-split-dir: llvm/ --- llvm/lib/CodeGen/SelectionDAG/FastISel.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp index d718214f97aaa..6a691de4aecc9 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -90,10 +90,7 @@ void FastISel::ArgListEntry::setAttributes(ImmutableCallSite *CS, IsInAlloca = CS->paramHasAttr(AttrIdx, Attribute::InAlloca); IsReturned = CS->paramHasAttr(AttrIdx, Attribute::Returned); IsSwiftSelf = CS->paramHasAttr(AttrIdx, Attribute::SwiftSelf); -<<<<<<< HEAD IsSwiftError = CS->paramHasAttr(AttrIdx, Attribute::SwiftError); -======= ->>>>>>> mirror/master Alignment = CS->getParamAlignment(AttrIdx); } @@ -964,11 +961,8 @@ bool FastISel::lowerCallTo(CallLoweringInfo &CLI) { Flags.setSRet(); if (Arg.IsSwiftSelf) Flags.setSwiftSelf(); -<<<<<<< HEAD if (Arg.IsSwiftError) Flags.setSwiftError(); -======= ->>>>>>> mirror/master if (Arg.IsByVal) Flags.setByVal(); if (Arg.IsInAlloca) { From 8b235645d1def8a9d3962a235a86bd72f687af88 Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Tue, 29 Mar 2016 11:37:19 -0700 Subject: [PATCH 044/582] Fix merge error again. apple-llvm-split-commit: 0e8d609bab4cc1772be1fc308c724320df193635 apple-llvm-split-dir: llvm/ --- llvm/lib/Target/ARM/ARMCallingConv.td | 30 ++++++--------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/llvm/lib/Target/ARM/ARMCallingConv.td b/llvm/lib/Target/ARM/ARMCallingConv.td index 6c95a1b98e9ea..21ec04d0911d7 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.td +++ b/llvm/lib/Target/ARM/ARMCallingConv.td @@ -23,18 +23,12 @@ def CC_ARM_APCS : CallingConv<[ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>, -<<<<<<< HEAD - // An SwiftSelf is passed in R9. + // A SwiftSelf is passed in R9. CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, - // An SwiftError is passed in R6. + // A SwiftError is passed in R6. CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, -======= - // A SwiftSelf is passed in R9. - CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, - ->>>>>>> mirror/master // Handle all vector types as either f64 or v2f64. CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, @@ -166,18 +160,12 @@ def CC_ARM_AAPCS : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, -<<<<<<< HEAD - // An SwiftSelf is passed in R9. + // A SwiftSelf is passed in R9. CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, - // An SwiftError is passed in R6. + // A SwiftError is passed in R6. CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, -======= - // A SwiftSelf is passed in R9. - CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, - ->>>>>>> mirror/master CCIfType<[f64, v2f64], CCCustom<"CC_ARM_AAPCS_Custom_f64">>, CCIfType<[f32], CCBitConvertToType<i32>>, CCDelegateTo<CC_ARM_AAPCS_Common> @@ -209,18 +197,12 @@ def CC_ARM_AAPCS_VFP : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, -<<<<<<< HEAD - // An SwiftSelf is passed in R9. + // A SwiftSelf is passed in R9. CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, - // An SwiftError is passed in R6. + // A SwiftError is passed in R6. CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, -======= - // A SwiftSelf is passed in R9. - CCIfSwiftSelf<CCIfType<[i32], CCAssignToReg<[R9]>>>, - ->>>>>>> mirror/master // HFAs are passed in a contiguous block of registers, or on the stack CCIfConsecutiveRegs<CCCustom<"CC_ARM_AAPCS_Custom_Aggregate">>, From 4abe07ef9f2a3e26f417930f113236c74e14f71c Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Wed, 30 Mar 2016 15:50:56 -0700 Subject: [PATCH 045/582] [Import as member] Module map attribute swift_infer_import_as_member Introduces a new module map attribute, allowing a module to opt-into import-as-member inference. This allows the provider of the APIs to use inference, rather than require building with a flag. apple-llvm-split-commit: 74fdc2be88845ac7e1fd24e12eb11bc44d400db2 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Module.h | 3 +++ clang/include/clang/Lex/ModuleMap.h | 3 +++ clang/lib/Basic/Module.cpp | 2 ++ clang/lib/Lex/ModuleMap.cpp | 9 ++++++++- clang/test/Modules/Inputs/swift_name/module.modulemap | 2 ++ clang/test/Modules/infer_swift_name.m | 6 ++++++ 6 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 clang/test/Modules/Inputs/swift_name/module.modulemap create mode 100644 clang/test/Modules/infer_swift_name.m diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index c95968d9a755f..f8bdf0618fe9d 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -183,6 +183,9 @@ class Module { /// \brief Whether this is an inferred submodule (module * { ... }). unsigned IsInferred : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// \brief Whether we should infer submodules for this module based on /// the headers. /// diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index d9d66c1065074..4d7d4436ac6c6 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -176,6 +176,9 @@ class ModuleMap { /// \brief Whether this is an exhaustive set of configuration macros. unsigned IsExhaustive : 1; + + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; }; /// \brief A directory for which framework modules can be inferred. diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 48d36b67db3fe..fbae283fa6964 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -337,6 +337,8 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS << " [system]"; if (IsExternC) OS << " [extern_c]"; + if (IsSwiftInferImportAsMember) + OS << " [swift_infer_import_as_member]"; } OS << " {\n"; diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index 8fe88c8197323..8a2d2584de8e7 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -1301,7 +1301,9 @@ namespace { /// \brief The 'extern_c' attribute. AT_extern_c, /// \brief The 'exhaustive' attribute. - AT_exhaustive + AT_exhaustive, + // \brief The 'swift_infer_import_as_member' attribute. + AT_swift_infer_import_as_member, }; } @@ -2379,6 +2381,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { .Case("exhaustive", AT_exhaustive) .Case("extern_c", AT_extern_c) .Case("system", AT_system) + .Case("swift_infer_import_as_member", AT_swift_infer_import_as_member) .Default(AT_unknown); switch (Attribute) { case AT_unknown: @@ -2394,6 +2397,10 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { Attrs.IsExternC = true; break; + case AT_swift_infer_import_as_member: + Attrs.IsSwiftInferImportAsMember = true; + break; + case AT_exhaustive: Attrs.IsExhaustive = true; break; diff --git a/clang/test/Modules/Inputs/swift_name/module.modulemap b/clang/test/Modules/Inputs/swift_name/module.modulemap new file mode 100644 index 0000000000000..b7ec6b15988b0 --- /dev/null +++ b/clang/test/Modules/Inputs/swift_name/module.modulemap @@ -0,0 +1,2 @@ +module SwiftNameInferred [swift_infer_import_as_member] { +} \ No newline at end of file diff --git a/clang/test/Modules/infer_swift_name.m b/clang/test/Modules/infer_swift_name.m new file mode 100644 index 0000000000000..d4b4a5d4979bd --- /dev/null +++ b/clang/test/Modules/infer_swift_name.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/swift_name %s -verify +// REQUIRES: shell + +@import SwiftNameInferred; // ok +@import SwiftName; // expected-error{{module 'SwiftName' not found}} From e1754572da6f9a072840c30b09fd18dc8640bd7e Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Wed, 30 Mar 2016 17:36:32 -0700 Subject: [PATCH 046/582] [Import as member] APINotes for SwiftInferImportAsMember. Adds APINotes support for adding the swift_infer_import_as_member module map attribute. apple-llvm-split-commit: 7b3e765344c9c89e9b083b92101438d37f41162d apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 3 +++ clang/include/clang/APINotes/APINotesWriter.h | 3 +++ clang/include/clang/APINotes/Types.h | 5 +++++ clang/lib/APINotes/APINotesFormat.h | 8 +++++++- clang/lib/APINotes/APINotesReader.cpp | 10 ++++++++++ clang/lib/APINotes/APINotesWriter.cpp | 12 ++++++++++++ clang/lib/APINotes/APINotesYAMLCompiler.cpp | 11 +++++++++++ clang/test/APINotes/Inputs/Headers/APINotes.apinotes | 1 + clang/test/APINotes/Inputs/roundtrip.apinotes | 1 + 9 files changed, 53 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 839e1dc5ee942..29dcc1f56f920 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -50,6 +50,9 @@ class APINotesReader { /// notes. StringRef getModuleName() const; + /// Retrieve the module options + ModuleOptions getModuleOptions() const; + /// Look for information regarding the given Objective-C class. /// /// \param name The name of the class we're looking for. diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index 66dc8ffebbaeb..aa41756b2e778 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -107,6 +107,9 @@ class APINotesWriter { /// \param name The name of this typedef. /// \param info Information about this typedef. void addTypedef(StringRef name, const TypedefInfo &info); + + /// Add module options + void addModuleOptions(ModuleOptions opts); }; } // end namespace api_notes diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 2fc4ee8fe7328..7b4d0647ef24d 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -474,6 +474,11 @@ class TypedefInfo : public CommonTypeInfo { TypedefInfo() : CommonTypeInfo() { } }; +/// Descripts a series of options for a module +struct ModuleOptions { + bool SwiftInferImportAsMember; +}; + } // end namespace api_notes } // end namespace clang diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 2e52a4d7fddf5..bc57e451ec549 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -105,7 +105,8 @@ namespace control_block { // VERSION_MAJOR. enum { METADATA = 1, - MODULE_NAME = 2 + MODULE_NAME = 2, + MODULE_OPTIONS = 3 }; using MetadataLayout = BCRecordLayout< @@ -118,6 +119,11 @@ namespace control_block { MODULE_NAME, BCBlob // Module name >; + + using ModuleOptionsLayout = BCRecordLayout< + MODULE_OPTIONS, + BCFixed<1> // SwiftInferImportAsMember + >; } namespace identifier_block { diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 40f0f8938857d..67574c9d4b2c9 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -562,6 +562,9 @@ class APINotesReader::Implementation { /// The name of the module that we read from the control block. std::string ModuleName; + /// Various options and attributes for the module + ModuleOptions moduleOptions; + using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>; @@ -735,6 +738,9 @@ bool APINotesReader::Implementation::readControlBlock( ModuleName = blobData.str(); break; + case control_block::MODULE_OPTIONS: + moduleOptions = {(scratch.front() & 1) != 0}; + default: // Unknown metadata record, possibly for use by a future version of the // module format. @@ -1440,6 +1446,10 @@ StringRef APINotesReader::getModuleName() const { return Impl.ModuleName; } +ModuleOptions APINotesReader::getModuleOptions() const { + return Impl.moduleOptions; +} + auto APINotesReader::lookupObjCClass(StringRef name) -> Optional<std::pair<ContextID, ObjCContextInfo>> { if (!Impl.ObjCContextTable) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index dd90ee2e59012..d2bd6f19ec88c 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -43,6 +43,8 @@ class APINotesWriter::Implementation { /// The name of the module std::string ModuleName; + bool SwiftInferImportAsMember = false; + /// Information about Objective-C contexts (classes or protocols). /// /// Indexed by the identifier ID and a bit indication whether we're looking @@ -214,6 +216,11 @@ void APINotesWriter::Implementation::writeControlBlock( control_block::ModuleNameLayout moduleName(writer); moduleName.emit(ScratchRecord, ModuleName); + + if (SwiftInferImportAsMember) { + control_block::ModuleOptionsLayout moduleOptions(writer); + moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember); + } } namespace { @@ -1090,3 +1097,8 @@ void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { assert(!Impl.Typedefs.count(typedefID)); Impl.Typedefs[typedefID] = info; } + +void APINotesWriter::addModuleOptions(ModuleOptions opts) { + Impl.SwiftInferImportAsMember = opts.SwiftInferImportAsMember; +} + diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 4e84b9fbf1a79..6fe87bc7fa256 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -252,6 +252,8 @@ namespace { TagsSeq Tags; TypedefsSeq Typedefs; + llvm::Optional<bool> SwiftInferImportAsMember; + LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, "only for use within the debugger"); @@ -418,6 +420,7 @@ namespace llvm { io.mapRequired("Name", m.Name); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember); io.mapOptional("Classes", m.Classes); io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); @@ -773,6 +776,9 @@ namespace { Writer->addTypedef(t.Name, typedefInfo); } + if (TheModule.SwiftInferImportAsMember) + Writer->addModuleOptions({true}); + if (!ErrorOccured) Writer->writeToStream(OS); @@ -1034,6 +1040,11 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, // Set module name. module.Name = reader->getModuleName(); + // Set module options + auto opts = reader->getModuleOptions(); + if (opts.SwiftInferImportAsMember) + module.SwiftInferImportAsMember = true; + // Sort classes. std::sort(module.Classes.begin(), module.Classes.end(), [](const Class &lhs, const Class &rhs) -> bool { diff --git a/clang/test/APINotes/Inputs/Headers/APINotes.apinotes b/clang/test/APINotes/Inputs/Headers/APINotes.apinotes index a4ddafe2892ec..08210fc705651 100644 --- a/clang/test/APINotes/Inputs/Headers/APINotes.apinotes +++ b/clang/test/APINotes/Inputs/Headers/APINotes.apinotes @@ -1,4 +1,5 @@ Name: HeaderLib +SwiftInferImportAsMember: true Functions: - Name: custom_realloc NullabilityOfRet: N diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index c34907ff67dff..8509c79389b71 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -2,6 +2,7 @@ Name: AppKit Availability: available AvailabilityMsg: '' +SwiftInferImportAsMember: true Classes: - Name: NSCell Availability: available From 7a5269f2976b4c8361a3f16c26c2e4cbb3b27625 Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Fri, 1 Apr 2016 11:22:42 -0700 Subject: [PATCH 047/582] [APINotes] Apply ModuleOptions CompilerInstance will now check for ModuleOptions in the current module's APINotes, and apply them if present. apple-llvm-split-commit: 9defcfa9fd3486b91fad78974dafcb12cdd4c786 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesManager.h | 4 ++++ clang/lib/Frontend/CompilerInstance.cpp | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index 69752a9c77e90..7f540b9c17dee 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -111,6 +111,10 @@ class APINotesManager { /// Find the API notes reader that corresponds to the given source location. APINotesReader *findAPINotes(SourceLocation Loc); + + APINotesReader *getCurrentModuleReader() { + return CurrentModuleReader.get(); + } }; } // end namespace api_notes diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 46f9d43d169cf..e4f8c597064b0 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/CompilerInstance.h" +#include "clang/APINotes/APINotesReader.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -537,10 +538,21 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, TUKind, CompletionConsumer)); // If we're building a module, notify the API notes manager. - if (!getLangOpts().CurrentModule.empty()) { + StringRef currentModuleName = getLangOpts().CurrentModule; + if (!currentModuleName.empty()) { (void)TheSema->APINotes.loadCurrentModuleAPINotes( - getLangOpts().CurrentModule, + currentModuleName, getAPINotesOpts().ModuleSearchPaths); + // Check for any attributes we should add to the module + if (auto curReader = TheSema->APINotes.getCurrentModuleReader()) { + auto currentModule = getPreprocessor().getCurrentModule(); + assert(currentModule && "how can we have a reader for it?"); + + // swift_infer_import_as_member + if (curReader->getModuleOptions().SwiftInferImportAsMember) { + currentModule->IsSwiftInferImportAsMember = true; + } + } } } From 85cc59e541628ae283495b1004a7e3f43c89841a Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Fri, 1 Apr 2016 13:34:49 -0700 Subject: [PATCH 048/582] [APINotes] Incorporate Doug's feedback apple-llvm-split-commit: d3f65076e94c422efe213c9a3c8f015be8bd0bf2 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index bc57e451ec549..79143c48533db 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 10; // enum constants +const uint16_t VERSION_MINOR = 11; // SwiftInferImportAsMember using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 67574c9d4b2c9..b05eb9d603db6 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -563,7 +563,7 @@ class APINotesReader::Implementation { std::string ModuleName; /// Various options and attributes for the module - ModuleOptions moduleOptions; + ModuleOptions ModuleOpts; using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>; @@ -739,7 +739,8 @@ bool APINotesReader::Implementation::readControlBlock( break; case control_block::MODULE_OPTIONS: - moduleOptions = {(scratch.front() & 1) != 0}; + ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0; + break; default: // Unknown metadata record, possibly for use by a future version of the @@ -1447,7 +1448,7 @@ StringRef APINotesReader::getModuleName() const { } ModuleOptions APINotesReader::getModuleOptions() const { - return Impl.moduleOptions; + return Impl.ModuleOpts; } auto APINotesReader::lookupObjCClass(StringRef name) From ffe0b11e1fc902112f02ab46c03a9ec2fb910bce Mon Sep 17 00:00:00 2001 From: Douglas Gregor <dgregor@apple.com> Date: Fri, 1 Apr 2016 23:23:52 +0000 Subject: [PATCH 049/582] [Objective-C] Introduce objc_runtime_visible attribute. The objc_runtime_visible attribute deals with an odd corner case where a particular Objective-C class is known to the Objective-C runtime (and, therefore, accessible by name) but its symbol has been hidden for some reason. For such classes, teach CodeGen to use objc_lookUpClass to retrieve the Class object, rather than referencing the class symbol directly. Classes annotated with objc_runtime_visible have two major limitations that fall out from places where Objective-C metadata needs to refer to the class (or metaclass) symbol directly: * One cannot implement a subclass of an objc_runtime_visible class. * One cannot implement a category on an objc_runtime_visible class. Implements rdar://problem/25494092. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265201 91177308-0d34-0410-b5e6-96231b3b80d8 apple-llvm-split-commit: dfc8d2d85ae7bc36824c3f3427207710c9051d6e apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 6 +++ clang/include/clang/Basic/AttrDocs.td | 7 +++ .../clang/Basic/DiagnosticSemaKinds.td | 6 +++ clang/lib/CodeGen/CGObjCMac.cpp | 49 +++++++++++++++++++ clang/lib/Sema/SemaDeclAttr.cpp | 3 ++ clang/lib/Sema/SemaDeclObjC.cpp | 17 +++++++ .../CodeGenObjC/attr-objc-runtime-visible.m | 19 +++++++ .../test/SemaObjC/attr-objc-runtime-visible.m | 19 +++++++ 8 files changed, 126 insertions(+) create mode 100644 clang/test/CodeGenObjC/attr-objc-runtime-visible.m create mode 100644 clang/test/SemaObjC/attr-objc-runtime-visible.m diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 85a54b6569505..cab04d247ff58 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1269,6 +1269,12 @@ def ObjCRuntimeName : Attr { let Documentation = [ObjCRuntimeNameDocs]; } +def ObjCRuntimeVisible : Attr { + let Spellings = [GNU<"objc_runtime_visible">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [ObjCRuntimeVisibleDocs]; +} + def ObjCBoxable : Attr { let Spellings = [GNU<"objc_boxable">]; let Subjects = SubjectList<[Record], ErrorDiag, "ExpectedStructOrUnion">; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 521b38f650997..317fdb4385032 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -612,6 +612,13 @@ can only be placed before an @protocol or @interface declaration: }]; } +def ObjCRuntimeVisibleDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute specifies that the Objective-C class to which it applies is visible to the Objective-C runtime but not to the linker. Classes annotated with this attribute cannot be subclassed and cannot have categories defined for them. + }]; +} + def ObjCBoxableDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 85e9f5b3f41ec..edf9d47eddc3c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -717,6 +717,12 @@ def err_restricted_superclass_mismatch : Error< def warn_objc_root_class_missing : Warning< "class %0 defined without specifying a base class">, InGroup<ObjCRootClass>; +def err_objc_runtime_visible_category : Error< + "cannot implement a category for class %0 that is only visible via the " + "Objective-C runtime">; +def err_objc_runtime_visible_subclass : Error< + "cannot implement subclass %0 of a superclass %1 that is only visible via the " + "Objective-C runtime">; def note_objc_needs_superclass : Note< "add a super class to fix this problem">; def warn_dup_category_def : Warning< diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index b2301bdaf2a78..df98ae0669a4b 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -349,6 +349,20 @@ class ObjCCommonTypesHelper { return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } + llvm::Constant *getLookUpClassFn() { + CodeGen::CodeGenTypes &Types = CGM.getTypes(); + ASTContext &Ctx = CGM.getContext(); + // Class objc_lookUpClass (const char *) + SmallVector<CanQualType,1> Params; + Params.push_back( + Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); + llvm::FunctionType *FTy = + Types.GetFunctionType(Types.arrangeBuiltinFunctionDeclaration( + Ctx.getCanonicalType(Ctx.getObjCClassType()), + Params)); + return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); + } + /// GcReadWeakFn -- LLVM objc_read_weak (id *src) function. llvm::Constant *getGcReadWeakFn() { // id objc_read_weak (id *) @@ -981,6 +995,12 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { /// defined. The return value has type ProtocolPtrTy. llvm::Constant *GetProtocolRef(const ObjCProtocolDecl *PD); + /// Return a reference to the given Class using runtime calls rather than + /// by a symbol reference. + llvm::Value *EmitClassRefViaRuntime(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes); + public: /// CreateMetadataVar - Create a global variable with internal /// linkage for use by the Objective-C runtime. @@ -2674,6 +2694,25 @@ llvm::Constant *CGObjCCommonMac::GetProtocolRef(const ObjCProtocolDecl *PD) { return GetOrEmitProtocolRef(PD); } +llvm::Value *CGObjCCommonMac::EmitClassRefViaRuntime( + CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes) { + llvm::Constant *lookUpClassFn = ObjCTypes.getLookUpClassFn(); + + llvm::Value *className = + CGF.CGM.GetAddrOfConstantCString(ID->getObjCRuntimeNameAsString()) + .getPointer(); + ASTContext &ctx = CGF.CGM.getContext(); + className = + CGF.Builder.CreateBitCast(className, + CGF.ConvertType( + ctx.getPointerType(ctx.CharTy.withConst()))); + llvm::CallInst *call = CGF.Builder.CreateCall(lookUpClassFn, className); + call->setDoesNotThrow(); + return call; +} + /* // Objective-C 1.0 extensions struct _objc_protocol { @@ -4634,6 +4673,11 @@ llvm::Value *CGObjCMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr<ObjCRuntimeVisibleAttr>()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier()); } @@ -6894,6 +6938,11 @@ llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr<ObjCRuntimeVisibleAttr>()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier(), ID->isWeakImported(), ID); } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index f9e469621db67..4dae401c3d937 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5926,6 +5926,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCRuntimeName: handleObjCRuntimeName(S, D, Attr); break; + case AttributeList::AT_ObjCRuntimeVisible: + handleSimpleAttribute<ObjCRuntimeVisibleAttr>(S, D, Attr); + break; case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index b7eec4750b5c7..60410bc62b89a 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1836,6 +1836,13 @@ Decl *Sema::ActOnStartCategoryImplementation( if (IDecl) DiagnoseUseOfDecl(IDecl, ClassLoc); + // If the interface has the objc_runtime_visible attribute, we + // cannot implement a category for it. + if (IDecl && IDecl->hasAttr<ObjCRuntimeVisibleAttr>()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_category) + << IDecl->getDeclName(); + } + /// Check that CatName, category name, is not used in another implementation. if (CatIDecl) { if (CatIDecl->getImplementation()) { @@ -1973,6 +1980,16 @@ Decl *Sema::ActOnStartClassImplementation( dyn_cast<NamedDecl>(IDecl), IMPDecl->getLocation(), 1); } + + // If the superclass has the objc_runtime_visible attribute, we + // cannot implement a subclass of it. + if (IDecl->getSuperClass() && + IDecl->getSuperClass()->hasAttr<ObjCRuntimeVisibleAttr>()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_subclass) + << IDecl->getDeclName() + << IDecl->getSuperClass()->getDeclName(); + } + return ActOnObjCContainerStartDefinition(IMPDecl); } diff --git a/clang/test/CodeGenObjC/attr-objc-runtime-visible.m b/clang/test/CodeGenObjC/attr-objc-runtime-visible.m new file mode 100644 index 0000000000000..6e224e7189037 --- /dev/null +++ b/clang/test/CodeGenObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fobjc-runtime=macosx-10.9.0 -emit-llvm %s -o - | FileCheck %s + +// RUN: %clang_cc1 -triple i386-apple-darwin -fobjc-runtime=macosx-fragile-10.9.0 -emit-llvm %s -o - | FileCheck %s + +@interface Root ++(Class)class; +@end + +__attribute__((objc_runtime_visible)) +__attribute__((objc_runtime_name("MyRuntimeVisibleClass"))) +@interface A : Root +@end + +// CHECK: [[CLASSNAME:@.*]] = private unnamed_addr constant [22 x i8] c"MyRuntimeVisibleClass +// CHECK: define i8* @getClass() #0 { +Class getClass(void) { + // CHECK: call i8* @objc_lookUpClass(i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[CLASSNAME]], i32 0, i32 0)) #2 + return [A class]; +} diff --git a/clang/test/SemaObjC/attr-objc-runtime-visible.m b/clang/test/SemaObjC/attr-objc-runtime-visible.m new file mode 100644 index 0000000000000..b5ec809ff2f58 --- /dev/null +++ b/clang/test/SemaObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +__attribute__((objc_runtime_visible)) +@interface A +@end + +@interface A(X) +@end + +@implementation A(X) // expected-error{{cannot implement a category for class 'A' that is only visible via the Objective-C runtime}} +@end + +@interface B : A +@end + +@implementation B // expected-error{{cannot implement subclass 'B' of a superclass 'A' that is only visible via the Objective-C runtime}} +@end + + From 4b14de0a03dbfdd83811d71ed1c5fb4e4875c301 Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Sat, 2 Apr 2016 13:59:41 -0700 Subject: [PATCH 050/582] [Import as member] Default initialize new attribute to false apple-llvm-split-commit: 9c3676d2786793ded0d8576770cdc5943dfe4597 apple-llvm-split-dir: clang/ --- clang/lib/Basic/Module.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index fbae283fa6964..3cad40c7c0429 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -31,7 +31,8 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, IsMissingRequirement(false), HasIncompatibleModuleFile(false), IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit), IsSystem(false), IsExternC(false), - IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false), + IsInferred(false), IsSwiftInferImportAsMember(false), + InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), ConfigMacrosExhaustive(false), NameVisibility(Hidden) { if (Parent) { From af8875d5137d023d3f405119099e3fc1434a52b1 Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Mon, 4 Apr 2016 11:48:49 -0700 Subject: [PATCH 051/582] [Import as member] ASTReader/Writer for swift_infer_import_as_member apple-llvm-split-commit: 097804bb99d4afb1f0bb3f543e028a404ab8f6a2 apple-llvm-split-dir: clang/ --- clang/lib/Serialization/ASTReader.cpp | 2 ++ clang/lib/Serialization/ASTWriter.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index c0517e7db81ff..3dee7236a9888 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4488,6 +4488,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { bool IsExplicit = Record[Idx++]; bool IsSystem = Record[Idx++]; bool IsExternC = Record[Idx++]; + bool IsSwiftInferImportAsMember = Record[Idx++]; bool InferSubmodules = Record[Idx++]; bool InferExplicitSubmodules = Record[Idx++]; bool InferExportWildcard = Record[Idx++]; @@ -4532,6 +4533,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { CurrentModule->IsFromModuleFile = true; CurrentModule->IsSystem = IsSystem || CurrentModule->IsSystem; CurrentModule->IsExternC = IsExternC; + CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember; CurrentModule->InferSubmodules = InferSubmodules; CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules; CurrentModule->InferExportWildcard = InferExportWildcard; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index c77a7099642db..e520d13e1e2ca 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -2428,6 +2428,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExplicit Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExternC + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferSubmodules... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild... @@ -2522,9 +2523,9 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { { RecordData::value_type Record[] = { SUBMODULE_DEFINITION, ID, ParentID, Mod->IsFramework, Mod->IsExplicit, - Mod->IsSystem, Mod->IsExternC, Mod->InferSubmodules, - Mod->InferExplicitSubmodules, Mod->InferExportWildcard, - Mod->ConfigMacrosExhaustive}; + Mod->IsSystem, Mod->IsExternC, Mod->IsSwiftInferImportAsMember, + Mod->InferSubmodules, Mod->InferExplicitSubmodules, + Mod->InferExportWildcard, Mod->ConfigMacrosExhaustive}; Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name); } From bdc785943492921c6e834b978952194692fa1801 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Mon, 4 Apr 2016 15:57:22 -0700 Subject: [PATCH 052/582] Fix for the fact that the IR asm writer actually implements swiftcc correctly on this branch. apple-llvm-split-commit: aee0cff195d01ce19cf013efab2a6d31113e87cc apple-llvm-split-dir: clang/ --- clang/test/CodeGen/arm-swiftcall.c | 2 +- clang/test/CodeGenCXX/arm-swiftcall.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/CodeGen/arm-swiftcall.c b/clang/test/CodeGen/arm-swiftcall.c index f8c97c418a5ab..d54a31337085f 100644 --- a/clang/test/CodeGen/arm-swiftcall.c +++ b/clang/test/CodeGen/arm-swiftcall.c @@ -47,7 +47,7 @@ void test_context_error_1() { // CHECK: [[TEMP:%.*]] = alloca swifterror float*, align 4 // CHECK: [[T0:%.*]] = load float*, float** [[ERROR]], align 4 // CHECK: store float* [[T0]], float** [[TEMP]], align 4 -// CHECK: call [[SWIFTCC:cc16]] void @context_error_1(i32* swiftself [[X]], float** swifterror [[TEMP]]) +// CHECK: call [[SWIFTCC:swiftcc]] void @context_error_1(i32* swiftself [[X]], float** swifterror [[TEMP]]) // CHECK: [[T0:%.*]] = load float*, float** [[TEMP]], align 4 // CHECK: store float* [[T0]], float** [[ERROR]], align 4 diff --git a/clang/test/CodeGenCXX/arm-swiftcall.cpp b/clang/test/CodeGenCXX/arm-swiftcall.cpp index d67a9a0282d6f..535350c808d3a 100644 --- a/clang/test/CodeGenCXX/arm-swiftcall.cpp +++ b/clang/test/CodeGenCXX/arm-swiftcall.cpp @@ -77,7 +77,7 @@ TEST(struct_1); // CHECK: ret void // CHECK-LABEL: define void @test_struct_1() // CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 -// CHECK: [[CALL:%.*]] = call [[SWIFTCC:cc16]] [[UAGG]] @return_struct_1() +// CHECK: [[CALL:%.*]] = call [[SWIFTCC:swiftcc]] [[UAGG]] @return_struct_1() // CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* // CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 // CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 From e780b78883bb413db6f866424d47b5982294cd8a Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 5 Apr 2016 09:45:49 -0700 Subject: [PATCH 053/582] Ignore redundant, implicit swift_name attributes. apple-llvm-split-commit: fba0d40ddee1793f9dc7bc15608bbd3b2cccf25f apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclAttr.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 4dae401c3d937..c46b138925e7a 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3689,8 +3689,12 @@ SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, SourceRange Range, // FIXME: Warn about an incompatible override. return nullptr; } - Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; - Diag(Range.getBegin(), diag::note_conflicting_attribute); + + if (Inline->getName() != Name && !Inline->isImplicit()) { + Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; + Diag(Range.getBegin(), diag::note_conflicting_attribute); + } + D->dropAttr<SwiftNameAttr>(); } From edef80528ccb9ad635756bbf05f32a2f04090b77 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 5 Apr 2016 09:47:08 -0700 Subject: [PATCH 054/582] [API notes] Make sure to remove our module caches before running tests. apple-llvm-split-commit: b5ef83c0a635f1049baf466eb4321784934fc434 apple-llvm-split-dir: clang/ --- clang/test/APINotes/nullability.c | 1 + clang/test/APINotes/nullability.m | 1 + clang/test/APINotes/objc_designated_inits.m | 1 + 3 files changed, 3 insertions(+) diff --git a/clang/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c index 54b0df6cea94c..86054eade82e4 100644 --- a/clang/test/APINotes/nullability.c +++ b/clang/test/APINotes/nullability.c @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index ba51cd64d825f..ce4beabb597ff 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/objc_designated_inits.m b/clang/test/APINotes/objc_designated_inits.m index 5eb5fa103d5d5..bbb50ba1a74bc 100644 --- a/clang/test/APINotes/objc_designated_inits.m +++ b/clang/test/APINotes/objc_designated_inits.m @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" From 7ea89a69d5a6586e03c2698c307998292ad5d69c Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Tue, 5 Apr 2016 16:17:46 -0700 Subject: [PATCH 055/582] Resolve merge error for r265482. apple-llvm-split-commit: 4a40a31dcde209ea82bbba284ccfb49edf06b515 apple-llvm-split-dir: llvm/ --- llvm/lib/Target/ARM/ARMFastISel.cpp | 1 - llvm/lib/Target/ARM/ARMISelLowering.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/llvm/lib/Target/ARM/ARMFastISel.cpp b/llvm/lib/Target/ARM/ARMFastISel.cpp index b3154bd8c0438..8bbad22ee3a91 100644 --- a/llvm/lib/Target/ARM/ARMFastISel.cpp +++ b/llvm/lib/Target/ARM/ARMFastISel.cpp @@ -1861,7 +1861,6 @@ CCAssignFn *ARMFastISel::CCAssignFnForCall(CallingConv::ID CC, default: llvm_unreachable("Unsupported calling convention"); case CallingConv::Fast: - case CallingConv::Swift: if (Subtarget->hasVFP2() && !isVarArg) { if (!Subtarget->isAAPCS_ABI()) return (Return ? RetFastCC_ARM_APCS : FastCC_ARM_APCS); diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp index b2d9fb95ed7c7..00446b61ed7c3 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -1400,7 +1400,6 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC, else return CallingConv::ARM_AAPCS; case CallingConv::Fast: - case CallingConv::Swift: case CallingConv::CXX_FAST_TLS: if (!Subtarget->isAAPCS_ABI()) { if (Subtarget->hasVFP2() && !Subtarget->isThumb1Only() && !isVarArg) From 4be3aa4419ed070406cac96b807dc9751e384da3 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 7 Apr 2016 10:38:40 -0700 Subject: [PATCH 056/582] [API notes] Process API notes for enumerators. apple-llvm-split-commit: f959c76dc38d96aea9bfd61df29655798957267b apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDecl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e421453cd19fe..b02cbbc5b36a7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14363,6 +14363,8 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, // Process attributes. if (Attr) ProcessDeclAttributeList(S, New, Attr); + ProcessAPINotes(New); + // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); PushOnScopeChains(New, S); From 8d420d7a99da1f24ee745493bcd9677aaf909d8a Mon Sep 17 00:00:00 2001 From: Manman Ren <manman.ren@gmail.com> Date: Mon, 11 Apr 2016 21:08:06 +0000 Subject: [PATCH 057/582] Swift Calling Convention: swifterror target support. Differential Revision: http://reviews.llvm.org/D18716 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@265997 91177308-0d34-0410-b5e6-96231b3b80d8 apple-llvm-split-commit: 86c76e126c4c8d714257e409ed1908113365a9dc apple-llvm-split-dir: llvm/ --- .../AArch64/AArch64CallingConvention.td | 6 +- llvm/lib/Target/AArch64/AArch64FastISel.cpp | 40 ++++--- .../Target/AArch64/AArch64FrameLowering.cpp | 3 +- llvm/lib/Target/AArch64/AArch64ISelLowering.h | 8 +- .../Target/AArch64/AArch64RegisterInfo.cpp | 30 ++--- llvm/lib/Target/AArch64/AArch64RegisterInfo.h | 5 - llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp | 4 +- llvm/lib/Target/ARM/ARMCallingConv.td | 6 +- llvm/lib/Target/ARM/ARMFastISel.cpp | 40 ++++--- llvm/lib/Target/ARM/ARMISelLowering.h | 8 +- llvm/lib/Target/X86/X86CallingConv.td | 4 +- llvm/lib/Target/X86/X86FastISel.cpp | 40 ++++--- llvm/lib/Target/X86/X86ISelLowering.cpp | 19 +++ llvm/lib/Target/X86/X86ISelLowering.h | 8 +- llvm/lib/Target/X86/X86RegisterInfo.cpp | 10 +- llvm/test/CodeGen/AArch64/swifterror.ll | 56 +++++---- llvm/test/CodeGen/ARM/swifterror.ll | 55 +++++---- llvm/test/CodeGen/X86/swifterror.ll | 111 +++++++++++++++--- 18 files changed, 293 insertions(+), 160 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.td b/llvm/lib/Target/AArch64/AArch64CallingConvention.td index 7877a7b6da4ac..f6bd012d8c8bf 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -278,10 +278,8 @@ def CSR_AArch64_AAPCS : CalleeSavedRegs<(add LR, FP, X19, X20, X21, X22, // case) def CSR_AArch64_AAPCS_ThisReturn : CalleeSavedRegs<(add CSR_AArch64_AAPCS, X0)>; -def CSR_AArch64_AAPCS_SwiftError : CalleeSavedRegs<(add LR, FP, X20, X21, X22, - X23, X24, X25, X26, X27, X28, - D8, D9, D10, D11, - D12, D13, D14, D15)>; +def CSR_AArch64_AAPCS_SwiftError + : CalleeSavedRegs<(sub CSR_AArch64_AAPCS, X19)>; // The function used by Darwin to obtain the address of a thread-local variable // guarantees more than a normal AAPCS function. x16 and x17 are used on the diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index c04878ba2e182..2015d12e79438 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -1901,14 +1901,18 @@ bool AArch64FastISel::selectLoad(const Instruction *I) { return false; const Value *SV = I->getOperand(0); - if (const Argument *Arg = dyn_cast<Argument>(SV)) { - if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) - return false; - } + if (TLI.supportSwiftError()) { + // Swifterror values can come from either a function parameter with + // swifterror attribute or an alloca with swifterror attribute. + if (const Argument *Arg = dyn_cast<Argument>(SV)) { + if (Arg->hasSwiftErrorAttr()) + return false; + } - if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { - if (Alloca->isSwiftError() && TLI.supportSwiftError()) - return false; + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { + if (Alloca->isSwiftError()) + return false; + } } // See if we can handle this address. @@ -2076,14 +2080,18 @@ bool AArch64FastISel::selectStore(const Instruction *I) { return false; const Value *PtrV = I->getOperand(1); - if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { - if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) - return false; - } + if (TLI.supportSwiftError()) { + // Swifterror values can come from either a function parameter with + // swifterror attribute or an alloca with swifterror attribute. + if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { + if (Arg->hasSwiftErrorAttr()) + return false; + } - if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { - if (Alloca->isSwiftError() && TLI.supportSwiftError()) - return false; + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { + if (Alloca->isSwiftError()) + return false; + } } // Get the value to be stored into a register. Use the zero register directly @@ -3667,8 +3675,8 @@ bool AArch64FastISel::selectRet(const Instruction *I) { if (F.isVarArg()) return false; - if (F.getAttributes().hasAttrSomewhere(Attribute::SwiftError) && - TLI.supportSwiftError()) + if (TLI.supportSwiftError() && + F.getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return false; if (TLI.supportSplitCSR(FuncInfo.MF)) diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index ceeba0a6283e0..047cd577e008b 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -710,7 +710,8 @@ static bool produceCompactUnwindFrame(MachineFunction &MF) { const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>(); AttributeSet Attrs = MF.getFunction()->getAttributes(); return Subtarget.isTargetMachO() && - !Attrs.hasAttrSomewhere(Attribute::SwiftError); + !(Subtarget.getTargetLowering()->supportSwiftError() && + Attrs.hasAttrSomewhere(Attribute::SwiftError)); } diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 2bd48e1b2271d..977f7237bda2f 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -400,6 +400,10 @@ class AArch64TargetLowering : public TargetLowering { MachineBasicBlock *Entry, const SmallVectorImpl<MachineBasicBlock *> &Exits) const override; + bool supportSwiftError() const override { + return true; + } + private: bool isExtFreeImpl(const Instruction *Ext) const override; @@ -411,10 +415,6 @@ class AArch64TargetLowering : public TargetLowering { void addDRTypeForNEON(MVT VT); void addQRTypeForNEON(MVT VT); - bool supportSwiftError() const override { - return true; - } - SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl<ISD::InputArg> &Ins, SDLoc DL, diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index 8d4c55d482bf4..e41f276c81907 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -51,8 +51,10 @@ AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { return MF->getInfo<AArch64FunctionInfo>()->isSplitCSR() ? CSR_AArch64_CXX_TLS_Darwin_PE_SaveList : CSR_AArch64_CXX_TLS_Darwin_SaveList; - if (MF->getFunction()->getAttributes().hasAttrSomewhere( - Attribute::SwiftError)) + if (MF->getSubtarget<AArch64Subtarget>().getTargetLowering() + ->supportSwiftError() && + MF->getFunction()->getAttributes().hasAttrSomewhere( + Attribute::SwiftError)) return CSR_AArch64_AAPCS_SwiftError_SaveList; if (MF->getFunction()->getCallingConv() == CallingConv::PreserveMost) return CSR_AArch64_RT_MostRegs_SaveList; @@ -60,26 +62,6 @@ AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { return CSR_AArch64_AAPCS_SaveList; } -const MCPhysReg * -AArch64RegisterInfo::getCalleeSavedRegsForLayout( - const MachineFunction *MF) const { - assert(MF && "Invalid MachineFunction pointer."); - if (MF->getFunction()->getCallingConv() == CallingConv::GHC) - // GHC set of callee saved regs is empty as all those regs are - // used for passing STG regs around - return CSR_AArch64_NoRegs_SaveList; - if (MF->getFunction()->getCallingConv() == CallingConv::AnyReg) - return CSR_AArch64_AllRegs_SaveList; - if (MF->getFunction()->getCallingConv() == CallingConv::CXX_FAST_TLS) - return MF->getInfo<AArch64FunctionInfo>()->isSplitCSR() ? - CSR_AArch64_CXX_TLS_Darwin_PE_SaveList : - CSR_AArch64_CXX_TLS_Darwin_SaveList; - if (MF->getFunction()->getCallingConv() == CallingConv::PreserveMost) - return CSR_AArch64_RT_MostRegs_SaveList; - else - return CSR_AArch64_AAPCS_SaveList; -} - const MCPhysReg *AArch64RegisterInfo::getCalleeSavedRegsViaCopy( const MachineFunction *MF) const { assert(MF && "Invalid MachineFunction pointer."); @@ -99,7 +81,9 @@ AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF, return CSR_AArch64_AllRegs_RegMask; if (CC == CallingConv::CXX_FAST_TLS) return CSR_AArch64_CXX_TLS_Darwin_RegMask; - if (MF.getFunction()->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) + if (MF.getSubtarget<AArch64Subtarget>().getTargetLowering() + ->supportSwiftError() && + MF.getFunction()->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return CSR_AArch64_AAPCS_SwiftError_RegMask; if (CC == CallingConv::PreserveMost) return CSR_AArch64_RT_MostRegs_RegMask; diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h index 41562b5194996..f33f788fd437e 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h @@ -62,11 +62,6 @@ struct AArch64RegisterInfo : public AArch64GenRegisterInfo { const uint32_t *getThisReturnPreservedMask(const MachineFunction &MF, CallingConv::ID) const; - /// Return callee-saved registers for stack layout purpose. When we use - /// SwiftError CSR, we still need to use the standard CSR for layout purpose, - /// since compact unwinding expects the layout according to standard CSR. - const MCPhysReg *getCalleeSavedRegsForLayout(const MachineFunction *MF) const; - BitVector getReservedRegs(const MachineFunction &MF) const override; const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp index b3f8187048313..6db2aca99ba2f 100644 --- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -87,7 +87,7 @@ ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { } } - if (STI.isTargetDarwin() && + if (STI.isTargetDarwin() && STI.getTargetLowering()->supportSwiftError() && F->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return CSR_iOS_SwiftError_SaveList; @@ -115,7 +115,7 @@ ARMBaseRegisterInfo::getCallPreservedMask(const MachineFunction &MF, // This is academic becase all GHC calls are (supposed to be) tail calls return CSR_NoRegs_RegMask; - if (STI.isTargetDarwin() && + if (STI.isTargetDarwin() && STI.getTargetLowering()->supportSwiftError() && MF.getFunction()->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return CSR_iOS_SwiftError_RegMask; diff --git a/llvm/lib/Target/ARM/ARMCallingConv.td b/llvm/lib/Target/ARM/ARMCallingConv.td index 21ec04d0911d7..922b800151101 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.td +++ b/llvm/lib/Target/ARM/ARMCallingConv.td @@ -48,7 +48,7 @@ def RetCC_ARM_APCS : CallingConv<[ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>, CCIfType<[f32], CCBitConvertToType<i32>>, - // An SwiftError is returned in R6. + // A SwiftError is returned in R6. CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, // Handle all vector types as either f64 or v2f64. @@ -176,7 +176,7 @@ def RetCC_ARM_AAPCS : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, - // An SwiftError is returned in R6. + // A SwiftError is returned in R6. CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, CCIfType<[f64, v2f64], CCCustom<"RetCC_ARM_AAPCS_Custom_f64">>, @@ -218,7 +218,7 @@ def RetCC_ARM_AAPCS_VFP : CallingConv<[ CCIfType<[v1i64, v2i32, v4i16, v8i8, v2f32], CCBitConvertToType<f64>>, CCIfType<[v2i64, v4i32, v8i16, v16i8, v4f32], CCBitConvertToType<v2f64>>, - // An SwiftError is returned in R6. + // A SwiftError is returned in R6. CCIfSwiftError<CCIfType<[i32], CCAssignToReg<[R6]>>>, CCIfType<[v2f64], CCAssignToReg<[Q0, Q1, Q2, Q3]>>, diff --git a/llvm/lib/Target/ARM/ARMFastISel.cpp b/llvm/lib/Target/ARM/ARMFastISel.cpp index 8bbad22ee3a91..28d72d8c6ca61 100644 --- a/llvm/lib/Target/ARM/ARMFastISel.cpp +++ b/llvm/lib/Target/ARM/ARMFastISel.cpp @@ -1063,14 +1063,18 @@ bool ARMFastISel::SelectLoad(const Instruction *I) { return false; const Value *SV = I->getOperand(0); - if (const Argument *Arg = dyn_cast<Argument>(SV)) { - if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) - return false; - } + if (TLI.supportSwiftError()) { + // Swifterror values can come from either a function parameter with + // swifterror attribute or an alloca with swifterror attribute. + if (const Argument *Arg = dyn_cast<Argument>(SV)) { + if (Arg->hasSwiftErrorAttr()) + return false; + } - if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { - if (Alloca->isSwiftError() && TLI.supportSwiftError()) - return false; + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { + if (Alloca->isSwiftError()) + return false; + } } // Verify we have a legal type before going any further. @@ -1189,14 +1193,18 @@ bool ARMFastISel::SelectStore(const Instruction *I) { return false; const Value *PtrV = I->getOperand(1); - if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { - if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) - return false; - } + if (TLI.supportSwiftError()) { + // Swifterror values can come from either a function parameter with + // swifterror attribute or an alloca with swifterror attribute. + if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { + if (Arg->hasSwiftErrorAttr()) + return false; + } - if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { - if (Alloca->isSwiftError() && TLI.supportSwiftError()) - return false; + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { + if (Alloca->isSwiftError()) + return false; + } } // Verify we have a legal type before going any further. @@ -2107,8 +2115,8 @@ bool ARMFastISel::SelectRet(const Instruction *I) { if (!FuncInfo.CanLowerReturn) return false; - if (F.getAttributes().hasAttrSomewhere(Attribute::SwiftError) && - TLI.supportSwiftError()) + if (TLI.supportSwiftError() && + F.getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return false; if (TLI.supportSplitCSR(FuncInfo.MF)) diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h index be094d74637a2..f4f86b4cbb1a5 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.h +++ b/llvm/lib/Target/ARM/ARMISelLowering.h @@ -470,6 +470,10 @@ namespace llvm { bool isCheapToSpeculateCttz() const override; bool isCheapToSpeculateCtlz() const override; + bool supportSwiftError() const override { + return true; + } + protected: std::pair<const TargetRegisterClass *, uint8_t> findRepresentativeClass(const TargetRegisterInfo *TRI, @@ -587,10 +591,6 @@ namespace llvm { SmallVectorImpl<SDValue> &InVals, bool isThisReturn, SDValue ThisVal) const; - bool supportSwiftError() const override { - return true; - } - bool supportSplitCSR(MachineFunction *MF) const override { return MF->getFunction()->getCallingConv() == CallingConv::CXX_FAST_TLS && MF->getFunction()->hasFnAttribute(Attribute::NoUnwind); diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td index a7a3bc25abb53..4d99af68a041d 100644 --- a/llvm/lib/Target/X86/X86CallingConv.td +++ b/llvm/lib/Target/X86/X86CallingConv.td @@ -300,7 +300,7 @@ def CC_X86_64_C : CallingConv<[ // A SwiftSelf is passed in R10. CCIfSwiftSelf<CCIfType<[i64], CCAssignToReg<[R10]>>>, - // An SwiftError is passed in R12. + // A SwiftError is passed in R12. CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>, // The first 6 integer arguments are passed in integer registers. @@ -851,7 +851,7 @@ def CSR_NoRegs : CalleeSavedRegs<(add)>; def CSR_32 : CalleeSavedRegs<(add ESI, EDI, EBX, EBP)>; def CSR_64 : CalleeSavedRegs<(add RBX, R12, R13, R14, R15, RBP)>; -def CSR_64_SwiftError : CalleeSavedRegs<(add RBX, R13, R14, R15, RBP)>; +def CSR_64_SwiftError : CalleeSavedRegs<(sub CSR_64, R12)>; def CSR_32EHRet : CalleeSavedRegs<(add EAX, EDX, CSR_32)>; def CSR_64EHRet : CalleeSavedRegs<(add RAX, RDX, CSR_64)>; diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index 1e3812ce15cda..8aab37666b00b 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -973,14 +973,18 @@ bool X86FastISel::X86SelectStore(const Instruction *I) { return false; const Value *PtrV = I->getOperand(1); - if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { - if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) - return false; - } + if (TLI.supportSwiftError()) { + // Swifterror values can come from either a function parameter with + // swifterror attribute or an alloca with swifterror attribute. + if (const Argument *Arg = dyn_cast<Argument>(PtrV)) { + if (Arg->hasSwiftErrorAttr()) + return false; + } - if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { - if (Alloca->isSwiftError() && TLI.supportSwiftError()) - return false; + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(PtrV)) { + if (Alloca->isSwiftError()) + return false; + } } const Value *Val = S->getValueOperand(); @@ -1013,8 +1017,8 @@ bool X86FastISel::X86SelectRet(const Instruction *I) { if (!FuncInfo.CanLowerReturn) return false; - if (F.getAttributes().hasAttrSomewhere(Attribute::SwiftError) && - TLI.supportSwiftError()) + if (TLI.supportSwiftError() && + F.getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return false; if (TLI.supportSplitCSR(FuncInfo.MF)) @@ -1149,14 +1153,18 @@ bool X86FastISel::X86SelectLoad(const Instruction *I) { return false; const Value *SV = I->getOperand(0); - if (const Argument *Arg = dyn_cast<Argument>(SV)) { - if (Arg->hasSwiftErrorAttr() && TLI.supportSwiftError()) - return false; - } + if (TLI.supportSwiftError()) { + // Swifterror values can come from either a function parameter with + // swifterror attribute or an alloca with swifterror attribute. + if (const Argument *Arg = dyn_cast<Argument>(SV)) { + if (Arg->hasSwiftErrorAttr()) + return false; + } - if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { - if (Alloca->isSwiftError() && TLI.supportSwiftError()) - return false; + if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(SV)) { + if (Alloca->isSwiftError()) + return false; + } } MVT VT; diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index b050920bbd891..b1b8b91a1c60c 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -2227,6 +2227,25 @@ X86TargetLowering::LowerReturn(SDValue Chain, // false, then an sret argument may be implicitly inserted in the SelDAG. In // either case FuncInfo->setSRetReturnReg() will have been called. if (unsigned SRetReg = FuncInfo->getSRetReturnReg()) { + // When we have both sret and another return value, we should use the + // original Chain stored in RetOps[0], instead of the current Chain updated + // in the above loop. If we only have sret, RetOps[0] equals to Chain. + + // For the case of sret and another return value, we have + // Chain_0 at the function entry + // Chain_1 = getCopyToReg(Chain_0) in the above loop + // If we use Chain_1 in getCopyFromReg, we will have + // Val = getCopyFromReg(Chain_1) + // Chain_2 = getCopyToReg(Chain_1, Val) from below + + // getCopyToReg(Chain_0) will be glued together with + // getCopyToReg(Chain_1, Val) into Unit A, getCopyFromReg(Chain_1) will be + // in Unit B, and we will have cyclic dependency between Unit A and Unit B: + // Data dependency from Unit B to Unit A due to usage of Val in + // getCopyToReg(Chain_1, Val) + // Chain dependency from Unit A to Unit B + + // So here, we use RetOps[0] (i.e Chain_0) for getCopyFromReg. SDValue Val = DAG.getCopyFromReg(RetOps[0], dl, SRetReg, getPointerTy(MF.getDataLayout())); diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h index a8bad8e955be4..c73c47c124f2b 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.h +++ b/llvm/lib/Target/X86/X86ISelLowering.h @@ -984,6 +984,10 @@ namespace llvm { bool isIntDivCheap(EVT VT, AttributeSet Attr) const override; + bool supportSwiftError() const override { + return true; + } + protected: std::pair<const TargetRegisterClass *, uint8_t> findRepresentativeClass(const TargetRegisterInfo *TRI, @@ -1097,10 +1101,6 @@ namespace llvm { SDValue LowerGC_TRANSITION_START(SDValue Op, SelectionDAG &DAG) const; SDValue LowerGC_TRANSITION_END(SDValue Op, SelectionDAG &DAG) const; - bool supportSwiftError() const override { - return true; - } - SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index 2989e761c044d..1ea9533eaf95e 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -299,8 +299,9 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { return CSR_Win64_SaveList; if (CallsEHReturn) return CSR_64EHRet_SaveList; - if (MF->getFunction()->getAttributes().hasAttrSomewhere( - Attribute::SwiftError)) + if (Subtarget.getTargetLowering()->supportSwiftError() && + MF->getFunction()->getAttributes().hasAttrSomewhere( + Attribute::SwiftError)) return CSR_64_SwiftError_SaveList; return CSR_64_SaveList; } @@ -388,8 +389,9 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF, if (Is64Bit) { if (IsWin64) return CSR_Win64_RegMask; - if (MF.getFunction()->getAttributes().hasAttrSomewhere( - Attribute::SwiftError)) + if (Subtarget.getTargetLowering()->supportSwiftError() && + MF.getFunction()->getAttributes().hasAttrSomewhere( + Attribute::SwiftError)) return CSR_64_SwiftError_RegMask; return CSR_64_RegMask; } diff --git a/llvm/test/CodeGen/AArch64/swifterror.ll b/llvm/test/CodeGen/AArch64/swifterror.ll index ec9d256fb82ee..4c32513729529 100644 --- a/llvm/test/CodeGen/AArch64/swifterror.ll +++ b/llvm/test/CodeGen/AArch64/swifterror.ll @@ -5,6 +5,8 @@ declare i8* @malloc(i64) declare void @free(i8*) %swift_error = type {i64, i8} +; This tests the basic usage of a swifterror parameter. "foo" is the function +; that takes a swifterror parameter and "caller" is the caller of "foo". define float @foo(%swift_error** swifterror %error_ptr_ref) { ; CHECK-APPLE-LABEL: foo: ; CHECK-APPLE: orr w0, wzr, #0x10 @@ -26,11 +28,12 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp ret float 1.0 } +; "caller" calls "foo" that takes a swifterror parameter. define float @caller(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller: ; CHECK-APPLE: mov [[ID:x[0-9]+]], x0 @@ -54,7 +57,7 @@ entry: %call = call float @foo(%swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 @@ -62,10 +65,11 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "caller2" is the caller of "foo", it calls "foo" inside a loop. define float @caller2(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller2: ; CHECK-APPLE: mov [[ID:x[0-9]+]], x0 @@ -94,7 +98,7 @@ bb_loop: %call = call float @foo(%swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %cmp = fcmp ogt float %call, 1.000000e+00 @@ -105,10 +109,12 @@ bb_end: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "foo_if" is a function that takes a swifterror parameter, it sets swifterror +; under a certain condition. define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) { ; CHECK-APPLE-LABEL: foo_if: ; CHECK-APPLE: cbz w0 @@ -142,14 +148,16 @@ gen_error: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp ret float 1.0 normal: ret float 0.0 } +; "foo_loop" is a function that takes a swifterror parameter, it sets swifterror +; under a certain condition inside a loop. define float @foo_loop(%swift_error** swifterror %error_ptr_ref, i32 %cc, float %cc2) { ; CHECK-APPLE-LABEL: foo_loop: ; CHECK-APPLE: mov x0, x19 @@ -188,8 +196,8 @@ gen_error: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp br label %bb_cont bb_cont: @@ -201,6 +209,8 @@ bb_end: %struct.S = type { i32, i32, i32, i32, i32, i32 } +; "foo_sret" is a function that takes a swifterror parameter, it also has a sret +; parameter. define void @foo_sret(%struct.S* sret %agg.result, i32 %val1, %swift_error** swifterror %error_ptr_ref) { ; CHECK-APPLE-LABEL: foo_sret: ; CHECK-APPLE: mov [[SRET:x[0-9]+]], x8 @@ -230,13 +240,14 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp %v2 = getelementptr inbounds %struct.S, %struct.S* %agg.result, i32 0, i32 1 store i32 %val1, i32* %v2 ret void } +; "caller3" calls "foo_sret" that takes a swifterror parameter. define float @caller3(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller3: ; CHECK-APPLE: mov [[ID:x[0-9]+]], x0 @@ -269,7 +280,7 @@ entry: call void @foo_sret(%struct.S* sret %s, i32 1, %swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 @@ -277,22 +288,24 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "foo_vararg" is a function that takes a swifterror parameter, it also has +; variable number of arguments. declare void @llvm.va_start(i8*) nounwind define float @foo_vararg(%swift_error** swifterror %error_ptr_ref, ...) { ; CHECK-APPLE-LABEL: foo_vararg: ; CHECK-APPLE: orr w0, wzr, #0x10 ; CHECK-APPLE: malloc ; CHECK-APPLE: orr [[ID:w[0-9]+]], wzr, #0x1 -; CHECK-APPLE: add [[ARGS:x[0-9]+]], [[TMP:x[0-9]+]], #16 +; CHECK-FIXMEAPPLE: add [[ARGS:x[0-9]+]], [[TMP:x[0-9]+]], #16 ; CHECK-APPLE: strb [[ID]], [x0, #8] ; First vararg -; CHECK-APPLE-DAG: orr {{x[0-9]+}}, [[ARGS]], #0x8 -; CHECK-APPLE-DAG: ldr {{w[0-9]+}}, [{{.*}}[[TMP]], #16] +; CHECK-FIXMEAPPLE-DAG: orr {{x[0-9]+}}, [[ARGS]], #0x8 +; CHECK-FIXMEAPPLE-DAG: ldr {{w[0-9]+}}, [{{.*}}[[TMP]], #16] ; CHECK-APPLE: add {{x[0-9]+}}, {{x[0-9]+}}, #8 ; Second vararg ; CHECK-APPLE: ldr {{w[0-9]+}}, [{{x[0-9]+}}] @@ -306,8 +319,8 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp %args = alloca i8*, align 8 %a10 = alloca i32, align 4 @@ -325,6 +338,7 @@ entry: ret float 1.0 } +; "caller4" calls "foo_vararg" that takes a swifterror parameter. define float @caller4(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller4: @@ -357,7 +371,7 @@ entry: %call = call float (%swift_error**, ...) @foo_vararg(%swift_error** swifterror %error_ptr_ref, i32 %v10, i32 %v11, i32 %v12) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: @@ -366,6 +380,6 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } diff --git a/llvm/test/CodeGen/ARM/swifterror.ll b/llvm/test/CodeGen/ARM/swifterror.ll index e48bf82e5731a..17bd7059f6d40 100644 --- a/llvm/test/CodeGen/ARM/swifterror.ll +++ b/llvm/test/CodeGen/ARM/swifterror.ll @@ -3,8 +3,11 @@ declare i8* @malloc(i64) declare void @free(i8*) -%swift_error = type {i64, i8} +%swift_error = type { i64, i8 } +%struct.S = type { i32, i32, i32, i32, i32, i32 } +; This tests the basic usage of a swifterror parameter. "foo" is the function +; that takes a swifterror parameter and "caller" is the caller of "foo". define float @foo(%swift_error** swifterror %error_ptr_ref) { ; CHECK-APPLE-LABEL: foo: ; CHECK-APPLE: mov r0, #16 @@ -24,11 +27,12 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp ret float 1.0 } +; "caller" calls "foo" that takes a swifterror parameter. define float @caller(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller: ; CHECK-APPLE-DAG: mov [[ID:r[0-9]+]], r0 @@ -60,7 +64,7 @@ entry: %call = call float @foo(%swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 @@ -68,10 +72,11 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "caller2" is the caller of "foo", it calls "foo" inside a loop. define float @caller2(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller2: ; CHECK-APPLE-DAG: mov [[ID:r[0-9]+]], r0 @@ -107,7 +112,7 @@ bb_loop: %call = call float @foo(%swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %cmp = fcmp ogt float %call, 1.000000e+00 @@ -118,10 +123,12 @@ bb_end: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "foo_if" is a function that takes a swifterror parameter, it sets swifterror +; under a certain condition. define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) { ; CHECK-APPLE-LABEL: foo_if: ; CHECK-APPLE: cmp r0, #0 @@ -153,14 +160,16 @@ gen_error: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp ret float 1.0 normal: ret float 0.0 } +; "foo_loop" is a function that takes a swifterror parameter, it sets swifterror +; under a certain condition inside a loop. define float @foo_loop(%swift_error** swifterror %error_ptr_ref, i32 %cc, float %cc2) { ; CHECK-APPLE-LABEL: foo_loop: ; CHECK-APPLE: mov [[CODE:r[0-9]+]], r0 @@ -201,8 +210,8 @@ gen_error: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp br label %bb_cont bb_cont: @@ -212,8 +221,8 @@ bb_end: ret float 0.0 } -%struct.S = type { i32, i32, i32, i32, i32, i32 } - +; "foo_sret" is a function that takes a swifterror parameter, it also has a sret +; parameter. define void @foo_sret(%struct.S* sret %agg.result, i32 %val1, %swift_error** swifterror %error_ptr_ref) { ; CHECK-APPLE-LABEL: foo_sret: ; CHECK-APPLE: mov [[SRET:r[0-9]+]], r0 @@ -241,13 +250,14 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp %v2 = getelementptr inbounds %struct.S, %struct.S* %agg.result, i32 0, i32 1 store i32 %val1, i32* %v2 ret void } +; "caller3" calls "foo_sret" that takes a swifterror parameter. define float @caller3(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller3: ; CHECK-APPLE: mov [[ID:r[0-9]+]], r0 @@ -281,7 +291,7 @@ entry: call void @foo_sret(%struct.S* sret %s, i32 1, %swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 @@ -289,10 +299,12 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "foo_vararg" is a function that takes a swifterror parameter, it also has +; variable number of arguments. declare void @llvm.va_start(i8*) nounwind define float @foo_vararg(%swift_error** swifterror %error_ptr_ref, ...) { ; CHECK-APPLE-LABEL: foo_vararg: @@ -307,8 +319,8 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp %args = alloca i8*, align 8 %a10 = alloca i32, align 4 @@ -326,6 +338,7 @@ entry: ret float 1.0 } +; "caller4" calls "foo_vararg" that takes a swifterror parameter. define float @caller4(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller4: ; CHECK-APPLE: mov [[ID:r[0-9]+]], r0 @@ -354,7 +367,7 @@ entry: %call = call float (%swift_error**, ...) @foo_vararg(%swift_error** swifterror %error_ptr_ref, i32 %v10, i32 %v11, i32 %v12) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: @@ -363,6 +376,6 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } diff --git a/llvm/test/CodeGen/X86/swifterror.ll b/llvm/test/CodeGen/X86/swifterror.ll index 484a5f29a3b3e..d8db36b09c25b 100644 --- a/llvm/test/CodeGen/X86/swifterror.ll +++ b/llvm/test/CodeGen/X86/swifterror.ll @@ -5,6 +5,8 @@ declare i8* @malloc(i64) declare void @free(i8*) %swift_error = type {i64, i8} +; This tests the basic usage of a swifterror parameter. "foo" is the function +; that takes a swifterror parameter and "caller" is the caller of "foo". define float @foo(%swift_error** swifterror %error_ptr_ref) { ; CHECK-APPLE-LABEL: foo: ; CHECK-APPLE: movl $16, %edi @@ -21,11 +23,12 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp ret float 1.0 } +; "caller" calls "foo" that takes a swifterror parameter. define float @caller(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller: ; CHECK-APPLE: xorl %r12d, %r12d @@ -48,7 +51,7 @@ entry: %call = call float @foo(%swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 @@ -56,10 +59,11 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "caller2" is the caller of "foo", it calls "foo" inside a loop. define float @caller2(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller2: ; CHECK-APPLE: xorl %r12d, %r12d @@ -88,7 +92,7 @@ bb_loop: %call = call float @foo(%swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %cmp = fcmp ogt float %call, 1.000000e+00 @@ -99,10 +103,12 @@ bb_end: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) ret float 1.0 } +; "foo_if" is a function that takes a swifterror parameter, it sets swifterror +; under a certain condition. define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) { ; CHECK-APPLE-LABEL: foo_if: ; CHECK-APPLE: testl %edi, %edi @@ -136,14 +142,16 @@ gen_error: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp ret float 1.0 normal: ret float 0.0 } +; "foo_loop" is a function that takes a swifterror parameter, it sets swifterror +; under a certain condition inside a loop. define float @foo_loop(%swift_error** swifterror %error_ptr_ref, i32 %cc, float %cc2) { ; CHECK-APPLE-LABEL: foo_loop: ; CHECK-APPLE: movq %r12, %rax @@ -181,8 +189,8 @@ gen_error: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp br label %bb_cont bb_cont: @@ -194,6 +202,8 @@ bb_end: %struct.S = type { i32, i32, i32, i32, i32, i32 } +; "foo_sret" is a function that takes a swifterror parameter, it also has a sret +; parameter. define void @foo_sret(%struct.S* sret %agg.result, i32 %val1, %swift_error** swifterror %error_ptr_ref) { ; CHECK-APPLE-LABEL: foo_sret: ; CHECK-APPLE: movq %rdi, %{{.*}} @@ -221,13 +231,14 @@ entry: %call = call i8* @malloc(i64 16) %call.0 = bitcast i8* %call to %swift_error* store %swift_error* %call.0, %swift_error** %error_ptr_ref - %0 = getelementptr inbounds i8, i8* %call, i64 8 - store i8 1, i8* %0 + %tmp = getelementptr inbounds i8, i8* %call, i64 8 + store i8 1, i8* %tmp %v2 = getelementptr inbounds %struct.S, %struct.S* %agg.result, i32 0, i32 1 store i32 %val1, i32* %v2 ret void } +; "caller3" calls "foo_sret" that takes a swifterror parameter. define float @caller3(i8* %error_ref) { ; CHECK-APPLE-LABEL: caller3: ; CHECK-APPLE: movl $1, %esi @@ -263,7 +274,7 @@ entry: call void @foo_sret(%struct.S* sret %s, i32 1, %swift_error** swifterror %error_ptr_ref) %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null - %0 = bitcast %swift_error* %error_from_foo to i8* + %tmp = bitcast %swift_error* %error_from_foo to i8* br i1 %had_error_from_foo, label %handler, label %cont cont: %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 @@ -271,6 +282,78 @@ cont: store i8 %t, i8* %error_ref br label %handler handler: - call void @free(i8* %0) + call void @free(i8* %tmp) + ret float 1.0 +} + +; This is a caller with multiple swifterror values, it calls "foo" twice, each +; time with a different swifterror value, from "alloca swifterror". +define float @caller_with_multiple_swifterror_values(i8* %error_ref, i8* %error_ref2) { +; CHECK-APPLE-LABEL: caller_with_multiple_swifterror_values: + +; The first swifterror value: +; CHECK-APPLE: xorl %r12d, %r12d +; CHECK-APPLE: callq {{.*}}foo +; CHECK-APPLE: testq %r12, %r12 +; CHECK-APPLE: jne +; Access part of the error object and save it to error_ref +; CHECK-APPLE: movb 8(%r12) +; CHECK-APPLE: movq %r12, %rdi +; CHECK_APPLE: callq {{.*}}free + +; The second swifterror value: +; CHECK-APPLE: xorl %r12d, %r12d +; CHECK-APPLE: callq {{.*}}foo +; CHECK-APPLE: testq %r12, %r12 +; CHECK-APPLE: jne +; Access part of the error object and save it to error_ref +; CHECK-APPLE: movb 8(%r12) +; CHECK-APPLE: movq %r12, %rdi +; CHECK_APPLE: callq {{.*}}free + +; CHECK-O0-LABEL: caller_with_multiple_swifterror_values: + +; The first swifterror value: +; CHECK-O0: xorl +; CHECK-O0: movl %{{.*}}, %r12d +; CHECK-O0: callq {{.*}}foo +; CHECK-O0: jne + +; The second swifterror value: +; CHECK-O0: xorl +; CHECK-O0: movl %{{.*}}, %r12d +; CHECK-O0: callq {{.*}}foo +; CHECK-O0: jne +entry: + %error_ptr_ref = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref + %call = call float @foo(%swift_error** swifterror %error_ptr_ref) + %error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref + %had_error_from_foo = icmp ne %swift_error* %error_from_foo, null + %tmp = bitcast %swift_error* %error_from_foo to i8* + br i1 %had_error_from_foo, label %handler, label %cont +cont: + %v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1 + %t = load i8, i8* %v1 + store i8 %t, i8* %error_ref + br label %handler +handler: + call void @free(i8* %tmp) + + %error_ptr_ref2 = alloca swifterror %swift_error* + store %swift_error* null, %swift_error** %error_ptr_ref2 + %call2 = call float @foo(%swift_error** swifterror %error_ptr_ref2) + %error_from_foo2 = load %swift_error*, %swift_error** %error_ptr_ref2 + %had_error_from_foo2 = icmp ne %swift_error* %error_from_foo2, null + %bitcast2 = bitcast %swift_error* %error_from_foo2 to i8* + br i1 %had_error_from_foo2, label %handler2, label %cont2 +cont2: + %v2 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo2, i64 0, i32 1 + %t2 = load i8, i8* %v2 + store i8 %t2, i8* %error_ref2 + br label %handler2 +handler2: + call void @free(i8* %bitcast2) + ret float 1.0 } From 638b0ac101d3e4cefa5f0cc52ffdfe0acc29fea1 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Tue, 12 Apr 2016 14:26:53 -0700 Subject: [PATCH 058/582] Remove some leftover changes related to the Swift calling convention. Support for the Swift calling convention has now been added to LLVM trunk. There were a few changes in the upstream-with-swift branch on github that were related and are no longer needed. This change removes them. apple-llvm-split-commit: 6da1b2710fd3befb75bce79aefd4fc3783dcf802 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Target/TargetFrameLowering.h | 5 +---- llvm/lib/CodeGen/PrologEpilogInserter.cpp | 3 +-- llvm/lib/IR/Attributes.cpp | 8 ++++---- llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp | 3 +-- llvm/lib/Target/Hexagon/HexagonFrameLowering.h | 3 +-- llvm/lib/Target/X86/X86FrameLowering.cpp | 3 +-- llvm/lib/Target/X86/X86FrameLowering.h | 4 +--- 7 files changed, 10 insertions(+), 19 deletions(-) diff --git a/llvm/include/llvm/Target/TargetFrameLowering.h b/llvm/include/llvm/Target/TargetFrameLowering.h index b48b6979c25c7..9f42adaf814e3 100644 --- a/llvm/include/llvm/Target/TargetFrameLowering.h +++ b/llvm/include/llvm/Target/TargetFrameLowering.h @@ -120,10 +120,7 @@ class TargetFrameLowering { virtual bool assignCalleeSavedSpillSlots(MachineFunction &MF, const TargetRegisterInfo *TRI, - std::vector<CalleeSavedInfo> &CSI, - unsigned &MinCSFrameIndex, - unsigned &MaxCSFrameIndex) const { - + std::vector<CalleeSavedInfo> &CSI) const { return false; } diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index dbaa7f4303d1e..7c3fe33cf7505 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -316,8 +316,7 @@ void PEI::assignCalleeSavedSpillSlots(MachineFunction &F, const TargetFrameLowering *TFI = F.getSubtarget().getFrameLowering(); MachineFrameInfo *MFI = F.getFrameInfo(); - if (!TFI->assignCalleeSavedSpillSlots(F, RegInfo, CSI, MinCSFrameIndex, - MaxCSFrameIndex)) { + if (!TFI->assignCalleeSavedSpillSlots(F, RegInfo, CSI)) { // If target doesn't implement this, use generic code. if (CSI.empty()) diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 601920ad5df31..0b775337be5f1 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -512,10 +512,10 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) { case Attribute::Convergent: return 1ULL << 46; case Attribute::SafeStack: return 1ULL << 47; case Attribute::NoRecurse: return 1ULL << 48; - case Attribute::SwiftSelf: return 1ULL << 49; - case Attribute::SwiftError: return 1ULL << 50; - case Attribute::InaccessibleMemOnly: return 1ULL << 51; - case Attribute::InaccessibleMemOrArgMemOnly: return 1ULL << 52; + case Attribute::InaccessibleMemOnly: return 1ULL << 49; + case Attribute::InaccessibleMemOrArgMemOnly: return 1ULL << 50; + case Attribute::SwiftSelf: return 1ULL << 51; + case Attribute::SwiftError: return 1ULL << 52; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; diff --git a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp index fb3493536d300..3c08eaabe18b1 100644 --- a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp +++ b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp @@ -1152,8 +1152,7 @@ static void dump_registers(BitVector &Regs, const TargetRegisterInfo &TRI) { bool HexagonFrameLowering::assignCalleeSavedSpillSlots(MachineFunction &MF, - const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI, - unsigned &MinCSFrameIndex, unsigned &MaxCSFrameIndex) const { + const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI) const { DEBUG(dbgs() << LLVM_FUNCTION_NAME << " on " << MF.getFunction()->getName() << '\n'); MachineFrameInfo *MFI = MF.getFrameInfo(); diff --git a/llvm/lib/Target/Hexagon/HexagonFrameLowering.h b/llvm/lib/Target/Hexagon/HexagonFrameLowering.h index 187d7919b0f96..1f578f30c77fc 100644 --- a/llvm/lib/Target/Hexagon/HexagonFrameLowering.h +++ b/llvm/lib/Target/Hexagon/HexagonFrameLowering.h @@ -72,8 +72,7 @@ class HexagonFrameLowering : public TargetFrameLowering { } bool assignCalleeSavedSpillSlots(MachineFunction &MF, - const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI, - unsigned &MinCSFrameIndex, unsigned &MaxCSFrameIndex) + const TargetRegisterInfo *TRI, std::vector<CalleeSavedInfo> &CSI) const override; bool needsAligna(const MachineFunction &MF) const; diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index e65bf52a7bf79..ec858671c42b9 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -1799,8 +1799,7 @@ int X86FrameLowering::getFrameIndexReferenceFromSP(const MachineFunction &MF, bool X86FrameLowering::assignCalleeSavedSpillSlots( MachineFunction &MF, const TargetRegisterInfo *TRI, - std::vector<CalleeSavedInfo> &CSI, - unsigned &MinCSFrameIndex, unsigned &MaxCSFrameIndex) const { + std::vector<CalleeSavedInfo> &CSI) const { MachineFrameInfo *MFI = MF.getFrameInfo(); X86MachineFunctionInfo *X86FI = MF.getInfo<X86MachineFunctionInfo>(); diff --git a/llvm/lib/Target/X86/X86FrameLowering.h b/llvm/lib/Target/X86/X86FrameLowering.h index dbe6dfc92acd4..49a46a12407ef 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.h +++ b/llvm/lib/Target/X86/X86FrameLowering.h @@ -80,9 +80,7 @@ class X86FrameLowering : public TargetFrameLowering { bool assignCalleeSavedSpillSlots(MachineFunction &MF, const TargetRegisterInfo *TRI, - std::vector<CalleeSavedInfo> &CSI, - unsigned &MinCSFrameIndex, - unsigned &MaxCSFrameIndex) const override; + std::vector<CalleeSavedInfo> &CSI) const override; bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, From c1d42180719646df77c35e8e6403e36258e890fe Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Thu, 14 Apr 2016 09:10:42 -0700 Subject: [PATCH 059/582] Remove LoopInfoBase::getBlockMap(). This was only used in one place for Swift and Arnold removed that use yesterday (001998c). apple-llvm-split-commit: f17708b0136063153b20965a335e6b8e417be6bc apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Analysis/LoopInfo.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h index 6b5f0b3a8ff79..934db11abf597 100644 --- a/llvm/include/llvm/Analysis/LoopInfo.h +++ b/llvm/include/llvm/Analysis/LoopInfo.h @@ -550,8 +550,6 @@ class LoopInfoBase { /// loop (for example the entry node), null is returned. LoopT *getLoopFor(const BlockT *BB) const { return BBMap.lookup(BB); } - const DenseMap<const BlockT *, LoopT *> &getBlockMap() const { return BBMap; } - /// Same as getLoopFor. const LoopT *operator[](const BlockT *BB) const { return getLoopFor(BB); From 14281ed06731147e6312e6dbf87b9c8e96f4ff8a Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Wed, 6 Apr 2016 13:02:52 -0700 Subject: [PATCH 060/582] [Import as member] Default initialize ModuleOptions fields apple-llvm-split-commit: e519fc7b6e9fa281814de24a7f5980465fd813a5 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 7b4d0647ef24d..b19eea5199125 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -476,7 +476,7 @@ class TypedefInfo : public CommonTypeInfo { /// Descripts a series of options for a module struct ModuleOptions { - bool SwiftInferImportAsMember; + bool SwiftInferImportAsMember = false; }; } // end namespace api_notes From 92bd48c7be6bb66ca91f085b41724cd8745d08bd Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Wed, 6 Apr 2016 13:07:47 -0700 Subject: [PATCH 061/582] [Import as member] Fix my screw up apple-llvm-split-commit: 08c04436b73131323e5b360429b46ea96d33010a apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 6fe87bc7fa256..606825c0ce107 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -252,7 +252,7 @@ namespace { TagsSeq Tags; TypedefsSeq Typedefs; - llvm::Optional<bool> SwiftInferImportAsMember; + llvm::Optional<bool> SwiftInferImportAsMember = {llvm::None}; LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, @@ -776,8 +776,11 @@ namespace { Writer->addTypedef(t.Name, typedefInfo); } - if (TheModule.SwiftInferImportAsMember) - Writer->addModuleOptions({true}); + if (TheModule.SwiftInferImportAsMember) { + ModuleOptions opts; + opts.SwiftInferImportAsMember = true; + Writer->addModuleOptions(opts); + } if (!ErrorOccured) Writer->writeToStream(OS); From 54a3624b20b4bbbb2004c81d2688020ba5819f73 Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Wed, 20 Apr 2016 09:41:48 -0700 Subject: [PATCH 062/582] [swift_newtype] Support for swift_wrapper/newtype attribute. Adds in compiler support, and tests. apple-llvm-split-commit: 4c2a76b39a7d446e10188f6ceeec79c5fd948c95 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 9 ++++ clang/include/clang/Basic/AttrDocs.td | 10 +++++ .../clang/Basic/DiagnosticSemaKinds.td | 3 ++ clang/include/clang/Parse/Parser.h | 8 ++++ clang/lib/Parse/ParseDecl.cpp | 36 ++++++++++++++++ clang/lib/Sema/SemaDeclAttr.cpp | 42 +++++++++++++++++++ clang/test/SemaObjC/attr-swift_newtype.c | 20 +++++++++ 7 files changed, 128 insertions(+) create mode 100644 clang/test/SemaObjC/attr-swift_newtype.c diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index e59cc88b92cc6..1f158437d34de 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1385,6 +1385,15 @@ def SwiftName : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftNewtype : Attr { + let Spellings = [GNU<"swift_newtype">, GNU<"swift_wrapper">]; + let Subjects = SubjectList<[TypedefName], ErrorDiag, "ExpectedType">; + let Args = [EnumArgument<"NewtypeKind", "NewtypeKind", + ["struct", "enum"], + ["NK_Struct", "NK_Enum"]>]; + let Documentation = [SwiftNewtypeDocs]; +} + def SwiftPrivate : InheritableAttr { let Spellings = [GCC<"swift_private">]; let Documentation = [Undocumented]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 08274e18de626..8f7c3c2b0eda4 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1982,6 +1982,16 @@ All of these conventions except ``none`` require the function to have an error p }]; } +def SwiftNewtypeDocs : Documentation { + let Category = SwiftDocs; + let Content = [{ +The ``swift_newtype`` attribute indicates that the typedef to which the attribute appertains is imported as a new Swift type of the typedef's name. +* ``swift_newtype(struct)`` means that a Swift struct will be created for this typedef. +* ``swift_newtype(enum)`` means that a Swift enum will be created for this typedef. + }]; +} + + def OMPDeclareTargetDocs : Documentation { let Category = DocCatFunction; let Heading = "#pragma omp declare target"; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cdafd8cfb99f0..82d0004954c9c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3056,6 +3056,9 @@ def err_attr_swift_error_no_error_parameter : Error< def err_attr_swift_error_return_type : Error< "%0 attribute with '%1' convention can only be applied to a " "%select{function|method}2 returning %select{an integral type|a pointer}3">; +def warn_swift_newtype_attribute_non_typedef : Warning< + "'swift_newtype' attribute may be put on a typedef only; " + "attribute is ignored">; // Function Parameter Semantic Analysis. def err_param_with_void_type : Error<"argument may not have 'void' type">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 66406ba36ef70..9bbcbf70bdef1 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2241,6 +2241,14 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, AttributeList::Syntax Syntax); + void ParseSwiftNewtypeAttribute(IdentifierInfo &SwiftNewtype, + SourceLocation SwiftNewtypeLoc, + ParsedAttributes &attrs, + SourceLocation *endLoc, + IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, + AttributeList::Syntax Syntax); + void ParseAttributeWithTypeArg(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index b06eda9d23ca6..323ce537c1340 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -238,6 +238,38 @@ IdentifierLoc *Parser::ParseIdentifierLoc() { return IL; } +void Parser::ParseSwiftNewtypeAttribute( + IdentifierInfo &SwiftNewtype, SourceLocation SwiftNewtypeLoc, + ParsedAttributes &attrs, SourceLocation *endLoc, IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, AttributeList::Syntax Syntax) { + + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + + if (Tok.is(tok::r_paren)) { + Diag(Tok.getLocation(), diag::err_argument_required_after_attribute); + Parens.consumeClose(); + return; + } + if (Tok.isNot(tok::kw_struct) && Tok.isNot(tok::kw_enum)) { + Diag(Tok.getLocation(), diag::warn_attribute_type_not_supported) + << &SwiftNewtype << Tok.getIdentifierInfo(); + if (!isTokenSpecial()) + ConsumeToken(); + Parens.consumeClose(); + return; + } + auto IL = IdentifierLoc::create(Actions.Context, Tok.getLocation(), + Tok.getIdentifierInfo()); + ConsumeToken(); + auto identLoc = ArgsUnion(IL); + + attrs.addNew(&SwiftNewtype, + SourceRange(SwiftNewtypeLoc, Parens.getCloseLocation()), + ScopeName, ScopeLoc, &identLoc, 1, Syntax); + Parens.consumeClose(); +} + void Parser::ParseAttributeWithTypeArg(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, @@ -357,6 +389,10 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, Syntax); return; + } else if (AttrKind == AttributeList::AT_SwiftNewtype) { + ParseSwiftNewtypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, + ScopeName, ScopeLoc, Syntax); + return; } else if (attributeIsTypeArgAttr(*AttrName)) { ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, Syntax); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index b8ab12eae946a..d257c2486e6fd 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4971,6 +4971,45 @@ static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } +static void handleSwiftNewtypeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Make sure that there is an identifier as the annotation's single + // argument. + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) + << Attr.getName() << 1; + Attr.setInvalid(); + return; + } + if (!Attr.isArgIdent(0)) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) + << Attr.getName() << AANT_ArgumentIdentifier; + Attr.setInvalid(); + return; + } + + IdentifierInfo *II = Attr.getArgAsIdent(0)->Ident; + SwiftNewtypeAttr::NewtypeKind Kind; + if (II->isStr("struct")) + Kind = SwiftNewtypeAttr::NK_Struct; + else if (II->isStr("enum")) + Kind = SwiftNewtypeAttr::NK_Enum; + else { + S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported) + << Attr.getName() << II; + Attr.setInvalid(); + return; + } + + if (!isa<TypedefNameDecl>(D)) { + S.Diag(Attr.getLoc(), diag::warn_swift_newtype_attribute_non_typedef); + return; + } + + D->addAttr(::new (S.Context) + SwiftNewtypeAttr(Attr.getRange(), S.Context, Kind, + Attr.getAttributeSpellingListIndex())); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -6283,6 +6322,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SwiftBridge: handleSwiftBridgeAttr(S, D, Attr); break; + case AttributeList::AT_SwiftNewtype: + handleSwiftNewtypeAttr(S, D, Attr); + break; } } diff --git a/clang/test/SemaObjC/attr-swift_newtype.c b/clang/test/SemaObjC/attr-swift_newtype.c new file mode 100644 index 0000000000000..61e4d89642a7c --- /dev/null +++ b/clang/test/SemaObjC/attr-swift_newtype.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +typedef int T1 __attribute__((swift_newtype(struct))); +typedef int T2 __attribute__((swift_newtype(enum))); + +typedef int T3 __attribute__((swift_wrapper(struct))); +typedef int T4 __attribute__((swift_wrapper(enum))); + + +typedef int Bad1 __attribute__((swift_newtype(bad))); // expected-warning{{'swift_newtype' attribute argument not supported: 'bad'}} +typedef int Bad2 __attribute__((swift_newtype())); // expected-error{{argument required after attribute}} +typedef int Bad3 __attribute__((swift_newtype(bad, badder))); + // expected-error@-1{{expected ')'}} + // expected-note@-2{{to match this '('}} + // expected-warning@-3{{'swift_newtype' attribute argument not supported: 'bad'}} + + +// TODO: better error message below +// FIXME: why is this a parse error, rather than Sema error triggering? +struct Bad4 __attribute__((swift_newtype(struct))) { }; // expected-error{{expected identifier or '('}} From a3db799d69ebfe8179803b7ff33b60a78b6e7f93 Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Wed, 20 Apr 2016 12:47:18 -0700 Subject: [PATCH 063/582] [swift_name] add warning group, to enable flags apple-llvm-split-commit: a64266f95c3c173440294c23b5b8980d3d7cfc89 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 82d0004954c9c..9b09c33c1ef00 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3058,7 +3058,7 @@ def err_attr_swift_error_return_type : Error< "%select{function|method}2 returning %select{an integral type|a pointer}3">; def warn_swift_newtype_attribute_non_typedef : Warning< "'swift_newtype' attribute may be put on a typedef only; " - "attribute is ignored">; + "attribute is ignored">, InGroup<DiagGroup<"swift-newtype-attribute">>; // Function Parameter Semantic Analysis. def err_param_with_void_type : Error<"argument may not have 'void' type">; From 1f4ca9724538c1ff5183dd4cc3b29f2e0cfeb628 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Thu, 21 Apr 2016 21:07:25 +0000 Subject: [PATCH 064/582] [ProfileData] Report errors from InstrProfSymtab::create InstrProfSymtab::create can fail with instrprof_error::malformed, but this error is silently dropped. Propagate the error up to the caller so we fail early. Eventually, I'd like to transition ProfileData over to the new Error class so we can't ignore hard failures like this. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@267055 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 089f755c042bc9cab7a7e8899f1b0c706611146c) apple-llvm-split-commit: a47bc3328dbf87c0d334354dc3c9e2678faef85e apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ProfileData/InstrProfReader.h | 2 +- llvm/lib/ProfileData/InstrProfReader.cpp | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/llvm/include/llvm/ProfileData/InstrProfReader.h b/llvm/include/llvm/ProfileData/InstrProfReader.h index 74610f7ff558b..303d9af90f51d 100644 --- a/llvm/include/llvm/ProfileData/InstrProfReader.h +++ b/llvm/include/llvm/ProfileData/InstrProfReader.h @@ -196,7 +196,7 @@ class RawInstrProfReader : public InstrProfReader { } private: - void createSymtab(InstrProfSymtab &Symtab); + std::error_code createSymtab(InstrProfSymtab &Symtab); std::error_code readNextHeader(const char *CurrentPos); std::error_code readHeader(const RawInstrProf::Header &Header); template <class IntT> IntT swap(IntT Int) const { diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp index 27142e0533f5c..b3b89d74dfb9d 100644 --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -297,8 +297,11 @@ RawInstrProfReader<IntPtrT>::readNextHeader(const char *CurrentPos) { } template <class IntPtrT> -void RawInstrProfReader<IntPtrT>::createSymtab(InstrProfSymtab &Symtab) { - Symtab.create(StringRef(NamesStart, NamesSize)); +std::error_code +RawInstrProfReader<IntPtrT>::createSymtab(InstrProfSymtab &Symtab) { + std::error_code EC = Symtab.create(StringRef(NamesStart, NamesSize)); + if (EC) + return EC; for (const RawInstrProf::ProfileData<IntPtrT> *I = Data; I != DataEnd; ++I) { const IntPtrT FPtr = swap(I->FunctionPointer); if (!FPtr) @@ -306,6 +309,7 @@ void RawInstrProfReader<IntPtrT>::createSymtab(InstrProfSymtab &Symtab) { Symtab.mapAddress(FPtr, I->NameRef); } Symtab.finalizeSymtab(); + return success(); } template <class IntPtrT> @@ -345,7 +349,9 @@ RawInstrProfReader<IntPtrT>::readHeader(const RawInstrProf::Header &Header) { ProfileEnd = Start + ProfileSize; std::unique_ptr<InstrProfSymtab> NewSymtab = make_unique<InstrProfSymtab>(); - createSymtab(*NewSymtab.get()); + if (auto EC = createSymtab(*NewSymtab.get())) + return EC; + Symtab = std::move(NewSymtab); return success(); } From 08065b224a60f1cea6e7557ae088779b5c8994c7 Mon Sep 17 00:00:00 2001 From: Michael Ilseman <milseman@apple.com> Date: Thu, 21 Apr 2016 15:03:37 -0700 Subject: [PATCH 065/582] [swift_newtype] Add heading to doc apple-llvm-split-commit: 11e75401c4007dce102f7eac3ddb6d81292d8a38 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/AttrDocs.td | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 8f7c3c2b0eda4..73ef8e59a2f2b 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1984,6 +1984,7 @@ All of these conventions except ``none`` require the function to have an error p def SwiftNewtypeDocs : Documentation { let Category = SwiftDocs; + let Heading = "swift_newtype"; let Content = [{ The ``swift_newtype`` attribute indicates that the typedef to which the attribute appertains is imported as a new Swift type of the typedef's name. * ``swift_newtype(struct)`` means that a Swift struct will be created for this typedef. From 0a395e26f5271cd1f3985d88b4b04302f4499f14 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Thu, 21 Apr 2016 23:23:40 -0700 Subject: [PATCH 066/582] [API Notes] Add support for SwiftPrivate -> swift_private Fixes rdar://problem/25872038. apple-llvm-split-commit: e6bdd6d24a6651fa890dd9954702bb2268cb302e apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 9 ++++++- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 1 + clang/lib/APINotes/APINotesWriter.cpp | 4 +++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 24 ++++++++++++++++++- clang/lib/Sema/SemaAPINotes.cpp | 5 ++++ clang/test/APINotes/Inputs/roundtrip.apinotes | 16 +++++++++++++ 7 files changed, 57 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index b19eea5199125..11a1e7acf54d6 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -70,16 +70,20 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; + /// Whether this entity is considered "private" to a Swift overlay. + unsigned SwiftPrivate : 1; + /// Swift name of this entity. std::string SwiftName; - CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0), SwiftPrivate(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && lhs.UnavailableInSwift == rhs.UnavailableInSwift && + lhs.SwiftPrivate == rhs.SwiftPrivate && lhs.SwiftName == rhs.SwiftName; } @@ -107,6 +111,9 @@ class CommonEntityInfo { } } + if (rhs.SwiftPrivate) + lhs.SwiftPrivate = true; + if (rhs.SwiftName.length() != 0 && lhs.SwiftName.length() == 0) lhs.SwiftName = rhs.SwiftName; diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 79143c48533db..2d878ef866308 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 11; // SwiftInferImportAsMember +const uint16_t VERSION_MINOR = 12; // SwiftPrivate using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index b05eb9d603db6..6de82ebe64be2 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -33,6 +33,7 @@ namespace { uint8_t unavailableBits = *data++; info.Unavailable = (unavailableBits >> 1) & 0x01; info.UnavailableInSwift = unavailableBits & 0x01; + info.SwiftPrivate = (unavailableBits >> 2) & 0x01; unsigned msgLength = endian::readNext<uint16_t, little, unaligned>(data); info.UnavailableMsg diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index d2bd6f19ec88c..3d4938e7cd002 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -296,7 +296,9 @@ namespace { static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer<little> writer(out); - writer.write<uint8_t>(info.Unavailable << 1 | info.UnavailableInSwift); + writer.write<uint8_t>(info.SwiftPrivate << 2 + | info.Unavailable << 1 + | info.UnavailableInSwift); writer.write<uint16_t>(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); writer.write<uint16_t>(info.SwiftName.size()); diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 606825c0ce107..a5e4fa82ceb17 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -174,6 +174,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; api_notes::FactoryAsInitKind FactoryAsInit = api_notes::FactoryAsInitKind::Infer; @@ -186,6 +187,7 @@ namespace { StringRef Name; llvm::Optional<NullabilityKind> Nullability; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector<Property> PropertiesSeq; @@ -194,6 +196,7 @@ namespace { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; StringRef SwiftBridge; MethodsSeq Methods; @@ -206,6 +209,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector<Function> FunctionsSeq; @@ -214,6 +218,7 @@ namespace { StringRef Name; llvm::Optional<NullabilityKind> Nullability; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector<GlobalVariable> GlobalVariablesSeq; @@ -221,6 +226,7 @@ namespace { struct EnumConstant { StringRef Name; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector<EnumConstant> EnumConstantsSeq; @@ -229,6 +235,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; + bool SwiftPrivate = false; StringRef SwiftBridge; }; typedef std::vector<Tag> TagsSeq; @@ -237,6 +244,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; + bool SwiftPrivate = false; StringRef SwiftBridge; }; typedef std::vector<Typedef> TypedefsSeq; @@ -321,6 +329,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); io.mapOptional("AvailabilityMsg", p.Availability.Msg); + io.mapOptional("SwiftPrivate", p.SwiftPrivate); io.mapOptional("SwiftName", p.SwiftName); } }; @@ -335,6 +344,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftPrivate", m.SwiftPrivate); io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, api_notes::FactoryAsInitKind::Infer); @@ -350,6 +360,7 @@ namespace llvm { io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); + io.mapOptional("SwiftPrivate", c.SwiftPrivate); io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("Methods", c.Methods); @@ -366,6 +377,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", f.Availability.Mode); io.mapOptional("AvailabilityMsg", f.Availability.Msg); + io.mapOptional("SwiftPrivate", f.SwiftPrivate); io.mapOptional("SwiftName", f.SwiftName); } }; @@ -378,6 +390,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); } }; @@ -388,6 +401,7 @@ namespace llvm { io.mapRequired("Name", v.Name); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); } }; @@ -398,6 +412,7 @@ namespace llvm { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); } @@ -409,6 +424,7 @@ namespace llvm { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); } @@ -543,6 +559,7 @@ namespace { return true; convertAvailability(common.Availability, info, apiName); + info.SwiftPrivate = common.SwiftPrivate; info.SwiftName = common.SwiftName; return false; } @@ -644,6 +661,7 @@ namespace { if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); + pInfo.SwiftPrivate = prop.SwiftPrivate; pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); @@ -698,6 +716,7 @@ namespace { if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); + info.SwiftPrivate = global.SwiftPrivate; info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); @@ -718,6 +737,7 @@ namespace { if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); + info.SwiftPrivate = function.SwiftPrivate; info.SwiftName = function.SwiftName; convertNullability(function.Nullability, function.NullabilityOfRet, @@ -740,6 +760,7 @@ namespace { if (!isAvailable(enumConstant.Availability)) continue; convertAvailability(enumConstant.Availability, info, enumConstant.Name); + info.SwiftPrivate = enumConstant.SwiftPrivate; info.SwiftName = enumConstant.SwiftName; Writer->addEnumConstant(enumConstant.Name, info); } @@ -749,7 +770,7 @@ namespace { for (const auto &t : TheModule.Tags) { // Check for duplicate tag definitions. if (!knownTags.insert(t.Name).second) { - emitError("multiple definitions of tag '" + t.Name + "'"); + emitError("multiple definitions Of tag '" + t.Name + "'"); continue; } @@ -861,6 +882,7 @@ namespace { template<typename T> void handleCommon(T &record, const CommonEntityInfo &info) { handleAvailability(record.Availability, info); + record.SwiftPrivate = info.SwiftPrivate; record.SwiftName = copyString(info.SwiftName); } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index bef88e2f87227..5072824313e6c 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -122,6 +122,11 @@ static void ProcessAPINotes(Sema &S, Decl *D, /*Replacement=*/StringRef())); } + // swift_private + if (Info.SwiftPrivate && !D->hasAttr<SwiftPrivateAttr>()) { + D->addAttr(SwiftPrivateAttr::CreateImplicit(S.Context)); + } + // swift_name if (!Info.SwiftName.empty() && !D->hasAttr<SwiftNameAttr>()) { D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 8509c79389b71..f02aa003dc0b3 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -7,6 +7,7 @@ Classes: - Name: NSCell Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' SwiftBridge: '' Methods: @@ -15,6 +16,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: true SwiftName: '' DesignatedInit: true - Selector: 'initImageCell:' @@ -23,6 +25,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initTextCell:' @@ -31,6 +34,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initWithCoder:' @@ -39,6 +43,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true Required: true @@ -46,6 +51,7 @@ Classes: AuditedForNullability: true Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' SwiftBridge: View Methods: @@ -55,6 +61,7 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance @@ -62,6 +69,7 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance @@ -69,44 +77,52 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView Nullability: O Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: enclosing - Name: makeBackingLayer Nullability: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: 'availableWindowDepths()' Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: calibratedWhite Enumerators: - Name: NSColorRed Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: Red Tags: - Name: NSSomeStruct Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: SomeStruct SwiftBridge: '' Typedefs: - Name: NSTypedef Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: Typedef SwiftBridge: '' From 4bace0c25885baff6b96370a735dfe1796d2b978 Mon Sep 17 00:00:00 2001 From: Joe Groff <jgroff@apple.com> Date: Tue, 17 May 2016 18:24:56 -0700 Subject: [PATCH 067/582] Tighten up validation of swift_name for properties and subscripts. Fix a logic bug that turned our check for parameter counts on getters and setters into a no-op. Add support for subscript getters and setters, and validate that they're imported as instance methods, and that subscript setters have a 'newValue:' labeled argument. Improve QoI by producing more specific diagnostics for problems with swift names. apple-llvm-split-commit: e3985c9f45c5b8462caaa5c5be383dbb582fa3e9 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h | 2 +- .../clang/Basic/DiagnosticSemaKinds.td | 26 +++++ clang/lib/Sema/SemaDeclAttr.cpp | 96 +++++++++++++++---- clang/test/SemaObjC/attr-swift.m | 40 ++++++-- 4 files changed, 137 insertions(+), 27 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index fcd04a0be187d..d13ae4c95a52e 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -36,7 +36,7 @@ namespace clang { DIAG_START_AST = DIAG_START_PARSE + 500, DIAG_START_COMMENT = DIAG_START_AST + 110, DIAG_START_SEMA = DIAG_START_COMMENT + 100, - DIAG_START_ANALYSIS = DIAG_START_SEMA + 3500, + DIAG_START_ANALYSIS = DIAG_START_SEMA + 4000, DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100 }; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1e768588d1174..de146008d3cb4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3063,6 +3063,32 @@ def err_attr_swift_name_identifier : Error< "parameter of %0 attribute must be an ASCII identifier string">; def err_attr_swift_name_function : Error< "parameter of %0 attribute must be a Swift function name string">; +def err_attr_swift_name_context_name_invalid_identifier : Error< + "%0 attribute has invalid identifier for context name">; +def err_attr_swift_name_basename_invalid_identifier : Error< + "%0 attribute has invalid identifier for base name">; +def err_attr_swift_name_parameter_invalid_identifier : Error< + "%0 attribute has invalid identifier for parameter name">; +def err_attr_swift_name_missing_parameters : Error< + "%0 attribute is missing parameter label clause">; +def err_attr_swift_name_subscript_not_accessor : Error< + "%0 attribute for 'subscript' must be a getter or setter">; +def err_attr_swift_name_subscript_no_parameter : Error< + "%0 attribute for 'subscript' must take at least one parameter">; +def err_attr_swift_name_subscript_getter_newValue : Error< + "%0 attribute for 'subscript' getter cannot take a 'newValue:' parameter">; +def err_attr_swift_name_subscript_setter_no_newValue : Error< + "%0 attribute for 'subscript' setter must take a 'newValue:' parameter">; +def err_attr_swift_name_subscript_setter_multiple_newValues : Error< + "%0 attribute for 'subscript' setter cannot take multiple 'newValue:' parameters">; +def err_attr_swift_name_getter_parameters : Error< + "%0 attribute for getter must not take any parameters besides 'self:'">; +def err_attr_swift_name_setter_parameters : Error< + "%0 attribute for setter must take one parameter for new value">; +def err_attr_swift_name_multiple_selfs : Error< + "%0 attribute cannot specify more than one 'self:' parameter">; +def err_attr_swift_name_static_subscript : Error< + "%0 attribute for 'subscript' must take a 'self:' parameter">; def warn_attr_swift_name_num_params : Warning< "too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">, InGroup<DiagGroup<"swift-name-attribute">>; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 84ed524a3012d..3c131b6436c76 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4671,8 +4671,10 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D, /// Do a very rough check to make sure \p Name looks like a Swift function name, /// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>, -/// and return the number of parameter names. -static bool validateSwiftFunctionName(StringRef Name, +/// and output the number of parameter names, whether this is a single-arg +/// initializer. Returns None if the name is valid, otherwise returns a +/// one-parameter diagnostic ID describing the problem. +static Optional<unsigned> validateSwiftFunctionName(StringRef Name, unsigned &ParamCount, bool &IsSingleParamInit) { ParamCount = 0; @@ -4690,7 +4692,7 @@ static bool validateSwiftFunctionName(StringRef Name, } if (Name.back() != ')') - return false; + return diag::err_attr_swift_name_function; StringRef BaseName, Parameters; std::tie(BaseName, Parameters) = Name.split('('); @@ -4704,63 +4706,116 @@ static bool validateSwiftFunctionName(StringRef Name, BaseName = ContextName; ContextName = StringRef(); } else if (ContextName.empty() || !isValidIdentifier(ContextName)) { - return false; + return diag::err_attr_swift_name_context_name_invalid_identifier; } else { IsMember = true; } if (!isValidIdentifier(BaseName) || BaseName == "_") - return false; + return diag::err_attr_swift_name_basename_invalid_identifier; + bool IsSubscript = BaseName == "subscript"; + // A subscript accessor must be a getter or setter. + if (IsSubscript && !isGetter && !isSetter) + return diag::err_attr_swift_name_subscript_not_accessor; + if (Parameters.empty()) - return false; + return diag::err_attr_swift_name_missing_parameters; Parameters = Parameters.drop_back(); // ')' if (Parameters.empty()) { - // Setters must have at least one parameter. - if (isSetter) return false; - - return true; + // Setters and subscripts must have at least one parameter. + if (IsSubscript) + return diag::err_attr_swift_name_subscript_no_parameter; + if (isSetter) + return diag::err_attr_swift_name_setter_parameters; + + return None; } if (Parameters.back() != ':') - return false; + return diag::err_attr_swift_name_function; Optional<unsigned> SelfLocation; + Optional<unsigned> NewValueLocation; + unsigned NewValueCount = 0; StringRef NextParam; do { std::tie(NextParam, Parameters) = Parameters.split(':'); if (!isValidIdentifier(NextParam)) - return false; + return diag::err_attr_swift_name_parameter_invalid_identifier; // "self" indicates the "self" argument for a member. if (IsMember && NextParam == "self") { // More than one "self"? - if (SelfLocation) return false; + if (SelfLocation) return diag::err_attr_swift_name_multiple_selfs; // The "self" location is the current parameter. SelfLocation = ParamCount; } - + + // "newValue" indicates the "newValue" argument for a setter. + if (NextParam == "newValue") { + // There should only be one 'newValue', but it's only significant for + // subscript accessors, so don't error right away. + ++NewValueCount; + + NewValueLocation = ParamCount; + } ++ParamCount; } while (!Parameters.empty()); + // Only instance subscripts are currently supported. + if (IsSubscript && !SelfLocation) + return diag::err_attr_swift_name_static_subscript; + IsSingleParamInit = (ParamCount == 1 && BaseName == "init" && NextParam != "_"); // Check the number of parameters for a getter/setter. if (isGetter || isSetter) { // Setters have one parameter for the new value. - unsigned NumExpectedParams = isSetter ? 1 : 0; + unsigned NumExpectedParams; + unsigned ParamDiag; + + if (isSetter) { + NumExpectedParams = 1; + ParamDiag = diag::err_attr_swift_name_setter_parameters; + } else { + NumExpectedParams = 0; + ParamDiag = diag::err_attr_swift_name_getter_parameters; + } // Instance methods have one parameter for "self". if (SelfLocation) ++NumExpectedParams; - - if (ParamCount != NumExpectedParams) return true; + + // Subscripts may have additional parameters beyond the expected params for + // the index. + if (IsSubscript) { + if (ParamCount < NumExpectedParams) + return ParamDiag; + // A subscript setter must explicitly label its newValue parameter to + // distinguish it from index parameters. + if (isSetter) { + if (!NewValueLocation) + return diag::err_attr_swift_name_subscript_setter_no_newValue; + // There can only be one. + if (NewValueCount > 1) + return diag::err_attr_swift_name_subscript_setter_multiple_newValues; + } else { + // Subscript getters should have no 'newValue:' parameter. + if (NewValueLocation) + return diag::err_attr_swift_name_subscript_getter_newValue; + } + } else { + // Property accessors must have exactly the number of expected params. + if (ParamCount != NumExpectedParams) + return ParamDiag; + } } - return true; + return None; } static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { @@ -4784,8 +4839,9 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { bool IsSingleParamInit; unsigned SwiftParamCount; - if (!validateSwiftFunctionName(Name, SwiftParamCount, IsSingleParamInit)) { - S.Diag(ArgLoc, diag::err_attr_swift_name_function) << Attr.getName(); + if (auto diagID + = validateSwiftFunctionName(Name, SwiftParamCount, IsSingleParamInit)){ + S.Diag(ArgLoc, *diagID) << Attr.getName(); return; } diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index effcfefff2f05..d28430dd1d43f 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -70,8 +70,8 @@ + (instancetype)specialGarply __attribute__((swift_name("foo(options:)"))); // e + (instancetype)trailingParen __attribute__((swift_name("foo("))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} + (instancetype)trailingColon:(int)value __attribute__((swift_name("foo(value)"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} -+ (instancetype)initialIgnore:(int)value __attribute__((swift_name("_(value:)"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} -+ (instancetype)middleOmitted:(int)value __attribute__((swift_name("foo(:)"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (instancetype)initialIgnore:(int)value __attribute__((swift_name("_(value:)"))); // expected-error {{'swift_name' attribute has invalid identifier for base name}} ++ (instancetype)middleOmitted:(int)value __attribute__((swift_name("foo(:)"))); // expected-error {{'swift_name' attribute has invalid identifier for parameter name}} @property(strong) id someProp __attribute__((swift_name("prop"))); @end @@ -106,19 +106,47 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { // Getters and setters. float Point3DGetMagnitude(Point3D point) __attribute__((swift_name("getter:Point3D.magnitude(self:)"))); +float Point3DGetMagnitudeAndSomethingElse(Point3D point, float wat) __attribute__((swift_name("getter:Point3D.magnitude(self:wat:)"))); // expected-error {{'swift_name' attribute for getter must not take any parameters besides 'self:'}} + float Point3DGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.radius(self:)"))); -void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:_:)"))); +void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:newValue:)"))); + +float Point3DPreGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.preRadius(self:)"))); +void Point3DPreSetRadius(float radius, Point3D point) __attribute__((swift_name("setter:Point3D.preRadius(newValue:self:)"))); + +void Point3DSetRadiusAndSomethingElse(Point3D point, float radius, float wat) __attribute__((swift_name("setter:Point3D.radius(self:newValue:wat:)"))); // expected-error {{'swift_name' attribute for setter must take one parameter for new value}} + +float Point3DGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)"))); +float Point3DSetComponent(Point3D point, unsigned index, float value) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:)"))); + +float Point3DGetMatrixComponent(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("getter:Point3D.subscript(self:x:y:)"))); +void Point3DSetMatrixComponent(Point3D point, unsigned x, float value, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:newValue:y:)"))); + +float Point3DSetWithoutNewValue(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:y:)"))); // expected-error {{'swift_name' attribute for 'subscript' setter must take a 'newValue:' parameter}} + +float Point3DSubscriptButNotGetterSetter(Point3D point, unsigned x) __attribute__((swift_name("Point3D.subscript(self:_:)"))); // expected-error {{'swift_name' attribute for 'subscript' must be a getter or setter}} + +void Point3DSubscriptSetterTwoNewValues(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-error {{'swift_name' attribute for 'subscript' setter cannot take multiple 'newValue:' parameters}} +float Point3DSubscriptGetterNewValue(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("getter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-error {{'swift_name' attribute for 'subscript' getter cannot take a 'newValue:' parameter}} + +void Point3DMethodWithNewValue(Point3D point, float newValue) __attribute__((swift_name("Point3D.method(self:newValue:)"))); +void Point3DMethodWithNewValues(Point3D point, float newValue, float newValueB) __attribute__((swift_name("Point3D.method(self:newValue:newValue:)"))); + +float Point3DStaticSubscript(unsigned x) __attribute__((swift_name("getter:Point3D.subscript(_:)"))); // expected-error {{'swift_name' attribute for 'subscript' must take a 'self:' parameter}} +float Point3DStaticSubscriptNoArgs(void) __attribute__((swift_name("getter:Point3D.subscript()"))); // expected-error {{'swift_name' attribute for 'subscript' must take at least one parameter}} + +float Point3DPreGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)"))); Point3D getCurrentPoint3D(void) __attribute__((swift_name("getter:currentPoint3D()"))); -void setCurrentPoint3D(Point3D point) __attribute__((swift_name("setter:currentPoint3D(_:)"))); +void setCurrentPoint3D(Point3D point) __attribute__((swift_name("setter:currentPoint3D(newValue:)"))); Point3D getLastPoint3D(void) __attribute__((swift_name("getter:lastPoint3D()"))); -void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(_:)"))); +void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(newValue:)"))); Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D.zero()"))); -void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(_:)"))); +void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(newValue:)"))); Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} From 02cfc2945fc86dd00bbc915e36b57b4c7786df1e Mon Sep 17 00:00:00 2001 From: Joe Groff <jgroff@apple.com> Date: Wed, 18 May 2016 11:33:32 -0700 Subject: [PATCH 068/582] Only allow swift_name attributes on prototyped declarations. apple-llvm-split-commit: 6138e27b79204f47b8057de3e3f97b7647912cfb apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ clang/lib/Sema/SemaDeclAttr.cpp | 4 ++++ clang/test/SemaObjC/attr-swift.m | 5 +++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index de146008d3cb4..4929af3d05c7d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3063,6 +3063,8 @@ def err_attr_swift_name_identifier : Error< "parameter of %0 attribute must be an ASCII identifier string">; def err_attr_swift_name_function : Error< "parameter of %0 attribute must be a Swift function name string">; +def err_attr_swift_name_function_no_prototype : Error< + "%0 attribute can only be applied to function declarations with prototypes">; def err_attr_swift_name_context_name_invalid_identifier : Error< "%0 attribute has invalid identifier for context name">; def err_attr_swift_name_basename_invalid_identifier : Error< diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index b9e080f368d92..8c12dd230e9b4 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4836,6 +4836,10 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { const auto *Function = cast<FunctionDecl>(D); ParamCount = Function->getNumParams(); Params = Function->parameters(); + + if (!Function->hasWrittenPrototype()) + S.Diag(ArgLoc, diag::err_attr_swift_name_function_no_prototype) + << Attr.getName(); } bool IsSingleParamInit; diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index d28430dd1d43f..c8b316cbe94f6 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -145,11 +145,12 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(newValue:)"))); -Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D.zero()"))); +Point3D getZeroPoint(void) __attribute__((swift_name("getter:Point3D.zero()"))); +Point3D getZeroPointNoPrototype() __attribute__((swift_name("getter:Point3D.zeroNoPrototype()"))); // expected-error{{'swift_name' attribute can only be applied to function declarations with prototypes}} void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(newValue:)"))); Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} -void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +void badSetter1(void) __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} From 1a1ef7565539c95e3d0dd7519805591ddaf374f9 Mon Sep 17 00:00:00 2001 From: Joe Groff <jgroff@apple.com> Date: Thu, 19 May 2016 12:56:16 -0700 Subject: [PATCH 069/582] Validate swift_names that come from API notes. And relax swift_name validation errors into warnings, so that SDKs don't break when we change or tighten the rules. apple-llvm-split-commit: 7ef73896edc6b84fce64fb472fb62c393ed3f223 apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticSemaKinds.td | 34 ++--- clang/include/clang/Sema/Sema.h | 18 +++ clang/lib/Sema/SemaAPINotes.cpp | 11 +- clang/lib/Sema/SemaDeclAttr.cpp | 138 ++++++++++-------- clang/test/SemaObjC/attr-swift.m | 40 ++--- 5 files changed, 141 insertions(+), 100 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4929af3d05c7d..c4fe8546f4501 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3057,39 +3057,37 @@ def err_objc_attr_protocol_requires_definition : Error< "attribute %0 can only be applied to @protocol definitions, not forward declarations">; // Swift attributes -def err_attr_swift_name_decl_kind : Error< +def warn_attr_swift_name_decl_kind : Warning< "%0 attribute cannot be applied to this declaration">; -def err_attr_swift_name_identifier : Error< - "parameter of %0 attribute must be an ASCII identifier string">; -def err_attr_swift_name_function : Error< +def warn_attr_swift_name_function : Warning< "parameter of %0 attribute must be a Swift function name string">; -def err_attr_swift_name_function_no_prototype : Error< +def warn_attr_swift_name_function_no_prototype : Warning< "%0 attribute can only be applied to function declarations with prototypes">; -def err_attr_swift_name_context_name_invalid_identifier : Error< +def warn_attr_swift_name_context_name_invalid_identifier : Warning< "%0 attribute has invalid identifier for context name">; -def err_attr_swift_name_basename_invalid_identifier : Error< +def warn_attr_swift_name_basename_invalid_identifier : Warning< "%0 attribute has invalid identifier for base name">; -def err_attr_swift_name_parameter_invalid_identifier : Error< +def warn_attr_swift_name_parameter_invalid_identifier : Warning< "%0 attribute has invalid identifier for parameter name">; -def err_attr_swift_name_missing_parameters : Error< +def warn_attr_swift_name_missing_parameters : Warning< "%0 attribute is missing parameter label clause">; -def err_attr_swift_name_subscript_not_accessor : Error< +def warn_attr_swift_name_subscript_not_accessor : Warning< "%0 attribute for 'subscript' must be a getter or setter">; -def err_attr_swift_name_subscript_no_parameter : Error< +def warn_attr_swift_name_subscript_no_parameter : Warning< "%0 attribute for 'subscript' must take at least one parameter">; -def err_attr_swift_name_subscript_getter_newValue : Error< +def warn_attr_swift_name_subscript_getter_newValue : Warning< "%0 attribute for 'subscript' getter cannot take a 'newValue:' parameter">; -def err_attr_swift_name_subscript_setter_no_newValue : Error< +def warn_attr_swift_name_subscript_setter_no_newValue : Warning< "%0 attribute for 'subscript' setter must take a 'newValue:' parameter">; -def err_attr_swift_name_subscript_setter_multiple_newValues : Error< +def warn_attr_swift_name_subscript_setter_multiple_newValues : Warning< "%0 attribute for 'subscript' setter cannot take multiple 'newValue:' parameters">; -def err_attr_swift_name_getter_parameters : Error< +def warn_attr_swift_name_getter_parameters : Warning< "%0 attribute for getter must not take any parameters besides 'self:'">; -def err_attr_swift_name_setter_parameters : Error< +def warn_attr_swift_name_setter_parameters : Warning< "%0 attribute for setter must take one parameter for new value">; -def err_attr_swift_name_multiple_selfs : Error< +def warn_attr_swift_name_multiple_selfs : Warning< "%0 attribute cannot specify more than one 'self:' parameter">; -def err_attr_swift_name_static_subscript : Error< +def warn_attr_swift_name_static_subscript : Warning< "%0 attribute for 'subscript' must take a 'self:' parameter">; def warn_attr_swift_name_num_params : Warning< "too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6c7c60c2d4aed..d72c1912f2bfe 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1382,6 +1382,24 @@ class Sema { } }; + /// Do a check to make sure \p Name looks like a legal swift_name + /// attribute for the decl \p D. Raise a diagnostic if the name is invalid + /// for the given declaration. + /// + /// For a function, this will validate a compound Swift name, + /// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>, + /// and the function will output the number of parameter names, and whether + /// this is a single-arg initializer. + /// + /// For a type, enum constant, property, or variable declaration, this will + /// validate either a simple identifier, or a qualified + /// <code>context.identifier</code> name. + /// + /// \returns true if the name is a valid swift name for \p D, false otherwise. + bool DiagnoseSwiftName(Decl *D, StringRef Name, + SourceLocation ArgLoc, + IdentifierInfo *AttrName); + private: bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser *Diagnoser); diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 5072824313e6c..4468d22f30a53 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -111,7 +111,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (Info.UnavailableInSwift) { D->addAttr(AvailabilityAttr::CreateImplicit( - S.Context, + S.Context, &S.Context.Idents.get("swift"), VersionTuple(), VersionTuple(), @@ -129,9 +129,14 @@ static void ProcessAPINotes(Sema &S, Decl *D, // swift_name if (!Info.SwiftName.empty() && !D->hasAttr<SwiftNameAttr>()) { + auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); + + if (!S.DiagnoseSwiftName(D, Info.SwiftName, D->getLocation(), + &APINoteName)) { + return; + } D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, - CopyString(S.Context, - Info.SwiftName))); + CopyString(S.Context, Info.SwiftName))); } } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 8c12dd230e9b4..9e9c28f92cdc3 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4670,15 +4670,11 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } -/// Do a very rough check to make sure \p Name looks like a Swift function name, -/// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>, -/// and output the number of parameter names, whether this is a single-arg -/// initializer. Returns None if the name is valid, otherwise returns a -/// one-parameter diagnostic ID describing the problem. -static Optional<unsigned> validateSwiftFunctionName(StringRef Name, - unsigned &ParamCount, - bool &IsSingleParamInit) { - ParamCount = 0; +static Optional<unsigned> +validateSwiftFunctionName(StringRef Name, + unsigned &SwiftParamCount, + bool &IsSingleParamInit) { + SwiftParamCount = 0; // Check whether this will be mapped to a getter or setter of a // property. @@ -4693,7 +4689,7 @@ static Optional<unsigned> validateSwiftFunctionName(StringRef Name, } if (Name.back() != ')') - return diag::err_attr_swift_name_function; + return diag::warn_attr_swift_name_function; StringRef BaseName, Parameters; std::tie(BaseName, Parameters) = Name.split('('); @@ -4707,35 +4703,35 @@ static Optional<unsigned> validateSwiftFunctionName(StringRef Name, BaseName = ContextName; ContextName = StringRef(); } else if (ContextName.empty() || !isValidIdentifier(ContextName)) { - return diag::err_attr_swift_name_context_name_invalid_identifier; + return diag::warn_attr_swift_name_context_name_invalid_identifier; } else { IsMember = true; } if (!isValidIdentifier(BaseName) || BaseName == "_") - return diag::err_attr_swift_name_basename_invalid_identifier; + return diag::warn_attr_swift_name_basename_invalid_identifier; bool IsSubscript = BaseName == "subscript"; // A subscript accessor must be a getter or setter. if (IsSubscript && !isGetter && !isSetter) - return diag::err_attr_swift_name_subscript_not_accessor; + return diag::warn_attr_swift_name_subscript_not_accessor; if (Parameters.empty()) - return diag::err_attr_swift_name_missing_parameters; + return diag::warn_attr_swift_name_missing_parameters; Parameters = Parameters.drop_back(); // ')' if (Parameters.empty()) { // Setters and subscripts must have at least one parameter. if (IsSubscript) - return diag::err_attr_swift_name_subscript_no_parameter; + return diag::warn_attr_swift_name_subscript_no_parameter; if (isSetter) - return diag::err_attr_swift_name_setter_parameters; + return diag::warn_attr_swift_name_setter_parameters; return None; } if (Parameters.back() != ':') - return diag::err_attr_swift_name_function; + return diag::warn_attr_swift_name_function; Optional<unsigned> SelfLocation; Optional<unsigned> NewValueLocation; @@ -4745,15 +4741,15 @@ static Optional<unsigned> validateSwiftFunctionName(StringRef Name, std::tie(NextParam, Parameters) = Parameters.split(':'); if (!isValidIdentifier(NextParam)) - return diag::err_attr_swift_name_parameter_invalid_identifier; + return diag::warn_attr_swift_name_parameter_invalid_identifier; // "self" indicates the "self" argument for a member. if (IsMember && NextParam == "self") { // More than one "self"? - if (SelfLocation) return diag::err_attr_swift_name_multiple_selfs; + if (SelfLocation) return diag::warn_attr_swift_name_multiple_selfs; // The "self" location is the current parameter. - SelfLocation = ParamCount; + SelfLocation = SwiftParamCount; } // "newValue" indicates the "newValue" argument for a setter. @@ -4762,17 +4758,17 @@ static Optional<unsigned> validateSwiftFunctionName(StringRef Name, // subscript accessors, so don't error right away. ++NewValueCount; - NewValueLocation = ParamCount; + NewValueLocation = SwiftParamCount; } - ++ParamCount; + ++SwiftParamCount; } while (!Parameters.empty()); // Only instance subscripts are currently supported. if (IsSubscript && !SelfLocation) - return diag::err_attr_swift_name_static_subscript; + return diag::warn_attr_swift_name_static_subscript; IsSingleParamInit = - (ParamCount == 1 && BaseName == "init" && NextParam != "_"); + (SwiftParamCount == 1 && BaseName == "init" && NextParam != "_"); // Check the number of parameters for a getter/setter. if (isGetter || isSetter) { @@ -4782,10 +4778,10 @@ static Optional<unsigned> validateSwiftFunctionName(StringRef Name, if (isSetter) { NumExpectedParams = 1; - ParamDiag = diag::err_attr_swift_name_setter_parameters; + ParamDiag = diag::warn_attr_swift_name_setter_parameters; } else { NumExpectedParams = 0; - ParamDiag = diag::err_attr_swift_name_getter_parameters; + ParamDiag = diag::warn_attr_swift_name_getter_parameters; } // Instance methods have one parameter for "self". @@ -4794,24 +4790,24 @@ static Optional<unsigned> validateSwiftFunctionName(StringRef Name, // Subscripts may have additional parameters beyond the expected params for // the index. if (IsSubscript) { - if (ParamCount < NumExpectedParams) + if (SwiftParamCount < NumExpectedParams) return ParamDiag; // A subscript setter must explicitly label its newValue parameter to // distinguish it from index parameters. if (isSetter) { if (!NewValueLocation) - return diag::err_attr_swift_name_subscript_setter_no_newValue; + return diag::warn_attr_swift_name_subscript_setter_no_newValue; // There can only be one. if (NewValueCount > 1) - return diag::err_attr_swift_name_subscript_setter_multiple_newValues; + return diag::warn_attr_swift_name_subscript_setter_multiple_newValues; } else { // Subscript getters should have no 'newValue:' parameter. if (NewValueLocation) - return diag::err_attr_swift_name_subscript_getter_newValue; + return diag::warn_attr_swift_name_subscript_getter_newValue; } } else { // Property accessors must have exactly the number of expected params. - if (ParamCount != NumExpectedParams) + if (SwiftParamCount != NumExpectedParams) return ParamDiag; } } @@ -4819,12 +4815,23 @@ static Optional<unsigned> validateSwiftFunctionName(StringRef Name, return None; } -static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { - StringRef Name; - SourceLocation ArgLoc; - if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc)) - return; - +/// Do a check to make sure \p Name looks like a legal swift_name +/// attribute for the decl \p D. Raise a diagnostic if the name is invalid +/// for the given declaration. +/// +/// For a function, this will validate a compound Swift name, +/// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>, +/// and the function will output the number of parameter names, and whether this +/// is a single-arg initializer. +/// +/// For a type, enum constant, property, or variable declaration, this will +/// validate either a simple identifier, or a qualified +/// <code>context.identifier</code> name. +/// +/// \returns true if the name is a valid swift name for \p D, false otherwise. +bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name, + SourceLocation ArgLoc, + IdentifierInfo *AttrName) { if (isa<ObjCMethodDecl>(D) || isa<FunctionDecl>(D)) { ArrayRef<ParmVarDecl*> Params; unsigned ParamCount; @@ -4837,19 +4844,21 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { ParamCount = Function->getNumParams(); Params = Function->parameters(); - if (!Function->hasWrittenPrototype()) - S.Diag(ArgLoc, diag::err_attr_swift_name_function_no_prototype) - << Attr.getName(); + if (!Function->hasWrittenPrototype()) { + Diag(ArgLoc, diag::warn_attr_swift_name_function_no_prototype) + << AttrName; + return false; + } } - bool IsSingleParamInit; unsigned SwiftParamCount; - if (auto diagID - = validateSwiftFunctionName(Name, SwiftParamCount, IsSingleParamInit)){ - S.Diag(ArgLoc, *diagID) << Attr.getName(); - return; + bool IsSingleParamInit; + if (auto diagID = validateSwiftFunctionName(Name, SwiftParamCount, + IsSingleParamInit)) { + Diag(ArgLoc, *diagID) << AttrName; + return false; } - + bool ParamsOK; if (SwiftParamCount == ParamCount) { ParamsOK = true; @@ -4871,10 +4880,10 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { } if (!ParamsOK) { - S.Diag(ArgLoc, diag::warn_attr_swift_name_num_params) - << (SwiftParamCount > ParamCount) << Attr.getName() + Diag(ArgLoc, diag::warn_attr_swift_name_num_params) + << (SwiftParamCount > ParamCount) << AttrName << ParamCount << SwiftParamCount; - return; + return false; } } else if (isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) || @@ -4887,24 +4896,35 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { BaseName = ContextName; ContextName = StringRef(); } else if (!isValidIdentifier(ContextName)) { - S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); - return; + Diag(ArgLoc, diag::warn_attr_swift_name_context_name_invalid_identifier) + << AttrName; + return false; } if (!isValidIdentifier(BaseName)) { - S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); - return; + Diag(ArgLoc, diag::warn_attr_swift_name_basename_invalid_identifier) + << AttrName; + return false; } } else { - S.Diag(Attr.getLoc(), diag::err_attr_swift_name_decl_kind) - << Attr.getName(); - return; + Diag(ArgLoc, diag::warn_attr_swift_name_decl_kind) << AttrName; + return false; } + return true; +} - D->addAttr(::new (S.Context) - SwiftNameAttr(Attr.getRange(), S.Context, Name, - Attr.getAttributeSpellingListIndex())); +static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { + StringRef Name; + SourceLocation ArgLoc; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc)) + return; + + if (!S.DiagnoseSwiftName(D, Name, ArgLoc, Attr.getName())) + return; + + D->addAttr(::new (S.Context) SwiftNameAttr(Attr.getRange(), S.Context, Name, + Attr.getAttributeSpellingListIndex())); } static bool isErrorParameter(Sema &S, QualType paramType) { diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index c8b316cbe94f6..bdcbbd0ace605 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -50,7 +50,7 @@ + (SNFoo *)fooWithValue:(int)value __attribute__((swift_name("foo(value:)"))); + (SNFoo *)fooWithValue:(int)value value:(int)value2 __attribute__((swift_name("foo(value:extra:)"))); + (SNFoo *)fooWithConvertingValue:(int)value value:(int)value2 __attribute__((swift_name("init(_:extra:)"))); -+ (SNFoo *)fooWithOtherValue:(int)value __attribute__((swift_name("init"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (SNFoo *)fooWithOtherValue:(int)value __attribute__((swift_name("init"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}} + (SNFoo *)fooWithAnotherValue:(int)value __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}} + (SNFoo *)fooWithYetAnotherValue:(int)value __attribute__((swift_name("foo(value:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 1; got 2)}} @@ -68,10 +68,10 @@ + (instancetype)specialBar __attribute__((swift_name("init(options:extra:)"))); + (instancetype)specialBaz __attribute__((swift_name("init(_:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}} + (instancetype)specialGarply __attribute__((swift_name("foo(options:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}} -+ (instancetype)trailingParen __attribute__((swift_name("foo("))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} -+ (instancetype)trailingColon:(int)value __attribute__((swift_name("foo(value)"))); // expected-error {{parameter of 'swift_name' attribute must be a Swift function name string}} -+ (instancetype)initialIgnore:(int)value __attribute__((swift_name("_(value:)"))); // expected-error {{'swift_name' attribute has invalid identifier for base name}} -+ (instancetype)middleOmitted:(int)value __attribute__((swift_name("foo(:)"))); // expected-error {{'swift_name' attribute has invalid identifier for parameter name}} ++ (instancetype)trailingParen __attribute__((swift_name("foo("))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (instancetype)trailingColon:(int)value __attribute__((swift_name("foo(value)"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}} ++ (instancetype)initialIgnore:(int)value __attribute__((swift_name("_(value:)"))); // expected-warning {{'swift_name' attribute has invalid identifier for base name}} ++ (instancetype)middleOmitted:(int)value __attribute__((swift_name("foo(:)"))); // expected-warning {{'swift_name' attribute has invalid identifier for parameter name}} @property(strong) id someProp __attribute__((swift_name("prop"))); @end @@ -80,7 +80,7 @@ enum __attribute__((swift_name("MoreColors"))) MoreColors { Cyan, Magenta, Yellow __attribute__((swift_name("RoseGold"))), - Black __attribute__((swift_name("SpaceGrey()"))) // expected-error {{parameter of 'swift_name' attribute must be an ASCII identifier string}} + Black __attribute__((swift_name("SpaceGrey()"))) // expected-warning {{'swift_name' attribute has invalid identifier for base name}} }; struct __attribute__((swift_name("FooStruct"))) BarStruct { @@ -89,7 +89,7 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { int global_int __attribute__((swift_name("GlobalInt"))); -void foo1(int i) __attribute__((swift_name("foo"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +void foo1(int i) __attribute__((swift_name("foo"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}} void foo2(int i) __attribute__((swift_name("foo()"))); // expected-warning{{too few parameters in 'swift_name' attribute (expected 1; got 0)}} void foo2(int i) __attribute__((swift_name("foo(a:b:)"))); // expected-warning{{too many parameters in 'swift_name' attribute (expected 1; got 2)}} void foo3(int i, int j) __attribute__((swift_name("fooWithX(_:y:)"))); // okay @@ -106,7 +106,7 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { // Getters and setters. float Point3DGetMagnitude(Point3D point) __attribute__((swift_name("getter:Point3D.magnitude(self:)"))); -float Point3DGetMagnitudeAndSomethingElse(Point3D point, float wat) __attribute__((swift_name("getter:Point3D.magnitude(self:wat:)"))); // expected-error {{'swift_name' attribute for getter must not take any parameters besides 'self:'}} +float Point3DGetMagnitudeAndSomethingElse(Point3D point, float wat) __attribute__((swift_name("getter:Point3D.magnitude(self:wat:)"))); // expected-warning {{'swift_name' attribute for getter must not take any parameters besides 'self:'}} float Point3DGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.radius(self:)"))); void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:newValue:)"))); @@ -114,7 +114,7 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { float Point3DPreGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.preRadius(self:)"))); void Point3DPreSetRadius(float radius, Point3D point) __attribute__((swift_name("setter:Point3D.preRadius(newValue:self:)"))); -void Point3DSetRadiusAndSomethingElse(Point3D point, float radius, float wat) __attribute__((swift_name("setter:Point3D.radius(self:newValue:wat:)"))); // expected-error {{'swift_name' attribute for setter must take one parameter for new value}} +void Point3DSetRadiusAndSomethingElse(Point3D point, float radius, float wat) __attribute__((swift_name("setter:Point3D.radius(self:newValue:wat:)"))); // expected-warning {{'swift_name' attribute for setter must take one parameter for new value}} float Point3DGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)"))); float Point3DSetComponent(Point3D point, unsigned index, float value) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:)"))); @@ -122,18 +122,18 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { float Point3DGetMatrixComponent(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("getter:Point3D.subscript(self:x:y:)"))); void Point3DSetMatrixComponent(Point3D point, unsigned x, float value, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:newValue:y:)"))); -float Point3DSetWithoutNewValue(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:y:)"))); // expected-error {{'swift_name' attribute for 'subscript' setter must take a 'newValue:' parameter}} +float Point3DSetWithoutNewValue(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:y:)"))); // expected-warning {{'swift_name' attribute for 'subscript' setter must take a 'newValue:' parameter}} -float Point3DSubscriptButNotGetterSetter(Point3D point, unsigned x) __attribute__((swift_name("Point3D.subscript(self:_:)"))); // expected-error {{'swift_name' attribute for 'subscript' must be a getter or setter}} +float Point3DSubscriptButNotGetterSetter(Point3D point, unsigned x) __attribute__((swift_name("Point3D.subscript(self:_:)"))); // expected-warning {{'swift_name' attribute for 'subscript' must be a getter or setter}} -void Point3DSubscriptSetterTwoNewValues(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-error {{'swift_name' attribute for 'subscript' setter cannot take multiple 'newValue:' parameters}} -float Point3DSubscriptGetterNewValue(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("getter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-error {{'swift_name' attribute for 'subscript' getter cannot take a 'newValue:' parameter}} +void Point3DSubscriptSetterTwoNewValues(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-warning {{'swift_name' attribute for 'subscript' setter cannot take multiple 'newValue:' parameters}} +float Point3DSubscriptGetterNewValue(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("getter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-warning {{'swift_name' attribute for 'subscript' getter cannot take a 'newValue:' parameter}} void Point3DMethodWithNewValue(Point3D point, float newValue) __attribute__((swift_name("Point3D.method(self:newValue:)"))); void Point3DMethodWithNewValues(Point3D point, float newValue, float newValueB) __attribute__((swift_name("Point3D.method(self:newValue:newValue:)"))); -float Point3DStaticSubscript(unsigned x) __attribute__((swift_name("getter:Point3D.subscript(_:)"))); // expected-error {{'swift_name' attribute for 'subscript' must take a 'self:' parameter}} -float Point3DStaticSubscriptNoArgs(void) __attribute__((swift_name("getter:Point3D.subscript()"))); // expected-error {{'swift_name' attribute for 'subscript' must take at least one parameter}} +float Point3DStaticSubscript(unsigned x) __attribute__((swift_name("getter:Point3D.subscript(_:)"))); // expected-warning {{'swift_name' attribute for 'subscript' must take a 'self:' parameter}} +float Point3DStaticSubscriptNoArgs(void) __attribute__((swift_name("getter:Point3D.subscript()"))); // expected-warning {{'swift_name' attribute for 'subscript' must take at least one parameter}} float Point3DPreGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)"))); @@ -146,15 +146,15 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(newValue:)"))); Point3D getZeroPoint(void) __attribute__((swift_name("getter:Point3D.zero()"))); -Point3D getZeroPointNoPrototype() __attribute__((swift_name("getter:Point3D.zeroNoPrototype()"))); // expected-error{{'swift_name' attribute can only be applied to function declarations with prototypes}} +Point3D getZeroPointNoPrototype() __attribute__((swift_name("getter:Point3D.zeroNoPrototype()"))); // expected-warning{{'swift_name' attribute can only be applied to function declarations with prototypes}} void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(newValue:)"))); -Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} -void badSetter1(void) __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}} +void badSetter1(void) __attribute__((swift_name("getter:bad1())"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}} -Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}} -void badSetter2(Point3D point) __attribute__((swift_name("setter:bad2(self:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +void badSetter2(Point3D point) __attribute__((swift_name("setter:bad2(self:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}} // --- swift_error --- From e226ae71c546ba35152a1f54f28aa1aa8f1ff98d Mon Sep 17 00:00:00 2001 From: Joe Groff <jgroff@apple.com> Date: Thu, 19 May 2016 14:47:39 -0700 Subject: [PATCH 070/582] Collect swift_name warnings under a swift-name-attribute warning group. apple-llvm-split-commit: c23b39088cfb1aa1ddaaaee4fbd22410b05ca17c apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticGroups.td | 3 ++ .../clang/Basic/DiagnosticSemaKinds.td | 50 ++++++++++++------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 7b4740fe22a2f..5335545cad8ca 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -365,6 +365,9 @@ def StringCompare : DiagGroup<"string-compare">; def StringPlusInt : DiagGroup<"string-plus-int">; def StringPlusChar : DiagGroup<"string-plus-char">; def StrncatSize : DiagGroup<"strncat-size">; + +def SwiftNameAttribute : DiagGroup<"swift-name-attribute">; + def TautologicalOutOfRangeCompare : DiagGroup<"tautological-constant-out-of-range-compare">; def TautologicalPointerCompare : DiagGroup<"tautological-pointer-compare">; def TautologicalOverlapCompare : DiagGroup<"tautological-overlap-compare">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c4fe8546f4501..d61de2428859e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3058,40 +3058,56 @@ def err_objc_attr_protocol_requires_definition : Error< // Swift attributes def warn_attr_swift_name_decl_kind : Warning< - "%0 attribute cannot be applied to this declaration">; + "%0 attribute cannot be applied to this declaration">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_function : Warning< - "parameter of %0 attribute must be a Swift function name string">; + "parameter of %0 attribute must be a Swift function name string">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_function_no_prototype : Warning< - "%0 attribute can only be applied to function declarations with prototypes">; + "%0 attribute can only be applied to function declarations with prototypes">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_context_name_invalid_identifier : Warning< - "%0 attribute has invalid identifier for context name">; + "%0 attribute has invalid identifier for context name">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_basename_invalid_identifier : Warning< - "%0 attribute has invalid identifier for base name">; + "%0 attribute has invalid identifier for base name">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_parameter_invalid_identifier : Warning< - "%0 attribute has invalid identifier for parameter name">; + "%0 attribute has invalid identifier for parameter name">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_missing_parameters : Warning< - "%0 attribute is missing parameter label clause">; + "%0 attribute is missing parameter label clause">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_subscript_not_accessor : Warning< - "%0 attribute for 'subscript' must be a getter or setter">; + "%0 attribute for 'subscript' must be a getter or setter">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_subscript_no_parameter : Warning< - "%0 attribute for 'subscript' must take at least one parameter">; + "%0 attribute for 'subscript' must take at least one parameter">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_subscript_getter_newValue : Warning< - "%0 attribute for 'subscript' getter cannot take a 'newValue:' parameter">; + "%0 attribute for 'subscript' getter cannot take a 'newValue:' parameter">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_subscript_setter_no_newValue : Warning< - "%0 attribute for 'subscript' setter must take a 'newValue:' parameter">; + "%0 attribute for 'subscript' setter must take a 'newValue:' parameter">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_subscript_setter_multiple_newValues : Warning< - "%0 attribute for 'subscript' setter cannot take multiple 'newValue:' parameters">; + "%0 attribute for 'subscript' setter cannot take multiple 'newValue:' parameters">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_getter_parameters : Warning< - "%0 attribute for getter must not take any parameters besides 'self:'">; + "%0 attribute for getter must not take any parameters besides 'self:'">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_setter_parameters : Warning< - "%0 attribute for setter must take one parameter for new value">; + "%0 attribute for setter must take one parameter for new value">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_multiple_selfs : Warning< - "%0 attribute cannot specify more than one 'self:' parameter">; + "%0 attribute cannot specify more than one 'self:' parameter">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_static_subscript : Warning< - "%0 attribute for 'subscript' must take a 'self:' parameter">; + "%0 attribute for 'subscript' must take a 'self:' parameter">, + InGroup<SwiftNameAttribute>; def warn_attr_swift_name_num_params : Warning< "too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">, - InGroup<DiagGroup<"swift-name-attribute">>; + InGroup<SwiftNameAttribute>; def err_attr_swift_error_no_error_parameter : Error< "%0 attribute can only be applied to a %select{function|method}1 " "with an error parameter">; From 5e9ad90ce7e9aea3ac7c90cb17f045636dfbc974 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Mon, 13 Jun 2016 10:27:18 -0700 Subject: [PATCH 071/582] Add support for __builtin_os_log_format[_buffer_size] These new builtins support a mechanism for logging OS events, using a printf-like format string to specify the layout of data in a buffer. The _buffer_size version of the builtin can be used to determine the size of the buffer to allocate to hold the data, and then __builtin_os_log_format can write data into that buffer. This implements format checking to report mismatches between the format string and the data arguments. Most of this code was written by Chris Willmore. apple-llvm-split-commit: 7378eb19555c81a736d85b966512e5803db69e03 apple-llvm-split-dir: clang/ --- .../clang/Analysis/Analyses/FormatString.h | 19 +- clang/include/clang/Analysis/Analyses/OSLog.h | 146 ++++++++++++ clang/include/clang/Basic/Builtins.def | 3 + .../clang/Basic/DiagnosticSemaKinds.td | 13 ++ clang/include/clang/Sema/Sema.h | 3 + clang/lib/Analysis/CMakeLists.txt | 1 + clang/lib/Analysis/FormatString.cpp | 2 + clang/lib/Analysis/OSLog.cpp | 153 +++++++++++++ clang/lib/Analysis/PrintfFormatString.cpp | 37 ++- clang/lib/CodeGen/CGBuiltin.cpp | 82 +++++++ clang/lib/Sema/SemaChecking.cpp | 214 +++++++++++++++--- clang/lib/Sema/SemaDeclAttr.cpp | 1 + clang/test/CodeGen/builtins.c | 59 +++++ clang/test/CodeGenObjC/os_log.m | 39 ++++ clang/test/Sema/format-strings.c | 27 +++ clang/test/SemaObjC/format-strings-objc.m | 15 ++ 16 files changed, 784 insertions(+), 30 deletions(-) create mode 100644 clang/include/clang/Analysis/Analyses/OSLog.h create mode 100644 clang/lib/Analysis/OSLog.cpp create mode 100644 clang/test/CodeGenObjC/os_log.m diff --git a/clang/include/clang/Analysis/Analyses/FormatString.h b/clang/include/clang/Analysis/Analyses/FormatString.h index 74803a295d712..170cfad0ca3cf 100644 --- a/clang/include/clang/Analysis/Analyses/FormatString.h +++ b/clang/include/clang/Analysis/Analyses/FormatString.h @@ -35,7 +35,7 @@ class OptionalFlag { public: OptionalFlag(const char *Representation) : representation(Representation), flag(false) {} - bool isSet() { return flag; } + bool isSet() const { return flag; } void set() { flag = true; } void clear() { flag = false; } void setPosition(const char *position) { @@ -154,6 +154,11 @@ class ConversionSpecifier { CArg, SArg, + // Apple extension: P specifies to os_log that the data being pointed to is + // to be copied by os_log. The precision indicates the number of bytes to + // copy. + PArg, + // ** Printf-specific ** ZArg, // MS extension @@ -437,13 +442,15 @@ class PrintfSpecifier : public analyze_format_string::FormatSpecifier { OptionalFlag HasAlternativeForm; // '#' OptionalFlag HasLeadingZeroes; // '0' OptionalFlag HasObjCTechnicalTerm; // '[tt]' + OptionalFlag IsPrivate; // '{private}' + OptionalFlag IsPublic; // '{public}' OptionalAmount Precision; public: PrintfSpecifier() : FormatSpecifier(/* isPrintf = */ true), HasThousandsGrouping("'"), IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "), HasAlternativeForm("#"), HasLeadingZeroes("0"), - HasObjCTechnicalTerm("tt") {} + HasObjCTechnicalTerm("tt"), IsPrivate("private"), IsPublic("public") {} static PrintfSpecifier Parse(const char *beg, const char *end); @@ -472,6 +479,12 @@ class PrintfSpecifier : public analyze_format_string::FormatSpecifier { void setHasObjCTechnicalTerm(const char *position) { HasObjCTechnicalTerm.setPosition(position); } + void setIsPrivate(const char *position) { + IsPrivate.setPosition(position); + } + void setIsPublic(const char *position) { + IsPublic.setPosition(position); + } void setUsesPositionalArg() { UsesPositionalArg = true; } // Methods for querying the format specifier. @@ -509,6 +522,8 @@ class PrintfSpecifier : public analyze_format_string::FormatSpecifier { const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; } const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; } const OptionalFlag &hasObjCTechnicalTerm() const { return HasObjCTechnicalTerm; } + const OptionalFlag &isPrivate() const { return IsPrivate; } + const OptionalFlag &isPublic() const { return IsPublic; } bool usesPositionalArg() const { return UsesPositionalArg; } /// Changes the specifier and length according to a QualType, retaining any diff --git a/clang/include/clang/Analysis/Analyses/OSLog.h b/clang/include/clang/Analysis/Analyses/OSLog.h new file mode 100644 index 0000000000000..9fde26aaea4dd --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/OSLog.h @@ -0,0 +1,146 @@ +//= OSLog.h - Analysis of calls to os_log builtins --*- C++ -*-===============// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs for determining the layout of the data buffer for +// os_log() and os_trace(). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" + +namespace clang { +namespace analyze_os_log { + +/// An OSLogBufferItem represents a single item in the data written by a call +/// to os_log() or os_trace(). +class OSLogBufferItem { +public: + enum Kind { + // The item is a scalar (int, float, raw pointer, etc.). No further copying + // is required. This is the only kind allowed by os_trace(). + ScalarKind = 0, + + // The item is a count, which describes the length of the following item to + // be copied. A count may only be followed by an item of kind StringKind or + // PointerKind. + CountKind, + + // The item is a pointer to a C string. If preceded by a count 'n', + // os_log() will copy at most 'n' bytes from the pointer. + StringKind, + + // The item is a pointer to a block of raw data. This item must be preceded + // by a count 'n'. os_log() will copy exactly 'n' bytes from the pointer. + PointerKind, + + // The item is a pointer to an Objective-C object. os_log() may retain the + // object for later processing. + ObjCObjKind + }; + + enum { + // The item is marked "private" in the format string. + IsPrivate = 0x1, + + // The item is marked "public" in the format string. + IsPublic = 0x2 + }; + +private: + Kind TheKind = ScalarKind; + const Expr *TheExpr = nullptr; + CharUnits ConstValue; + CharUnits Size; // size of the data, not including the header bytes + unsigned Flags = 0; + +public: + OSLogBufferItem(Kind kind, const Expr *expr, CharUnits size, unsigned flags) + : TheKind(kind), TheExpr(expr), Size(size), Flags(flags) {} + + OSLogBufferItem(ASTContext &Ctx, CharUnits value, unsigned flags) + : TheKind(CountKind), ConstValue(value), + Size(Ctx.getTypeSizeInChars(Ctx.IntTy)), Flags(flags) {} + + unsigned char getDescriptorByte() const { + unsigned char result = 0; + if (getIsPrivate()) result |= 0x01; + if (getIsPublic()) result |= 0x02; + result |= ((unsigned)getKind()) << 4; + return result; + } + + unsigned char getSizeByte() const { + return getSize().getQuantity(); + } + + Kind getKind() const { return TheKind; } + bool getIsPrivate() const { return (Flags & IsPrivate) != 0; } + bool getIsPublic() const { return (Flags & IsPublic) != 0; } + + const Expr *getExpr() const { return TheExpr; } + CharUnits getConstValue() const { return ConstValue; } + CharUnits getSize() const { return Size; } +}; + +class OSLogBufferLayout { +public: + SmallVector<OSLogBufferItem, 4> Items; + + CharUnits getSize() const { + CharUnits result; + result += CharUnits::fromQuantity(2); // summary byte, num-args byte + for (auto &item : Items) { + // descriptor byte, size byte + result += item.getSize() + CharUnits::fromQuantity(2); + } + return result; + } + + bool getHasPrivateItems() const { + return std::any_of(Items.begin(), Items.end(), + [](const OSLogBufferItem &item) { return item.getIsPrivate(); }); + } + + bool getHasPublicItems() const { + return std::any_of(Items.begin(), Items.end(), + [](const OSLogBufferItem &item) { return item.getIsPublic(); }); + } + + bool getHasNonScalar() const { + return std::any_of(Items.begin(), Items.end(), + [](const OSLogBufferItem &item) { + return item.getKind() != OSLogBufferItem::ScalarKind; + }); + } + + unsigned char getSummaryByte() const { + unsigned char result = 0; + if (getHasPrivateItems()) result |= 0x01; + if (getHasNonScalar()) result |= 0x02; + return result; + } + + unsigned char getNumArgsByte() const { + return Items.size(); + } +}; + +// Given a call 'E' to one of the builtins __builtin_os_log_format() or +// __builtin_os_log_format_buffer_size(), compute the layout of the buffer that +// the call will write into and store it in 'layout'. Returns 'false' if there +// was some error encountered while computing the layout, and 'true' otherwise. +bool computeOSLogBufferLayout(clang::ASTContext &Ctx, const clang::CallExpr *E, OSLogBufferLayout &layout); + +} // namespace analyze_os_log +} // namespace clang +#endif diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 532fa8f755dfe..e99aed5760d66 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1279,6 +1279,9 @@ BUILTIN(__builtin___get_unsafe_stack_ptr, "v*", "Fn") BUILTIN(__builtin_nontemporal_store, "v.", "t") BUILTIN(__builtin_nontemporal_load, "v.", "t") +// Builtins for os_log/os_trace +BUILTIN(__builtin_os_log_format_buffer_size, "zcC*.", "p:0:nut") +BUILTIN(__builtin_os_log_format, "v*v*cC*.", "p:0:nt") // OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions. // We need the generic prototype, since the packet type could be anything. LANGBUILTIN(read_pipe, "i.", "tn", OCLC_LANG) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 86fd60b1c4819..3b00079edbeb1 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7300,6 +7300,12 @@ def warn_format_non_standard: Warning< def warn_format_non_standard_conversion_spec: Warning< "using length modifier '%0' with conversion specifier '%1' is not supported by ISO C">, InGroup<FormatNonStandard>, DefaultIgnore; +def warn_format_invalid_annotation : Warning< + "using '%0' format specifier annotation outside of os_log()/os_trace()">, + InGroup<Format>; +def warn_format_P_no_precision : Warning< + "using '%%P' format specifier without precision">, + InGroup<Format>; def warn_printf_ignored_flag: Warning< "flag '%0' is ignored when flag '%1' is present">, InGroup<Format>; @@ -7436,6 +7442,13 @@ def warn_cfstring_truncated : Warning< "belong to the input codeset UTF-8">, InGroup<DiagGroup<"CFString-literal">>; +// os_log checking +// TODO: separate diagnostic for os_trace() +def err_os_log_format_not_string_constant : Error< + "os_log() format argument is not a string constant">; +def err_os_log_argument_too_big : Error< + "os_log() argument %d is too big (%d bytes, max %d)">; + // Statements. def err_continue_not_in_loop : Error< "'continue' statement not in loop statement">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 78e84f0dc6e64..ab1be9e559529 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9294,6 +9294,7 @@ class Sema { VariadicCallType CallType); bool CheckObjCString(Expr *Arg); + ExprResult CheckOSLogFormatStringArg(Expr *Arg); ExprResult CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall); @@ -9315,6 +9316,7 @@ class Sema { bool SemaBuiltinVAStartARM(CallExpr *Call); bool SemaBuiltinUnorderedCompare(CallExpr *TheCall); bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs); + bool SemaBuiltinOSLogFormat(CallExpr *TheCall); public: // Used by C++ template instantiation. @@ -9350,6 +9352,7 @@ class Sema { FST_Kprintf, FST_FreeBSDKPrintf, FST_OSTrace, + FST_OSLog, FST_Unknown }; static FormatStringType GetFormatStringType(const FormatAttr *Format); diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 1df093d850983..a3990d6de3ecf 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -15,6 +15,7 @@ add_clang_library(clangAnalysis Dominators.cpp FormatString.cpp LiveVariables.cpp + OSLog.cpp ObjCNoReturn.cpp PostOrderCFGView.cpp PrintfFormatString.cpp diff --git a/clang/lib/Analysis/FormatString.cpp b/clang/lib/Analysis/FormatString.cpp index 83d08b55427fd..a0a357d0a8a58 100644 --- a/clang/lib/Analysis/FormatString.cpp +++ b/clang/lib/Analysis/FormatString.cpp @@ -579,6 +579,7 @@ const char *ConversionSpecifier::toString() const { case cArg: return "c"; case sArg: return "s"; case pArg: return "p"; + case PArg: return "P"; case nArg: return "n"; case PercentArg: return "%"; case ScanListArg: return "["; @@ -854,6 +855,7 @@ bool FormatSpecifier::hasStandardConversionSpecifier( case ConversionSpecifier::ObjCObjArg: case ConversionSpecifier::ScanListArg: case ConversionSpecifier::PercentArg: + case ConversionSpecifier::PArg: return true; case ConversionSpecifier::CArg: case ConversionSpecifier::SArg: diff --git a/clang/lib/Analysis/OSLog.cpp b/clang/lib/Analysis/OSLog.cpp new file mode 100644 index 0000000000000..804cee2c88929 --- /dev/null +++ b/clang/lib/Analysis/OSLog.cpp @@ -0,0 +1,153 @@ +// TODO: header template + +#include "clang/Analysis/Analyses/OSLog.h" +#include "clang/Analysis/Analyses/FormatString.h" +#include "clang/Basic/Builtins.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprObjC.h" +#include "llvm/ADT/SmallBitVector.h" + +using namespace clang; +using llvm::APInt; + +using clang::analyze_os_log::OSLogBufferItem; +using clang::analyze_os_log::OSLogBufferLayout; + +class OSLogFormatStringHandler + : public analyze_format_string::FormatStringHandler { +private: + ArrayRef<const Expr *> Args; + SmallVector<Optional<OSLogBufferItem::Kind>, 4> ArgKind; + SmallVector<Optional<unsigned>, 4> ArgSize; + SmallVector<unsigned char, 4> ArgFlags; + +public: + OSLogFormatStringHandler(ArrayRef<const Expr *> args) + : FormatStringHandler(), Args(args), ArgKind(args.size(), None), + ArgSize(args.size(), None), ArgFlags(args.size(), 0) + {} + + virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen) { + + // Cases to handle: + // * "%f", "%d"... scalar (assumed for anything that doesn't fit the below + // cases) + // * "%s" pointer to null-terminated string + // * "%.*s" strlen (arg), pointer to string + // * "%.16s" strlen (non-arg), pointer to string + // * "%.*P" len (arg), pointer to data + // * "%.16P" len (non-arg), pointer to data + // * "%@" pointer to objc object + + unsigned argIndex = FS.getArgIndex(); + if (argIndex >= Args.size()) { + return false; + } + switch (FS.getConversionSpecifier().getKind()) { + case clang::analyze_format_string::ConversionSpecifier::sArg: { // "%s" + ArgKind[argIndex] = OSLogBufferItem::StringKind; + auto &precision = FS.getPrecision(); + switch (precision.getHowSpecified()) { + case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s" + break; + case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s" + ArgSize[argIndex] = precision.getConstantAmount(); + break; + case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s" + ArgKind[precision.getArgIndex()] = OSLogBufferItem::CountKind; + break; + case clang::analyze_format_string::OptionalAmount::Invalid: + return false; + } + break; + } + case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" + ArgKind[argIndex] = OSLogBufferItem::PointerKind; + auto &precision = FS.getPrecision(); + switch (precision.getHowSpecified()) { + case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P" + return false; // length must be supplied with pointer format specifier + case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P" + ArgSize[argIndex] = precision.getConstantAmount(); + break; + case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P" + ArgKind[precision.getArgIndex()] = OSLogBufferItem::CountKind; + break; + case clang::analyze_format_string::OptionalAmount::Invalid: + return false; + } + break; + } + case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@" + ArgKind[argIndex] = OSLogBufferItem::ObjCObjKind; + break; + default: + ArgKind[argIndex] = OSLogBufferItem::ScalarKind; + break; + } + + if (FS.isPrivate()) { + ArgFlags[argIndex] |= OSLogBufferItem::IsPrivate; + } + if (FS.isPublic()) { + ArgFlags[argIndex] |= OSLogBufferItem::IsPublic; + } + return true; + } + + void computeLayout(ASTContext &Ctx, OSLogBufferLayout &layout) const { + layout.Items.clear(); + for (unsigned i = 0; i < Args.size(); i++) { + const Expr *arg = Args[i]; + if (ArgSize[i]) { + layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*ArgSize[i]), + ArgFlags[i]); + } + CharUnits size = Ctx.getTypeSizeInChars(arg->getType()); + if (ArgKind[i]) { + layout.Items.emplace_back(*ArgKind[i], arg, size, ArgFlags[i]); + } else { + layout.Items.emplace_back(OSLogBufferItem::ScalarKind, arg, size, + ArgFlags[i]); + } + } + } +}; + +bool clang::analyze_os_log::computeOSLogBufferLayout(ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &layout) +{ + ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs()); + + const Expr *StringArg; + ArrayRef<const Expr *> VarArgs; + switch (E->getBuiltinCallee()) { + case Builtin::BI__builtin_os_log_format_buffer_size: + assert(E->getNumArgs() >= 1 && + "__builtin_os_log_format_buffer_size takes at least 1 argument"); + StringArg = E->getArg(0); + VarArgs = Args.slice(1); + break; + case Builtin::BI__builtin_os_log_format: + assert(E->getNumArgs() >= 2 && + "__builtin_os_log_format takes at least 2 arguments"); + StringArg = E->getArg(1); + VarArgs = Args.slice(2); + break; + default: + llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout"); + } + + const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts()); + assert(Lit && (Lit->isAscii() || Lit->isUTF8())); + StringRef data = Lit->getString(); + OSLogFormatStringHandler H(VarArgs); + ParsePrintfString(H, data.begin(), data.end(), Ctx.getLangOpts(), + Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/false); + + H.computeLayout(Ctx, layout); + return true; +} diff --git a/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp index ac6cef9d08420..1966344254a2b 100644 --- a/clang/lib/Analysis/PrintfFormatString.cpp +++ b/clang/lib/Analysis/PrintfFormatString.cpp @@ -119,6 +119,35 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, return true; } + const char *OSLogVisibilityFlagsStart = nullptr, + *OSLogVisibilityFlagsEnd = nullptr; + if (*I == '{') { + OSLogVisibilityFlagsStart = I++; + // Find the end of the modifier. + while (I != E && *I != '}') { I++; } + if (I == E) { + if (Warn) + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + assert(*I == '}'); + OSLogVisibilityFlagsEnd = I++; + + // Just see if 'private' or 'public' is the first word. os_log itself will + // do any further parsing. + const char *P = OSLogVisibilityFlagsStart + 1; + while (P < OSLogVisibilityFlagsEnd && isspace(*P)) P++; + const char *WordStart = P; + while (P < OSLogVisibilityFlagsEnd && (isalnum(*P) || *P == '_')) P++; + const char *WordEnd = P; + StringRef Word(WordStart, WordEnd - WordStart); + if (Word == "private") { + FS.setIsPrivate(WordStart); + } else if (Word == "public") { + FS.setIsPublic(WordStart); + } + } + // Look for flags (if any). bool hasMore = true; for ( ; I != E; ++I) { @@ -253,6 +282,8 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, // POSIX specific. case 'C': k = ConversionSpecifier::CArg; break; case 'S': k = ConversionSpecifier::SArg; break; + // Apple extension for os_log + case 'P': k = ConversionSpecifier::PArg; break; // Objective-C. case '@': k = ConversionSpecifier::ObjCObjArg; break; // Glibc specific. @@ -301,7 +332,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, conversionPosition); return true; } - + PrintfConversionSpecifier CS(conversionPosition, k); FS.setConversionSpecifier(CS); if (CS.consumesDataArgument() && !FS.usesPositionalArg()) @@ -541,6 +572,7 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx, return Ctx.IntTy; return ArgType(Ctx.WideCharTy, "wchar_t"); case ConversionSpecifier::pArg: + case ConversionSpecifier::PArg: return ArgType::CPointerTy; case ConversionSpecifier::ObjCObjArg: return ArgType::ObjCPointerTy; @@ -900,7 +932,7 @@ bool PrintfSpecifier::hasValidPrecision() const { if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) return true; - // Precision is only valid with the diouxXaAeEfFgGs conversions + // Precision is only valid with the diouxXaAeEfFgGsP conversions switch (CS.getKind()) { case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: @@ -922,6 +954,7 @@ bool PrintfSpecifier::hasValidPrecision() const { case ConversionSpecifier::sArg: case ConversionSpecifier::FreeBSDrArg: case ConversionSpecifier::FreeBSDyArg: + case ConversionSpecifier::PArg: return true; default: diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index bf450b9ec2336..b997747c02062 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -18,6 +18,7 @@ #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/Analysis/Analyses/OSLog.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" @@ -439,6 +440,17 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type, return Builder.CreateCall(F, {EmitScalarExpr(E), CI}); } +namespace { + struct CallObjCArcUse final : EHScopeStack::Cleanup { + CallObjCArcUse(llvm::Value *object) : object(object) {} + llvm::Value *object; + + void Emit(CodeGenFunction &CGF, Flags flags) override { + CGF.EmitARCIntrinsicUse(object); + } + }; +} + RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, unsigned BuiltinID, const CallExpr *E, ReturnValueSlot ReturnValue) { @@ -1991,6 +2003,76 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, return RValue::get(llvm::ConstantExpr::getBitCast(GV, CGM.Int8PtrTy)); break; } + case Builtin::BI__builtin_os_log_format: { + assert(E->getNumArgs() >= 2 && + "__builtin_os_log_format takes at least 2 arguments"); + analyze_os_log::OSLogBufferLayout Layout; + analyze_os_log::computeOSLogBufferLayout(CGM.getContext(), E, Layout); + Address BufAddr = EmitPointerWithAlignment(E->getArg(0)); + // Ignore argument 1, the format string. It is not currently used. + CharUnits offset; + Builder.CreateStore( + Builder.getInt8(Layout.getSummaryByte()), + Builder.CreateConstByteGEP(BufAddr, offset++, "summary")); + Builder.CreateStore( + Builder.getInt8(Layout.getNumArgsByte()), + Builder.CreateConstByteGEP(BufAddr, offset++, "numArgs")); + + llvm::SmallVector<llvm::Value *, 4> RetainableOperands; + for (const auto &item : Layout.Items) { + Builder.CreateStore( + Builder.getInt8(item.getDescriptorByte()), + Builder.CreateConstByteGEP(BufAddr, offset++, "argDescriptor")); + Builder.CreateStore( + Builder.getInt8(item.getSizeByte()), + Builder.CreateConstByteGEP(BufAddr, offset++, "argSize")); + Address addr = Builder.CreateConstByteGEP(BufAddr, offset); + if (const Expr *expr = item.getExpr()) { + addr = Builder.CreateElementBitCast(addr, + ConvertTypeForMem(expr->getType())); + // Check if this is a retainable type. + if (expr->getType()->isObjCRetainableType()) { + assert(getEvaluationKind(expr->getType()) == TEK_Scalar && + "Only scalar can be a ObjC retainable type"); + llvm::Value *SV = EmitScalarExpr(expr, /*Ignore*/ false); + RValue RV = RValue::get(SV); + LValue LV = MakeAddrLValue(addr, expr->getType()); + EmitStoreThroughLValue(RV, LV); + // Check if the object is constant, if not, save it in + // RetainableOperands. + if (!isa<Constant>(SV)) + RetainableOperands.push_back(SV); + } else { + EmitAnyExprToMem(expr, addr, Qualifiers(), /*isInit*/true); + } + } else { + addr = Builder.CreateElementBitCast(addr, Int32Ty); + Builder.CreateStore( + Builder.getInt32(item.getConstValue().getQuantity()), addr); + } + offset += item.getSize(); + } + + // Push a clang.arc.use cleanup for each object in RetainableOperands. The + // cleanup will cause the use to appear after the final log call, keeping + // the object valid while it’s held in the log buffer. Note that if there’s + // a release cleanup on the object, it will already be active; since + // cleanups are emitted in reverse order, the use will occur before the + // object is released. + if (!RetainableOperands.empty() && getLangOpts().ObjCAutoRefCount && + CGM.getCodeGenOpts().OptimizationLevel != 0) + for (llvm::Value *object : RetainableOperands) + pushFullExprCleanup<CallObjCArcUse>(getARCCleanupKind(), object); + + return RValue::get(BufAddr.getPointer()); + } + + case Builtin::BI__builtin_os_log_format_buffer_size: { + analyze_os_log::OSLogBufferLayout Layout; + analyze_os_log::computeOSLogBufferLayout(CGM.getContext(), E, Layout); + return RValue::get(ConstantInt::get(ConvertType(E->getType()), + Layout.getSize().getQuantity())); + } // OpenCL v2.0 s6.13.16.2, Built-in pipe read and write functions case Builtin::BIread_pipe: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 6bd910ed586a1..76929dd5c2b77 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -835,6 +835,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, if (SemaBuiltinPipePackets(*this, TheCall)) return ExprError(); break; + case Builtin::BI__builtin_os_log_format: + case Builtin::BI__builtin_os_log_format_buffer_size: + if (SemaBuiltinOSLogFormat(TheCall)) { + return ExprError(); + } + break; case Builtin::BIto_global: case Builtin::BIto_local: case Builtin::BIto_private: @@ -2904,6 +2910,31 @@ bool Sema::CheckObjCString(Expr *Arg) { return false; } +/// CheckObjCString - Checks that the format string argument to the os_log() +/// and os_trace() functions is correct, and converts it to const char *. +ExprResult Sema::CheckOSLogFormatStringArg(Expr *Arg) { + Arg = Arg->IgnoreParenCasts(); + StringLiteral *Literal = dyn_cast<StringLiteral>(Arg); + if (!Literal) { + if (auto *ObjcLiteral = dyn_cast<ObjCStringLiteral>(Arg)) { + Literal = ObjcLiteral->getString(); + } + } + + if (!Literal || (!Literal->isAscii() && !Literal->isUTF8())) { + return ExprError( + Diag(Arg->getLocStart(), diag::err_os_log_format_not_string_constant) + << Arg->getSourceRange()); + } + + ExprResult Result(Literal); + QualType ResultTy = Context.getPointerType(Context.CharTy.withConst()); + InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, + ResultTy, false); + Result = PerformCopyInitialization(Entity, SourceLocation(), Result); + return Result; +} + /// Check the arguments to '__builtin_va_start' or '__builtin_ms_va_start' /// for validity. Emit an error and return true on failure; return false /// on success. @@ -3356,6 +3387,86 @@ bool Sema::SemaBuiltinAssumeAligned(CallExpr *TheCall) { return false; } +bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) { + unsigned BuiltinID = + cast<FunctionDecl>(TheCall->getCalleeDecl())->getBuiltinID(); + bool IsSizeCall = BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size; + + unsigned NumArgs = TheCall->getNumArgs(); + unsigned NumRequiredArgs = IsSizeCall ? 1 : 2; + if (NumArgs < NumRequiredArgs) { + return Diag(TheCall->getLocEnd(), + diag::err_typecheck_call_too_few_args) + << 0 /* function call */ << NumRequiredArgs << NumArgs + << TheCall->getSourceRange(); + } + if (NumArgs >= NumRequiredArgs + 0x100) { + return Diag(TheCall->getLocEnd(), + diag::err_typecheck_call_too_many_args_at_most) + << 0 /* function call */ << (NumRequiredArgs + 0xff) + << NumArgs << TheCall->getSourceRange(); + } + unsigned i = 0; + + // For formatting call, check buffer arg. + if (!IsSizeCall) { + ExprResult Arg(TheCall->getArg(i)); + InitializedEntity Entity = + InitializedEntity::InitializeParameter(Context, Context.VoidPtrTy, false); + Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg); + if (Arg.isInvalid()) return true; + TheCall->setArg(i, Arg.get()); + i++; + } + + // Check string literal arg. + unsigned FormatIdx = i; + { + ExprResult Arg = CheckOSLogFormatStringArg(TheCall->getArg(i)); + if (Arg.isInvalid()) return true; + TheCall->setArg(i, Arg.get()); + i++; + } + + // Make sure variadic args are scalar. + unsigned FirstDataArg = i; + while (i < NumArgs) { + ExprResult Arg = DefaultVariadicArgumentPromotion(TheCall->getArg(i), + VariadicFunction, + nullptr); + if (Arg.isInvalid()) return true; + CharUnits ArgSize = Context.getTypeSizeInChars(Arg.get()->getType()); + if (ArgSize.getQuantity() >= 0x100) { + return Diag(Arg.get()->getLocEnd(), + diag::err_os_log_argument_too_big) + << i << (int)ArgSize.getQuantity() << 0xff + << TheCall->getSourceRange(); + } + TheCall->setArg(i, Arg.get()); + i++; + } + + // Check formatting specifiers. NOTE: We're only doing this for the non-size + // call to avoid duplicate diagnostics. + if (!IsSizeCall) { + llvm::SmallBitVector CheckedVarArgs(NumArgs, false); + ArrayRef<const Expr *> Args(TheCall->getArgs(), TheCall->getNumArgs()); + bool Success = CheckFormatArguments(Args, /*HasVAListArg*/false, FormatIdx, + FirstDataArg, FST_OSLog, + VariadicFunction, + TheCall->getLocStart(), SourceRange(), + CheckedVarArgs); + if (!Success) return true; + } + + if (IsSizeCall) { + TheCall->setType(Context.getSizeType()); + } else { + TheCall->setType(Context.VoidPtrTy); + } + return false; +} + /// SemaBuiltinConstantArg - Handle a check if argument ArgNum of CallExpr /// TheCall is a constant expression. bool Sema::SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum, @@ -3811,7 +3922,8 @@ Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) { .Case("strfmon", FST_Strfmon) .Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FST_Kprintf) .Case("freebsd_kprintf", FST_FreeBSDKPrintf) - .Case("os_trace", FST_OSTrace) + .Case("os_trace", FST_OSLog) + .Case("os_log", FST_OSLog) .Default(FST_Unknown); } @@ -3921,6 +4033,7 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { Sema &S; const StringLiteral *FExpr; const Expr *OrigFormatExpr; + const Sema::FormatStringType FSType; const unsigned FirstDataArg; const unsigned NumDataArgs; const char *Beg; // Start of format string. @@ -3937,14 +4050,14 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { public: CheckFormatHandler(Sema &s, const StringLiteral *fexpr, - const Expr *origFormatExpr, unsigned firstDataArg, - unsigned numDataArgs, const char *beg, bool hasVAListArg, - ArrayRef<const Expr *> Args, + const Expr *origFormatExpr, const Sema::FormatStringType + type, unsigned firstDataArg, unsigned numDataArgs, const + char *beg, bool hasVAListArg, ArrayRef<const Expr *> Args, unsigned formatIdx, bool inFunctionCall, Sema::VariadicCallType callType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg) - : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), + : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FSType(type), FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg), HasVAListArg(hasVAListArg), Args(Args), FormatIdx(formatIdx), @@ -4376,25 +4489,32 @@ void CheckFormatHandler::EmitFormatDiagnostic(Sema &S, bool InFunctionCall, namespace { class CheckPrintfHandler : public CheckFormatHandler { - bool ObjCContext; - public: CheckPrintfHandler(Sema &s, const StringLiteral *fexpr, - const Expr *origFormatExpr, unsigned firstDataArg, - unsigned numDataArgs, bool isObjC, + const Expr *origFormatExpr, const Sema::FormatStringType type, + unsigned firstDataArg, unsigned numDataArgs, const char *beg, bool hasVAListArg, ArrayRef<const Expr *> Args, unsigned formatIdx, bool inFunctionCall, Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg) - : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, + : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg, numDataArgs, beg, hasVAListArg, Args, formatIdx, inFunctionCall, CallType, CheckedVarArgs, - UncoveredArg), - ObjCContext(isObjC) + UncoveredArg) {} + bool isObjCContext() const { + return FSType == Sema::FST_NSString; + } + + /// Returns true if '%@' specifiers are allowed in the format string. + bool allowsObjCArg() const { + return FSType == Sema::FST_NSString || FSType == Sema::FST_OSLog || + FSType == Sema::FST_OSTrace; + } + bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, @@ -4747,11 +4867,45 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier // Check for using an Objective-C specific conversion specifier // in a non-ObjC literal. - if (!ObjCContext && CS.isObjCArg()) { + if (!allowsObjCArg() && CS.isObjCArg()) { + return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, + specifierLen); + } + + // %P can only be used with os_log. + if (FSType != Sema::FST_OSLog && + CS.getKind() == ConversionSpecifier::PArg) { return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, specifierLen); } + // Only scalars are allowed for os_trace. + if (FSType == Sema::FST_OSTrace && + (CS.getKind() == ConversionSpecifier::PArg || + CS.getKind() == ConversionSpecifier::sArg || + CS.getKind() == ConversionSpecifier::ObjCObjArg)) { + return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, + specifierLen); + } + + // Check for use of public/private annotation outside of os_log(). + if (FSType != Sema::FST_OSLog) { + if (FS.isPublic().isSet()) { + EmitFormatDiagnostic( + S.PDiag(diag::warn_format_invalid_annotation) << "public", + getLocationOfByte(FS.isPublic().getPosition()), + /*IsStringLocation*/false, + getSpecifierRange(startSpecifier, specifierLen)); + } + if (FS.isPrivate().isSet()) { + EmitFormatDiagnostic( + S.PDiag(diag::warn_format_invalid_annotation) << "private", + getLocationOfByte(FS.isPrivate().getPosition()), + /*IsStringLocation*/false, + getSpecifierRange(startSpecifier, specifierLen)); + } + } + // Check for invalid use of field width if (!FS.hasValidFieldWidth()) { HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 0, @@ -4764,6 +4918,15 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier startSpecifier, specifierLen); } + // Precision is mandatory for %P specifier. + if (CS.getKind() == ConversionSpecifier::PArg && + FS.getPrecision().getHowSpecified() == OptionalAmount::NotSpecified) { + EmitFormatDiagnostic( + S.PDiag(diag::warn_format_P_no_precision), + getLocationOfByte(startSpecifier), /*IsStringLocation*/false, + getSpecifierRange(startSpecifier, specifierLen)); + } + // Check each flag does not conflict with any other component. if (!FS.hasValidThousandsGroupingPrefix()) HandleFlag(FS, FS.hasThousandsGrouping(), startSpecifier, specifierLen); @@ -4914,7 +5077,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, // Now type check the data expression that matches the // format specifier. const analyze_printf::ArgType &AT = FS.getArgType(S.Context, - ObjCContext); + isObjCContext()); if (!AT.isValid()) return true; @@ -4969,7 +5132,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, // If the argument is an integer of some kind, believe the %C and suggest // a cast instead of changing the conversion specifier. QualType IntendedTy = ExprTy; - if (ObjCContext && + if (isObjCContext() && FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) { if (ExprTy->isIntegralOrUnscopedEnumerationType() && !ExprTy->isCharType()) { @@ -5011,7 +5174,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, // We may be able to offer a FixItHint if it is a supported type. PrintfSpecifier fixedFS = FS; bool success = fixedFS.fixType(IntendedTy, S.getLangOpts(), - S.Context, ObjCContext); + S.Context, isObjCContext()); if (success) { // Get the fix string from the fixed format specifier @@ -5167,14 +5330,14 @@ namespace { class CheckScanfHandler : public CheckFormatHandler { public: CheckScanfHandler(Sema &s, const StringLiteral *fexpr, - const Expr *origFormatExpr, unsigned firstDataArg, - unsigned numDataArgs, const char *beg, bool hasVAListArg, - ArrayRef<const Expr *> Args, + const Expr *origFormatExpr, Sema::FormatStringType type, + unsigned firstDataArg, unsigned numDataArgs, const char + *beg, bool hasVAListArg, ArrayRef<const Expr *> Args, unsigned formatIdx, bool inFunctionCall, Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg) - : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, + : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg, numDataArgs, beg, hasVAListArg, Args, formatIdx, inFunctionCall, CallType, CheckedVarArgs, UncoveredArg) @@ -5389,11 +5552,10 @@ static void CheckFormatString(Sema &S, const StringLiteral *FExpr, } if (Type == Sema::FST_Printf || Type == Sema::FST_NSString || - Type == Sema::FST_FreeBSDKPrintf || Type == Sema::FST_OSTrace) { - CheckPrintfHandler H(S, FExpr, OrigFormatExpr, firstDataArg, - numDataArgs, (Type == Sema::FST_NSString || - Type == Sema::FST_OSTrace), - Str, HasVAListArg, Args, format_idx, + Type == Sema::FST_FreeBSDKPrintf || Type == Sema::FST_OSLog || + Type == Sema::FST_OSTrace) { + CheckPrintfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg, + numDataArgs, Str, HasVAListArg, Args, format_idx, inFunctionCall, CallType, CheckedVarArgs, UncoveredArg); @@ -5403,7 +5565,7 @@ static void CheckFormatString(Sema &S, const StringLiteral *FExpr, Type == Sema::FST_FreeBSDKPrintf)) H.DoneProcessing(); } else if (Type == Sema::FST_Scanf) { - CheckScanfHandler H(S, FExpr, OrigFormatExpr, firstDataArg, numDataArgs, + CheckScanfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg, numDataArgs, Str, HasVAListArg, Args, format_idx, inFunctionCall, CallType, CheckedVarArgs, UncoveredArg); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index afe36f69b63d4..9ce607cd7c168 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2842,6 +2842,7 @@ static FormatAttrKind getFormatAttrKind(StringRef Format) { .Case("kprintf", SupportedFormat) // OpenBSD. .Case("freebsd_kprintf", SupportedFormat) // FreeBSD. .Case("os_trace", SupportedFormat) + .Case("os_log", SupportedFormat) .Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat) .Default(InvalidFormat); diff --git a/clang/test/CodeGen/builtins.c b/clang/test/CodeGen/builtins.c index 43ce8f13ade60..a0681516a1a6e 100644 --- a/clang/test/CodeGen/builtins.c +++ b/clang/test/CodeGen/builtins.c @@ -275,3 +275,62 @@ long long test_builtin_readcyclecounter() { // CHECK: call i64 @llvm.readcyclecounter() return __builtin_readcyclecounter(); } + +// Behavior of __builtin_os_log differs between platforms, so only test on X86 +#ifdef __x86_64__ +// CHECK-LABEL: define void @test_builtin_os_log +// CHECK: (i8* [[BUF:%.*]], i32 [[I:%.*]], i8* [[DATA:%.*]]) +void test_builtin_os_log(void *buf, int i, const char *data) { + volatile int len; + // CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8 + // CHECK: store i32 [[I]], i32* [[I_ADDR:%.*]], align 4 + // CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8 + + // CHECK: store volatile i32 34 + len = __builtin_os_log_format_buffer_size("%d %{public}s %{private}.16P", i, data, data); + + // CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]] + // CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0 + // CHECK: store i8 3, i8* [[SUMMARY]] + // CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1 + // CHECK: store i8 4, i8* [[NUM_ARGS]] + // + // CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2 + // CHECK: store i8 0, i8* [[ARG1_DESC]] + // CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3 + // CHECK: store i8 4, i8* [[ARG1_SIZE]] + // CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4 + // CHECK: [[ARG1_INT:%.*]] = bitcast i8* [[ARG1]] to i32* + // CHECK: [[I2:%.*]] = load i32, i32* [[I_ADDR]] + // CHECK: store i32 [[I2]], i32* [[ARG1_INT]] + + // CHECK: [[ARG2_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 8 + // CHECK: store i8 34, i8* [[ARG2_DESC]] + // CHECK: [[ARG2_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 9 + // CHECK: store i8 8, i8* [[ARG2_SIZE]] + // CHECK: [[ARG2:%.*]] = getelementptr i8, i8* [[BUF2]], i64 10 + // CHECK: [[ARG2_PTR:%.*]] = bitcast i8* [[ARG2]] to i8** + // CHECK: [[DATA2:%.*]] = load i8*, i8** [[DATA_ADDR]] + // CHECK: store i8* [[DATA2]], i8** [[ARG2_PTR]] + + // CHECK: [[ARG3_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 18 + // CHECK: store i8 17, i8* [[ARG3_DESC]] + // CHECK: [[ARG3_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 19 + // CHECK: store i8 4, i8* [[ARG3_SIZE]] + // CHECK: [[ARG3:%.*]] = getelementptr i8, i8* [[BUF2]], i64 20 + // CHECK: [[ARG3_INT:%.*]] = bitcast i8* [[ARG3]] to i32* + // CHECK: store i32 16, i32* [[ARG3_INT]] + + // CHECK: [[ARG4_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 24 + // CHECK: store i8 49, i8* [[ARG4_DESC]] + // CHECK: [[ARG4_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 25 + // CHECK: store i8 8, i8* [[ARG4_SIZE]] + // CHECK: [[ARG4:%.*]] = getelementptr i8, i8* [[BUF2]], i64 26 + // CHECK: [[ARG4_PTR:%.*]] = bitcast i8* [[ARG4]] to i8** + // CHECK: [[DATA3:%.*]] = load i8*, i8** [[DATA_ADDR]] + // CHECK: store i8* [[DATA3]], i8** [[ARG4_PTR]] + + __builtin_os_log_format(buf, "%d %{public}s %{private}.16P", i, data, data); +} + +#endif diff --git a/clang/test/CodeGenObjC/os_log.m b/clang/test/CodeGenObjC/os_log.m new file mode 100644 index 0000000000000..144d1cc6ba1f3 --- /dev/null +++ b/clang/test/CodeGenObjC/os_log.m @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -fobjc-arc -O2 | FileCheck %s + +// Make sure we emit clang.arc.use before calling objc_release as part of the +// cleanup. This way we make sure the object will not be released until the +// end of the full expression. + +// rdar://problem/24528966 + +@class NSString; +extern __attribute__((visibility("default"))) NSString * GenString(); + +// Behavior of __builtin_os_log differs between platforms, so only test on X86 +#ifdef __x86_64__ +// CHECK-LABEL: define i8* @test_builtin_os_log +void *test_builtin_os_log(void *buf) { + return __builtin_os_log_format(buf, "capabilities: %@", GenString()); + + // CHECK: store i8 2, i8* + // CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* {{.*}}, i64 1 + // CHECK: store i8 1, i8* [[NUM_ARGS]] + // + // CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* {{.*}}, i64 2 + // CHECK: store i8 64, i8* [[ARG1_DESC]] + // CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* {{.*}}, i64 3 + // CHECK: store i8 8, i8* [[ARG1_SIZE]] + // CHECK: [[ARG1:%.*]] = getelementptr i8, i8* {{.*}}, i64 4 + // CHECK: [[ARG1_CAST:%.*]] = bitcast i8* [[ARG1]] to + + // CHECK: [[STRING:%.*]] = {{.*}} call {{.*}} @GenString() + // CHECK: [[STRING_CAST:%.*]] = bitcast {{.*}} [[STRING]] to + // CHECK: call {{.*}} @objc_retainAutoreleasedReturnValue(i8* [[STRING_CAST]]) + // CHECK: store {{.*}} [[STRING]], {{.*}} [[ARG1_CAST]] + + // CHECK: call void (...) @clang.arc.use({{.*}} [[STRING]]) + // CHECK: call void @objc_release(i8* [[STRING_CAST]]) + // CHECK: ret i8* +} + +#endif diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c index 5559710c60355..28a2db836f554 100644 --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -652,3 +652,30 @@ void test_format_security_pos(char* string) { // expected-note@-1{{treat the string as an argument to avoid this}} } #pragma GCC diagnostic warning "-Wformat-nonliteral" + +void test_os_log_format(char c, const char *pc, int i, int *pi, void *p, void *buf) { + __builtin_os_log_format(buf, ""); + __builtin_os_log_format(buf, "%d"); // expected-warning {{more '%' conversions than data arguments}} + __builtin_os_log_format(buf, "%d", i); + __builtin_os_log_format(buf, "%P", p); // expected-warning {{using '%P' format specifier without precision}} + __builtin_os_log_format(buf, "%.10P", p); + __builtin_os_log_format(buf, "%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}} + __builtin_os_log_format(buf, "%.*P", i, p); + __builtin_os_log_format(buf, "%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}} + __builtin_os_log_format(buf, pc); // expected-error {{os_log() format argument is not a string constant}} + + printf("%{private}s", pc); // expected-warning {{using 'private' format specifier annotation outside of os_log()/os_trace()}} + __builtin_os_log_format(buf, "%{private}s", pc); + + // <rdar://problem/23835805> + __builtin_os_log_format_buffer_size("no-args"); + __builtin_os_log_format(buf, "%s", "hi"); + + // <rdar://problem/24828090> + wchar_t wc = 'a'; + __builtin_os_log_format(buf, "%C", wc); + printf("%C", wc); + wchar_t wcs[] = {'a', 0}; + __builtin_os_log_format(buf, "%S", wcs); + printf("%S", wcs); +} diff --git a/clang/test/SemaObjC/format-strings-objc.m b/clang/test/SemaObjC/format-strings-objc.m index d81f166a6540b..26dae2e3dbc87 100644 --- a/clang/test/SemaObjC/format-strings-objc.m +++ b/clang/test/SemaObjC/format-strings-objc.m @@ -264,3 +264,18 @@ void testObjCModifierFlags() { NSLog(@"%2$[tt]@ %1$[tt]@", @"Foo", @"Bar"); // no-warning NSLog(@"%2$[tt]@ %1$[tt]s", @"Foo", @"Bar"); // expected-warning {{object format flags cannot be used with 's' conversion specifier}} } + +// Test os_log_format primitive with ObjC string literal format argument. +void test_os_log_format(char c, const char *pc, int i, int *pi, void *p, void *buf, NSString *nss) { + __builtin_os_log_format(buf, @""); + __builtin_os_log_format(buf, @"%d"); // expected-warning {{more '%' conversions than data arguments}} + __builtin_os_log_format(buf, @"%d", i); + __builtin_os_log_format(buf, @"%P", p); // expected-warning {{using '%P' format specifier without precision}} + __builtin_os_log_format(buf, @"%.10P", p); + __builtin_os_log_format(buf, @"%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}} + __builtin_os_log_format(buf, @"%.*P", i, p); + __builtin_os_log_format(buf, @"%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}} + + __builtin_os_log_format(buf, @"%{private}s", pc); + __builtin_os_log_format(buf, @"%@", nss); +} From 33e7e500fa3987b3b0fc0e73161659da9d7bed95 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Mon, 13 Jun 2016 11:42:00 -0700 Subject: [PATCH 072/582] Clean up some whitespace and comments for the new logging builtins patch. apple-llvm-split-commit: 53636e55132f5e81d26c7f3c4d08ba7ccec052aa apple-llvm-split-dir: clang/ --- clang/include/clang/Analysis/Analyses/OSLog.h | 5 +++-- clang/lib/Analysis/OSLog.cpp | 18 ++++++++++++++-- clang/lib/Sema/SemaChecking.cpp | 21 +++++++++++-------- clang/test/CodeGen/builtins.c | 2 +- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/OSLog.h b/clang/include/clang/Analysis/Analyses/OSLog.h index 9fde26aaea4dd..084b07920e715 100644 --- a/clang/include/clang/Analysis/Analyses/OSLog.h +++ b/clang/include/clang/Analysis/Analyses/OSLog.h @@ -105,7 +105,7 @@ class OSLogBufferLayout { } return result; } - + bool getHasPrivateItems() const { return std::any_of(Items.begin(), Items.end(), [](const OSLogBufferItem &item) { return item.getIsPrivate(); }); @@ -139,7 +139,8 @@ class OSLogBufferLayout { // __builtin_os_log_format_buffer_size(), compute the layout of the buffer that // the call will write into and store it in 'layout'. Returns 'false' if there // was some error encountered while computing the layout, and 'true' otherwise. -bool computeOSLogBufferLayout(clang::ASTContext &Ctx, const clang::CallExpr *E, OSLogBufferLayout &layout); +bool computeOSLogBufferLayout(clang::ASTContext &Ctx, const clang::CallExpr *E, + OSLogBufferLayout &layout); } // namespace analyze_os_log } // namespace clang diff --git a/clang/lib/Analysis/OSLog.cpp b/clang/lib/Analysis/OSLog.cpp index 804cee2c88929..b6e9c49ef548e 100644 --- a/clang/lib/Analysis/OSLog.cpp +++ b/clang/lib/Analysis/OSLog.cpp @@ -1,4 +1,16 @@ -// TODO: header template +//===--- OSLog.cpp - Analysis of calls to os_log builtins -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs for determining the layout of the data buffer for +// os_log() and os_trace(). +// +//===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/OSLog.h" #include "clang/Analysis/Analyses/FormatString.h" @@ -118,7 +130,9 @@ class OSLogFormatStringHandler } }; -bool clang::analyze_os_log::computeOSLogBufferLayout(ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &layout) +bool clang::analyze_os_log::computeOSLogBufferLayout(ASTContext &Ctx, + const CallExpr *E, + OSLogBufferLayout &layout) { ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs()); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 76929dd5c2b77..820f6e2f3438f 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3391,7 +3391,7 @@ bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) { unsigned BuiltinID = cast<FunctionDecl>(TheCall->getCalleeDecl())->getBuiltinID(); bool IsSizeCall = BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size; - + unsigned NumArgs = TheCall->getNumArgs(); unsigned NumRequiredArgs = IsSizeCall ? 1 : 2; if (NumArgs < NumRequiredArgs) { @@ -3445,7 +3445,7 @@ bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) { TheCall->setArg(i, Arg.get()); i++; } - + // Check formatting specifiers. NOTE: We're only doing this for the non-size // call to avoid duplicate diagnostics. if (!IsSizeCall) { @@ -4050,9 +4050,10 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { public: CheckFormatHandler(Sema &s, const StringLiteral *fexpr, - const Expr *origFormatExpr, const Sema::FormatStringType - type, unsigned firstDataArg, unsigned numDataArgs, const - char *beg, bool hasVAListArg, ArrayRef<const Expr *> Args, + const Expr *origFormatExpr, + const Sema::FormatStringType type, unsigned firstDataArg, + unsigned numDataArgs, const char *beg, bool hasVAListArg, + ArrayRef<const Expr *> Args, unsigned formatIdx, bool inFunctionCall, Sema::VariadicCallType callType, llvm::SmallBitVector &CheckedVarArgs, @@ -4491,8 +4492,9 @@ namespace { class CheckPrintfHandler : public CheckFormatHandler { public: CheckPrintfHandler(Sema &s, const StringLiteral *fexpr, - const Expr *origFormatExpr, const Sema::FormatStringType type, - unsigned firstDataArg, unsigned numDataArgs, + const Expr *origFormatExpr, + const Sema::FormatStringType type, unsigned firstDataArg, + unsigned numDataArgs, const char *beg, bool hasVAListArg, ArrayRef<const Expr *> Args, unsigned formatIdx, bool inFunctionCall, @@ -5331,8 +5333,9 @@ class CheckScanfHandler : public CheckFormatHandler { public: CheckScanfHandler(Sema &s, const StringLiteral *fexpr, const Expr *origFormatExpr, Sema::FormatStringType type, - unsigned firstDataArg, unsigned numDataArgs, const char - *beg, bool hasVAListArg, ArrayRef<const Expr *> Args, + unsigned firstDataArg, + unsigned numDataArgs, const char *beg, bool hasVAListArg, + ArrayRef<const Expr *> Args, unsigned formatIdx, bool inFunctionCall, Sema::VariadicCallType CallType, llvm::SmallBitVector &CheckedVarArgs, diff --git a/clang/test/CodeGen/builtins.c b/clang/test/CodeGen/builtins.c index a0681516a1a6e..fa857598945f2 100644 --- a/clang/test/CodeGen/builtins.c +++ b/clang/test/CodeGen/builtins.c @@ -308,7 +308,7 @@ void test_builtin_os_log(void *buf, int i, const char *data) { // CHECK: store i8 34, i8* [[ARG2_DESC]] // CHECK: [[ARG2_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 9 // CHECK: store i8 8, i8* [[ARG2_SIZE]] - // CHECK: [[ARG2:%.*]] = getelementptr i8, i8* [[BUF2]], i64 10 + // CHECK: [[ARG2:%.*]] = getelementptr i8, i8* [[BUF2]], i64 10 // CHECK: [[ARG2_PTR:%.*]] = bitcast i8* [[ARG2]] to i8** // CHECK: [[DATA2:%.*]] = load i8*, i8** [[DATA_ADDR]] // CHECK: store i8* [[DATA2]], i8** [[ARG2_PTR]] From 5d5108b9e0860dd34a87eccf373ab1170815fe98 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Mon, 13 Jun 2016 23:12:58 -0700 Subject: [PATCH 073/582] Fix a build failure after merge from llvm.org. apple-llvm-split-commit: 06b8211db57b33659efce0690174724e4b7e9a79 apple-llvm-split-dir: llvm/ --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 0d1bf88524780..b7d1c4993da77 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2316,7 +2316,7 @@ std::error_code BitcodeReader::parseMetadata(bool ModuleLevel) { return error("Invalid record"); MetadataList.assignValue( - GET_OR_DISTINCT(DIModule, Record[0], + GET_OR_DISTINCT(DIModule, (Context, getMDOrNull(Record[4]), getMDString(Record[5]), nullptr, nullptr, nullptr)), From 0be9146278b34130efef2a3e77b852e536e54ac4 Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Sun, 19 Jun 2016 20:43:20 -0700 Subject: [PATCH 074/582] [lit] Refactor lit detection of macOS triples to use a centralized query in lit.util.isMacOSTriple. Currently, lit assumes that a triple is a macOS triple if it contains the string 'darwin' (pattern matching against $ARCH-apple-darwin$NUM). This is incorrect since the $ARCH-apple-macosx$NUM triples are /also/ valid triples for macOS. More importantly, turns out Swift is using this triple as well, preventing a build-script built llvm from passing all of its unit tests on a bot. Given where we are in the schedule I don't want to touch the triples on either projects, so instead I am adding this workaround, teaching lit that a triple with macosx in it should have a 'darwin' supported_feature. This commit centralizes all the places in LLVM to use the query, lit.util.isMacOSTriple, instead of checking if 'darwin' is in a triple directly. In subsequent commits I will: 1. Change clang/compiler-rt's lit to use this utility function as well. 2. Update isMacOSTriple to recognize other valid triples for macOS such as triples that replace darwin with macosx (i.e. x86_64-apple-darwin vs x86_64-apple-macosx). rdar://26780128 apple-llvm-split-commit: 01436117652b41eb389ddbad9c9fd8df0de9b4b2 apple-llvm-split-dir: llvm/ --- llvm/test/lit.cfg | 2 +- llvm/utils/lit/lit/util.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/llvm/test/lit.cfg b/llvm/test/lit.cfg index 67e851b6e81d3..b0126857b3d5e 100644 --- a/llvm/test/lit.cfg +++ b/llvm/test/lit.cfg @@ -445,7 +445,7 @@ if re.search(r'ON', llvm_config_cmd.stdout.read().decode('ascii')): config.available_features.add('asserts') llvm_config_cmd.wait() -if 'darwin' == sys.platform: +if lit.util.isMacOSTriple(config.target_triple): try: sysctl_cmd = subprocess.Popen(['sysctl', 'hw.optional.fma'], stdout = subprocess.PIPE) diff --git a/llvm/utils/lit/lit/util.py b/llvm/utils/lit/lit/util.py index 40a5771686924..f70d7968b6595 100644 --- a/llvm/utils/lit/lit/util.py +++ b/llvm/utils/lit/lit/util.py @@ -237,10 +237,26 @@ def killProcess(): return out, err, exitCode +# A predicate to determine whether or not a specific config's target_triple is +# referring to macOS. The reason that this is useful is that macOS has multiple +# valid triples. This just centralizes the query into a convenient place. +def isMacOSTriple(target): + arches = [ + 'x86_64', + 'i386', + 'x86_64h' + ] + for a in arches: + triple = '%s-apple-darwin' % a + if triple not in target: + continue + return True + return False + def usePlatformSdkOnDarwin(config, lit_config): # On Darwin, support relocatable SDKs by providing Clang with a # default system root path. - if 'darwin' in config.target_triple: + if isMacOSTriple(config.target_triple): try: cmd = subprocess.Popen(['xcrun', '--show-sdk-path'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) From 61d2664ad4a7d877f1e6be345c11c761030eae0a Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Sun, 19 Jun 2016 23:51:21 -0700 Subject: [PATCH 075/582] {x86_64,i386,x86_64h}-apple-macosx should also be XFAILED when 'darwin' is XFAILED. See 01436117652b41eb389ddbad9c9fd8df0de9b4b2 for more info. rdar://26780128 apple-llvm-split-commit: 9ecb0ea4b899f55f0d4a63fb3ca390203bec0204 apple-llvm-split-dir: llvm/ --- llvm/test/lit.cfg | 3 +++ llvm/utils/lit/lit/util.py | 15 +++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/llvm/test/lit.cfg b/llvm/test/lit.cfg index b0126857b3d5e..6ced60f5ed02b 100644 --- a/llvm/test/lit.cfg +++ b/llvm/test/lit.cfg @@ -378,6 +378,9 @@ if config.target_triple: if config.host_triple == config.target_triple: config.available_features.add("native") +if lit.util.isMacOSTriple(config.target_triple): + config.available_features.add('darwin') + import subprocess def have_ld_plugin_support(): diff --git a/llvm/utils/lit/lit/util.py b/llvm/utils/lit/lit/util.py index f70d7968b6595..ba44b56ff328d 100644 --- a/llvm/utils/lit/lit/util.py +++ b/llvm/utils/lit/lit/util.py @@ -246,11 +246,18 @@ def isMacOSTriple(target): 'i386', 'x86_64h' ] + + names = [ + 'darwin', + 'macosx' + ] + for a in arches: - triple = '%s-apple-darwin' % a - if triple not in target: - continue - return True + for n in names: + triple = '%s-apple-%s' % (a,n) + if triple not in target: + continue + return True return False def usePlatformSdkOnDarwin(config, lit_config): From 667b58378d86ada506e9b06ef18a529db6c0f366 Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Sun, 19 Jun 2016 21:01:50 -0700 Subject: [PATCH 076/582] Use the new lit.util.isMacOSTriple function to determine if a target triple is a macOS target triple. See 01436117652b41eb389ddbad9c9fd8df0de9b4b2 in llvm. rdar://26780128 apple-llvm-split-commit: 466f3ab46e3bd128c8cf770c1a7ba729716b99af apple-llvm-split-dir: clang/ --- clang/utils/perf-training/lit.cfg | 2 +- clang/utils/perf-training/order-files.lit.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/utils/perf-training/lit.cfg b/clang/utils/perf-training/lit.cfg index 85d3551434102..eed87ba413c14 100644 --- a/clang/utils/perf-training/lit.cfg +++ b/clang/utils/perf-training/lit.cfg @@ -7,7 +7,7 @@ import lit.util def getSysrootFlagsOnDarwin(config, lit_config): # On Darwin, support relocatable SDKs by providing Clang with a # default system root path. - if 'darwin' in config.target_triple: + if lit.util.isMacOSTriple(config.target_triple): try: out = lit.util.capture(['xcrun', '--show-sdk-path']).strip() res = 0 diff --git a/clang/utils/perf-training/order-files.lit.cfg b/clang/utils/perf-training/order-files.lit.cfg index 75501f8c62979..2e8f7ed2da026 100644 --- a/clang/utils/perf-training/order-files.lit.cfg +++ b/clang/utils/perf-training/order-files.lit.cfg @@ -8,7 +8,7 @@ import os def getSysrootFlagsOnDarwin(config, lit_config): # On Darwin, support relocatable SDKs by providing Clang with a # default system root path. - if 'darwin' in config.target_triple: + if lit.util.isMacOSTriple(config.target_triple): try: out = lit.util.capture(['xcrun', '--show-sdk-path']).strip() res = 0 From bc3e41eb93ace9feb3a50d7565ac3c1ff15f8b02 Mon Sep 17 00:00:00 2001 From: Michael Gottesman <mgottesman@apple.com> Date: Mon, 20 Jun 2016 00:42:49 -0700 Subject: [PATCH 077/582] All triples that are valid macOS triples should have a darwin feature. See commit 01436117652b41eb389ddbad9c9fd8df0de9b4b2 in LLVM. rdar://26780128 apple-llvm-split-commit: b19c89de6c6784a3913c0ec48799bc2673cf7df5 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/test/lit.common.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler-rt/test/lit.common.cfg b/compiler-rt/test/lit.common.cfg index c1a70ffbac719..53aa6eaed5f16 100644 --- a/compiler-rt/test/lit.common.cfg +++ b/compiler-rt/test/lit.common.cfg @@ -75,6 +75,9 @@ if platform.system() == 'Windows' and '-win' in config.target_triple: if re.match(r'^x86_64.*-linux', config.target_triple): config.available_features.add("x86_64-linux") +if lit.util.isMacOSTriple(config.target_triple): + config.available_features.add('darwin') + # Use ugly construction to explicitly prohibit "clang", "clang++" etc. # in RUN lines. config.substitutions.append( From d6ef652c64f4ee16467b16b66dad540730364816 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Wed, 22 Jun 2016 13:10:41 -0700 Subject: [PATCH 078/582] Allow iOS and tvOS version numbers with 2-digit major version numbers. rdar://problem/26921601 apple-llvm-split-commit: 0e2c76507a690e4b4c14e4b56587799d996d5690 apple-llvm-split-dir: clang/ --- clang/lib/Basic/Targets.cpp | 27 +++++++++++++++++++-------- clang/lib/Driver/ToolChains.cpp | 4 ++-- clang/test/Frontend/darwin-version.c | 4 ++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 626c5af8fdc35..7c0628252631e 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -157,14 +157,25 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, // Set the appropriate OS version define. if (Triple.isiOS()) { - assert(Maj < 10 && Min < 100 && Rev < 100 && "Invalid version!"); - char Str[6]; - Str[0] = '0' + Maj; - Str[1] = '0' + (Min / 10); - Str[2] = '0' + (Min % 10); - Str[3] = '0' + (Rev / 10); - Str[4] = '0' + (Rev % 10); - Str[5] = '\0'; + assert(Maj < 100 && Min < 100 && Rev < 100 && "Invalid version!"); + char Str[7]; + if (Maj < 10) { + Str[0] = '0' + Maj; + Str[1] = '0' + (Min / 10); + Str[2] = '0' + (Min % 10); + Str[3] = '0' + (Rev / 10); + Str[4] = '0' + (Rev % 10); + Str[5] = '\0'; + } else { + // Handle versions >= 10. + Str[0] = '0' + (Maj / 10); + Str[1] = '0' + (Maj % 10); + Str[2] = '0' + (Min / 10); + Str[3] = '0' + (Min % 10); + Str[4] = '0' + (Rev / 10); + Str[5] = '0' + (Rev % 10); + Str[6] = '\0'; + } if (Triple.isTvOS()) Builder.defineMacro("__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__", Str); else diff --git a/clang/lib/Driver/ToolChains.cpp b/clang/lib/Driver/ToolChains.cpp index dec791c9358b3..f510a4b4c190d 100644 --- a/clang/lib/Driver/ToolChains.cpp +++ b/clang/lib/Driver/ToolChains.cpp @@ -694,13 +694,13 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { assert(iOSVersion && "Unknown target platform!"); if (!Driver::GetReleaseVersion(iOSVersion->getValue(), Major, Minor, Micro, HadExtra) || - HadExtra || Major >= 10 || Minor >= 100 || Micro >= 100) + HadExtra || Major >= 100 || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << iOSVersion->getAsString(Args); } else if (Platform == TvOS) { if (!Driver::GetReleaseVersion(TvOSVersion->getValue(), Major, Minor, Micro, HadExtra) || HadExtra || - Major >= 10 || Minor >= 100 || Micro >= 100) + Major >= 100 || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << TvOSVersion->getAsString(Args); } else if (Platform == WatchOS) { diff --git a/clang/test/Frontend/darwin-version.c b/clang/test/Frontend/darwin-version.c index e7bc41117e3fc..eb05a48cfd36e 100644 --- a/clang/test/Frontend/darwin-version.c +++ b/clang/test/Frontend/darwin-version.c @@ -10,6 +10,8 @@ // RUN: %clang_cc1 -triple armv6-apple-ios2.3.1 -dM -E -o %t %s // RUN: grep '__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__' %t | grep '20301' | count 1 // RUN: not grep '__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__' %t +// RUN: %clang_cc1 -triple armv7-apple-ios10.1.2 -dM -E -o %t %s +// RUN: grep '__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__' %t | grep '100102' | count 1 // RUN: %clang_cc1 -triple i386-apple-macosx10.4.0 -dM -E -o %t %s // RUN: grep '__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__' %t | grep '1040' | count 1 // RUN: not grep '__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__' %t @@ -32,6 +34,8 @@ // RUN: grep '__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__' %t | grep '80300' | count 1 // RUN: not grep '__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__' %t // RUN: not grep '__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__' %t +// RUN: %clang_cc1 -triple arm64-apple-tvos10.2.3 -dM -E -o %t %s +// RUN: grep '__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__' %t | grep '100203' | count 1 // RUN: %clang_cc1 -triple x86_64-apple-tvos8.3 -dM -E -o %t %s // RUN: grep '__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__' %t | grep '80300' | count 1 From 9c7c55c48d3b9e99ad67a1e457669d2f1aa02258 Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Wed, 29 Jun 2016 14:27:30 -0700 Subject: [PATCH 079/582] Fix a bad merge where we include an extra '}'. apple-llvm-split-commit: 6d7b57a6eba55a0def8a84adccb3dd1b03e2218d apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticFrontendKinds.td | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index d684caa35e096..757c329757463 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -218,7 +218,6 @@ def err_invalid_vfs_overlay : Error< def err_no_apinotes_cache_path : Error< "-fapinotes was provided without -fapinotes-cache-path=<directory>">, DefaultFatal; -} def warn_option_invalid_ocl_version : Warning< "OpenCL version %0 does not support the option '%1'">, InGroup<Deprecated>; From 9798ae2281290dcf0df862b77549388380aebd12 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 5 Jul 2016 23:25:45 -0700 Subject: [PATCH 080/582] [API Notes] Add support for the ns_error_domain attribute. Addresses rdar://problem/27185793. apple-llvm-split-commit: 7c3fa473f90459af9342386e1c423b6e01cf82a3 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 11 ++++++++++- clang/lib/APINotes/APINotesReader.cpp | 6 ++++++ clang/lib/APINotes/APINotesWriter.cpp | 6 +++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 8 ++++++++ clang/lib/Sema/SemaAPINotes.cpp | 9 +++++++++ clang/test/APINotes/Inputs/roundtrip.apinotes | 11 +++++++++++ 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 11a1e7acf54d6..5d5a347336385 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -129,24 +129,33 @@ class CommonTypeInfo : public CommonEntityInfo { /// Reflects the swift_bridge attribute. std::string SwiftBridge; + /// The NS error domain for this type. + std::string NSErrorDomain; + public: CommonTypeInfo() : CommonEntityInfo() { } const std::string &getSwiftBridge() const { return SwiftBridge; } void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + const std::string &getNSErrorDomain() const { return NSErrorDomain; } + void setNSErrorDomain(const std::string &domain) { NSErrorDomain = domain; } + friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs, const CommonTypeInfo &rhs) { static_cast<CommonEntityInfo &>(lhs) |= rhs; if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) lhs.SwiftBridge = rhs.SwiftBridge; + if (lhs.NSErrorDomain.empty() && !rhs.NSErrorDomain.empty()) + lhs.NSErrorDomain = rhs.NSErrorDomain; return lhs; } friend bool operator==(const CommonTypeInfo &lhs, const CommonTypeInfo &rhs) { return static_cast<const CommonEntityInfo &>(lhs) == rhs && - lhs.SwiftBridge == rhs.SwiftBridge; + lhs.SwiftBridge == rhs.SwiftBridge && + lhs.NSErrorDomain == rhs.NSErrorDomain; } friend bool operator!=(const CommonTypeInfo &lhs, diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 6de82ebe64be2..81cfed71cf3ae 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -58,6 +58,12 @@ namespace { info.setSwiftBridge( StringRef(reinterpret_cast<const char *>(data), swiftBridgeLength)); data += swiftBridgeLength; + + unsigned errorDomainLength = + endian::readNext<uint16_t, little, unaligned>(data); + info.setNSErrorDomain( + StringRef(reinterpret_cast<const char *>(data), errorDomainLength)); + data += errorDomainLength; } /// Used to deserialize the on-disk identifier table. diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 3d4938e7cd002..e2392c6f52fb8 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -308,7 +308,9 @@ namespace { // Retrieve the serialized size of the given CommonTypeInfo, for use // in on-disk hash tables. static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) { - return 2 + info.getSwiftBridge().size() + getCommonEntityInfoSize(info); + return 2 + info.getSwiftBridge().size() + + 2 + info.getNSErrorDomain().size() + + getCommonEntityInfoSize(info); } /// Emit a serialized representation of the common type information. @@ -317,6 +319,8 @@ namespace { endian::Writer<little> writer(out); writer.write<uint16_t>(info.getSwiftBridge().size()); out.write(info.getSwiftBridge().c_str(), info.getSwiftBridge().size()); + writer.write<uint16_t>(info.getNSErrorDomain().size()); + out.write(info.getNSErrorDomain().c_str(), info.getNSErrorDomain().size()); } /// Used to serialize the on-disk Objective-C context table. diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index a5e4fa82ceb17..10648e29313a1 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -199,6 +199,7 @@ namespace { bool SwiftPrivate = false; StringRef SwiftName; StringRef SwiftBridge; + StringRef NSErrorDomain; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -237,6 +238,7 @@ namespace { StringRef SwiftName; bool SwiftPrivate = false; StringRef SwiftBridge; + StringRef NSErrorDomain; }; typedef std::vector<Tag> TagsSeq; @@ -246,6 +248,7 @@ namespace { StringRef SwiftName; bool SwiftPrivate = false; StringRef SwiftBridge; + StringRef NSErrorDomain; }; typedef std::vector<Typedef> TypedefsSeq; @@ -363,6 +366,7 @@ namespace llvm { io.mapOptional("SwiftPrivate", c.SwiftPrivate); io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("SwiftBridge", c.SwiftBridge); + io.mapOptional("NSErrorDomain", c.NSErrorDomain); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -415,6 +419,7 @@ namespace llvm { io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); + io.mapOptional("NSErrorDomain", t.NSErrorDomain); } }; @@ -427,6 +432,7 @@ namespace llvm { io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); + io.mapOptional("NSErrorDomain", t.NSErrorDomain); } }; @@ -572,6 +578,7 @@ namespace { return true; info.setSwiftBridge(common.SwiftBridge); + info.setNSErrorDomain(common.NSErrorDomain); return false; } @@ -890,6 +897,7 @@ namespace { void handleCommonType(T &record, const CommonTypeInfo &info) { handleCommon(record, info); record.SwiftBridge = copyString(info.getSwiftBridge()); + record.NSErrorDomain = copyString(info.getNSErrorDomain()); } /// Map Objective-C context info. diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 4468d22f30a53..0515be234e4ea 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -151,6 +151,15 @@ static void ProcessAPINotes(Sema &S, Decl *D, Info.getSwiftBridge()))); } + // ns_error_domain + if (!Info.getNSErrorDomain().empty() && + !D->getAttr<NSErrorDomainAttr>()) { + D->addAttr( + NSErrorDomainAttr::CreateImplicit( + S.Context, + &S.Context.Idents.get(Info.getNSErrorDomain()))); + } + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); } diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index f02aa003dc0b3..66f5ea7f8f2e9 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -10,6 +10,7 @@ Classes: SwiftPrivate: false SwiftName: '' SwiftBridge: '' + NSErrorDomain: '' Methods: - Selector: init MethodKind: Instance @@ -54,6 +55,7 @@ Classes: SwiftPrivate: false SwiftName: '' SwiftBridge: View + NSErrorDomain: '' Methods: - Selector: 'addSubview:' MethodKind: Instance @@ -113,12 +115,20 @@ Enumerators: SwiftPrivate: false SwiftName: Red Tags: + - Name: NSSomeEnum + Availability: available + AvailabilityMsg: '' + SwiftPrivate: false + SwiftName: SomeEnum + SwiftBridge: '' + NSErrorDomain: some_error_domain - Name: NSSomeStruct Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: SomeStruct SwiftBridge: '' + NSErrorDomain: '' Typedefs: - Name: NSTypedef Availability: available @@ -126,3 +136,4 @@ Typedefs: SwiftPrivate: false SwiftName: Typedef SwiftBridge: '' + NSErrorDomain: '' From 8a3d10919b3b62e353c9ce590d96585c3859270a Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Sun, 10 Jul 2016 17:51:30 -0700 Subject: [PATCH 081/582] [API Notes] Add support for parameters, 'noescape' attribute <rdar://problem/27256643> apple-llvm-split-commit: 54a75ad253996e21d65ae464b2611b57ef8c7335 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 39 ++++++++++++- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 16 ++++++ clang/lib/APINotes/APINotesWriter.cpp | 17 +++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 55 +++++++++++++++++++ clang/lib/Sema/SemaAPINotes.cpp | 46 ++++++++++------ .../Inputs/APINotes/HeaderLib.apinotes | 7 +++ .../test/APINotes/Inputs/Headers/HeaderLib.h | 2 + clang/test/APINotes/Inputs/roundtrip.apinotes | 7 +++ clang/test/APINotes/nullability.c | 1 + 10 files changed, 173 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 5d5a347336385..9761510ead2b6 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -268,7 +268,6 @@ class VariableInfo : public CommonEntityInfo { Nullable = static_cast<unsigned>(kind); } - friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) { return static_cast<const CommonEntityInfo &>(lhs) == rhs && lhs.NullabilityAudited == rhs.NullabilityAudited && @@ -279,6 +278,13 @@ class VariableInfo : public CommonEntityInfo { return !(lhs == rhs); } + friend VariableInfo &operator|=(VariableInfo &lhs, + const VariableInfo &rhs) { + static_cast<CommonEntityInfo &>(lhs) |= rhs; + if (!lhs.NullabilityAudited && rhs.NullabilityAudited) + lhs.setNullabilityAudited(*rhs.getNullability()); + return lhs; + } }; /// Describes API notes data for an Objective-C property. @@ -300,6 +306,34 @@ class ObjCPropertyInfo : public VariableInfo { } }; +/// Describes a function or method parameter. +class ParamInfo : public VariableInfo { + /// Whether the this parameter has the 'noescape' attribute. + unsigned NoEscape : 1; + +public: + ParamInfo() : VariableInfo(), NoEscape(false) { } + + bool isNoEscape() const { return NoEscape; } + void setNoEscape(bool noescape) { NoEscape = noescape; } + + friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) { + static_cast<VariableInfo &>(lhs) |= rhs; + if (!lhs.NoEscape && rhs.NoEscape) + lhs.NoEscape = true; + return lhs; + } + + friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) { + return static_cast<const VariableInfo &>(lhs) == rhs && + lhs.NoEscape == rhs.NoEscape; + } + + friend bool operator!=(const ParamInfo &lhs, const ParamInfo &rhs) { + return !(lhs == rhs); + } +}; + /// A temporary reference to an Objective-C selector, suitable for /// referencing selector data on the stack. /// @@ -333,6 +367,9 @@ class FunctionInfo : public CommonEntityInfo { // of the parameters. uint64_t NullabilityPayload = 0; + /// The function parameters. + std::vector<ParamInfo> Params; + FunctionInfo() : CommonEntityInfo(), NullabilityAudited(false), diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 2d878ef866308..aad13f76126bc 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 12; // SwiftPrivate +const uint16_t VERSION_MINOR = 13; // Function/method parameters using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 81cfed71cf3ae..56c782a8c9ec0 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -228,6 +228,22 @@ namespace { = endian::readNext<uint8_t, little, unaligned>(data); info.NullabilityPayload = endian::readNext<uint64_t, little, unaligned>(data); + + unsigned numParams = endian::readNext<uint16_t, little, unaligned>(data); + while (numParams > 0) { + uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data); + + ParamInfo pi; + uint8_t nullabilityValue = payload & 0x3; payload >>= 2; + if (payload & 0x01) + pi.setNullabilityAudited(static_cast<NullabilityKind>(nullabilityValue)); + payload >>= 1; + pi.setNoEscape(payload & 0x01); + payload >>= 1; assert(payload == 0 && "Bad API notes"); + + info.Params.push_back(pi); + --numParams; + } } /// Used to deserialize the on-disk Objective-C method table. diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index e2392c6f52fb8..0c178becc16f7 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -493,7 +493,8 @@ namespace { /// Retrieve the serialized size of the given FunctionInfo, for use in /// on-disk hash tables. static unsigned getFunctionInfoSize(const FunctionInfo &info) { - return 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info); + return 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + + 2 + info.Params.size() * 1; } /// Emit a serialized representation of the function information. @@ -504,6 +505,20 @@ namespace { writer.write<uint8_t>(info.NullabilityAudited); writer.write<uint8_t>(info.NumAdjustedNullable); writer.write<uint64_t>(info.NullabilityPayload); + + // Parameters. + writer.write<uint16_t>(info.Params.size()); + for (const auto &pi : info.Params) { + uint8_t payload = pi.isNoEscape(); + + auto nullability = pi.getNullability(); + payload = (payload << 1) | nullability.hasValue(); + + payload = payload << 2; + if (nullability) + payload |= static_cast<uint8_t>(*nullability); + writer.write<uint8_t>(payload); + } } /// Used to serialize the on-disk Objective-C method table. diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 10648e29313a1..a218859077739 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -168,9 +168,17 @@ namespace { NullabilityKind::NonNull; typedef std::vector<clang::NullabilityKind> NullabilitySeq; + struct Param { + unsigned Position; + bool NoEscape = false; + llvm::Optional<NullabilityKind> Nullability; + }; + typedef std::vector<Param> ParamsSeq; + struct Method { StringRef Selector; MethodKind Kind; + ParamsSeq Params; NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; @@ -207,6 +215,7 @@ namespace { struct Function { StringRef Name; + ParamsSeq Params; NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; @@ -274,6 +283,7 @@ namespace { LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(clang::NullabilityKind) LLVM_YAML_IS_SEQUENCE_VECTOR(Method) LLVM_YAML_IS_SEQUENCE_VECTOR(Property) +LLVM_YAML_IS_SEQUENCE_VECTOR(Param) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) @@ -324,6 +334,16 @@ namespace llvm { } }; + template <> + struct MappingTraits<Param> { + static void mapping(IO &io, Param& p) { + io.mapRequired("Position", p.Position); + io.mapOptional("Nullability", p.Nullability, + AbsentNullability); + io.mapOptional("NoEscape", p.NoEscape); + } + }; + template <> struct MappingTraits<Property> { static void mapping(IO &io, Property& p) { @@ -342,6 +362,7 @@ namespace llvm { static void mapping(IO &io, Method& m) { io.mapRequired("Selector", m.Selector); io.mapRequired("MethodKind", m.Kind); + io.mapOptional("Parameters", m.Params); io.mapOptional("Nullability", m.Nullability); io.mapOptional("NullabilityOfRet", m.NullabilityOfRet, AbsentNullability); @@ -376,6 +397,7 @@ namespace llvm { struct MappingTraits<Function> { static void mapping(IO &io, Function& f) { io.mapRequired("Name", f.Name); + io.mapOptional("Parameters", f.Params); io.mapOptional("Nullability", f.Nullability); io.mapOptional("NullabilityOfRet", f.NullabilityOfRet, AbsentNullability); @@ -529,6 +551,20 @@ namespace { return false; } + void convertParams(const ParamsSeq ¶ms, FunctionInfo &outInfo) { + for (const auto &p : params) { + ParamInfo pi; + if (p.Nullability) + pi.setNullabilityAudited(*p.Nullability); + pi.setNoEscape(p.NoEscape); + + while (outInfo.Params.size() <= p.Position) { + outInfo.Params.push_back(ParamInfo()); + } + outInfo.Params[p.Position] |= pi; + } + } + void convertNullability(const NullabilitySeq &nullability, Optional<NullabilityKind> nullabilityOfRet, FunctionInfo &outInfo, @@ -612,6 +648,9 @@ namespace { if (meth.FactoryAsInit != FactoryAsInitKind::Infer) mInfo.setFactoryAsInitKind(meth.FactoryAsInit); + // Translate parameter information. + convertParams(meth.Params, mInfo); + // Translate nullability info. convertNullability(meth.Nullability, meth.NullabilityOfRet, mInfo, meth.Selector); @@ -746,6 +785,7 @@ namespace { convertAvailability(function.Availability, info, function.Name); info.SwiftPrivate = function.SwiftPrivate; info.SwiftName = function.SwiftName; + convertParams(function.Params, info); convertNullability(function.Nullability, function.NullabilityOfRet, info, function.Name); @@ -926,6 +966,19 @@ namespace { } } + /// Map parameter information for a function. + void handleParameters(ParamsSeq ¶ms, + const FunctionInfo &info) { + unsigned position = 0; + for (const auto &pi: info.Params) { + Param p; + p.Position = position++; + p.Nullability = pi.getNullability(); + p.NoEscape = pi.isNoEscape(); + params.push_back(p); + } + } + /// Map nullability information for a function. void handleNullability(NullabilitySeq &nullability, llvm::Optional<NullabilityKind> &nullabilityOfRet, @@ -969,6 +1022,7 @@ namespace { method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; handleCommon(method, info); + handleParameters(method.Params, info); handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); method.FactoryAsInit = info.getFactoryAsInitKind(); @@ -1005,6 +1059,7 @@ namespace { Function function; function.Name = name; handleCommon(function, info); + handleParameters(function.Params, info); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 0515be234e4ea..445a52c8ed1de 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -175,10 +175,20 @@ static void ProcessAPINotes(Sema &S, Decl *D, ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); } +/// Process API notes for a parameter. +static void ProcessAPINotes(Sema &S, ParmVarDecl *D, + const api_notes::ParamInfo &Info) { + // noescape + if (Info.isNoEscape() && !D->getAttr<NoEscapeAttr>()) + D->addAttr(NoEscapeAttr::CreateImplicit(S.Context)); + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); +} + /// Process API notes for a global variable. static void ProcessAPINotes(Sema &S, VarDecl *D, const api_notes::GlobalVariableInfo &Info) { - // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); } @@ -186,7 +196,6 @@ static void ProcessAPINotes(Sema &S, VarDecl *D, /// Process API notes for an Objective-C property. static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, const api_notes::ObjCPropertyInfo &Info) { - // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); } @@ -207,26 +216,31 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, D = MD; } - // Nullability. + // Nullability of return type. if (Info.NullabilityAudited) { - // Return type. applyNullability(S, D, Info.getReturnTypeInfo()); + } - // Parameters. - unsigned NumParams; + // Parameters. + unsigned NumParams; + if (FD) + NumParams = FD->getNumParams(); + else + NumParams = MD->param_size(); + + for (unsigned I = 0; I != NumParams; ++I) { + ParmVarDecl *Param; if (FD) - NumParams = FD->getNumParams(); + Param = FD->getParamDecl(I); else - NumParams = MD->param_size(); - - for (unsigned I = 0; I != NumParams; ++I) { - ParmVarDecl *Param; - if (FD) - Param = FD->getParamDecl(I); - else - Param = MD->param_begin()[I]; - + Param = MD->param_begin()[I]; + + // Nullability. + if (Info.NullabilityAudited) applyNullability(S, Param, Info.getParamTypeInfo(I)); + + if (I < Info.Params.size()) { + ProcessAPINotes(S, Param, Info.Params[I]); } } diff --git a/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes index 8d8ff11f69b57..9df8c3d5e464c 100644 --- a/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes +++ b/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -9,6 +9,13 @@ Functions: - Name: do_something_with_pointers NullabilityOfRet: O Nullability: [ N, O ] + - Name: take_pointer_and_int + Parameters: + - Position: 0 + Nullability: N + NoEscape: true + - Position: 1 + NoEscape: true Globals: - Name: global_int diff --git a/clang/test/APINotes/Inputs/Headers/HeaderLib.h b/clang/test/APINotes/Inputs/Headers/HeaderLib.h index 81a7d63d4684d..ec66166adb236 100644 --- a/clang/test/APINotes/Inputs/Headers/HeaderLib.h +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.h @@ -13,4 +13,6 @@ void do_something_with_pointers(int *ptr1, int *ptr2); typedef int unavailable_typedef; struct unavailable_struct { int x, y, z; }; +void take_pointer_and_int(int *ptr1, int value); + #endif diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 66f5ea7f8f2e9..090273479018b 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -67,6 +67,13 @@ Classes: SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance + Parameters: + - Position: 0 + NoEscape: false + - Position: 1 + NoEscape: false + - Position: 2 + NoEscape: true Nullability: [ N, N, O ] NullabilityOfRet: N Availability: available diff --git a/clang/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c index 86054eade82e4..1d5939bf92e2c 100644 --- a/clang/test/APINotes/nullability.c +++ b/clang/test/APINotes/nullability.c @@ -8,6 +8,7 @@ int main() { int i = 0; do_something_with_pointers(&i, 0); do_something_with_pointers(0, &i); // expected-warning{{null passed to a callee that requires a non-null argument}} + take_pointer_and_int(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}} float *fp = global_int; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * _Nonnull'}} return 0; From cda67b500f8cabb6c8b5c75c6423e8bac8bed00f Mon Sep 17 00:00:00 2001 From: Lang Hames <lhames@gmail.com> Date: Mon, 18 Jul 2016 13:48:26 -0700 Subject: [PATCH 082/582] Add fixes left out of previous merge commit. (Actual fixes by John McCall -- thanks John!) apple-llvm-split-commit: b0e86c9a67f564a17710d822529573b2ad2e0458 apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCGNU.cpp | 2 +- clang/lib/CodeGen/CGObjCRuntime.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 6a1f9930ab668..0a6c60a73d5ea 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -592,7 +592,7 @@ class CGObjCGNU : public CGObjCRuntime { } llvm::Constant *GetClassGlobal(StringRef Name, bool ForDefinition, - bool Weak) override { + bool Weak, bool DLLImport) override { return nullptr; } }; diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h index dfe37b48e37a8..dee1e9cbc2c10 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.h +++ b/clang/lib/CodeGen/CGObjCRuntime.h @@ -281,7 +281,7 @@ class CGObjCRuntime { QualType T) = 0; virtual llvm::Constant *GetClassGlobal(StringRef Name, bool ForDefinition, - bool Weak = false) = 0; + bool Weak = false, bool DLLImport = false) = 0; struct MessageSendInfo { const CGFunctionInfo &CallInfo; From 4e46faf006cee2463f2cc0feec2076da82527fef Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Wed, 3 Aug 2016 14:16:35 -0700 Subject: [PATCH 083/582] Availability for Swift: support deprecated for all versions. rdar://27682300 apple-llvm-split-commit: e5b87f265aede41c8381094bbf54e2715c8293b0 apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticSemaKinds.td | 4 +-- clang/lib/Parse/ParseDecl.cpp | 17 ++++++++++- clang/lib/Sema/SemaDeclAttr.cpp | 7 +++-- clang/test/Sema/attr-availability-swift.c | 29 +++++++++++++++++++ clang/test/Sema/attr-availability.c | 16 ---------- 5 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 clang/test/Sema/attr-availability-swift.c diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3919b3d006f30..bc1d9f5a52d45 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2621,8 +2621,8 @@ def warn_mismatched_availability_override_unavail : Warning< InGroup<Availability>; def note_overridden_method : Note< "overridden method is here">; -def warn_availability_swift_unavailable_only : Warning< - "only 'unavailable' is supported for Swift availability">, +def warn_availability_swift_unavailable_deprecated_only : Warning< + "only 'unavailable' and 'deprecated' are supported for Swift availability">, InGroup<Availability>; def note_protocol_method : Note< "protocol method is here">; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index a3a365ede1c07..bc510d04a1e2f 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -884,7 +884,7 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// /// version-arg: /// 'introduced' '=' version -/// 'deprecated' '=' version +/// 'deprecated' ['=' version] /// 'obsoleted' = version /// 'unavailable' /// opt-replacement: @@ -972,6 +972,21 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, continue; } + if (Keyword == Ident_deprecated && Platform->Ident && + Platform->Ident->getName() == "swift") { + // For swift, we deprecate for all versions. + if (!Changes[Deprecated].KeywordLoc.isInvalid()) { + Diag(KeywordLoc, diag::err_availability_redundant) + << Keyword + << SourceRange(Changes[Deprecated].KeywordLoc); + } + + Changes[Deprecated].KeywordLoc = KeywordLoc; + // Use a fake version here. + Changes[Deprecated].Version = VersionTuple(1); + continue; + } + if (Tok.isNot(tok::equal)) { Diag(Tok, diag::err_expected_after) << Keyword << tok::equal; SkipUntil(tok::r_paren, StopAtSemi); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 2a16ef2ead33d..c281e7dbb2a7c 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2173,9 +2173,10 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Replacement = SE->getString(); if (II->getName() == "swift") { - if (Introduced.isValid() || Deprecated.isValid() || Obsoleted.isValid() || - !IsUnavailable) { - S.Diag(Attr.getLoc(), diag::warn_availability_swift_unavailable_only); + if (Introduced.isValid() || Obsoleted.isValid() || + (!IsUnavailable && !Deprecated.isValid())) { + S.Diag(Attr.getLoc(), + diag::warn_availability_swift_unavailable_deprecated_only); return; } } diff --git a/clang/test/Sema/attr-availability-swift.c b/clang/test/Sema/attr-availability-swift.c new file mode 100644 index 0000000000000..42e75246d332f --- /dev/null +++ b/clang/test/Sema/attr-availability-swift.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fsyntax-only -fblocks -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fsyntax-only -ast-dump %s | FileCheck %s +// + +#if !__has_feature(attribute_availability_with_message) +# error "Missing __has_feature" +#endif + +#if __has_feature(attribute_availability_swift) +# warning "okay" +// expected-warning@-1{{okay}} +#else +# error "Missing __has_feature" +#endif + +extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable))); +// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "" "" +extern int noSwiftGlobal1 __attribute__((availability(macosx, introduced=10.1))); // okay +// CHECK: AvailabilityAttr {{.*}}macos 10.1 0 0 "" "" +// CHECK: AvailabilityAttr {{.*}}Inherited swift 0 0 0 Unavailable "" "" +extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable, message="and this one has a message"))); // okay +// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "and this one has a message" "" +// CHECK: AvailabilityAttr {{.*}}Inherited macos 10.1 0 0 "" "" +extern int noSwiftGlobal2 __attribute__((availability(swift, introduced=5))); // expected-warning{{only 'unavailable' and 'deprecated' are supported for Swift availability}} +// CHECK: VarDecl +// CHECK-NOT: AvailabilityAttr +extern int noSwiftGlobal3 __attribute__((availability(swift, deprecated, message="t"))); +// CHECK: VarDecl +// CHECK: AvailabilityAttr {{.*}}swift 0 1 0 "t" "" diff --git a/clang/test/Sema/attr-availability.c b/clang/test/Sema/attr-availability.c index 6b1b3a643997e..e3322f1b19e8d 100644 --- a/clang/test/Sema/attr-availability.c +++ b/clang/test/Sema/attr-availability.c @@ -80,22 +80,6 @@ void f8() { extern int x2 __attribute__((availability(macosx,introduced=10.2))); // expected-note {{previous attribute is here}} extern int x2 __attribute__((availability(macosx,introduced=10.5))); // expected-warning {{availability does not match previous declaration}} - - -#if __has_feature(attribute_availability_swift) -# warning "okay" -// expected-warning@-1{{okay}} -#else -# error "Missing __has_feature" -#endif - - -extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable))); -extern int noSwiftGlobal1 __attribute__((availability(macosx, introduced=10.1))); // okay -extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable, message="and this one has a message"))); // okay - -extern int noSwiftGlobal2 __attribute__((availability(swift, introduced=5))); // expected-warning{{only 'unavailable' is supported for Swift availability}} - enum Original { OriginalDeprecated __attribute__((availability(macosx, deprecated=10.2))), // expected-note + {{'OriginalDeprecated' has been explicitly marked deprecated here}} OriginalUnavailable __attribute__((availability(macosx, unavailable))) // expected-note + {{'OriginalUnavailable' has been explicitly marked unavailable here}} From 4a1b4edbe1d1969284c1528e2950ac81b25edc8f Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 12:33:08 -0700 Subject: [PATCH 084/582] Add hash_value support for tuples. apple-llvm-split-commit: f018bd9ae7cf418188285c03d8fe2aacecbd4730 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/Hashing.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h index c3b574102f69d..8b1a305852b64 100644 --- a/llvm/include/llvm/ADT/Hashing.h +++ b/llvm/include/llvm/ADT/Hashing.h @@ -53,6 +53,7 @@ #include <cassert> #include <cstring> #include <string> +#include <tuple> #include <utility> namespace llvm { @@ -656,6 +657,33 @@ hash_code hash_value(const std::basic_string<T> &arg) { return hash_combine_range(arg.begin(), arg.end()); } +template<unsigned ...Indices> +struct UnsignedConstantIndexSet { }; + +template<unsigned I, unsigned N, unsigned ...Indices> +struct MakeUnsignedConstantIndexSet { + typedef typename MakeUnsignedConstantIndexSet<I+1, N, Indices..., I>::Type + Type; +}; + +template<unsigned N, unsigned ...Indices> +struct MakeUnsignedConstantIndexSet<N, N, Indices...> { + typedef UnsignedConstantIndexSet<Indices...> Type; +}; + +template <typename ...Ts, unsigned ...Indices> +hash_code hash_value_tuple_helper(const std::tuple<Ts...> &arg, + UnsignedConstantIndexSet<Indices...> indices) { + return hash_combine(hash_value(std::get<Indices>(arg))...); +} + +template <typename ...Ts> +hash_code hash_value(const std::tuple<Ts...> &arg) { + return hash_value_tuple_helper( + arg, + typename MakeUnsignedConstantIndexSet<0, sizeof...(Ts)>::Type()); +} + } // namespace llvm #endif From 098bcad53ea77c09476f6b81b8fc6ca1427d782c Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 12:34:02 -0700 Subject: [PATCH 085/582] [APINotes] Add support for distinguishing class vs. instance properties. Fixes rdar://problem/28455756. apple-llvm-split-commit: 7bde131b05f106eeeff0bc874a6c8e28305a9835 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 6 ++- clang/include/clang/APINotes/APINotesWriter.h | 1 + clang/lib/APINotes/APINotesFormat.h | 4 +- clang/lib/APINotes/APINotesReader.cpp | 22 +++++++---- clang/lib/APINotes/APINotesWriter.cpp | 20 ++++++---- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 38 ++++++++++++++++--- clang/lib/Sema/SemaAPINotes.cpp | 6 ++- clang/lib/Sema/SemaObjCProperty.cpp | 4 +- .../APINotes/Inputs/APINotes/SomeKit.apinotes | 8 ++++ .../APINotes/SomeKit.apinotes | 9 +++++ .../SomeKit.framework/Headers/SomeKit.h | 11 ++++++ clang/test/APINotes/Inputs/roundtrip.apinotes | 2 + clang/test/APINotes/nullability.m | 10 ++++- clang/test/APINotes/yaml-reader-errors.c | 2 +- 14 files changed, 115 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 29dcc1f56f920..cc6dafb863f65 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -74,10 +74,13 @@ class APINotesReader { /// /// \param contextID The ID that references the context we are looking for. /// \param name The name of the property we're looking for. + /// \param isInstance Whether we are looking for an instance property (vs. + /// a class property). /// /// \returns Information about the property, if known. Optional<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID, - StringRef name); + StringRef name, + bool isInstance); /// Look for information regarding the given Objective-C method in /// the given context. @@ -147,6 +150,7 @@ class APINotesReader { /// Visit an Objective-C property. virtual void visitObjCProperty(ContextID contextID, StringRef name, + bool isInstance, const ObjCPropertyInfo &info); /// Visit a global variable. diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index aa41756b2e778..cc93a5f5899b6 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -66,6 +66,7 @@ class APINotesWriter { /// \param name The name of this property. /// \param info Information about this property. void addObjCProperty(ContextID contextID, StringRef name, + bool isInstanceProperty, const ObjCPropertyInfo &info); /// Add information about a specific Objective-C method. diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index aad13f76126bc..8e5210cc319d2 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 13; // Function/method parameters +const uint16_t VERSION_MINOR = 14; // Objective-C class properties using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; @@ -291,4 +291,6 @@ namespace llvm { }; } +} + #endif // LLVM_CLANG_API_NOTES_FORMAT_H diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 56c782a8c9ec0..2b49125fffc01 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -175,8 +175,8 @@ namespace { /// Used to deserialize the on-disk Objective-C property table. class ObjCPropertyTableInfo { public: - // (context ID, name ID) - using internal_key_type = std::pair<unsigned, unsigned>; + // (context ID, name ID, isInstance) + using internal_key_type = std::tuple<unsigned, unsigned, char>; using external_key_type = internal_key_type; using data_type = ObjCPropertyInfo; using hash_value_type = size_t; @@ -208,7 +208,8 @@ namespace { static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext<uint32_t, little, unaligned>(data); auto nameID = endian::readNext<uint32_t, little, unaligned>(data); - return { classID, nameID }; + char isInstance = endian::readNext<uint8_t, little, unaligned>(data); + return std::make_tuple(classID, nameID, isInstance); } static data_type ReadData(internal_key_type key, const uint8_t *data, @@ -1510,7 +1511,8 @@ auto APINotesReader::lookupObjCProtocol(StringRef name) Optional<ObjCPropertyInfo> APINotesReader::lookupObjCProperty( ContextID contextID, - StringRef name) { + StringRef name, + bool isInstance) { if (!Impl.ObjCPropertyTable) return None; @@ -1518,7 +1520,9 @@ Optional<ObjCPropertyInfo> APINotesReader::lookupObjCProperty( if (!propertyID) return None; - auto known = Impl.ObjCPropertyTable->find({contextID.Value, *propertyID}); + auto known = Impl.ObjCPropertyTable->find(std::make_tuple(contextID.Value, + *propertyID, + (char)isInstance)); if (known == Impl.ObjCPropertyTable->end()) return None; @@ -1639,6 +1643,7 @@ void APINotesReader::Visitor::visitObjCMethod(ContextID contextID, void APINotesReader::Visitor::visitObjCProperty(ContextID contextID, StringRef name, + bool isInstance, const ObjCPropertyInfo &info) { } void APINotesReader::Visitor::visitGlobalVariable( @@ -1723,10 +1728,11 @@ void APINotesReader::visit(Visitor &visitor) { // Visit properties. if (Impl.ObjCPropertyTable) { for (auto key : Impl.ObjCPropertyTable->keys()) { - ContextID contextID(key.first); - auto name = identifiers[key.second]; + ContextID contextID(std::get<0>(key)); + auto name = identifiers[std::get<1>(key)]; + char isInstance = std::get<2>(key); auto info = *Impl.ObjCPropertyTable->find(key); - visitor.visitObjCProperty(contextID, name, info); + visitor.visitObjCProperty(contextID, name, isInstance, info); } } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 0c178becc16f7..0e0bfaf1bf4ac 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -58,8 +58,9 @@ class APINotesWriter::Implementation { /// Information about Objective-C properties. /// - /// Indexed by the context ID and property name. - llvm::DenseMap<std::pair<unsigned, unsigned>, ObjCPropertyInfo> + /// Indexed by the context ID, property name, and whether this is an + /// instance property. + llvm::DenseMap<std::tuple<unsigned, unsigned, char>, ObjCPropertyInfo> ObjCProperties; /// Information about Objective-C methods. @@ -430,7 +431,8 @@ namespace { /// Used to serialize the on-disk Objective-C property table. class ObjCPropertyTableInfo { public: - using key_type = std::pair<unsigned, unsigned>; // (class ID, name ID) + // (class ID, name ID, isInstance) + using key_type = std::tuple<unsigned, unsigned, char>; using key_type_ref = key_type; using data_type = ObjCPropertyInfo; using data_type_ref = const data_type &; @@ -444,7 +446,7 @@ namespace { std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t); + uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); uint32_t dataLength = getVariableInfoSize(data); endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); @@ -454,8 +456,9 @@ namespace { void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); - writer.write<uint32_t>(key.first); - writer.write<uint32_t>(key.second); + writer.write<uint32_t>(std::get<0>(key)); + writer.write<uint32_t>(std::get<1>(key)); + writer.write<uint8_t>(std::get<2>(key)); } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, @@ -1060,10 +1063,11 @@ ContextID APINotesWriter::addObjCProtocol(StringRef name, return ContextID(known->second.first); } void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name, + bool isInstance, const ObjCPropertyInfo &info) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.ObjCProperties.count({contextID.Value, nameID})); - Impl.ObjCProperties[{contextID.Value, nameID}] = info; + assert(!Impl.ObjCProperties.count({contextID.Value, nameID, isInstance})); + Impl.ObjCProperties[{contextID.Value, nameID, isInstance}] = info; } void APINotesWriter::addObjCMethod(ContextID contextID, diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index a218859077739..b9640954f58fa 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -193,6 +193,7 @@ namespace { struct Property { StringRef Name; + llvm::Optional<MethodKind> Kind; llvm::Optional<NullabilityKind> Nullability; AvailabilityItem Availability; bool SwiftPrivate = false; @@ -348,6 +349,7 @@ namespace llvm { struct MappingTraits<Property> { static void mapping(IO &io, Property& p) { io.mapRequired("Name", p.Name); + io.mapOptional("PropertyKind", p.Kind); io.mapOptional("Nullability", p.Nullability, AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); @@ -693,11 +695,20 @@ namespace { } // Write all properties. - llvm::StringSet<> knownProperties; + llvm::StringSet<> knownInstanceProperties; + llvm::StringSet<> knownClassProperties; for (const auto &prop : cl.Properties) { // Check for duplicate property definitions. - if (!knownProperties.insert(prop.Name).second) { - emitError("duplicate definition of property '" + cl.Name + "." + + if ((!prop.Kind || *prop.Kind == MethodKind::Instance) && + !knownInstanceProperties.insert(prop.Name).second) { + emitError("duplicate definition of instance property '" + cl.Name + + "." + prop.Name + "'"); + continue; + } + + if ((!prop.Kind || *prop.Kind == MethodKind::Class) && + !knownClassProperties.insert(prop.Name).second) { + emitError("duplicate definition of class property '" + cl.Name + "." + prop.Name + "'"); continue; } @@ -711,7 +722,14 @@ namespace { pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); - Writer->addObjCProperty(clID, prop.Name, pInfo); + if (prop.Kind) { + Writer->addObjCProperty(clID, prop.Name, + *prop.Kind == MethodKind::Instance, pInfo); + } else { + // Add both instance and class properties with this name. + Writer->addObjCProperty(clID, prop.Name, true, pInfo); + Writer->addObjCProperty(clID, prop.Name, false, pInfo); + } } } @@ -1037,9 +1055,11 @@ namespace { } virtual void visitObjCProperty(ContextID contextID, StringRef name, + bool isInstance, const ObjCPropertyInfo &info) { Property property; property.Name = name; + property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class; handleCommon(property, info); // FIXME: No way to represent "not audited for nullability". @@ -1109,6 +1129,11 @@ namespace { }; } +/// Produce a flattened, numeric value for optional method/property kinds. +static unsigned flattenPropertyKind(llvm::Optional<MethodKind> kind) { + return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0; +} + bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, llvm::raw_ostream &os) { // Try to read the file. @@ -1150,7 +1175,10 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, // Sort properties. std::sort(record.Properties.begin(), record.Properties.end(), [](const Property &lhs, const Property &rhs) -> bool { - return lhs.Name < rhs.Name; + return lhs.Name < rhs.Name || + (lhs.Name == rhs.Name && + flattenPropertyKind(lhs.Kind) < + flattenPropertyKind(rhs.Kind)); }); // Sort methods. diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 445a52c8ed1de..a0c5ca88236ca 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -491,8 +491,12 @@ void Sema::ProcessAPINotes(Decl *D) { if (api_notes::APINotesReader *Reader = APINotes.findAPINotes(D->getLocation())) { if (auto Context = GetContext(Reader)) { + bool isInstanceProperty = + (Property->getPropertyAttributesAsWritten() & + ObjCPropertyDecl::OBJC_PR_class) == 0; if (auto Info = Reader->lookupObjCProperty(*Context, - Property->getName())) { + Property->getName(), + isInstanceProperty)) { ::ProcessAPINotes(*this, Property, *Info); } } diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 5e38751f44a50..a03b320a3d3db 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -636,8 +636,6 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, PDecl->setInvalidDecl(); } - ProcessDeclAttributes(S, PDecl, FD.D); - // Regardless of setter/getter attribute, we save the default getter/setter // selector names in anticipation of declaration of setter/getter methods. PDecl->setGetterName(GetterSel); @@ -645,6 +643,8 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, PDecl->setPropertyAttributesAsWritten( makePropertyAttributesAsWritten(AttributesAsWritten)); + ProcessDeclAttributes(S, PDecl, FD.D); + if (Attributes & ObjCDeclSpec::DQ_PR_readonly) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly); diff --git a/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes index d251491ed43db..52336df400b41 100644 --- a/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes @@ -20,6 +20,14 @@ Classes: AvailabilityMsg: "wouldn't work anyway" - Name: internalProperty Nullability: N + - Name: nonnullAInstance + PropertyKind: Instance + Nullability: N + - Name: nonnullAClass + PropertyKind: Class + Nullability: N + - Name: nonnullABoth + Nullability: N - Name: B Availability: none AvailabilityMsg: "just don't" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index a585ca5f4df3b..ade66a10a932e 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -12,8 +12,17 @@ Classes: Nullability: [ N, S ] Properties: - Name: intValue + PropertyKind: Instance Availability: none AvailabilityMsg: "wouldn't work anyway" + - Name: nonnullAInstance + PropertyKind: Instance + Nullability: N + - Name: nonnullAClass + PropertyKind: Class + Nullability: N + - Name: nonnullABoth + Nullability: N - Name: B Availability: none AvailabilityMsg: "just don't" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h index 5e0d7e0b7ab26..eb25cc0c7fcc8 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -24,4 +24,15 @@ __attribute__((objc_root_class)) +(instancetype)processInfo; @end +@interface A(NonNullProperties) +@property (nonatomic, readwrite, retain) A *nonnullAInstance; +@property (class, nonatomic, readwrite, retain) A *nonnullAInstance; + +@property (nonatomic, readwrite, retain) A *nonnullAClass; +@property (class, nonatomic, readwrite, retain) A *nonnullAClass; + +@property (nonatomic, readwrite, retain) A *nonnullABoth; +@property (class, nonatomic, readwrite, retain) A *nonnullABoth; +@end + #endif diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 090273479018b..10ac249817d73 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -90,12 +90,14 @@ Classes: SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView + PropertyKind: Instance Nullability: O Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: enclosing - Name: makeBackingLayer + PropertyKind: Class Nullability: N Availability: available AvailabilityMsg: '' diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index ce4beabb597ff..486b2c5f366cc 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -3,12 +3,20 @@ #import <SomeKit/SomeKit.h> - int main() { A *a; [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + [a setNonnullAInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + [A setNonnullAInstance: 0]; // no warning + + [a setNonnullAClass: 0]; // no warning + [A setNonnullAClass: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + + [a setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + [A setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + return 0; } diff --git a/clang/test/APINotes/yaml-reader-errors.c b/clang/test/APINotes/yaml-reader-errors.c index 51dfe3aed841f..3e01eaaef03cb 100644 --- a/clang/test/APINotes/yaml-reader-errors.c +++ b/clang/test/APINotes/yaml-reader-errors.c @@ -34,7 +34,7 @@ AvailabilityMsg: iOSOnly Nullability: N Availability: iOS AvailabilityMsg: iOSOnly -# CHECK: duplicate definition of property 'UIFont.familyName' +# CHECK: duplicate definition of instance property 'UIFont.familyName' - Name: familyName Nullability: N Availability: iOS From 16ca907a8533fa32d33899e639140a722e2aa8fd Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 14:42:17 -0700 Subject: [PATCH 086/582] Fix bad code-motion apple-llvm-split-commit: f0171c8ba46246343439cafd8f315549f8b80845 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesFormat.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 8e5210cc319d2..1db2380462f49 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -291,6 +291,4 @@ namespace llvm { }; } -} - #endif // LLVM_CLANG_API_NOTES_FORMAT_H From 22fe14c242f82f06fc367a57a61e6e0a47ea280f Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 20:51:36 -0700 Subject: [PATCH 087/582] [API Notes] Use std::make_tuple to unbreak libstdc++ build. apple-llvm-split-commit: b4f0490f10b156be5e5c1cfc5b08c8153a1698b9 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesWriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 0e0bfaf1bf4ac..b0a46504e58ac 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -1066,8 +1066,8 @@ void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name, bool isInstance, const ObjCPropertyInfo &info) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.ObjCProperties.count({contextID.Value, nameID, isInstance})); - Impl.ObjCProperties[{contextID.Value, nameID, isInstance}] = info; + assert(!Impl.ObjCProperties.count(std::make_tuple(contextID.Value, nameID, isInstance))); + Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)] = info; } void APINotesWriter::addObjCMethod(ContextID contextID, From b772be2c2a9e29b8c081e197bf0167cbb66ed92c Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 20:14:55 -0700 Subject: [PATCH 088/582] [API Notes] Load API notes from framework/header search paths. Introduce a new command-line flag, -fapinotes-modules, that loads module-specific API notes based on where the module map itself was provided, e.g., within the framework (Foo.framework/APINotes/Foo.apinotes) or alongside the headers (ModuleName.apinotes). If found here, these take precedence over the module-specific API notes in the API notes search paths (-iapinotes-modules XXX). Fixes rdar://problem/28511719. apple-llvm-split-commit: 1452c9a3b67ad9bedc9c027e2075b0116e92a979 apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesManager.h | 15 +++- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/Options.td | 4 + clang/lib/APINotes/APINotesManager.cpp | 80 +++++++++++++------ clang/lib/Driver/Tools.cpp | 5 ++ clang/lib/Frontend/CompilerInstance.cpp | 14 ++-- clang/lib/Frontend/CompilerInvocation.cpp | 1 + .../APINotes/Inputs/APINotes/SomeKit.apinotes | 48 ----------- .../Inputs/APINotes/SomeOtherKit.apinotes | 8 ++ .../APINotes/SomeKit.apinotes | 6 +- .../APINotes/SomeOtherKit.apinotes | 8 ++ .../Headers/SomeOtherKit.h | 9 +++ .../Modules/module.modulemap | 5 ++ .../{APINotes => Headers}/HeaderLib.apinotes | 5 +- clang/test/APINotes/availability.m | 2 +- clang/test/APINotes/cache.m | 8 +- clang/test/APINotes/cache_pruning.m | 10 +-- clang/test/APINotes/nullability.c | 2 +- clang/test/APINotes/nullability.m | 2 +- clang/test/APINotes/objc_designated_inits.m | 2 +- clang/test/APINotes/search-order.m | 25 ++++++ 21 files changed, 159 insertions(+), 101 deletions(-) delete mode 100644 clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes create mode 100644 clang/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap rename clang/test/APINotes/Inputs/{APINotes => Headers}/HeaderLib.apinotes (92%) create mode 100644 clang/test/APINotes/search-order.m diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index 7f540b9c17dee..e3cc4dff3a1b8 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_APINOTES_APINOTESMANAGER_H #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/Module.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" @@ -81,6 +82,14 @@ class APINotesManager { bool loadAPINotes(const DirectoryEntry *HeaderDir, const FileEntry *APINotesFile); + /// Look for API notes relative to the given directory, adjusting + /// for whether it is a framework directory, and with the base + /// filename. + /// + /// This might find either a binary or source API notes. + const FileEntry *findAPINotes(const DirectoryEntry *directory, + bool isFramework, StringRef filename); + /// Attempt to load API notes for the given framework. /// /// \param FrameworkPath The path to the framework. @@ -100,13 +109,15 @@ class APINotesManager { /// Load the API notes for the current module. /// - /// \param moduleName The name of the current module. + /// \param module The current module. + /// \param lookInModule Whether to look inside the module itself. /// \param searchPaths The paths in which we should search for API notes /// for the current module. /// /// \returns the file entry for the API notes file loaded, or nullptr if /// no API notes were found. - const FileEntry *loadCurrentModuleAPINotes(StringRef moduleName, + const FileEntry *loadCurrentModuleAPINotes(const Module *module, + bool lookInModule, ArrayRef<std::string> searchPaths); /// Find the API notes reader that corresponds to the given source location. diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index eb79111483e9f..c184321c6c038 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -253,6 +253,7 @@ LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling") LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST") LANGOPT(APINotes, 1, 0, "use external API notes") +LANGOPT(APINotesModules, 1, 0, "use external API notes") LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " "field padding (0: none, 1:least " diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 98230d9b9c310..b4dba4b98c37e 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -542,8 +542,12 @@ def fno_profile_use : Flag<["-"], "fno-profile-use">, def fapinotes : Flag<["-"], "fapinotes">, Group<f_clang_Group>, Flags<[CC1Option]>, HelpText<"Enable external API notes support">; +def fapinotes_modules : Flag<["-"], "fapinotes-modules">, Group<f_clang_Group>, + Flags<[CC1Option]>, HelpText<"Enable module-based external API notes support">; def fno_apinotes : Flag<["-"], "fno-apinotes">, Group<f_clang_Group>, Flags<[CC1Option]>, HelpText<"Disable external API notes support">; +def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group<f_clang_Group>, + Flags<[CC1Option]>, HelpText<"Disable module-based external API notes support">; def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, Group<i_Group>, Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">, HelpText<"Specify the API notes cache path">; diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 3c3d5fdfc4101..be259663ed031 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -1,4 +1,4 @@ -//===--- APINotesMAnager.cpp - Manage API Notes Files ---------------------===// +//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===// // // The LLVM Compiler Infrastructure // @@ -275,6 +275,39 @@ bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, return true; } +const FileEntry *APINotesManager::findAPINotes(const DirectoryEntry *directory, + bool isFramework, + StringRef basename) { + FileManager &fileMgr = SourceMgr.getFileManager(); + + llvm::SmallString<128> path; + path += directory->getName(); + + if (isFramework) { + llvm::sys::path::append(path, "APINotes"); + auto apinotesDir = fileMgr.getDirectory(path); + if (!apinotesDir) return nullptr; + + // Find the API notes within this directory. + return findAPINotes(apinotesDir, /*isFramework=*/false, basename); + } + + unsigned pathLen = path.size(); + + // Look for a binary API notes file. + llvm::sys::path::append(path, + llvm::Twine(basename) + "." + BINARY_APINOTES_EXTENSION); + if (const FileEntry *binaryFile = fileMgr.getFile(path)) + return binaryFile; + + path.resize(pathLen); + + // Look for the source API notes file. + llvm::sys::path::append(path, + llvm::Twine(basename) + "." + SOURCE_APINOTES_EXTENSION); + return fileMgr.getFile(path); +} + const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, @@ -326,38 +359,33 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( } const FileEntry *APINotesManager::loadCurrentModuleAPINotes( - StringRef moduleName, + const Module *module, + bool lookInModule, ArrayRef<std::string> searchPaths) { assert(!CurrentModuleReader && "Already loaded API notes for the current module?"); FileManager &fileMgr = SourceMgr.getFileManager(); - - // Look for API notes for this module in the module search paths. - for (const auto &searchPath : searchPaths) { - // First, look for a binary API notes file. - llvm::SmallString<128> apiNotesFilePath; - apiNotesFilePath += searchPath; - llvm::sys::path::append( - apiNotesFilePath, - llvm::Twine(moduleName) + "." + BINARY_APINOTES_EXTENSION); - - // Try to open the binary API Notes file. - if (const FileEntry *binaryAPINotesFile - = fileMgr.getFile(apiNotesFilePath)) { - CurrentModuleReader = loadAPINotes(binaryAPINotesFile); - return CurrentModuleReader ? binaryAPINotesFile : nullptr; + auto moduleName = module->getTopLevelModuleName(); + + // First, look relative to the module itself. + if (lookInModule) { + if (auto file = findAPINotes(module->Directory, module->IsFramework, + moduleName)) { + CurrentModuleReader = loadAPINotes(file); + return CurrentModuleReader ? file : nullptr; } + } - // Try to open the source API Notes file. - apiNotesFilePath = searchPath; - llvm::sys::path::append( - apiNotesFilePath, - llvm::Twine(moduleName) + "." + SOURCE_APINOTES_EXTENSION); - if (const FileEntry *sourceAPINotesFile - = fileMgr.getFile(apiNotesFilePath)) { - CurrentModuleReader = loadAPINotes(sourceAPINotesFile); - return CurrentModuleReader ? sourceAPINotesFile : nullptr; + // Second, look for API notes for this module in the module API + // notes search paths. + for (const auto &searchPath : searchPaths) { + if (auto searchDir = fileMgr.getDirectory(searchPath)) { + if (auto file = findAPINotes(searchDir, /*isFramework=*/false, + moduleName)) { + CurrentModuleReader = loadAPINotes(file); + return CurrentModuleReader ? file : nullptr; + } } } diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index a6fdec54c4027..47644b12a11a1 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -5412,9 +5412,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false) || + Args.hasFlag(options::OPT_fapinotes_modules, + options::OPT_fno_apinotes_modules, false) || Args.hasArg(options::OPT_iapinotes_modules)) { if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false)) CmdArgs.push_back("-fapinotes"); + if (Args.hasFlag(options::OPT_fapinotes_modules, + options::OPT_fno_apinotes_modules, false)) + CmdArgs.push_back("-fapinotes-modules"); SmallString<128> APINotesCachePath; if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) { diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index de8630c15dcf2..9daec8baa6d8b 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -540,17 +540,15 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); - // If we're building a module, notify the API notes manager. - StringRef currentModuleName = getLangOpts().CurrentModule; - if (!currentModuleName.empty()) { + // If we're building a module and are supposed to load API notes, + // notify the API notes manager. + if (auto currentModule = getPreprocessor().getCurrentModule()) { (void)TheSema->APINotes.loadCurrentModuleAPINotes( - currentModuleName, + currentModule, + getLangOpts().APINotesModules, getAPINotesOpts().ModuleSearchPaths); // Check for any attributes we should add to the module if (auto curReader = TheSema->APINotes.getCurrentModuleReader()) { - auto currentModule = getPreprocessor().getCurrentModule(); - assert(currentModule && "how can we have a reader for it?"); - // swift_infer_import_as_member if (curReader->getModuleOptions().SwiftInferImportAsMember) { currentModule->IsSwiftInferImportAsMember = true; @@ -953,7 +951,7 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, Module *Module, StringRef ModuleFileName) { - ModuleMap &ModMap + ModuleMap &ModMap = ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); // Construct a compiler invocation for creating this module. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 6abf4638c4e3c..63f7af79aed58 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2001,6 +2001,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.HalfArgsAndReturns = Args.hasArg(OPT_fallow_half_arguments_and_returns) | Opts.NativeHalfArgsAndReturns; Opts.APINotes = Args.hasArg(OPT_fapinotes); + Opts.APINotesModules = Args.hasArg(OPT_fapinotes_modules); Opts.GNUAsm = !Args.hasArg(OPT_fno_gnu_inline_asm); // __declspec is enabled by default for the PS4 by the driver, and also diff --git a/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes deleted file mode 100644 index 52336df400b41..0000000000000 --- a/clang/test/APINotes/Inputs/APINotes/SomeKit.apinotes +++ /dev/null @@ -1,48 +0,0 @@ -Name: SomeKit -Classes: - - Name: A - Methods: - - Selector: "transform:" - MethodKind: Instance - Availability: none - AvailabilityMsg: "anything but this" - - Selector: "transform:integer:" - MethodKind: Instance - NullabilityOfRet: N - Nullability: [ N, S ] - - Selector: "privateTransform:input:" - MethodKind: Instance - NullabilityOfRet: N - Nullability: [ N, S ] - Properties: - - Name: intValue - Availability: none - AvailabilityMsg: "wouldn't work anyway" - - Name: internalProperty - Nullability: N - - Name: nonnullAInstance - PropertyKind: Instance - Nullability: N - - Name: nonnullAClass - PropertyKind: Class - Nullability: N - - Name: nonnullABoth - Nullability: N - - Name: B - Availability: none - AvailabilityMsg: "just don't" - - Name: C - Methods: - - Selector: "initWithA:" - MethodKind: Instance - DesignatedInit: true - - Name: ProcessInfo - Methods: - - Selector: "processInfo" - MethodKind: Class - FactoryAsInit: C - -Protocols: - - Name: InternalProtocol - Availability: none - AvailabilityMsg: "not for you" diff --git a/clang/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes b/clang/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes new file mode 100644 index 0000000000000..ccdc4e15d34d1 --- /dev/null +++ b/clang/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes @@ -0,0 +1,8 @@ +Name: SomeOtherKit +Classes: + - Name: A + Methods: + - Selector: "methodB" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index ade66a10a932e..51b571f67032d 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -1,7 +1,7 @@ Name: SomeKit Classes: - Name: A - Methods: + Methods: - Selector: "transform:" MethodKind: Instance Availability: none @@ -31,3 +31,7 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes new file mode 100644 index 0000000000000..2ad546b8f8bcc --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes @@ -0,0 +1,8 @@ +Name: SomeOtherKit +Classes: + - Name: A + Methods: + - Selector: "methodA" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h new file mode 100644 index 0000000000000..3911d765230c6 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h @@ -0,0 +1,9 @@ +#ifndef SOME_OTHER_KIT_H + +__attribute__((objc_root_class)) +@interface A +-(void)methodA; +-(void)methodB; +@end + +#endif diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..0aaad92e041ce --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module SomeOtherKit { + umbrella header "SomeOtherKit.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes similarity index 92% rename from clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes rename to clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes index 9df8c3d5e464c..f1cd086126862 100644 --- a/clang/test/APINotes/Inputs/APINotes/HeaderLib.apinotes +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes @@ -1,4 +1,5 @@ Name: HeaderLib +SwiftInferImportAsMember: true Functions: - Name: custom_realloc NullabilityOfRet: N @@ -16,17 +17,15 @@ Functions: NoEscape: true - Position: 1 NoEscape: true - Globals: - Name: global_int Nullability: N - Name: unavailable_global_int Availability: none - Tags: - Name: unavailable_struct Availability: none Typedefs: - Name: unavailable_typedef - Availability: none + Availability: none \ No newline at end of file diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m index 1cfc65862f46a..5537316dcaa01 100644 --- a/clang/test/APINotes/availability.m +++ b/clang/test/APINotes/availability.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/cache.m b/clang/test/APINotes/cache.m index 6a2c2f5d17a4d..05fb81c72dcf3 100644 --- a/clang/test/APINotes/cache.m +++ b/clang/test/APINotes/cache.m @@ -1,19 +1,19 @@ // RUN: rm -rf %t/APINotesCache -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // Check for the presence of the cached compiled form. // RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" // Run test again to ensure that caching doesn't cause problems. -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // Check that the driver provides a default -fapinotes-cache-path= -// RUN: %clang -fsyntax-only -fapinotes -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s +// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s // CHECK-DEFAULT-PATH: -fapinotes-cache-path={{.*}}org.llvm.clang/APINotesCache // Check that the driver passes through a provided -fapinotes-cache-path= -// RUN: %clang -fsyntax-only -fapinotes -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s +// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s // CHECK-PATH: -fapinotes-cache-path=/wobble #include "HeaderLib.h" diff --git a/clang/test/APINotes/cache_pruning.m b/clang/test/APINotes/cache_pruning.m index 54b0c647aef3e..1a36570bb43f0 100644 --- a/clang/test/APINotes/cache_pruning.m +++ b/clang/test/APINotes/cache_pruning.m @@ -4,7 +4,7 @@ // RUN: rm -rf %t/APINotesCache // Run Clang. This should generated the cached versions of both and a timestamp. -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB // RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "APINotes.timestamp" @@ -12,7 +12,7 @@ // Set the timestamp back a very long time. We should try to prune, // but nothing gets pruned because the API Notes files are new enough. // RUN: touch -m -a -t 201101010000 %t/APINotes.timestamp -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "APINotes.timestamp" @@ -21,7 +21,7 @@ // This shouldn't prune anything, because the timestamp has been updated, so // the pruning mechanism won't fire. // RUN: find %t/APINotesCache -name APINotes-*.apinotesc | xargs touch -a -t 201101010000 -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "APINotes.timestamp" @@ -30,13 +30,13 @@ // HeaderLib file, because the pruning mechanism should fire and // HeaderLib is both old and not used. // RUN: touch -m -a -t 201101010000 %t/APINotesCache/APINotes.timestamp -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: ls %t/APINotesCache | not grep "APINotes-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "APINotes.timestamp" // Run Clang. This should generated the cached versions of both and a timestamp. -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB // RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" // RUN: ls %t/APINotesCache | grep "APINotes.timestamp" diff --git a/clang/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c index 1d5939bf92e2c..36507f1b9724b 100644 --- a/clang/test/APINotes/nullability.c +++ b/clang/test/APINotes/nullability.c @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index 486b2c5f366cc..377256f0bec96 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/objc_designated_inits.m b/clang/test/APINotes/objc_designated_inits.m index bbb50ba1a74bc..1df8cf80edd0e 100644 --- a/clang/test/APINotes/objc_designated_inits.m +++ b/clang/test/APINotes/objc_designated_inits.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/search-order.m b/clang/test/APINotes/search-order.m new file mode 100644 index 0000000000000..2c667be38d29b --- /dev/null +++ b/clang/test/APINotes/search-order.m @@ -0,0 +1,25 @@ +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_SEARCH_PATH=1 -verify + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify + +@import SomeOtherKit; + +void test(A *a) { +#if FROM_FRAMEWORK + [a methodA]; // expected-error{{unavailable}} + [a methodB]; + + // expected-note@SomeOtherKit/SomeOtherKit.h:5{{'methodA' has been explicitly marked unavailable here}} +#elif FROM_SEARCH_PATH + [a methodA]; + [a methodB]; // expected-error{{unavailable}} + + // expected-note@SomeOtherKit/SomeOtherKit.h:6{{'methodB' has been explicitly marked unavailable here}} +#else +# error Not something we need to test +#endif +} From 10630737443b3bc2c8fe4ad9ebee9ebfa5000dfe Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 21:48:15 -0700 Subject: [PATCH 089/582] [API Notes] Load both public and private API notes when they are present. Finishes rdar://problem/28511719. apple-llvm-split-commit: f3843ce220dc99d4ec723812950774e80b06c4e8 apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesManager.h | 40 ++++--- clang/lib/APINotes/APINotesManager.cpp | 106 +++++++++++------- clang/lib/Frontend/CompilerInstance.cpp | 5 +- clang/lib/Sema/SemaAPINotes.cpp | 27 ++--- .../APINotes/SomeKit.apinotes | 4 - clang/test/APINotes/cache.m | 1 - clang/test/APINotes/nullability.m | 2 + 7 files changed, 103 insertions(+), 82 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index e3cc4dff3a1b8..a300c14ee9dd3 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -55,8 +55,11 @@ class APINotesManager { /// source file from which an entity was declared. bool ImplicitAPINotes; - /// The API notes reader for the current module. - std::unique_ptr<APINotesReader> CurrentModuleReader; + /// API notes readers for the current module. + /// + /// There can be up to two of these, one for public headers and one + /// for private headers. + APINotesReader *CurrentModuleReaders[2] = { nullptr, nullptr }; /// Whether we have already pruned the API notes cache. bool PrunedCache; @@ -82,13 +85,12 @@ class APINotesManager { bool loadAPINotes(const DirectoryEntry *HeaderDir, const FileEntry *APINotesFile); - /// Look for API notes relative to the given directory, adjusting - /// for whether it is a framework directory, and with the base - /// filename. + /// Look for API notes in the given directory. /// /// This might find either a binary or source API notes. - const FileEntry *findAPINotes(const DirectoryEntry *directory, - bool isFramework, StringRef filename); + const FileEntry *findAPINotesFile(const DirectoryEntry *directory, + StringRef filename, + bool wantPublic = true); /// Attempt to load API notes for the given framework. /// @@ -114,18 +116,20 @@ class APINotesManager { /// \param searchPaths The paths in which we should search for API notes /// for the current module. /// - /// \returns the file entry for the API notes file loaded, or nullptr if - /// no API notes were found. - const FileEntry *loadCurrentModuleAPINotes(const Module *module, - bool lookInModule, - ArrayRef<std::string> searchPaths); - - /// Find the API notes reader that corresponds to the given source location. - APINotesReader *findAPINotes(SourceLocation Loc); - - APINotesReader *getCurrentModuleReader() { - return CurrentModuleReader.get(); + /// \returns true if API notes were successfully loaded, \c false otherwise. + bool loadCurrentModuleAPINotes(const Module *module, + bool lookInModule, + ArrayRef<std::string> searchPaths); + + /// Retrieve the set of API notes readers for the current module. + ArrayRef<APINotesReader *> getCurrentModuleReaders() const { + unsigned numReaders = static_cast<unsigned>(CurrentModuleReaders[0] != nullptr) + + static_cast<unsigned>(CurrentModuleReaders[1] != nullptr); + return llvm::makeArrayRef(CurrentModuleReaders).slice(0, numReaders); } + + /// Find the API notes readers that correspond to the given source location. + llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc); }; } // end namespace api_notes diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index be259663ed031..78ee755655a4d 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -63,6 +63,9 @@ APINotesManager::~APINotesManager() { delete reader; } } + + delete CurrentModuleReaders[0]; + delete CurrentModuleReaders[1]; } /// \brief Write a new timestamp file with the given path. @@ -275,36 +278,31 @@ bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, return true; } -const FileEntry *APINotesManager::findAPINotes(const DirectoryEntry *directory, - bool isFramework, - StringRef basename) { +const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directory, + StringRef basename, + bool wantPublic) { FileManager &fileMgr = SourceMgr.getFileManager(); llvm::SmallString<128> path; path += directory->getName(); - if (isFramework) { - llvm::sys::path::append(path, "APINotes"); - auto apinotesDir = fileMgr.getDirectory(path); - if (!apinotesDir) return nullptr; - - // Find the API notes within this directory. - return findAPINotes(apinotesDir, /*isFramework=*/false, basename); - } - unsigned pathLen = path.size(); + StringRef basenameSuffix = ""; + if (!wantPublic) basenameSuffix = "_private"; + // Look for a binary API notes file. llvm::sys::path::append(path, - llvm::Twine(basename) + "." + BINARY_APINOTES_EXTENSION); + llvm::Twine(basename) + basenameSuffix + "." + BINARY_APINOTES_EXTENSION); if (const FileEntry *binaryFile = fileMgr.getFile(path)) return binaryFile; + // Go back to the original path. path.resize(pathLen); // Look for the source API notes file. llvm::sys::path::append(path, - llvm::Twine(basename) + "." + SOURCE_APINOTES_EXTENSION); + llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION); return fileMgr.getFile(path); } @@ -358,11 +356,11 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( return HeaderDir; } -const FileEntry *APINotesManager::loadCurrentModuleAPINotes( +bool APINotesManager::loadCurrentModuleAPINotes( const Module *module, bool lookInModule, ArrayRef<std::string> searchPaths) { - assert(!CurrentModuleReader && + assert(!CurrentModuleReaders[0] && "Already loaded API notes for the current module?"); FileManager &fileMgr = SourceMgr.getFileManager(); @@ -370,48 +368,76 @@ const FileEntry *APINotesManager::loadCurrentModuleAPINotes( // First, look relative to the module itself. if (lookInModule) { - if (auto file = findAPINotes(module->Directory, module->IsFramework, - moduleName)) { - CurrentModuleReader = loadAPINotes(file); - return CurrentModuleReader ? file : nullptr; + bool foundAny = false; + unsigned numReaders = 0; + + // Local function to try loading an API notes file in the given directory. + auto tryAPINotes = [&](const DirectoryEntry *dir, bool wantPublic) { + if (auto file = findAPINotesFile(dir, moduleName, wantPublic)) { + foundAny = true; + + // Try to load the API notes file. + CurrentModuleReaders[numReaders] = loadAPINotes(file).release(); + if (CurrentModuleReaders[numReaders]) + ++numReaders; + } + }; + + if (module->IsFramework) { + // For frameworks, we search in the "APINotes" subdirectory. + llvm::SmallString<128> path; + path += module->Directory->getName(); + llvm::sys::path::append(path, "APINotes"); + if (auto apinotesDir = fileMgr.getDirectory(path)) { + tryAPINotes(apinotesDir, /*wantPublic=*/true); + tryAPINotes(apinotesDir, /*wantPublic=*/false); + } + } else { + tryAPINotes(module->Directory, /*wantPublic=*/true); + tryAPINotes(module->Directory, /*wantPublic=*/false); } + + if (foundAny) + return numReaders > 0; } // Second, look for API notes for this module in the module API // notes search paths. for (const auto &searchPath : searchPaths) { if (auto searchDir = fileMgr.getDirectory(searchPath)) { - if (auto file = findAPINotes(searchDir, /*isFramework=*/false, - moduleName)) { - CurrentModuleReader = loadAPINotes(file); - return CurrentModuleReader ? file : nullptr; + if (auto file = findAPINotesFile(searchDir, moduleName)) { + CurrentModuleReaders[0] = loadAPINotes(file).release(); + return !getCurrentModuleReaders().empty(); } } } // Didn't find any API notes. - return nullptr; + return false; } -APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { - // If there is a reader for the current module, return it. - if (CurrentModuleReader) return CurrentModuleReader.get(); +llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocation Loc) { + llvm::SmallVector<APINotesReader *, 2> Results; + + // If there are readers for the current module, return them. + if (!getCurrentModuleReaders().empty()) { + Results.append(getCurrentModuleReaders().begin(), getCurrentModuleReaders().end()); + return Results; + } // If we're not allowed to implicitly load API notes files, we're done. - if (!ImplicitAPINotes) return nullptr; + if (!ImplicitAPINotes) return Results; // If we don't have source location information, we're done. - if (Loc.isInvalid()) return nullptr; + if (Loc.isInvalid()) return Results; // API notes are associated with the expansion location. Retrieve the // file for this location. SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc); FileID ID = SourceMgr.getFileID(ExpansionLoc); - if (ID.isInvalid()) - return nullptr; + if (ID.isInvalid()) return Results; const FileEntry *File = SourceMgr.getFileEntryForID(ID); - if (!File) - return nullptr; + if (!File) return Results; // Look for API notes in the directory corresponding to this file, or one of // its its parent directories. @@ -420,7 +446,6 @@ APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { llvm::SetVector<const DirectoryEntry *, SmallVector<const DirectoryEntry *, 4>, llvm::SmallPtrSet<const DirectoryEntry *, 4>> DirsVisited; - APINotesReader *Result = nullptr; do { // Look for an API notes reader for this header search directory. auto Known = Readers.find(Dir); @@ -437,7 +462,8 @@ APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { } // We have the answer. - Result = Known->second.dyn_cast<APINotesReader *>(); + if (auto Reader = Known->second.dyn_cast<APINotesReader *>()) + Results.push_back(Reader); break; } @@ -473,7 +499,8 @@ APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { } // Grab the result. - Result = Readers[Dir].dyn_cast<APINotesReader *>();; + if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>()) + Results.push_back(Reader); break; } } else { @@ -489,7 +516,8 @@ APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { if (const FileEntry *APINotesFile = FileMgr.getFile(APINotesPath)) { if (!loadAPINotes(Dir, APINotesFile)) { ++NumHeaderAPINotes; - Result = Readers[Dir].dyn_cast<APINotesReader *>(); + if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>()) + Results.push_back(Reader); break; } } @@ -519,5 +547,5 @@ APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { Readers[Visited] = Dir; } - return Result; + return Results; } diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 9daec8baa6d8b..43d89485d8ab5 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -548,10 +548,11 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, getLangOpts().APINotesModules, getAPINotesOpts().ModuleSearchPaths); // Check for any attributes we should add to the module - if (auto curReader = TheSema->APINotes.getCurrentModuleReader()) { + for (auto reader : TheSema->APINotes.getCurrentModuleReaders()) { // swift_infer_import_as_member - if (curReader->getModuleOptions().SwiftInferImportAsMember) { + if (reader->getModuleOptions().SwiftInferImportAsMember) { currentModule->IsSwiftInferImportAsMember = true; + break; } } } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index a0c5ca88236ca..29861d140f242 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -327,8 +327,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (D->getDeclContext()->isFileContext()) { // Global variables. if (auto VD = dyn_cast<VarDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupGlobalVariable(VD->getName())) { ::ProcessAPINotes(*this, VD, *Info); } @@ -340,8 +339,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Global functions. if (auto FD = dyn_cast<FunctionDecl>(D)) { if (FD->getDeclName().isIdentifier()) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupGlobalFunction(FD->getName())) { ::ProcessAPINotes(*this, FD, *Info); } @@ -353,8 +351,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C classes. if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupObjCClass(Class->getName())) { ::ProcessAPINotes(*this, Class, Info->second); } @@ -365,8 +362,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C protocols. if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupObjCProtocol(Protocol->getName())) { ::ProcessAPINotes(*this, Protocol, Info->second); } @@ -377,8 +373,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Tags if (auto Tag = dyn_cast<TagDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupTag(Tag->getName())) { ::ProcessAPINotes(*this, Tag, *Info); } @@ -389,8 +384,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Typedefs if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupTypedef(Typedef->getName())) { ::ProcessAPINotes(*this, Typedef, *Info); } @@ -405,8 +399,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Enumerators. if (D->getDeclContext()->getRedeclContext()->isFileContext()) { if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { ::ProcessAPINotes(*this, EnumConstant, *Info); } @@ -461,8 +454,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C methods. if (auto Method = dyn_cast<ObjCMethodDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Context = GetContext(Reader)) { // Map the selector. Selector Sel = Method->getSelector(); @@ -488,8 +480,7 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C properties. if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { - if (api_notes::APINotesReader *Reader - = APINotes.findAPINotes(D->getLocation())) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Context = GetContext(Reader)) { bool isInstanceProperty = (Property->getPropertyAttributesAsWritten() & diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index 51b571f67032d..e79a210e39aaf 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -31,7 +31,3 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true -Protocols: - - Name: InternalProtocol - Availability: none - AvailabilityMsg: "not for you" diff --git a/clang/test/APINotes/cache.m b/clang/test/APINotes/cache.m index 05fb81c72dcf3..b87bdf14fecc8 100644 --- a/clang/test/APINotes/cache.m +++ b/clang/test/APINotes/cache.m @@ -30,4 +30,3 @@ int main() { return 0; } - diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index 377256f0bec96..fc149b465ea35 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -17,6 +17,8 @@ int main() { [a setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} [A setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + [a setInternalProperty: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + return 0; } From 16a0f3116a9620308ae602e31af23429bea282f3 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 23:15:55 -0700 Subject: [PATCH 090/582] [API Notes] Ensure that modules get rebuilt when API notes change. The approach here is fairly simple: treat API notes source files as "sources" in the source manager, so that module files automatically check whether they have changed and rebuild when they do. Also, have API notes files compiled into the cache keep track of the size and modification time of the API notes source files from which they were generated. Fixes rdar://problem/25639554. apple-llvm-split-commit: b07d2eed96ce5bba8cba80f236e87db776a48c63 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 4 ++ clang/include/clang/APINotes/APINotesWriter.h | 8 ++- .../clang/APINotes/APINotesYAMLCompiler.h | 4 ++ clang/lib/APINotes/APINotesFormat.h | 11 +++- clang/lib/APINotes/APINotesManager.cpp | 31 +++++++---- clang/lib/APINotes/APINotesReader.cpp | 13 +++++ clang/lib/APINotes/APINotesWriter.cpp | 14 ++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 20 ++++--- clang/test/APINotes/module-cache.m | 55 +++++++++++++++++++ clang/tools/driver/apinotes_main.cpp | 2 +- 10 files changed, 137 insertions(+), 25 deletions(-) create mode 100644 clang/test/APINotes/module-cache.m diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index cc6dafb863f65..c64d6c31624bb 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -50,6 +50,10 @@ class APINotesReader { /// notes. StringRef getModuleName() const; + /// Retrieve the size and modification time of the source file from + /// which this API notes file was created, if known. + Optional<std::pair<off_t, time_t>> getSourceFileSizeAndModTime() const; + /// Retrieve the module options ModuleOptions getModuleOptions() const; diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index cc93a5f5899b6..4bf3ce1371641 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -23,6 +23,9 @@ namespace llvm { } namespace clang { + +class FileEntry; + namespace api_notes { /// A class that writes API notes data to a binary representation that can be @@ -32,8 +35,9 @@ class APINotesWriter { Implementation &Impl; public: - /// Create a new API notes writer with the given module name. - APINotesWriter(StringRef moduleName); + /// Create a new API notes writer with the given module name and + /// (optional) source file. + APINotesWriter(StringRef moduleName, const FileEntry *sourceFile); ~APINotesWriter(); APINotesWriter(const APINotesWriter &) = delete; diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h index f459e079d25bd..508da65993e7d 100644 --- a/clang/include/clang/APINotes/APINotesYAMLCompiler.h +++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h @@ -23,6 +23,9 @@ namespace llvm { } namespace clang { + +class FileEntry; + namespace api_notes { enum class ActionType { @@ -42,6 +45,7 @@ namespace api_notes { /// Converts API notes from YAML format to binary format. bool compileAPINotes(llvm::StringRef yamlInput, + const FileEntry *sourceFile, llvm::raw_ostream &os, OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler = nullptr, diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 1db2380462f49..542f90831a580 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 14; // Objective-C class properties +const uint16_t VERSION_MINOR = 15; // source file info using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; @@ -106,7 +106,8 @@ namespace control_block { enum { METADATA = 1, MODULE_NAME = 2, - MODULE_OPTIONS = 3 + MODULE_OPTIONS = 3, + SOURCE_FILE = 4, }; using MetadataLayout = BCRecordLayout< @@ -124,6 +125,12 @@ namespace control_block { MODULE_OPTIONS, BCFixed<1> // SwiftInferImportAsMember >; + + using SourceFileLayout = BCRecordLayout< + SOURCE_FILE, + BCVBR<16>, // file size + BCVBR<16> // creation time + >; } namespace identifier_block { diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 78ee755655a4d..8dc337827e6c9 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -181,11 +181,16 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { /*cacheFailure=*/false)) { // Load the file contents. if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { - // Make sure the file is up-to-date. - if (compiledFile->getModificationTime() - >= apiNotesFile->getModificationTime()) { - // Load the file. - if (auto reader = APINotesReader::get(std::move(buffer.get()))) { + // Load the file. + if (auto reader = APINotesReader::get(std::move(buffer.get()))) { + bool outOfDate = false; + if (auto sizeAndModTime = reader->getSourceFileSizeAndModTime()) { + if (sizeAndModTime->first != apiNotesFile->getSize() || + sizeAndModTime->second != apiNotesFile->getModificationTime()) + outOfDate = true; + } + + if (!outOfDate) { // Success. ++NumBinaryCacheHits; return reader; @@ -202,13 +207,15 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { } // Open the source file. - auto buffer = fileMgr.getBufferForFile(apiNotesFile); - if (!buffer) return nullptr; + auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User); + auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation()); + if (!sourceBuffer) return nullptr; // Compile the API notes source into a buffer. // FIXME: Either propagate OSType through or, better yet, improve the binary // APINotes format to maintain complete availability information. llvm::SmallVector<char, 1024> apiNotesBuffer; + std::unique_ptr<llvm::MemoryBuffer> compiledBuffer; { SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(), diag::err_apinotes_message, @@ -216,7 +223,8 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { diag::note_apinotes_message, apiNotesFile); llvm::raw_svector_ostream OS(apiNotesBuffer); - if (api_notes::compileAPINotes(buffer.get()->getBuffer(), + if (api_notes::compileAPINotes(sourceBuffer->getBuffer(), + SourceMgr.getFileEntryForID(sourceFileID), OS, api_notes::OSType::Absent, srcMgrAdapter.getDiagHandler(), @@ -224,7 +232,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { return nullptr; // Make a copy of the compiled form into the buffer. - buffer = llvm::MemoryBuffer::getMemBufferCopy( + compiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( StringRef(apiNotesBuffer.data(), apiNotesBuffer.size())); } @@ -247,7 +255,8 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { bool hadError; { llvm::raw_fd_ostream out(temporaryFD, /*shouldClose=*/true); - out.write(buffer.get()->getBufferStart(), buffer.get()->getBufferSize()); + out.write(compiledBuffer.get()->getBufferStart(), + compiledBuffer.get()->getBufferSize()); out.flush(); hadError = out.has_error(); @@ -261,7 +270,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { } // Load the binary form we just compiled. - auto reader = APINotesReader::get(std::move(*buffer)); + auto reader = APINotesReader::get(std::move(compiledBuffer)); assert(reader && "Could not load the API notes we just generated?"); return reader; } diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 2b49125fffc01..9618e70d5f84a 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -586,6 +586,10 @@ class APINotesReader::Implementation { /// The name of the module that we read from the control block. std::string ModuleName; + // The size and modification time of the source file from + // which this API notes file was created, if known. + Optional<std::pair<off_t, time_t>> SourceFileSizeAndModTime; + /// Various options and attributes for the module ModuleOptions ModuleOpts; @@ -766,6 +770,10 @@ bool APINotesReader::Implementation::readControlBlock( ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0; break; + case control_block::SOURCE_FILE: + SourceFileSizeAndModTime = { scratch[0], scratch[1] }; + break; + default: // Unknown metadata record, possibly for use by a future version of the // module format. @@ -1471,6 +1479,11 @@ StringRef APINotesReader::getModuleName() const { return Impl.ModuleName; } +Optional<std::pair<off_t, time_t>> +APINotesReader::getSourceFileSizeAndModTime() const { + return Impl.SourceFileSizeAndModTime; +} + ModuleOptions APINotesReader::getModuleOptions() const { return Impl.ModuleOpts; } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index b0a46504e58ac..2df5e76f7c8d4 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesWriter.h" #include "APINotesFormat.h" +#include "clang/Basic/FileManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallString.h" @@ -43,6 +44,10 @@ class APINotesWriter::Implementation { /// The name of the module std::string ModuleName; + /// The source file from which this binary representation was + /// created, if known. + const FileEntry *SourceFile; + bool SwiftInferImportAsMember = false; /// Information about Objective-C contexts (classes or protocols). @@ -222,6 +227,12 @@ void APINotesWriter::Implementation::writeControlBlock( control_block::ModuleOptionsLayout moduleOptions(writer); moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember); } + + if (SourceFile) { + control_block::SourceFileLayout sourceFile(writer); + sourceFile.emit(ScratchRecord, SourceFile->getSize(), + SourceFile->getModificationTime()); + } } namespace { @@ -1006,10 +1017,11 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { os.flush(); } -APINotesWriter::APINotesWriter(StringRef moduleName) +APINotesWriter::APINotesWriter(StringRef moduleName, const FileEntry *sourceFile) : Impl(*new Implementation) { Impl.ModuleName = moduleName; + Impl.SourceFile = sourceFile; } APINotesWriter::~APINotesWriter() { diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index b9640954f58fa..0d30731194c41 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -501,6 +501,7 @@ namespace { class YAMLConverter { const Module &TheModule; + const FileEntry *SourceFile; APINotesWriter *Writer; OSType TargetOS; llvm::raw_ostream &OS; @@ -519,11 +520,12 @@ namespace { public: YAMLConverter(const Module &module, - OSType targetOS, - llvm::raw_ostream &os, - llvm::SourceMgr::DiagHandlerTy diagHandler, - void *diagHandlerCtxt) : - TheModule(module), Writer(0), TargetOS(targetOS), OS(os), + const FileEntry *sourceFile, + OSType targetOS, + llvm::raw_ostream &os, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt) : + TheModule(module), SourceFile(sourceFile), Writer(0), TargetOS(targetOS), OS(os), DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt), ErrorOccured(false) {} @@ -739,7 +741,7 @@ namespace { // Set up the writer. // FIXME: This is kindof ugly. - APINotesWriter writer(TheModule.Name); + APINotesWriter writer(TheModule.Name, SourceFile); Writer = &writer; // Write all classes. @@ -877,13 +879,14 @@ namespace { } static bool compile(const Module &module, + const FileEntry *sourceFile, llvm::raw_ostream &os, api_notes::OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt){ using namespace api_notes; - YAMLConverter c(module, targetOS, os, diagHandler, diagHandlerCtxt); + YAMLConverter c(module, sourceFile, targetOS, os, diagHandler, diagHandlerCtxt); return c.convertModule(); } @@ -905,6 +908,7 @@ static void printDiagnostic(const llvm::SMDiagnostic &diag, void *context) { } bool api_notes::compileAPINotes(StringRef yamlInput, + const FileEntry *sourceFile, llvm::raw_ostream &os, OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler, @@ -918,7 +922,7 @@ bool api_notes::compileAPINotes(StringRef yamlInput, if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt)) return true; - return compile(module, os, targetOS, diagHandler, diagHandlerCtxt); + return compile(module, sourceFile, os, targetOS, diagHandler, diagHandlerCtxt); } namespace { diff --git a/clang/test/APINotes/module-cache.m b/clang/test/APINotes/module-cache.m new file mode 100644 index 0000000000000..dce003b3c7f86 --- /dev/null +++ b/clang/test/APINotes/module-cache.m @@ -0,0 +1,55 @@ +// RUN: rm -rf %t + +// Set up a directory with API notes +// RUN: mkdir -p %t/APINotes +// RUN: cp %S/Inputs/APINotes/SomeOtherKit.apinotes %t/APINotes/SomeOtherKit.apinotes + +// First build: check that 'methodB' is unavailable but 'methodA' is available. +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log +// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/before.log +// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log + +// Do it again; now we're using caches. +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log +// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/before.log +// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log + +// Change the API notes file. +// RUN: echo ' - Selector: "methodA"' >> %t/APINotes/SomeOtherKit.apinotes +// RUN: echo ' MethodKind: Instance' >> %t/APINotes/SomeOtherKit.apinotes +// RUN: echo ' Availability: none' >> %t/APINotes/SomeOtherKit.apinotes +// RUN: echo ' AvailabilityMsg: "not here either"' >> %t/APINotes/SomeOtherKit.apinotes + +// Build again: check that both methods are now unavailable and that the module rebuilt. +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log +// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/after.log +// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log + +// Run the build again: check that both methods are now unavailable +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log +// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/after.log +// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log + +@import SomeOtherKit; + +void test(A *a) { + // CHECK-METHODA: error: 'methodA' is unavailable: not here either + [a methodA]; + + // CHECK-METHODB: error: 'methodB' is unavailable: anything but this + [a methodB]; +} + +// CHECK-REBUILD: remark: building module{{.*}}SomeOtherKit + +// CHECK-WITHOUT-REBUILD-NOT: remark: building module{{.*}}SomeOtherKit + +// CHECK-ONE-ERROR: 1 error generated. +// CHECK-TWO-ERRORS: 2 errors generated. + diff --git a/clang/tools/driver/apinotes_main.cpp b/clang/tools/driver/apinotes_main.cpp index cbd8045922d2b..e4ccc2e7f3244 100644 --- a/clang/tools/driver/apinotes_main.cpp +++ b/clang/tools/driver/apinotes_main.cpp @@ -119,7 +119,7 @@ int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0, llvm::raw_fd_ostream os(OutputFilename, EC, llvm::sys::fs::OpenFlags::F_None); - if (api_notes::compileAPINotes(input, os, targetOS)) + if (api_notes::compileAPINotes(input, /*sourceFile=*/nullptr, os, targetOS)) return 1; os.flush(); From 682653710c0291238b21ced3cd2eb1bc4ab4f88c Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 27 Sep 2016 23:28:52 -0700 Subject: [PATCH 091/582] [API Notes] Ensure that modules get rebuilt when binary API notes change. Extend the solution for ensuring that modules get rebuilt to cases where the inputs are precompiled binary API notes, rather than just source API notes that get precompiled into the cache. Fixes the rest of rdar://problem/25639554. apple-llvm-split-commit: 35d7c61210eed20096bbd9488f89a8eb25b4ff51 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 10 +++++- clang/lib/APINotes/APINotesManager.cpp | 6 ++-- clang/lib/APINotes/APINotesReader.cpp | 31 +++++++++++++--- clang/lib/Frontend/CompilerInvocation.cpp | 4 +-- clang/test/APINotes/module-cache.m | 35 +++++++++++++++++++ 5 files changed, 76 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index c64d6c31624bb..aa88bac062662 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -31,7 +31,8 @@ class APINotesReader { Implementation &Impl; - APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, bool &failed); + APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, + bool &failed); public: /// Create a new API notes reader from the given member buffer, which @@ -41,6 +42,13 @@ class APINotesReader { static std::unique_ptr<APINotesReader> get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer); + /// Create a new API notes reader from the given member buffer, which + /// contains the contents of a binary API notes file. + /// + /// \returns the new API notes reader, or null if an error occurred. + static std::unique_ptr<APINotesReader> + getUnmanaged(llvm::MemoryBuffer *inputBuffer); + ~APINotesReader(); APINotesReader(const APINotesReader &) = delete; diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 8dc337827e6c9..834d7e1f72f41 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -146,12 +146,14 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName); if (!apiNotesFileExt.empty() && apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { + auto compiledFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User); + // Load the file. - auto buffer = fileMgr.getBufferForFile(apiNotesFile); + auto buffer = SourceMgr.getBuffer(compiledFileID, SourceLocation()); if (!buffer) return nullptr; // Load the binary form. - return APINotesReader::get(std::move(buffer.get())); + return APINotesReader::getUnmanaged(buffer); } // If we haven't pruned the API notes cache yet during this execution, do diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 9618e70d5f84a..0c93756a0053e 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -578,7 +578,10 @@ namespace { class APINotesReader::Implementation { public: /// The input buffer for the API notes data. - std::unique_ptr<llvm::MemoryBuffer> InputBuffer; + llvm::MemoryBuffer *InputBuffer; + + /// Whether we own the input buffer. + bool OwnsInputBuffer; /// The reader attached to \c InputBuffer. llvm::BitstreamReader InputReader; @@ -1321,14 +1324,16 @@ bool APINotesReader::Implementation::readTypedefBlock( return false; } -APINotesReader::APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, - bool &failed) +APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, + bool ownsInputBuffer, + bool &failed) : Impl(*new Implementation) { failed = false; // Initialize the input buffer. - Impl.InputBuffer = std::move(inputBuffer); + Impl.InputBuffer = inputBuffer; + Impl.OwnsInputBuffer = ownsInputBuffer; Impl.InputReader.init( reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferStart()), reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferEnd())); @@ -1461,6 +1466,9 @@ APINotesReader::APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, } APINotesReader::~APINotesReader() { + if (Impl.OwnsInputBuffer) + delete Impl.InputBuffer; + delete &Impl; } @@ -1468,7 +1476,20 @@ std::unique_ptr<APINotesReader> APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer) { bool failed = false; std::unique_ptr<APINotesReader> - reader(new APINotesReader(std::move(inputBuffer), failed)); + reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true, + failed)); + if (failed) + return nullptr; + + return reader; +} + +std::unique_ptr<APINotesReader> +APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer) { + bool failed = false; + std::unique_ptr<APINotesReader> + reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false, + failed)); if (failed) return nullptr; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 63f7af79aed58..0fc9c7fa1d5b6 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2423,8 +2423,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; - // -fapinotes requires -fapinotes-cache-path=<directory>. - if (LangOpts.APINotes && + // -fapinotes and -fapinotes-modules requires -fapinotes-cache-path=<directory>. + if ((LangOpts.APINotes || LangOpts.APINotesModules) && Res.getFileSystemOpts().APINotesCachePath.empty()) { Diags.Report(diag::err_no_apinotes_cache_path); Success = false; diff --git a/clang/test/APINotes/module-cache.m b/clang/test/APINotes/module-cache.m index dce003b3c7f86..2324697a0f701 100644 --- a/clang/test/APINotes/module-cache.m +++ b/clang/test/APINotes/module-cache.m @@ -36,6 +36,41 @@ // RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/after.log // RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log +// Set up a directory with pre-compiled API notes. +// RUN: mkdir -p %t/CompiledAPINotes +// RUN: rm -rf %t/ModulesCache +// RUN: rm -rf %t/APINotesCache +// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %S/Inputs/APINotes/SomeOtherKit.apinotes + +// First build: check that 'methodB' is unavailable but 'methodA' is available. +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log +// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-before.log +// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log + +// Do it again; now we're using caches. +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log +// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-before.log +// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log + +// Compile a new API notes file to replace the old one. +// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %t/APINotes/SomeOtherKit.apinotes + +// Build again: check that both methods are now unavailable and that the module rebuilt. +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log +// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-after.log +// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log + +// Run the build again: check that both methods are now unavailable +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log +// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-after.log +// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log + @import SomeOtherKit; void test(A *a) { From 14fb7a6aef93e82919e842d07684964bf5cd9a75 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Sun, 2 Oct 2016 21:01:55 -0700 Subject: [PATCH 092/582] [API Notes] Add Swift versioning to API notes files Extend the API notes format with support for specifying API notes at a particular Swift version. A single API notes file has "unversioned" information, followed optionally by more information about the various classes/functions/etc. at a specific Swift version. The intent is to allow API notes to provide backward-compatibility information Parse Swift-versioned API notes, store version API notes in the binary format, and ensure that it round-trips. For now, Clang still only adds attributes based on the unversioned information. This is the first step of rdar://problem/28455809. apple-llvm-split-commit: 16d41c34b0feb962d6974b311597944e74b7027b apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 53 +- clang/include/clang/APINotes/APINotesWriter.h | 46 +- clang/include/clang/Basic/VersionTuple.h | 35 + clang/lib/APINotes/APINotesFormat.h | 21 +- clang/lib/APINotes/APINotesReader.cpp | 635 +++++++++-------- clang/lib/APINotes/APINotesWriter.cpp | 636 ++++++++++-------- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 355 ++++++---- clang/lib/Sema/SemaAPINotes.cpp | 16 +- clang/test/APINotes/Inputs/roundtrip.apinotes | 19 + 9 files changed, 1049 insertions(+), 767 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index aa88bac062662..593e8174d4e08 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -17,6 +17,7 @@ #define LLVM_CLANG_API_NOTES_READER_H #include "clang/APINotes/Types.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/MemoryBuffer.h" #include <memory> @@ -65,21 +66,33 @@ class APINotesReader { /// Retrieve the module options ModuleOptions getModuleOptions() const; + /// Look for the context ID of the given Objective-C class. + /// + /// \param name The name of the class we're looking for. + /// + /// \returns The ID, if known. + Optional<ContextID> lookupObjCClassID(StringRef name); + /// Look for information regarding the given Objective-C class. /// /// \param name The name of the class we're looking for. /// - /// \returns The ID and information about the class, if known. - Optional<std::pair<ContextID, ObjCContextInfo>> - lookupObjCClass(StringRef name); + /// \returns The information about the class, if known. + Optional<ObjCContextInfo> lookupObjCClassInfo(StringRef name); + + /// Look for the context ID of the given Objective-C protocol. + /// + /// \param name The name of the protocol we're looking for. + /// + /// \returns The ID of the protocol, if known. + Optional<ContextID> lookupObjCProtocolID(StringRef name); /// Look for information regarding the given Objective-C protocol. /// /// \param name The name of the protocol we're looking for. /// - /// \returns The ID and information about the protocol, if known. - Optional<std::pair<ContextID, ObjCContextInfo>> - lookupObjCProtocol(StringRef name); + /// \returns The information about the protocol, if known. + Optional<ObjCContextInfo> lookupObjCProtocolInfo(StringRef name); /// Look for information regarding the given Objective-C property in /// the given context. @@ -88,6 +101,7 @@ class APINotesReader { /// \param name The name of the property we're looking for. /// \param isInstance Whether we are looking for an instance property (vs. /// a class property). + /// \param swiftVersion The Swift version to filter for, if any. /// /// \returns Information about the property, if known. Optional<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID, @@ -149,39 +163,48 @@ class APINotesReader { /// Visit an Objective-C class. virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info); + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C protocol. virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info); + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C method. virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info); + const ObjCMethodInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C property. virtual void visitObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info); + const ObjCPropertyInfo &info, + VersionTuple swiftVersion); /// Visit a global variable. virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info); + const GlobalVariableInfo &info, + VersionTuple swiftVersion); /// Visit a global function. virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info); + const GlobalFunctionInfo &info, + VersionTuple swiftVersion); /// Visit an enumerator. virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info); + const EnumConstantInfo &info, + VersionTuple swiftVersion); /// Visit a tag. - virtual void visitTag(StringRef name, const TagInfo &info); + virtual void visitTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion); /// Visit a typedef. - virtual void visitTypedef(StringRef name, const TypedefInfo &info); + virtual void visitTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion); }; /// Visit the contents of the API notes file, passing each entity to the diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index 4bf3ce1371641..62defc1f944f7 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -16,6 +16,7 @@ #ifndef LLVM_CLANG_API_NOTES_WRITER_H #define LLVM_CLANG_API_NOTES_WRITER_H +#include "clang/Basic/VersionTuple.h" #include "clang/APINotes/Types.h" namespace llvm { @@ -46,23 +47,17 @@ class APINotesWriter { /// Write the API notes data to the given stream. void writeToStream(llvm::raw_ostream &os); - /// Add information about a specific Objective-C class. + /// Add information about a specific Objective-C class or protocol. /// - /// \param name The name of this class. - /// \param info Information about this class. + /// \param name The name of this class/protocol. + /// \param isClass Whether this is a class (vs. a protocol). + /// \param info Information about this class/protocol. /// - /// \returns the ID of the class, which can be used to add properties and - /// methods to the class. - ContextID addObjCClass(StringRef name, const ObjCContextInfo &info); - - /// Add information about a specific Objective-C protocol. - /// - /// \param name The name of this protocol. - /// \param info Information about this protocol. - /// - /// \returns the ID of the protocol, which can be used to add properties and - /// methods to the protocol. - ContextID addObjCProtocol(StringRef name, const ObjCContextInfo &info); + /// \returns the ID of the class or protocol, which can be used to add + /// properties and methods to the class/protocol. + ContextID addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Add information about a specific Objective-C property. /// @@ -71,7 +66,8 @@ class APINotesWriter { /// \param info Information about this property. void addObjCProperty(ContextID contextID, StringRef name, bool isInstanceProperty, - const ObjCPropertyInfo &info); + const ObjCPropertyInfo &info, + VersionTuple swiftVersion); /// Add information about a specific Objective-C method. /// @@ -81,37 +77,43 @@ class APINotesWriter { /// (vs. a class method). /// \param info Information about this method. void addObjCMethod(ContextID contextID, ObjCSelectorRef selector, - bool isInstanceMethod, const ObjCMethodInfo &info); + bool isInstanceMethod, const ObjCMethodInfo &info, + VersionTuple swiftVersion); /// Add information about a global variable. /// /// \param name The name of this global variable. /// \param info Information about this global variable. - void addGlobalVariable(StringRef name, const GlobalVariableInfo &info); + void addGlobalVariable(StringRef name, const GlobalVariableInfo &info, + VersionTuple swiftVersion); /// Add information about a global function. /// /// \param name The name of this global function. /// \param info Information about this global function. - void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info, + VersionTuple swiftVersion); /// Add information about an enumerator. /// /// \param name The name of this enumerator. /// \param info Information about this enumerator. - void addEnumConstant(StringRef name, const EnumConstantInfo &info); + void addEnumConstant(StringRef name, const EnumConstantInfo &info, + VersionTuple swiftVersion); /// Add information about a tag (struct/union/enum/C++ class). /// /// \param name The name of this tag. /// \param info Information about this tag. - void addTag(StringRef name, const TagInfo &info); + void addTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion); /// Add information about a typedef. /// /// \param name The name of this typedef. /// \param info Information about this typedef. - void addTypedef(StringRef name, const TypedefInfo &info); + void addTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion); /// Add module options void addModuleOptions(ModuleOptions opts); diff --git a/clang/include/clang/Basic/VersionTuple.h b/clang/include/clang/Basic/VersionTuple.h index da3b01903ed93..07315f008cdde 100644 --- a/clang/include/clang/Basic/VersionTuple.h +++ b/clang/include/clang/Basic/VersionTuple.h @@ -17,6 +17,7 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/DenseMapInfo.h" #include <string> #include <tuple> @@ -70,6 +71,9 @@ class VersionTuple { return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0; } + /// Whether this is a non-empty version tuple. + explicit operator bool () const { return !empty(); } + /// \brief Retrieve the major version number. unsigned getMajor() const { return Major; } @@ -165,4 +169,35 @@ class VersionTuple { raw_ostream& operator<<(raw_ostream &Out, const VersionTuple &V); } // end namespace clang + +namespace llvm { + // Provide DenseMapInfo for version tuples. + template<> + struct DenseMapInfo<clang::VersionTuple> { + static inline clang::VersionTuple getEmptyKey() { + return clang::VersionTuple(0x7FFFFFFF); + } + static inline clang::VersionTuple getTombstoneKey() { + return clang::VersionTuple(0x7FFFFFFE); + } + static unsigned getHashValue(const clang::VersionTuple& value) { + unsigned result = value.getMajor(); + if (auto minor = value.getMinor()) + result = combineHashValue(result, *minor); + if (auto subminor = value.getSubminor()) + result = combineHashValue(result, *subminor); + if (auto build = value.getBuild()) + result = combineHashValue(result, *build); + + return result; + } + + static bool isEqual(const clang::VersionTuple &lhs, + const clang::VersionTuple &rhs) { + return lhs == rhs; + } + }; + +} // end namespace llvm + #endif // LLVM_CLANG_BASIC_VERSIONTUPLE_H diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 542f90831a580..9b68cf292386d 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 15; // source file info +const uint16_t VERSION_MINOR = 16; // versioned API notes. using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; @@ -60,8 +60,8 @@ enum BlockID { /// The identifier data block, which maps identifier strings to IDs. IDENTIFIER_BLOCK_ID, - /// The Objective-C class data block, which maps Objective-C class - /// names to information about the class. + /// The Objective-C context data block, which contains information about + /// Objective-C classes and protocols. OBJC_CONTEXT_BLOCK_ID, /// The Objective-C property data block, which maps Objective-C @@ -147,13 +147,20 @@ namespace identifier_block { namespace objc_context_block { enum { - OBJC_CONTEXT_DATA = 1, + OBJC_CONTEXT_ID_DATA = 1, + OBJC_CONTEXT_INFO_DATA = 2, }; - using ObjCContextDataLayout = BCRecordLayout< - OBJC_CONTEXT_DATA, // record ID + using ObjCContextIDLayout = BCRecordLayout< + OBJC_CONTEXT_ID_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) - BCBlob // map from ObjC class names (as IDs) to ObjC class information + BCBlob // map from ObjC class names/protocol (as IDs) to context IDs + >; + + using ObjCContextInfoLayout = BCRecordLayout< + OBJC_CONTEXT_INFO_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from ObjC context IDs to context information. >; } diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 0c93756a0053e..71e1e411602b2 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -28,6 +28,77 @@ using namespace llvm::support; using namespace llvm; namespace { + /// Deserialize a version tuple. + VersionTuple readVersionTuple(const uint8_t *&data) { + uint8_t numVersions = (*data++) & 0x03; + + unsigned major = endian::readNext<uint32_t, little, unaligned>(data); + if (numVersions == 0) + return VersionTuple(major); + + unsigned minor = endian::readNext<uint32_t, little, unaligned>(data); + if (numVersions == 1) + return VersionTuple(major, minor); + + unsigned subminor = endian::readNext<uint32_t, little, unaligned>(data); + if (numVersions == 2) + return VersionTuple(major, minor, subminor); + + unsigned build = endian::readNext<uint32_t, little, unaligned>(data); + return VersionTuple(major, minor, subminor, build); + } + + /// An on-disk hash table whose data is versioned based on the Swift version. + template<typename Derived, typename KeyType, typename UnversionedDataType> + class VersionedTableInfo { + public: + using internal_key_type = KeyType; + using external_key_type = KeyType; + using data_type = SmallVector<std::pair<VersionTuple, UnversionedDataType>, 1>; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast<size_t>(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); + unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); + return { keyLength, dataLength }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + unsigned numElements = endian::readNext<uint16_t, little, unaligned>(data); + data_type result; + result.reserve(numElements); + for (unsigned i = 0; i != numElements; ++i) { + auto version = readVersionTuple(data); + auto dataBefore = data; (void)data; + auto unversionedData = Derived::readUnversioned(key, data); + assert(data != dataBefore + && "Unversioned data reader didn't move pointer"); + result.push_back({version, unversionedData}); + } + return result; + } + }; + + /// Read serialized CommonEntityInfo. void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) { uint8_t unavailableBits = *data++; @@ -109,12 +180,12 @@ namespace { }; /// Used to deserialize the on-disk Objective-C class table. - class ObjCContextTableInfo { + class ObjCContextIDTableInfo { public: // identifier ID, is-protocol using internal_key_type = std::pair<unsigned, char>; using external_key_type = internal_key_type; - using data_type = std::pair<unsigned, ObjCContextInfo>; + using data_type = unsigned; using hash_value_type = size_t; using offset_type = unsigned; @@ -150,16 +221,35 @@ namespace { static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { - data_type result; - result.first = endian::readNext<uint32_t, little, unaligned>(data); - readCommonTypeInfo(data, result.second); - if (*data++) { - result.second.setDefaultNullability(static_cast<NullabilityKind>(*data)); - } - ++data; - result.second.setHasDesignatedInits(*data++); - - return result; + return endian::readNext<uint32_t, little, unaligned>(data); + } + }; + + /// Used to deserialize the on-disk Objective-C property table. + class ObjCContextInfoTableInfo + : public VersionedTableInfo<ObjCContextInfoTableInfo, + unsigned, + ObjCContextInfo> + { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + return endian::readNext<uint32_t, little, unaligned>(data); + } + + static ObjCContextInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + ObjCContextInfo info; + readCommonTypeInfo(data, info); + uint8_t payload = *data++; + + if (payload & 0x01) + info.setHasDesignatedInits(true); + payload = payload >> 1; + + if (payload & 0x4) + info.setDefaultNullability(static_cast<NullabilityKind>(payload&0x03)); + + return info; } }; @@ -173,38 +263,12 @@ namespace { } /// Used to deserialize the on-disk Objective-C property table. - class ObjCPropertyTableInfo { + class ObjCPropertyTableInfo + : public VersionedTableInfo<ObjCPropertyTableInfo, + std::tuple<unsigned, unsigned, char>, + ObjCPropertyInfo> + { public: - // (context ID, name ID, isInstance) - using internal_key_type = std::tuple<unsigned, unsigned, char>; - using external_key_type = internal_key_type; - using data_type = ObjCPropertyInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair<unsigned, unsigned> - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); - unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext<uint32_t, little, unaligned>(data); auto nameID = endian::readNext<uint32_t, little, unaligned>(data); @@ -212,8 +276,8 @@ namespace { return std::make_tuple(classID, nameID, isInstance); } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static ObjCPropertyInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { ObjCPropertyInfo info; readVariableInfo(data, info); return info; @@ -248,40 +312,11 @@ namespace { } /// Used to deserialize the on-disk Objective-C method table. - class ObjCMethodTableInfo { + class ObjCMethodTableInfo + : public VersionedTableInfo<ObjCMethodTableInfo, + std::tuple<unsigned, unsigned, char>, + ObjCMethodInfo> { public: - // (class ID, selector ID, is-instance) - using internal_key_type = std::tuple<unsigned, unsigned, char>; - using external_key_type = internal_key_type; - using data_type = ObjCMethodInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return llvm::hash_combine(std::get<0>(key), - std::get<1>(key), - std::get<2>(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair<unsigned, unsigned> - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); - unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext<uint32_t, little, unaligned>(data); auto selectorID = endian::readNext<uint32_t, little, unaligned>(data); @@ -289,13 +324,18 @@ namespace { return internal_key_type{ classID, selectorID, isInstance }; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static ObjCMethodInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { ObjCMethodInfo info; + uint8_t payload = *data++; + info.Required = payload & 0x01; + payload >>= 1; + info.DesignatedInit = payload & 0x01; + payload >>= 1; + info.FactoryAsInit = payload & 0x03; + payload >>= 2; + readFunctionInfo(data, info); - info.DesignatedInit = endian::readNext<uint8_t, little, unaligned>(data); - info.FactoryAsInit = endian::readNext<uint8_t, little, unaligned>(data); - info.Required = endian::readNext<uint8_t, little, unaligned>(data); return info; } }; @@ -350,44 +390,17 @@ namespace { }; /// Used to deserialize the on-disk global variable table. - class GlobalVariableTableInfo { + class GlobalVariableTableInfo + : public VersionedTableInfo<GlobalVariableTableInfo, unsigned, + GlobalVariableInfo> { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = GlobalVariableInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair<unsigned, unsigned> - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); - unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext<uint32_t, little, unaligned>(data); return nameID; } - - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + + static GlobalVariableInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { GlobalVariableInfo info; readVariableInfo(data, info); return info; @@ -395,44 +408,17 @@ namespace { }; /// Used to deserialize the on-disk global function table. - class GlobalFunctionTableInfo { + class GlobalFunctionTableInfo + : public VersionedTableInfo<GlobalFunctionTableInfo, unsigned, + GlobalFunctionInfo> { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = GlobalFunctionInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair<unsigned, unsigned> - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); - unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext<uint32_t, little, unaligned>(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static GlobalFunctionInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { GlobalFunctionInfo info; readFunctionInfo(data, info); return info; @@ -440,44 +426,17 @@ namespace { }; /// Used to deserialize the on-disk enumerator table. - class EnumConstantTableInfo { + class EnumConstantTableInfo + : public VersionedTableInfo<EnumConstantTableInfo, unsigned, + EnumConstantInfo> { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = EnumConstantInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair<unsigned, unsigned> - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); - unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext<uint32_t, little, unaligned>(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static EnumConstantInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { EnumConstantInfo info; readCommonEntityInfo(data, info); return info; @@ -485,44 +444,16 @@ namespace { }; /// Used to deserialize the on-disk tag table. - class TagTableInfo { + class TagTableInfo + : public VersionedTableInfo<TagTableInfo, unsigned, TagInfo> { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = TagInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair<unsigned, unsigned> - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); - unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static TagInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { TagInfo info; readCommonTypeInfo(data, info); return info; @@ -530,44 +461,16 @@ namespace { }; /// Used to deserialize the on-disk typedef table. - class TypedefTableInfo { + class TypedefTableInfo + : public VersionedTableInfo<TypedefTableInfo, unsigned, TypedefInfo> { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = TypedefInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair<unsigned, unsigned> - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data); - unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext<IdentifierID, little, unaligned>(data); return nameID; } - - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + + static TypedefInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { TypedefInfo info; readCommonTypeInfo(data, info); return info; @@ -602,11 +505,17 @@ class APINotesReader::Implementation { /// The identifier table. std::unique_ptr<SerializedIdentifierTable> IdentifierTable; - using SerializedObjCContextTable = - llvm::OnDiskIterableChainedHashTable<ObjCContextTableInfo>; + using SerializedObjCContextIDTable = + llvm::OnDiskIterableChainedHashTable<ObjCContextIDTableInfo>; - /// The Objective-C context table. - std::unique_ptr<SerializedObjCContextTable> ObjCContextTable; + /// The Objective-C context ID table. + std::unique_ptr<SerializedObjCContextIDTable> ObjCContextIDTable; + + using SerializedObjCContextInfoTable = + llvm::OnDiskIterableChainedHashTable<ObjCContextInfoTableInfo>; + + /// The Objective-C context info table. + std::unique_ptr<SerializedObjCContextInfoTable> ObjCContextInfoTable; using SerializedObjCPropertyTable = llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>; @@ -867,19 +776,36 @@ bool APINotesReader::Implementation::readObjCContextBlock( StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { - case objc_context_block::OBJC_CONTEXT_DATA: { - // Already saw Objective-C class table. - if (ObjCContextTable) + case objc_context_block::OBJC_CONTEXT_ID_DATA: { + // Already saw Objective-C context ID table. + if (ObjCContextIDTable) return true; uint32_t tableOffset; - objc_context_block::ObjCContextDataLayout::readRecord(scratch, tableOffset); + objc_context_block::ObjCContextIDLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast<const uint8_t *>(blobData.data()); - ObjCContextTable.reset( - SerializedObjCContextTable::Create(base + tableOffset, - base + sizeof(uint32_t), - base)); + ObjCContextIDTable.reset( + SerializedObjCContextIDTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + case objc_context_block::OBJC_CONTEXT_INFO_DATA: { + // Already saw Objective-C context info table. + if (ObjCContextInfoTable) + return true; + + uint32_t tableOffset; + objc_context_block::ObjCContextInfoLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast<const uint8_t *>(blobData.data()); + + ObjCContextInfoTable.reset( + SerializedObjCContextInfoTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); break; } @@ -1509,38 +1435,78 @@ ModuleOptions APINotesReader::getModuleOptions() const { return Impl.ModuleOpts; } -auto APINotesReader::lookupObjCClass(StringRef name) - -> Optional<std::pair<ContextID, ObjCContextInfo>> { - if (!Impl.ObjCContextTable) +namespace { + template<typename T> + Optional<T> getUnversioned( + const SmallVectorImpl<std::pair<VersionTuple, T>>& array) { + for (const auto &versioned : array) { + if (!versioned.first) return versioned.second; + } + return None; + } +} + +auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> { + if (!Impl.ObjCContextIDTable) return None; Optional<IdentifierID> classID = Impl.getIdentifier(name); if (!classID) return None; - auto known = Impl.ObjCContextTable->find({*classID, '\0'}); - if (known == Impl.ObjCContextTable->end()) + auto knownID = Impl.ObjCContextIDTable->find({*classID, '\0'}); + if (knownID == Impl.ObjCContextIDTable->end()) return None; - auto result = *known; - return std::make_pair(ContextID(result.first), result.second); + return ContextID(*knownID); } -auto APINotesReader::lookupObjCProtocol(StringRef name) - -> Optional<std::pair<ContextID, ObjCContextInfo>> { - if (!Impl.ObjCContextTable) +auto APINotesReader::lookupObjCClassInfo(StringRef name) + -> Optional<ObjCContextInfo> { + if (!Impl.ObjCContextInfoTable) return None; - Optional<IdentifierID> classID = Impl.getIdentifier(name); - if (!classID) + Optional<ContextID> contextID = lookupObjCClassID(name); + if (!contextID) return None; - auto known = Impl.ObjCContextTable->find({*classID, '\1'}); - if (known == Impl.ObjCContextTable->end()) + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) return None; - auto result = *known; - return std::make_pair(ContextID(result.first), result.second); + return getUnversioned(*knownInfo); +} + +auto APINotesReader::lookupObjCProtocolID(StringRef name) + -> Optional<ContextID> { + if (!Impl.ObjCContextIDTable) + return None; + + Optional<IdentifierID> classID = Impl.getIdentifier(name); + if (!classID) + return None; + + auto knownID = Impl.ObjCContextIDTable->find({*classID, '\1'}); + if (knownID == Impl.ObjCContextIDTable->end()) + return None; + + return ContextID(*knownID); +} + +auto APINotesReader::lookupObjCProtocolInfo(StringRef name) + -> Optional<ObjCContextInfo> { + if (!Impl.ObjCContextInfoTable) + return None; + + Optional<ContextID> contextID = lookupObjCProtocolID(name); + if (!contextID) + return None; + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) + return None; + + return getUnversioned(*knownInfo); } Optional<ObjCPropertyInfo> APINotesReader::lookupObjCProperty( @@ -1560,7 +1526,7 @@ Optional<ObjCPropertyInfo> APINotesReader::lookupObjCProperty( if (known == Impl.ObjCPropertyTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional<ObjCMethodInfo> APINotesReader::lookupObjCMethod( @@ -1580,7 +1546,7 @@ Optional<ObjCMethodInfo> APINotesReader::lookupObjCMethod( if (known == Impl.ObjCMethodTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional<GlobalVariableInfo> APINotesReader::lookupGlobalVariable( @@ -1596,7 +1562,7 @@ Optional<GlobalVariableInfo> APINotesReader::lookupGlobalVariable( if (known == Impl.GlobalVariableTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional<GlobalFunctionInfo> APINotesReader::lookupGlobalFunction( @@ -1612,7 +1578,7 @@ Optional<GlobalFunctionInfo> APINotesReader::lookupGlobalFunction( if (known == Impl.GlobalFunctionTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional<EnumConstantInfo> APINotesReader::lookupEnumConstant(StringRef name) { @@ -1627,7 +1593,7 @@ Optional<EnumConstantInfo> APINotesReader::lookupEnumConstant(StringRef name) { if (known == Impl.EnumConstantTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional<TagInfo> APINotesReader::lookupTag(StringRef name) { @@ -1642,7 +1608,7 @@ Optional<TagInfo> APINotesReader::lookupTag(StringRef name) { if (known == Impl.TagTable->end()) return None; - return *known; + return getUnversioned(*known); } Optional<TypedefInfo> APINotesReader::lookupTypedef(StringRef name) { @@ -1657,48 +1623,61 @@ Optional<TypedefInfo> APINotesReader::lookupTypedef(StringRef name) { if (known == Impl.TypedefTable->end()) return None; - return *known; + return getUnversioned(*known); } APINotesReader::Visitor::~Visitor() { } -void APINotesReader::Visitor::visitObjCClass(ContextID contextID, - StringRef name, - const ObjCContextInfo &info) { } - -void APINotesReader::Visitor::visitObjCProtocol(ContextID contextID, - StringRef name, - const ObjCContextInfo &info) { } - -void APINotesReader::Visitor::visitObjCMethod(ContextID contextID, - StringRef selector, - bool isInstanceMethod, - const ObjCMethodInfo &info) { } +void APINotesReader::Visitor::visitObjCClass( + ContextID contextID, + StringRef name, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { } -void APINotesReader::Visitor::visitObjCProperty(ContextID contextID, - StringRef name, - bool isInstance, - const ObjCPropertyInfo &info) { } +void APINotesReader::Visitor::visitObjCProtocol( + ContextID contextID, + StringRef name, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { } + +void APINotesReader::Visitor::visitObjCMethod( + ContextID contextID, + StringRef selector, + bool isInstanceMethod, + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { } + +void APINotesReader::Visitor::visitObjCProperty( + ContextID contextID, + StringRef name, + bool isInstance, + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitGlobalVariable( StringRef name, - const GlobalVariableInfo &info) { } + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitGlobalFunction( StringRef name, - const GlobalFunctionInfo &info) { } + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitEnumConstant( StringRef name, - const EnumConstantInfo &info) { } + const EnumConstantInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTag( StringRef name, - const TagInfo &info) { } + const TagInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTypedef( StringRef name, - const TypedefInfo &info) { } + const TypedefInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::visit(Visitor &visitor) { // FIXME: All of these iterations would be significantly more efficient if we @@ -1717,15 +1696,22 @@ void APINotesReader::visit(Visitor &visitor) { } // Visit classes and protocols. - if (Impl.ObjCContextTable) { - for (auto key : Impl.ObjCContextTable->keys()) { + if (Impl.ObjCContextIDTable && Impl.ObjCContextInfoTable) { + for (auto key : Impl.ObjCContextIDTable->keys()) { auto name = identifiers[key.first]; - auto info = *Impl.ObjCContextTable->find(key); - - if (key.second) - visitor.visitObjCProtocol(ContextID(info.first), name, info.second); - else - visitor.visitObjCClass(ContextID(info.first), name, info.second); + auto contextID = *Impl.ObjCContextIDTable->find(key); + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID); + if (knownInfo == Impl.ObjCContextInfoTable->end()) continue; + + for (const auto &versioned : *knownInfo) { + if (key.second) + visitor.visitObjCProtocol(ContextID(contextID), name, + versioned.second, versioned.first); + else + visitor.visitObjCClass(ContextID(contextID), name, versioned.second, + versioned.first); + } } } @@ -1754,8 +1740,9 @@ void APINotesReader::visit(Visitor &visitor) { for (auto key : Impl.ObjCMethodTable->keys()) { ContextID contextID(std::get<0>(key)); const auto &selector = selectors[std::get<1>(key)]; - auto info = *Impl.ObjCMethodTable->find(key); - visitor.visitObjCMethod(contextID, selector, std::get<2>(key), info); + for (const auto &versioned : *Impl.ObjCMethodTable->find(key)) + visitor.visitObjCMethod(contextID, selector, std::get<2>(key), + versioned.second, versioned.first); } } @@ -1765,8 +1752,10 @@ void APINotesReader::visit(Visitor &visitor) { ContextID contextID(std::get<0>(key)); auto name = identifiers[std::get<1>(key)]; char isInstance = std::get<2>(key); - auto info = *Impl.ObjCPropertyTable->find(key); - visitor.visitObjCProperty(contextID, name, isInstance, info); + for (const auto &versioned : *Impl.ObjCPropertyTable->find(key)) { + visitor.visitObjCProperty(contextID, name, isInstance, versioned.second, + versioned.first); + } } } @@ -1774,8 +1763,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.GlobalFunctionTable) { for (auto key : Impl.GlobalFunctionTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.GlobalFunctionTable->find(key); - visitor.visitGlobalFunction(name, info); + for (const auto &versioned : *Impl.GlobalFunctionTable->find(key)) + visitor.visitGlobalFunction(name, versioned.second, versioned.first); } } @@ -1783,8 +1772,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.GlobalVariableTable) { for (auto key : Impl.GlobalVariableTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.GlobalVariableTable->find(key); - visitor.visitGlobalVariable(name, info); + for (const auto &versioned : *Impl.GlobalVariableTable->find(key)) + visitor.visitGlobalVariable(name, versioned.second, versioned.first); } } @@ -1792,8 +1781,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.EnumConstantTable) { for (auto key : Impl.EnumConstantTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.EnumConstantTable->find(key); - visitor.visitEnumConstant(name, info); + for (const auto &versioned : *Impl.EnumConstantTable->find(key)) + visitor.visitEnumConstant(name, versioned.second, versioned.first); } } @@ -1801,8 +1790,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.TagTable) { for (auto key : Impl.TagTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.TagTable->find(key); - visitor.visitTag(name, info); + for (const auto &versioned : *Impl.TagTable->find(key)) + visitor.visitTag(name, versioned.second, versioned.first); } } @@ -1810,8 +1799,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.TypedefTable) { for (auto key : Impl.TypedefTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.TypedefTable->find(key); - visitor.visitTypedef(name, info); + for (const auto &versioned : *Impl.TypedefTable->find(key)) + visitor.visitTypedef(name, versioned.second, versioned.first); } } } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 2df5e76f7c8d4..36c504afc34cd 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -24,12 +24,18 @@ #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/DataTypes.h" #include <tuple> #include <vector> using namespace clang; using namespace api_notes; using namespace llvm::support; +namespace { + template<typename T> using VersionedSmallVector = + SmallVector<std::pair<VersionTuple, T>, 1>; +} + class APINotesWriter::Implementation { /// Mapping from strings to identifier IDs. llvm::StringMap<IdentifierID> IdentifierIDs; @@ -56,7 +62,8 @@ class APINotesWriter::Implementation { /// for a class (0) or protocol (1) and provides both the context ID and /// information describing the context within that module. llvm::DenseMap<std::pair<unsigned, char>, - std::pair<unsigned, ObjCContextInfo>> ObjCContexts; + std::pair<unsigned, VersionedSmallVector<ObjCContextInfo>>> + ObjCContexts; /// Mapping from context IDs to the identifier ID holding the name. llvm::DenseMap<unsigned, unsigned> ObjCContextNames; @@ -65,40 +72,56 @@ class APINotesWriter::Implementation { /// /// Indexed by the context ID, property name, and whether this is an /// instance property. - llvm::DenseMap<std::tuple<unsigned, unsigned, char>, ObjCPropertyInfo> + llvm::DenseMap<std::tuple<unsigned, unsigned, char>, + llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>, + 1>> ObjCProperties; /// Information about Objective-C methods. /// /// Indexed by the context ID, selector ID, and Boolean (stored as a /// char) indicating whether this is a class or instance method. - llvm::DenseMap<std::tuple<unsigned, unsigned, char>, ObjCMethodInfo> + llvm::DenseMap<std::tuple<unsigned, unsigned, char>, + llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>> ObjCMethods; /// Information about global variables. /// /// Indexed by the identifier ID. - llvm::DenseMap<unsigned, GlobalVariableInfo> GlobalVariables; + llvm::DenseMap<unsigned, + llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>, + 1>> + GlobalVariables; /// Information about global functions. /// /// Indexed by the identifier ID. - llvm::DenseMap<unsigned, GlobalFunctionInfo> GlobalFunctions; + llvm::DenseMap<unsigned, + llvm::SmallVector<std::pair<VersionTuple, GlobalFunctionInfo>, + 1>> + GlobalFunctions; /// Information about enumerators. /// /// Indexed by the identifier ID. - llvm::DenseMap<unsigned, EnumConstantInfo> EnumConstants; + llvm::DenseMap<unsigned, + llvm::SmallVector<std::pair<VersionTuple, EnumConstantInfo>, + 1>> + EnumConstants; /// Information about tags. /// /// Indexed by the identifier ID. - llvm::DenseMap<unsigned, TagInfo> Tags; + llvm::DenseMap<unsigned, + llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>> + Tags; /// Information about typedefs. /// /// Indexed by the identifier ID. - llvm::DenseMap<unsigned, TypedefInfo> Typedefs; + llvm::DenseMap<unsigned, + llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>> + Typedefs; /// Retrieve the ID for the given identifier. IdentifierID getIdentifier(StringRef identifier) { @@ -194,7 +217,7 @@ void APINotesWriter::Implementation::writeBlockInfoBlock( BLOCK_RECORD(identifier_block, IDENTIFIER_DATA); BLOCK(OBJC_CONTEXT_BLOCK); - BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_DATA); + BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA); BLOCK(OBJC_PROPERTY_BLOCK); BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA); @@ -336,18 +359,15 @@ namespace { } /// Used to serialize the on-disk Objective-C context table. - class ObjCContextTableInfo { + class ObjCContextIDTableInfo { public: using key_type = std::pair<unsigned, char>; // identifier ID, is-protocol using key_type_ref = key_type; - using data_type = std::pair<unsigned, ObjCContextInfo>; + using data_type = unsigned; using data_type_ref = const data_type &; using hash_value_type = size_t; using offset_type = unsigned; - /// The number of bytes in a data entry. - static const unsigned dataBytes = 3; - hash_value_type ComputeHash(key_type_ref key) { return static_cast<size_t>(llvm::hash_value(key)); } @@ -356,9 +376,7 @@ namespace { key_type_ref key, data_type_ref data) { uint32_t keyLength = sizeof(uint32_t) + 1; - uint32_t dataLength = sizeof(uint32_t) - + getCommonTypeInfoSize(data.second) - + dataBytes; + uint32_t dataLength = sizeof(uint32_t); endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); @@ -374,58 +392,92 @@ namespace { void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer<little> writer(out); - writer.write<uint32_t>(data.first); - - emitCommonTypeInfo(out, data.second); - - // FIXME: Inefficient representation. - uint8_t bytes[dataBytes] = { 0, 0, 0 }; - if (auto nullable = data.second.getDefaultNullability()) { - bytes[0] = 1; - bytes[1] = static_cast<uint8_t>(*nullable); - } else { - // Nothing to do. - } - bytes[2] = data.second.hasDesignatedInits(); - - out.write(reinterpret_cast<const char *>(bytes), dataBytes); + writer.write<uint32_t>(data); } }; } // end anonymous namespace -void APINotesWriter::Implementation::writeObjCContextBlock( - llvm::BitstreamWriter &writer) { - BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3); +namespace { + /// Retrieve the serialized size of the given VersionTuple, for use in + /// on-disk hash tables. + unsigned getVersionTupleSize(const VersionTuple &version) { + unsigned size = sizeof(uint8_t) + /*major*/sizeof(uint32_t); + if (version.getMinor()) size += sizeof(uint32_t); + if (version.getSubminor()) size += sizeof(uint32_t); + if (version.getBuild()) size += sizeof(uint32_t); + return size; + } - if (ObjCContexts.empty()) - return; + /// Emit a serialized representation of a version tuple. + void emitVersionTuple(raw_ostream &out, const VersionTuple &version) { + endian::Writer<little> writer(out); - llvm::SmallString<4096> hashTableBlob; - uint32_t tableOffset; - { - llvm::OnDiskChainedHashTableGenerator<ObjCContextTableInfo> generator; - for (auto &entry : ObjCContexts) - generator.insert(entry.first, entry.second); + // First byte contains the number of components beyond the 'major' + // component. + uint8_t descriptor; + if (version.getBuild()) descriptor = 3; + else if (version.getSubminor()) descriptor = 2; + else if (version.getMinor()) descriptor = 1; + else descriptor = 0; + assert(!version.usesUnderscores() && "Not a serializable version"); + writer.write<uint8_t>(descriptor); + + // Write the components. + writer.write<uint32_t>(version.getMajor()); + if (auto minor = version.getMinor()) + writer.write<uint32_t>(*minor); + if (auto subminor = version.getSubminor()) + writer.write<uint32_t>(*subminor); + if (auto build = version.getBuild()) + writer.write<uint32_t>(*build); + } - llvm::raw_svector_ostream blobStream(hashTableBlob); - // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); - tableOffset = generator.Emit(blobStream); + /// Localized helper to make a type dependent, thwarting template argument + /// deduction. + template<typename T> + struct MakeDependent { + typedef T Type; + }; + + /// Determine the size of an array of versioned information, + template<typename T> + unsigned getVersionedInfoSize( + const SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray, + llvm::function_ref<unsigned(const typename MakeDependent<T>::Type&)> + getInfoSize) { + unsigned result = sizeof(uint16_t); // # of elements + for (const auto &element : infoArray) { + result += getVersionTupleSize(element.first); + result += getInfoSize(element.second); + } + + return result; } - objc_context_block::ObjCContextDataLayout layout(writer); - layout.emit(ScratchRecord, tableOffset, hashTableBlob); -} + /// Emit versioned information. + template<typename T> + void emitVersionedInfo( + raw_ostream &out, + const SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray, + llvm::function_ref<void(raw_ostream &out, + const typename MakeDependent<T>::Type& info)> + emitInfo) { + endian::Writer<little> writer(out); + writer.write<uint16_t>(infoArray.size()); + for (const auto &element : infoArray) { + emitVersionTuple(out, element.first); + emitInfo(out, element.second); + } + } -namespace { /// Retrieve the serialized size of the given VariableInfo, for use in /// on-disk hash tables. - static unsigned getVariableInfoSize(const VariableInfo &info) { + unsigned getVariableInfoSize(const VariableInfo &info) { return 2 + getCommonEntityInfoSize(info); } /// Emit a serialized representation of the variable information. - static void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { + void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { emitCommonEntityInfo(out, info); uint8_t bytes[2] = { 0, 0 }; @@ -439,32 +491,96 @@ namespace { out.write(reinterpret_cast<const char *>(bytes), 2); } - /// Used to serialize the on-disk Objective-C property table. - class ObjCPropertyTableInfo { + /// On-dish hash table info key base for handling versioned data. + template<typename Derived, typename KeyType, typename UnversionedDataType> + class VersionedTableInfo { + Derived &asDerived() { + return *static_cast<Derived *>(this); + } + + const Derived &asDerived() const { + return *static_cast<const Derived *>(this); + } + public: - // (class ID, name ID, isInstance) - using key_type = std::tuple<unsigned, unsigned, char>; + using key_type = KeyType; using key_type_ref = key_type; - using data_type = ObjCPropertyInfo; + using data_type = + SmallVector<std::pair<VersionTuple, UnversionedDataType>, 1>; using data_type_ref = const data_type &; using hash_value_type = size_t; using offset_type = unsigned; hash_value_type ComputeHash(key_type_ref key) { - return static_cast<size_t>(llvm::hash_value(key)); + return llvm::hash_value(key); } std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); - uint32_t dataLength = getVariableInfoSize(data); + uint32_t keyLength = asDerived().getKeyLength(key); + uint32_t dataLength = getVersionedInfoSize(data, + [this](const UnversionedDataType &unversionedInfo) { + return asDerived().getUnversionedInfoSize(unversionedInfo); + }); + endian::Writer<little> writer(out); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); return { keyLength, dataLength }; } + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitVersionedInfo(out, data, + [this](llvm::raw_ostream &out, + const UnversionedDataType &unversionedInfo) { + asDerived().emitUnversionedInfo(out, unversionedInfo); + }); + } + }; + + /// Used to serialize the on-disk Objective-C property table. + class ObjCContextInfoTableInfo + : public VersionedTableInfo<ObjCContextInfoTableInfo, + unsigned, + ObjCContextInfo> { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer<little> writer(out); + writer.write<uint32_t>(key); + } + + unsigned getUnversionedInfoSize(const ObjCContextInfo &info) { + return getCommonTypeInfoSize(info) + 1; + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCContextInfo &info) { + emitCommonTypeInfo(out, info); + + uint8_t payload = 0; + if (auto nullable = info.getDefaultNullability()) { + payload = (0x01 << 2) | static_cast<uint8_t>(*nullable); + } + payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0); + out << payload; + } + }; + + /// Used to serialize the on-disk Objective-C property table. + class ObjCPropertyTableInfo + : public VersionedTableInfo<ObjCPropertyTableInfo, + std::tuple<unsigned, unsigned, char>, + ObjCPropertyInfo> { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); + } + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); writer.write<uint32_t>(std::get<0>(key)); @@ -472,13 +588,61 @@ namespace { writer.write<uint8_t>(std::get<2>(key)); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitVariableInfo(out, data); + unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) { + return getVariableInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) { + emitVariableInfo(out, info); } }; } // end anonymous namespace +void APINotesWriter::Implementation::writeObjCContextBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3); + + if (ObjCContexts.empty()) + return; + + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<ObjCContextIDTableInfo> generator; + for (auto &entry : ObjCContexts) + generator.insert(entry.first, entry.second.first); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextIDLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } + + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator<ObjCContextInfoTableInfo> + generator; + for (auto &entry : ObjCContexts) + generator.insert(entry.second.first, entry.second.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer<little>(blobStream).write<uint32_t>(0); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextInfoLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } +} + void APINotesWriter::Implementation::writeObjCPropertyBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, OBJC_PROPERTY_BLOCK_ID, 3); @@ -536,31 +700,13 @@ namespace { } /// Used to serialize the on-disk Objective-C method table. - class ObjCMethodTableInfo { + class ObjCMethodTableInfo + : public VersionedTableInfo<ObjCMethodTableInfo, + std::tuple<unsigned, unsigned, char>, + ObjCMethodInfo> { public: - // (class ID, selector ID, is-instance) - using key_type = std::tuple<unsigned, unsigned, char>; - using key_type_ref = key_type; - using data_type = ObjCMethodInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return llvm::hash_combine(std::get<0>(key), - std::get<1>(key), - std::get<2>(key)); - } - - std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + 1; - uint32_t dataLength = getFunctionInfoSize(data) + 3; - endian::Writer<little> writer(out); - writer.write<uint16_t>(keyLength); - writer.write<uint16_t>(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t) + 1; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -570,16 +716,18 @@ namespace { writer.write<uint8_t>(std::get<2>(key)); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitFunctionInfo(out, data); + unsigned getUnversionedInfoSize(const ObjCMethodInfo &info) { + return 1 + getFunctionInfoSize(info); + } + void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) { + uint8_t payload = info.FactoryAsInit << 2; + payload = (payload | info.DesignatedInit) << 1; + payload = (payload | info.Required); endian::Writer<little> writer(out); + writer.write<uint8_t>(payload); - // FIXME: Inefficient representation - writer.write<uint8_t>(data.DesignatedInit); - writer.write<uint8_t>(data.FactoryAsInit); - writer.write<uint8_t>(data.Required); + emitFunctionInfo(out, info); } }; } // end anonymous namespace @@ -678,28 +826,13 @@ void APINotesWriter::Implementation::writeObjCSelectorBlock( namespace { /// Used to serialize the on-disk global variable table. - class GlobalVariableTableInfo { + class GlobalVariableTableInfo + : public VersionedTableInfo<GlobalVariableTableInfo, + unsigned, + GlobalVariableInfo> { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = GlobalVariableInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getVariableInfoSize(data); - endian::Writer<little> writer(out); - writer.write<uint16_t>(keyLength); - writer.write<uint16_t>(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref key) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -707,9 +840,13 @@ namespace { writer.write<uint32_t>(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitVariableInfo(out, data); + unsigned getUnversionedInfoSize(const GlobalVariableInfo &info) { + return getVariableInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalVariableInfo &info) { + emitVariableInfo(out, info); } }; } // end anonymous namespace @@ -740,28 +877,13 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock( namespace { /// Used to serialize the on-disk global function table. - class GlobalFunctionTableInfo { + class GlobalFunctionTableInfo + : public VersionedTableInfo<GlobalFunctionTableInfo, + unsigned, + GlobalFunctionInfo> { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = GlobalFunctionInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return llvm::hash_value(key); - } - - std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getFunctionInfoSize(data); - endian::Writer<little> writer(out); - writer.write<uint16_t>(keyLength); - writer.write<uint16_t>(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -769,9 +891,13 @@ namespace { writer.write<uint32_t>(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitFunctionInfo(out, data); + unsigned getUnversionedInfoSize(const GlobalFunctionInfo &info) { + return getFunctionInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalFunctionInfo &info) { + emitFunctionInfo(out, info); } }; } // end anonymous namespace @@ -803,28 +929,13 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( namespace { /// Used to serialize the on-disk global enum constant. - class EnumConstantTableInfo { + class EnumConstantTableInfo + : public VersionedTableInfo<EnumConstantTableInfo, + unsigned, + EnumConstantInfo> { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = EnumConstantInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getCommonEntityInfoSize(data); - endian::Writer<little> writer(out); - writer.write<uint16_t>(keyLength); - writer.write<uint16_t>(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -832,9 +943,12 @@ namespace { writer.write<uint32_t>(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonEntityInfo(out, data); + unsigned getUnversionedInfoSize(const EnumConstantInfo &info) { + return getCommonEntityInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const EnumConstantInfo &info) { + emitCommonEntityInfo(out, info); } }; } // end anonymous namespace @@ -864,41 +978,32 @@ void APINotesWriter::Implementation::writeEnumConstantBlock( } namespace { - /// Used to serialize the on-disk tag table. - class TagTableInfo { + template<typename Derived, typename UnversionedDataType> + class CommonTypeTableInfo + : public VersionedTableInfo<Derived, unsigned, UnversionedDataType> { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = TagInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast<size_t>(llvm::hash_value(key)); - } + using key_type_ref = typename CommonTypeTableInfo::key_type_ref; - std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); - uint32_t dataLength = getCommonTypeInfoSize(data); - endian::Writer<little> writer(out); - writer.write<uint16_t>(keyLength); - writer.write<uint16_t>(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(IdentifierID); } - void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer<little> writer(out); writer.write<IdentifierID>(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonTypeInfo(out, data); + unsigned getUnversionedInfoSize(const UnversionedDataType &info) { + return getCommonTypeInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const UnversionedDataType &info) { + emitCommonTypeInfo(out, info); } }; + + /// Used to serialize the on-disk tag table. + class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { }; } // end anonymous namespace void APINotesWriter::Implementation::writeTagBlock( @@ -927,40 +1032,8 @@ void APINotesWriter::Implementation::writeTagBlock( namespace { /// Used to serialize the on-disk typedef table. - class TypedefTableInfo { - public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = TypedefInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast<size_t>(llvm::hash_value(key)); - } - - std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); - uint32_t dataLength = getCommonTypeInfoSize(data); - endian::Writer<little> writer(out); - writer.write<uint16_t>(keyLength); - writer.write<uint16_t>(dataLength); - return { keyLength, dataLength }; - } - - void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); - writer.write<IdentifierID>(key); - } - - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonTypeInfo(out, data); - } - }; + class TypedefTableInfo + : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> { }; } // end anonymous namespace void APINotesWriter::Implementation::writeTypedefBlock( @@ -1033,106 +1106,115 @@ void APINotesWriter::writeToStream(raw_ostream &os) { Impl.writeToStream(os); } -ContextID APINotesWriter::addObjCClass(StringRef name, - const ObjCContextInfo &info) { - IdentifierID classID = Impl.getIdentifier(name); +ContextID APINotesWriter::addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { + IdentifierID nameID = Impl.getIdentifier(name); - std::pair<unsigned, char> key(classID, 0); + std::pair<unsigned, char> key(nameID, isClass ? 0 : 1); auto known = Impl.ObjCContexts.find(key); - if (known != Impl.ObjCContexts.end()) { - known->second.second |= info; - } else { + if (known == Impl.ObjCContexts.end()) { unsigned nextID = Impl.ObjCContexts.size() + 1; + VersionedSmallVector<ObjCContextInfo> emptyVersionedInfo; known = Impl.ObjCContexts.insert( - std::make_pair(key, std::make_pair(nextID, info))) + std::make_pair(key, std::make_pair(nextID, emptyVersionedInfo))) .first; - Impl.ObjCContextNames[nextID] = classID; + Impl.ObjCContextNames[nextID] = nameID; } - return ContextID(known->second.first); -} - -ContextID APINotesWriter::addObjCProtocol(StringRef name, - const ObjCContextInfo &info) { - IdentifierID protocolID = Impl.getIdentifier(name); - - std::pair<unsigned, char> key(protocolID, 1); - auto known = Impl.ObjCContexts.find(key); - if (known != Impl.ObjCContexts.end()) { - known->second.second |= info; - } else { - unsigned nextID = Impl.ObjCContexts.size() + 1; - - known = Impl.ObjCContexts.insert( - std::make_pair(key, std::make_pair(nextID, info))) - .first; - - Impl.ObjCContextNames[nextID] = protocolID; + // Add this version information. + auto &versionedVec = known->second.second; + bool found = false; + for (auto &versioned : versionedVec){ + if (versioned.first == swiftVersion) { + versioned.second |= info; + found = true; + break; + } } + if (!found) + versionedVec.push_back({swiftVersion, info}); + return ContextID(known->second.first); } + void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info) { + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.ObjCProperties.count(std::make_tuple(contextID.Value, nameID, isInstance))); - Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)] = info; + Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)] + .push_back({swiftVersion, info}); } void APINotesWriter::addObjCMethod(ContextID contextID, ObjCSelectorRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info) { + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { SelectorID selectorID = Impl.getSelector(selector); auto key = std::tuple<unsigned, unsigned, char>{ contextID.Value, selectorID, isInstanceMethod}; - assert(!Impl.ObjCMethods.count(key)); - Impl.ObjCMethods[key] = info; + Impl.ObjCMethods[key].push_back({swiftVersion, info}); // If this method is a designated initializer, update the class to note that // it has designated initializers. if (info.DesignatedInit) { assert(Impl.ObjCContexts.count({Impl.ObjCContextNames[contextID.Value], (char)0})); - Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}] - .second.setHasDesignatedInits(true); + auto &versionedVec = + Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}] + .second; + bool found = false; + for (auto &versioned : versionedVec) { + if (versioned.first == swiftVersion) { + versioned.second.setHasDesignatedInits(true); + found = true; + break; + } + } + + if (!found) { + versionedVec.push_back({swiftVersion, ObjCContextInfo()}); + versionedVec.back().second.setHasDesignatedInits(true); + } } } void APINotesWriter::addGlobalVariable(llvm::StringRef name, - const GlobalVariableInfo &info) { + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { IdentifierID variableID = Impl.getIdentifier(name); - assert(!Impl.GlobalVariables.count(variableID)); - Impl.GlobalVariables[variableID] = info; + Impl.GlobalVariables[variableID].push_back({swiftVersion, info}); } void APINotesWriter::addGlobalFunction(llvm::StringRef name, - const GlobalFunctionInfo &info) { + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.GlobalFunctions.count(nameID)); - Impl.GlobalFunctions[nameID] = info; + Impl.GlobalFunctions[nameID].push_back({swiftVersion, info}); } void APINotesWriter::addEnumConstant(llvm::StringRef name, - const EnumConstantInfo &info) { + const EnumConstantInfo &info, + VersionTuple swiftVersion) { IdentifierID enumConstantID = Impl.getIdentifier(name); - assert(!Impl.EnumConstants.count(enumConstantID)); - Impl.EnumConstants[enumConstantID] = info; + Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info}); } -void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { +void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info, + VersionTuple swiftVersion) { IdentifierID tagID = Impl.getIdentifier(name); - assert(!Impl.Tags.count(tagID)); - Impl.Tags[tagID] = info; + Impl.Tags[tagID].push_back({swiftVersion, info}); } -void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { +void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion) { IdentifierID typedefID = Impl.getIdentifier(name); - assert(!Impl.Typedefs.count(typedefID)); - Impl.Typedefs[typedefID] = info; + Impl.Typedefs[typedefID].push_back({swiftVersion, info}); } void APINotesWriter::addModuleOptions(ModuleOptions opts) { diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 0d30731194c41..f15099e4677ca 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -14,6 +14,7 @@ #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/Types.h" #include "clang/APINotes/APINotesWriter.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/SourceMgr.h" @@ -262,9 +263,7 @@ namespace { }; typedef std::vector<Typedef> TypedefsSeq; - struct Module { - StringRef Name; - AvailabilityItem Availability; + struct TopLevelItems { ClassesSeq Classes; ClassesSeq Protocols; FunctionsSeq Functions; @@ -272,6 +271,20 @@ namespace { EnumConstantsSeq EnumConstants; TagsSeq Tags; TypedefsSeq Typedefs; + }; + + struct Versioned { + VersionTuple Version; + TopLevelItems Items; + }; + + typedef std::vector<Versioned> VersionedSeq; + + struct Module { + StringRef Name; + AvailabilityItem Availability; + TopLevelItems TopLevel; + VersionedSeq SwiftVersions; llvm::Optional<bool> SwiftInferImportAsMember = {llvm::None}; @@ -291,6 +304,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant) LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) +LLVM_YAML_IS_SEQUENCE_VECTOR(Versioned) namespace llvm { namespace yaml { @@ -335,6 +349,24 @@ namespace llvm { } }; + template <> + struct ScalarTraits<VersionTuple> { + static void output(const VersionTuple &value, void*, + llvm::raw_ostream &out) { + out << value; + } + static StringRef input(StringRef scalar, void*, VersionTuple &value) { + if (value.tryParse(scalar)) + return "not a version number in the form XX.YY"; + + // Canonicalize on '.' as a separator. + value.UseDotAsSeparator(); + return StringRef(); + } + + static bool mustQuote(StringRef) { return false; } + }; + template <> struct MappingTraits<Param> { static void mapping(IO &io, Param& p) { @@ -460,6 +492,24 @@ namespace llvm { } }; + static void mapTopLevelItems(IO &io, TopLevelItems &i) { + io.mapOptional("Classes", i.Classes); + io.mapOptional("Protocols", i.Protocols); + io.mapOptional("Functions", i.Functions); + io.mapOptional("Globals", i.Globals); + io.mapOptional("Enumerators", i.EnumConstants); + io.mapOptional("Tags", i.Tags); + io.mapOptional("Typedefs", i.Typedefs); + } + + template <> + struct MappingTraits<Versioned> { + static void mapping(IO &io, Versioned& v) { + io.mapRequired("Version", v.Version); + mapTopLevelItems(io, v.Items); + } + }; + template <> struct MappingTraits<Module> { static void mapping(IO &io, Module& m) { @@ -467,13 +517,10 @@ namespace llvm { io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember); - io.mapOptional("Classes", m.Classes); - io.mapOptional("Protocols", m.Protocols); - io.mapOptional("Functions", m.Functions); - io.mapOptional("Globals", m.Globals); - io.mapOptional("Enumerators", m.EnumConstants); - io.mapOptional("Tags", m.Tags); - io.mapOptional("Typedefs", m.Typedefs); + + mapTopLevelItems(io, m.TopLevel); + + io.mapOptional("SwiftVersions", m.SwiftVersions); } }; } @@ -624,7 +671,8 @@ namespace { // Translate from Method into ObjCMethodInfo and write it out. void convertMethod(const Method &meth, - ContextID classID, StringRef className) { + ContextID classID, StringRef className, + VersionTuple swiftVersion) { ObjCMethodInfo mInfo; if (convertCommon(meth, mInfo, meth.Selector)) @@ -662,10 +710,11 @@ namespace { // Write it. Writer->addObjCMethod(classID, selectorRef, meth.Kind == MethodKind::Instance, - mInfo); + mInfo, swiftVersion); } - void convertContext(const Class &cl, bool isClass) { + void convertContext(const Class &cl, bool isClass, + VersionTuple swiftVersion) { // Write the class. ObjCContextInfo cInfo; @@ -675,8 +724,8 @@ namespace { if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); - ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : - Writer->addObjCProtocol(cl.Name, cInfo); + ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo, + swiftVersion); // Write all methods. llvm::StringMap<std::pair<bool, bool>> knownMethods; @@ -693,7 +742,7 @@ namespace { } known = true; - convertMethod(method, clID, cl.Name); + convertMethod(method, clID, cl.Name, swiftVersion); } // Write all properties. @@ -726,51 +775,45 @@ namespace { pInfo.setNullabilityAudited(*prop.Nullability); if (prop.Kind) { Writer->addObjCProperty(clID, prop.Name, - *prop.Kind == MethodKind::Instance, pInfo); + *prop.Kind == MethodKind::Instance, pInfo, + swiftVersion); } else { // Add both instance and class properties with this name. - Writer->addObjCProperty(clID, prop.Name, true, pInfo); - Writer->addObjCProperty(clID, prop.Name, false, pInfo); + Writer->addObjCProperty(clID, prop.Name, true, pInfo, swiftVersion); + Writer->addObjCProperty(clID, prop.Name, false, pInfo, swiftVersion); } } } - bool convertModule() { - if (!isAvailable(TheModule.Availability)) - return false; - - // Set up the writer. - // FIXME: This is kindof ugly. - APINotesWriter writer(TheModule.Name, SourceFile); - Writer = &writer; - + void convertTopLevelItems(const TopLevelItems &items, + VersionTuple swiftVersion) { // Write all classes. llvm::StringSet<> knownClasses; - for (const auto &cl : TheModule.Classes) { + for (const auto &cl : items.Classes) { // Check for duplicate class definitions. if (!knownClasses.insert(cl.Name).second) { emitError("multiple definitions of class '" + cl.Name + "'"); continue; } - convertContext(cl, /*isClass*/ true); + convertContext(cl, /*isClass*/ true, swiftVersion); } // Write all protocols. llvm::StringSet<> knownProtocols; - for (const auto &pr : TheModule.Protocols) { + for (const auto &pr : items.Protocols) { // Check for duplicate protocol definitions. if (!knownProtocols.insert(pr.Name).second) { emitError("multiple definitions of protocol '" + pr.Name + "'"); continue; } - convertContext(pr, /*isClass*/ false); + convertContext(pr, /*isClass*/ false, swiftVersion); } // Write all global variables. llvm::StringSet<> knownGlobals; - for (const auto &global : TheModule.Globals) { + for (const auto &global : items.Globals) { // Check for duplicate global variables. if (!knownGlobals.insert(global.Name).second) { emitError("multiple definitions of global variable '" + @@ -786,12 +829,12 @@ namespace { info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); - Writer->addGlobalVariable(global.Name, info); + Writer->addGlobalVariable(global.Name, info, swiftVersion); } // Write all global functions. llvm::StringSet<> knownFunctions; - for (const auto &function : TheModule.Functions) { + for (const auto &function : items.Functions) { // Check for duplicate global functions. if (!knownFunctions.insert(function.Name).second) { emitError("multiple definitions of global function '" + @@ -810,12 +853,12 @@ namespace { function.NullabilityOfRet, info, function.Name); - Writer->addGlobalFunction(function.Name, info); + Writer->addGlobalFunction(function.Name, info, swiftVersion); } // Write all enumerators. llvm::StringSet<> knownEnumConstants; - for (const auto &enumConstant : TheModule.EnumConstants) { + for (const auto &enumConstant : items.EnumConstants) { // Check for duplicate enumerators if (!knownEnumConstants.insert(enumConstant.Name).second) { emitError("multiple definitions of enumerator '" + @@ -829,12 +872,12 @@ namespace { convertAvailability(enumConstant.Availability, info, enumConstant.Name); info.SwiftPrivate = enumConstant.SwiftPrivate; info.SwiftName = enumConstant.SwiftName; - Writer->addEnumConstant(enumConstant.Name, info); + Writer->addEnumConstant(enumConstant.Name, info, swiftVersion); } // Write all tags. llvm::StringSet<> knownTags; - for (const auto &t : TheModule.Tags) { + for (const auto &t : items.Tags) { // Check for duplicate tag definitions. if (!knownTags.insert(t.Name).second) { emitError("multiple definitions Of tag '" + t.Name + "'"); @@ -845,12 +888,12 @@ namespace { if (convertCommonType(t, tagInfo, t.Name)) continue; - Writer->addTag(t.Name, tagInfo); + Writer->addTag(t.Name, tagInfo, swiftVersion); } // Write all typedefs. llvm::StringSet<> knownTypedefs; - for (const auto &t : TheModule.Typedefs) { + for (const auto &t : items.Typedefs) { // Check for duplicate typedef definitions. if (!knownTypedefs.insert(t.Name).second) { emitError("multiple definitions of typedef '" + t.Name + "'"); @@ -860,9 +903,22 @@ namespace { TypedefInfo typedefInfo; if (convertCommonType(t, typedefInfo, t.Name)) continue; - - Writer->addTypedef(t.Name, typedefInfo); + + Writer->addTypedef(t.Name, typedefInfo, swiftVersion); } + } + + bool convertModule() { + if (!isAvailable(TheModule.Availability)) + return false; + + // Set up the writer. + // FIXME: This is kindof ugly. + APINotesWriter writer(TheModule.Name, SourceFile); + Writer = &writer; + + // Write the top-level items. + convertTopLevelItems(TheModule.TopLevel, VersionTuple()); if (TheModule.SwiftInferImportAsMember) { ModuleOptions opts; @@ -870,6 +926,12 @@ namespace { Writer->addModuleOptions(opts); } + // Convert the versioned information. + for (const auto &versioned : TheModule.SwiftVersions) { + convertTopLevelItems(versioned.Items, versioned.Version); + + } + if (!ErrorOccured) Writer->writeToStream(OS); @@ -934,10 +996,34 @@ namespace { /// The module we're building. Module TheModule; + /// A known context, which tracks what we know about a context ID. + struct KnownContext { + /// Whether this is a protocol (vs. a class). + bool isProtocol; + + /// The indices into the top-level items for this context at each + /// Swift version. + SmallVector<std::pair<VersionTuple, unsigned>, 1> indices; + + Class &getContext(const VersionTuple &swiftVersion, + TopLevelItems &items) { + ClassesSeq &seq = isProtocol ? items.Protocols : items.Classes; + + for (auto &index : indices) { + if (index.first == swiftVersion) + return seq[index.second]; + } + + indices.push_back({swiftVersion, seq.size()}); + seq.push_back(Class()); + return seq.back(); + } + }; + /// A mapping from context ID to a pair (index, is-protocol) that indicates /// the index of that class or protocol in the global "classes" or /// "protocols" list. - llvm::DenseMap<unsigned, std::pair<unsigned, bool>> knownContexts; + llvm::DenseMap<unsigned, KnownContext> knownContexts; /// Copy a string into allocated memory so it does disappear on us. StringRef copyString(StringRef string) { @@ -1015,30 +1101,46 @@ namespace { } } + TopLevelItems &getTopLevelItems(VersionTuple swiftVersion) { + if (!swiftVersion) return TheModule.TopLevel; + + for (auto &versioned : TheModule.SwiftVersions) { + if (versioned.Version == swiftVersion) + return versioned.Items; + } + + TheModule.SwiftVersions.push_back(Versioned()); + TheModule.SwiftVersions.back().Version = swiftVersion; + return TheModule.SwiftVersions.back().Items; + } + public: virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info) { + const ObjCContextInfo &info, + VersionTuple swiftVersion) { // Record this known context. - knownContexts[contextID.Value] = { TheModule.Classes.size(), false }; + auto &items = getTopLevelItems(swiftVersion); + auto &known = knownContexts[contextID.Value]; + known.isProtocol = false; - // Add the class. - TheModule.Classes.push_back(Class()); - handleObjCContext(TheModule.Classes.back(), name, info); + handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info) { + const ObjCContextInfo &info, + VersionTuple swiftVersion) { // Record this known context. - knownContexts[contextID.Value] = { TheModule.Protocols.size(), true }; + auto &items = getTopLevelItems(swiftVersion); + auto &known = knownContexts[contextID.Value]; + known.isProtocol = true; - // Add the protocol. - TheModule.Protocols.push_back(Class()); - handleObjCContext(TheModule.Protocols.back(), name, info); + handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info) { + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { Method method; method.Selector = copyString(selector); method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; @@ -1051,16 +1153,15 @@ namespace { method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; - auto known = knownContexts[contextID.Value]; - if (known.second) - TheModule.Protocols[known.first].Methods.push_back(method); - else - TheModule.Classes[known.first].Methods.push_back(method); + auto &items = getTopLevelItems(swiftVersion); + knownContexts[contextID.Value].getContext(swiftVersion, items) + .Methods.push_back(method); } virtual void visitObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info) { + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { Property property; property.Name = name; property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class; @@ -1071,15 +1172,14 @@ namespace { property.Nullability = *nullability; } - auto known = knownContexts[contextID.Value]; - if (known.second) - TheModule.Protocols[known.first].Properties.push_back(property); - else - TheModule.Classes[known.first].Properties.push_back(property); + auto &items = getTopLevelItems(swiftVersion); + knownContexts[contextID.Value].getContext(swiftVersion, items) + .Properties.push_back(property); } virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info) { + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { Function function; function.Name = name; handleCommon(function, info); @@ -1088,11 +1188,13 @@ namespace { handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); - TheModule.Functions.push_back(function); + auto &items = getTopLevelItems(swiftVersion); + items.Functions.push_back(function); } virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info) { + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { GlobalVariable global; global.Name = name; handleCommon(global, info); @@ -1102,30 +1204,37 @@ namespace { global.Nullability = *nullability; } - TheModule.Globals.push_back(global); + auto &items = getTopLevelItems(swiftVersion); + items.Globals.push_back(global); } virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info) { + const EnumConstantInfo &info, + VersionTuple swiftVersion) { EnumConstant enumConstant; enumConstant.Name = name; handleCommon(enumConstant, info); - TheModule.EnumConstants.push_back(enumConstant); + auto &items = getTopLevelItems(swiftVersion); + items.EnumConstants.push_back(enumConstant); } - virtual void visitTag(StringRef name, const TagInfo &info) { + virtual void visitTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion) { Tag tag; tag.Name = name; handleCommonType(tag, info); - TheModule.Tags.push_back(tag); + auto &items = getTopLevelItems(swiftVersion); + items.Tags.push_back(tag); } - virtual void visitTypedef(StringRef name, const TypedefInfo &info) { + virtual void visitTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion) { Typedef td; td.Name = name; handleCommonType(td, info); - TheModule.Typedefs.push_back(td); + auto &items = getTopLevelItems(swiftVersion); + items.Typedefs.push_back(td); } /// Retrieve the module. @@ -1138,38 +1247,16 @@ static unsigned flattenPropertyKind(llvm::Optional<MethodKind> kind) { return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0; } -bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, - llvm::raw_ostream &os) { - // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); - if (!reader) { - llvm::errs() << "not a well-formed API notes binary file\n"; - return true; - } - - DecompileVisitor decompileVisitor; - reader->visit(decompileVisitor); - - // Sort the data in the module, because the API notes reader doesn't preserve - // order. - auto &module = decompileVisitor.getModule(); - - // Set module name. - module.Name = reader->getModuleName(); - - // Set module options - auto opts = reader->getModuleOptions(); - if (opts.SwiftInferImportAsMember) - module.SwiftInferImportAsMember = true; - +/// Sort the items in the given block of "top-level" items. +static void sortTopLevelItems(TopLevelItems &items) { // Sort classes. - std::sort(module.Classes.begin(), module.Classes.end(), + std::sort(items.Classes.begin(), items.Classes.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort protocols. - std::sort(module.Protocols.begin(), module.Protocols.end(), + std::sort(items.Protocols.begin(), items.Protocols.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); @@ -1180,52 +1267,90 @@ bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, std::sort(record.Properties.begin(), record.Properties.end(), [](const Property &lhs, const Property &rhs) -> bool { return lhs.Name < rhs.Name || - (lhs.Name == rhs.Name && - flattenPropertyKind(lhs.Kind) < - flattenPropertyKind(rhs.Kind)); + (lhs.Name == rhs.Name && + flattenPropertyKind(lhs.Kind) < + flattenPropertyKind(rhs.Kind)); }); // Sort methods. std::sort(record.Methods.begin(), record.Methods.end(), [](const Method &lhs, const Method &rhs) -> bool { return lhs.Selector < rhs.Selector || - (lhs.Selector == rhs.Selector && - static_cast<unsigned>(lhs.Kind) - < static_cast<unsigned>(rhs.Kind)); + (lhs.Selector == rhs.Selector && + static_cast<unsigned>(lhs.Kind) + < static_cast<unsigned>(rhs.Kind)); }); }; - std::for_each(module.Classes.begin(), module.Classes.end(), sortMembers); - std::for_each(module.Protocols.begin(), module.Protocols.end(), sortMembers); + std::for_each(items.Classes.begin(), items.Classes.end(), sortMembers); + std::for_each(items.Protocols.begin(), items.Protocols.end(), sortMembers); // Sort functions. - std::sort(module.Functions.begin(), module.Functions.end(), + std::sort(items.Functions.begin(), items.Functions.end(), [](const Function &lhs, const Function &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort global variables. - std::sort(module.Globals.begin(), module.Globals.end(), + std::sort(items.Globals.begin(), items.Globals.end(), [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort enum constants. - std::sort(module.EnumConstants.begin(), module.EnumConstants.end(), + std::sort(items.EnumConstants.begin(), items.EnumConstants.end(), [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort tags. - std::sort(module.Tags.begin(), module.Tags.end(), + std::sort(items.Tags.begin(), items.Tags.end(), [](const Tag &lhs, const Tag &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort typedefs. - std::sort(module.Typedefs.begin(), module.Typedefs.end(), + std::sort(items.Typedefs.begin(), items.Typedefs.end(), [](const Typedef &lhs, const Typedef &rhs) -> bool { return lhs.Name < rhs.Name; }); +} + +bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, + llvm::raw_ostream &os) { + // Try to read the file. + auto reader = APINotesReader::get(std::move(input)); + if (!reader) { + llvm::errs() << "not a well-formed API notes binary file\n"; + return true; + } + + DecompileVisitor decompileVisitor; + reader->visit(decompileVisitor); + + // Sort the data in the module, because the API notes reader doesn't preserve + // order. + auto &module = decompileVisitor.getModule(); + + // Set module name. + module.Name = reader->getModuleName(); + + // Set module options + auto opts = reader->getModuleOptions(); + if (opts.SwiftInferImportAsMember) + module.SwiftInferImportAsMember = true; + + // Sort the top-level items. + sortTopLevelItems(module.TopLevel); + + // Sort the Swift versions. + std::sort(module.SwiftVersions.begin(), module.SwiftVersions.end(), + [](const Versioned &lhs, const Versioned &rhs) -> bool { + return lhs.Version < rhs.Version; + }); + + // Sort the top-level items within each Swift version. + for (auto &versioned : module.SwiftVersions) + sortTopLevelItems(versioned.Items); // Output the YAML representation. Output yout(os); diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 29861d140f242..4a666ccbd9421 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -352,8 +352,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C classes. if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCClass(Class->getName())) { - ::ProcessAPINotes(*this, Class, Info->second); + if (auto Info = Reader->lookupObjCClassInfo(Class->getName())) { + ::ProcessAPINotes(*this, Class, *Info); } } @@ -363,8 +363,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C protocols. if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCProtocol(Protocol->getName())) { - ::ProcessAPINotes(*this, Protocol, Info->second); + if (auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName())) { + ::ProcessAPINotes(*this, Protocol, *Info); } } @@ -414,8 +414,8 @@ void Sema::ProcessAPINotes(Decl *D) { auto GetContext = [&](api_notes::APINotesReader *Reader) -> Optional<api_notes::ContextID> { if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) { - if (auto Found = Reader->lookupObjCProtocol(Protocol->getName())) - return Found->first; + if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName())) + return *Found; return None; } @@ -442,8 +442,8 @@ void Sema::ProcessAPINotes(Decl *D) { } if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) { - if (auto Found = Reader->lookupObjCClass(Class->getName())) - return Found->first; + if (auto Found = Reader->lookupObjCClassID(Class->getName())) + return *Found; return None; diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 10ac249817d73..c0e91f93fde0f 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -146,3 +146,22 @@ Typedefs: SwiftName: Typedef SwiftBridge: '' NSErrorDomain: '' +SwiftVersions: + - Version: 3.0 + Classes: + - Name: NSCell + Availability: available + AvailabilityMsg: '' + SwiftPrivate: false + SwiftName: NSBox + SwiftBridge: '' + NSErrorDomain: '' + Methods: + - Selector: init + MethodKind: Instance + NullabilityOfRet: N + Availability: available + AvailabilityMsg: '' + SwiftPrivate: true + SwiftName: '' + DesignatedInit: true From 940124f4ac92308177e259f881916cf98b733261 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 3 Oct 2016 00:12:26 -0700 Subject: [PATCH 093/582] [Modules] Fix module hash computation when module file extensions are involved. Fix an egregious error in with modules and module file extensions, where only the hash code of the module file extension and sysroots would affect the module cache, leading to module file collisions. The effect of this error was likely masked by the old client of module file extensions (Swift) embedding Clang version information. apple-llvm-split-commit: 977557058d1208e7d099569d3327c00c1aa4b1cf apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInvocation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 3b3870df95598..663364ddb966d 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2548,7 +2548,9 @@ std::string CompilerInvocation::getModuleHash() const { // Extend the signature with the module file extensions. const FrontendOptions &frontendOpts = getFrontendOpts(); for (const auto &ext : frontendOpts.ModuleFileExtensions) { - code = ext->hashExtension(code); + code = hash_combine(code, ext->hashExtension(code)); + } + } // Darwin-specific hack: if we have a sysroot, use the contents and From 11403d1119df7dd1709fc657c99357fedb15a169 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 3 Oct 2016 00:12:34 -0700 Subject: [PATCH 094/582] [API Notes] Add a command-line option for Swift version selection. Introduce -fapinotes-swift-version=XX.YY, which sets the Swift version to use when applying versioned API notes to the Clang AST. When set, a versioned API note that matches the provided Swift version will take precedence over an unversioned API note. More of rdar://problem/28455809. apple-llvm-split-commit: e48790bfa156772574ef81db98f79da29eebe445 apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesManager.h | 9 ++ .../include/clang/APINotes/APINotesOptions.h | 4 + clang/include/clang/APINotes/APINotesReader.h | 84 ++++++++++++--- clang/include/clang/Driver/Options.td | 3 + clang/lib/APINotes/APINotesManager.cpp | 7 +- clang/lib/APINotes/APINotesReader.cpp | 102 +++++++++++------- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 2 +- clang/lib/Frontend/CompilerInstance.cpp | 3 + clang/lib/Frontend/CompilerInvocation.cpp | 20 +++- .../APINotes/SomeKit.apinotes | 9 ++ clang/test/APINotes/nullability.m | 12 ++- 11 files changed, 195 insertions(+), 60 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index a300c14ee9dd3..2adc29c0bf4c2 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -16,6 +16,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Module.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" @@ -55,6 +56,9 @@ class APINotesManager { /// source file from which an entity was declared. bool ImplicitAPINotes; + /// The Swift version to use when interpreting versioned API notes. + VersionTuple SwiftVersion; + /// API notes readers for the current module. /// /// There can be up to two of these, one for public headers and one @@ -109,6 +113,11 @@ class APINotesManager { APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts); ~APINotesManager(); + /// Set the Swift version to use when filtering API notes. + void setSwiftVersion(VersionTuple swiftVersion) { + SwiftVersion = swiftVersion; + } + /// Load the API notes for the current module. /// /// \param module The current module. diff --git a/clang/include/clang/APINotes/APINotesOptions.h b/clang/include/clang/APINotes/APINotesOptions.h index 01c7513a1d317..24bb9134b21b2 100644 --- a/clang/include/clang/APINotes/APINotesOptions.h +++ b/clang/include/clang/APINotes/APINotesOptions.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H #define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H +#include "clang/Basic/VersionTuple.h" #include <string> #include <vector> @@ -23,6 +24,9 @@ namespace clang { /// notes are found and handled. class APINotesOptions { public: + /// The Swift version which should be used for API notes. + VersionTuple SwiftVersion; + /// The set of search paths where we API notes can be found for /// particular modules. /// diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 593e8174d4e08..a09bcc76df181 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -33,7 +33,7 @@ class APINotesReader { Implementation &Impl; APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, - bool &failed); + VersionTuple swiftVersion, bool &failed); public: /// Create a new API notes reader from the given member buffer, which @@ -41,14 +41,16 @@ class APINotesReader { /// /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr<APINotesReader> - get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer); + get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, + VersionTuple swiftVersion); /// Create a new API notes reader from the given member buffer, which /// contains the contents of a binary API notes file. /// /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr<APINotesReader> - getUnmanaged(llvm::MemoryBuffer *inputBuffer); + getUnmanaged(llvm::MemoryBuffer *inputBuffer, + VersionTuple swiftVersion); ~APINotesReader(); @@ -66,6 +68,56 @@ class APINotesReader { /// Retrieve the module options ModuleOptions getModuleOptions() const; + /// Captures the completed versioned information for a particular part of + /// API notes, including both unversioned API notes and each versioned API + /// note for that particular entity. + template<typename T> + class VersionedInfo { + /// The complete set of results. + SmallVector<std::pair<VersionTuple, T>, 1> Results; + + /// The index of the result that is the "selected" set based on the desired + /// Swift version, or \c Results.size() if nothing matched. + unsigned Selected; + + public: + /// Form an empty set of versioned information. + VersionedInfo(llvm::NoneType) : Selected(0) { } + + /// Form a versioned info set given the desired version and a set of + /// results. + VersionedInfo(VersionTuple version, + SmallVector<std::pair<VersionTuple, T>, 1> results); + + /// Determine whether there is a result that should be applied directly + /// to the AST. + explicit operator bool() const { return Selected != size(); } + + /// Retrieve the information to apply directly to the AST. + const T& operator*() const { + assert(*this && "No result to apply directly"); + return (*this)[Selected].second; + } + + /// Retrieve the selected index in the result set. + Optional<unsigned> getSelected() const { + if (Selected == Results.size()) return None; + return Selected; + } + + /// Return the number of versioned results we know about. + unsigned size() const { return Results.size(); } + + /// Access all versioned results. + const std::pair<VersionTuple, T> *begin() const { return Results.begin(); } + const std::pair<VersionTuple, T> *end() const { return Results.end(); } + + /// Access a specific versioned result. + const std::pair<VersionTuple, T> &operator[](unsigned index) const { + return Results[index]; + } + }; + /// Look for the context ID of the given Objective-C class. /// /// \param name The name of the class we're looking for. @@ -78,7 +130,7 @@ class APINotesReader { /// \param name The name of the class we're looking for. /// /// \returns The information about the class, if known. - Optional<ObjCContextInfo> lookupObjCClassInfo(StringRef name); + VersionedInfo<ObjCContextInfo> lookupObjCClassInfo(StringRef name); /// Look for the context ID of the given Objective-C protocol. /// @@ -92,7 +144,7 @@ class APINotesReader { /// \param name The name of the protocol we're looking for. /// /// \returns The information about the protocol, if known. - Optional<ObjCContextInfo> lookupObjCProtocolInfo(StringRef name); + VersionedInfo<ObjCContextInfo> lookupObjCProtocolInfo(StringRef name); /// Look for information regarding the given Objective-C property in /// the given context. @@ -104,9 +156,9 @@ class APINotesReader { /// \param swiftVersion The Swift version to filter for, if any. /// /// \returns Information about the property, if known. - Optional<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID, - StringRef name, - bool isInstance); + VersionedInfo<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance); /// Look for information regarding the given Objective-C method in /// the given context. @@ -116,30 +168,30 @@ class APINotesReader { /// \param isInstanceMethod Whether we are looking for an instance method. /// /// \returns Information about the method, if known. - Optional<ObjCMethodInfo> lookupObjCMethod(ContextID contextID, - ObjCSelectorRef selector, - bool isInstanceMethod); + VersionedInfo<ObjCMethodInfo> lookupObjCMethod(ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod); /// Look for information regarding the given global variable. /// /// \param name The name of the global variable. /// /// \returns information about the global variable, if known. - Optional<GlobalVariableInfo> lookupGlobalVariable(StringRef name); + VersionedInfo<GlobalVariableInfo> lookupGlobalVariable(StringRef name); /// Look for information regarding the given global function. /// /// \param name The name of the global function. /// /// \returns information about the global function, if known. - Optional<GlobalFunctionInfo> lookupGlobalFunction(StringRef name); + VersionedInfo<GlobalFunctionInfo> lookupGlobalFunction(StringRef name); /// Look for information regarding the given enumerator. /// /// \param name The name of the enumerator. /// /// \returns information about the enumerator, if known. - Optional<EnumConstantInfo> lookupEnumConstant(StringRef name); + VersionedInfo<EnumConstantInfo> lookupEnumConstant(StringRef name); /// Look for information regarding the given tag /// (struct/union/enum/C++ class). @@ -147,14 +199,14 @@ class APINotesReader { /// \param name The name of the tag. /// /// \returns information about the tag, if known. - Optional<TagInfo> lookupTag(StringRef name); + VersionedInfo<TagInfo> lookupTag(StringRef name); /// Look for information regarding the given typedef. /// /// \param name The name of the typedef. /// /// \returns information about the typedef, if known. - Optional<TypedefInfo> lookupTypedef(StringRef name); + VersionedInfo<TypedefInfo> lookupTypedef(StringRef name); /// Visitor used when walking the contents of the API notes file. class Visitor { diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 4c2e97d667502..b055f2ced00b1 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -558,6 +558,9 @@ def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group<f_clang_Gr def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, Group<i_Group>, Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">, HelpText<"Specify the API notes cache path">; +def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, + Group<f_clang_Group>, Flags<[CC1Option]>, MetaVarName<"<version>">, + HelpText<"Specify the Swift version to use when filtering API notes">; def fblocks : Flag<["-"], "fblocks">, Group<f_Group>, Flags<[CC1Option]>, HelpText<"Enable the 'blocks' language feature">; diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 834d7e1f72f41..1622f8f40675a 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -153,7 +153,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { if (!buffer) return nullptr; // Load the binary form. - return APINotesReader::getUnmanaged(buffer); + return APINotesReader::getUnmanaged(buffer, SwiftVersion); } // If we haven't pruned the API notes cache yet during this execution, do @@ -184,7 +184,8 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { // Load the file contents. if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { // Load the file. - if (auto reader = APINotesReader::get(std::move(buffer.get()))) { + if (auto reader = APINotesReader::get(std::move(buffer.get()), + SwiftVersion)) { bool outOfDate = false; if (auto sizeAndModTime = reader->getSourceFileSizeAndModTime()) { if (sizeAndModTime->first != apiNotesFile->getSize() || @@ -272,7 +273,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { } // Load the binary form we just compiled. - auto reader = APINotesReader::get(std::move(compiledBuffer)); + auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion); assert(reader && "Could not load the API notes we just generated?"); return reader; } diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 71e1e411602b2..dda721e411293 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -486,6 +486,9 @@ class APINotesReader::Implementation { /// Whether we own the input buffer. bool OwnsInputBuffer; + /// The Swift version to use for filtering. + VersionTuple SwiftVersion; + /// The reader attached to \c InputBuffer. llvm::BitstreamReader InputReader; @@ -1252,6 +1255,7 @@ bool APINotesReader::Implementation::readTypedefBlock( APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, + VersionTuple swiftVersion, bool &failed) : Impl(*new Implementation) { @@ -1260,6 +1264,7 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, // Initialize the input buffer. Impl.InputBuffer = inputBuffer; Impl.OwnsInputBuffer = ownsInputBuffer; + Impl.SwiftVersion = swiftVersion; Impl.InputReader.init( reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferStart()), reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferEnd())); @@ -1399,11 +1404,12 @@ APINotesReader::~APINotesReader() { } std::unique_ptr<APINotesReader> -APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer) { +APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, + VersionTuple swiftVersion) { bool failed = false; std::unique_ptr<APINotesReader> reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true, - failed)); + swiftVersion, failed)); if (failed) return nullptr; @@ -1411,11 +1417,12 @@ APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer) { } std::unique_ptr<APINotesReader> -APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer) { +APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer, + VersionTuple swiftVersion) { bool failed = false; std::unique_ptr<APINotesReader> reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false, - failed)); + swiftVersion, failed)); if (failed) return nullptr; @@ -1435,15 +1442,31 @@ ModuleOptions APINotesReader::getModuleOptions() const { return Impl.ModuleOpts; } -namespace { - template<typename T> - Optional<T> getUnversioned( - const SmallVectorImpl<std::pair<VersionTuple, T>>& array) { - for (const auto &versioned : array) { - if (!versioned.first) return versioned.second; +template<typename T> +APINotesReader::VersionedInfo<T>::VersionedInfo( + VersionTuple version, + SmallVector<std::pair<VersionTuple, T>, 1> results) + : Results(std::move(results)) { + + // Look for an exact version match. + Optional<unsigned> unversioned; + Selected = Results.size(); + for (unsigned i = 0, n = Results.size(); i != n; ++i) { + if (Results[i].first == version) { + Selected = i; + break; + } + + if (!Results[i].first) { + assert(!unversioned && "Two unversioned entries?"); + unversioned = i; } - return None; } + + // If we didn't find a match but we have an unversioned result, use the + // unversioned result. + if (Selected == Results.size() && unversioned) + Selected = *unversioned; } auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> { @@ -1462,7 +1485,7 @@ auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> { } auto APINotesReader::lookupObjCClassInfo(StringRef name) - -> Optional<ObjCContextInfo> { + -> VersionedInfo<ObjCContextInfo> { if (!Impl.ObjCContextInfoTable) return None; @@ -1474,7 +1497,7 @@ auto APINotesReader::lookupObjCClassInfo(StringRef name) if (knownInfo == Impl.ObjCContextInfoTable->end()) return None; - return getUnversioned(*knownInfo); + return { Impl.SwiftVersion, *knownInfo }; } auto APINotesReader::lookupObjCProtocolID(StringRef name) @@ -1494,7 +1517,7 @@ auto APINotesReader::lookupObjCProtocolID(StringRef name) } auto APINotesReader::lookupObjCProtocolInfo(StringRef name) - -> Optional<ObjCContextInfo> { + -> VersionedInfo<ObjCContextInfo> { if (!Impl.ObjCContextInfoTable) return None; @@ -1506,13 +1529,14 @@ auto APINotesReader::lookupObjCProtocolInfo(StringRef name) if (knownInfo == Impl.ObjCContextInfoTable->end()) return None; - return getUnversioned(*knownInfo); + return { Impl.SwiftVersion, *knownInfo }; } -Optional<ObjCPropertyInfo> APINotesReader::lookupObjCProperty( - ContextID contextID, - StringRef name, - bool isInstance) { + +auto APINotesReader::lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance) + -> VersionedInfo<ObjCPropertyInfo> { if (!Impl.ObjCPropertyTable) return None; @@ -1526,13 +1550,14 @@ Optional<ObjCPropertyInfo> APINotesReader::lookupObjCProperty( if (known == Impl.ObjCPropertyTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional<ObjCMethodInfo> APINotesReader::lookupObjCMethod( - ContextID contextID, - ObjCSelectorRef selector, - bool isInstanceMethod) { +auto APINotesReader::lookupObjCMethod( + ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod) + -> VersionedInfo<ObjCMethodInfo> { if (!Impl.ObjCMethodTable) return None; @@ -1546,11 +1571,12 @@ Optional<ObjCMethodInfo> APINotesReader::lookupObjCMethod( if (known == Impl.ObjCMethodTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional<GlobalVariableInfo> APINotesReader::lookupGlobalVariable( - StringRef name) { +auto APINotesReader::lookupGlobalVariable( + StringRef name) + -> VersionedInfo<GlobalVariableInfo> { if (!Impl.GlobalVariableTable) return None; @@ -1562,11 +1588,11 @@ Optional<GlobalVariableInfo> APINotesReader::lookupGlobalVariable( if (known == Impl.GlobalVariableTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional<GlobalFunctionInfo> APINotesReader::lookupGlobalFunction( - StringRef name) { +auto APINotesReader::lookupGlobalFunction(StringRef name) + -> VersionedInfo<GlobalFunctionInfo> { if (!Impl.GlobalFunctionTable) return None; @@ -1578,10 +1604,11 @@ Optional<GlobalFunctionInfo> APINotesReader::lookupGlobalFunction( if (known == Impl.GlobalFunctionTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional<EnumConstantInfo> APINotesReader::lookupEnumConstant(StringRef name) { +auto APINotesReader::lookupEnumConstant(StringRef name) + -> VersionedInfo<EnumConstantInfo> { if (!Impl.EnumConstantTable) return None; @@ -1593,10 +1620,10 @@ Optional<EnumConstantInfo> APINotesReader::lookupEnumConstant(StringRef name) { if (known == Impl.EnumConstantTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional<TagInfo> APINotesReader::lookupTag(StringRef name) { +auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo<TagInfo> { if (!Impl.TagTable) return None; @@ -1608,10 +1635,11 @@ Optional<TagInfo> APINotesReader::lookupTag(StringRef name) { if (known == Impl.TagTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } -Optional<TypedefInfo> APINotesReader::lookupTypedef(StringRef name) { +auto APINotesReader::lookupTypedef(StringRef name) + -> VersionedInfo<TypedefInfo> { if (!Impl.TypedefTable) return None; @@ -1623,7 +1651,7 @@ Optional<TypedefInfo> APINotesReader::lookupTypedef(StringRef name) { if (known == Impl.TypedefTable->end()) return None; - return getUnversioned(*known); + return { Impl.SwiftVersion, *known }; } APINotesReader::Visitor::~Visitor() { } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index f15099e4677ca..8f1e5a29d4ebd 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -1318,7 +1318,7 @@ static void sortTopLevelItems(TopLevelItems &items) { bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, llvm::raw_ostream &os) { // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); + auto reader = APINotesReader::get(std::move(input), VersionTuple()); if (!reader) { llvm::errs() << "not a well-formed API notes binary file\n"; return true; diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 43d89485d8ab5..3696149f69476 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -540,6 +540,9 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); + // Set up API notes. + TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion); + // If we're building a module and are supposed to load API notes, // notify the API notes manager. if (auto currentModule = getPreprocessor().getCurrentModule()) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 663364ddb966d..6df547942d6bd 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1505,8 +1505,14 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.AddVFSOverlayFile(A->getValue()); } -static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args) { +static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args, + DiagnosticsEngine &diags) { using namespace options; + if (const Arg *A = Args.getLastArg(OPT_fapinotes_swift_version)) { + if (Opts.SwiftVersion.tryParse(A->getValue())) + diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } for (const Arg *A : Args.filtered(OPT_iapinotes_modules)) Opts.ModuleSearchPaths.push_back(A->getValue()); } @@ -2415,7 +2421,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, Res.getTargetOpts()); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args); - ParseAPINotesArgs(Res.getAPINotesOpts(), Args); + ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags); if (DashX == IK_AST || DashX == IK_LLVM_IR) { // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the @@ -2551,6 +2557,16 @@ std::string CompilerInvocation::getModuleHash() const { code = hash_combine(code, ext->hashExtension(code)); } + // Extend the signature with the SWift version for API notes. + const APINotesOptions &apiNotesOpts = getAPINotesOpts(); + if (apiNotesOpts.SwiftVersion) { + code = hash_combine(code, apiNotesOpts.SwiftVersion.getMajor()); + if (auto minor = apiNotesOpts.SwiftVersion.getMinor()) + code = hash_combine(code, *minor); + if (auto subminor = apiNotesOpts.SwiftVersion.getSubminor()) + code = hash_combine(code, *subminor); + if (auto build = apiNotesOpts.SwiftVersion.getBuild()) + code = hash_combine(code, *build); } // Darwin-specific hack: if we have a sysroot, use the contents and diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index e79a210e39aaf..9c855c6dc6ac8 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -31,3 +31,12 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true +SwiftVersions: + - Version: 3.0 + Classes: + - Name: A + Methods: + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: O + Nullability: [ O, S ] diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index fc149b465ea35..c11cea5bd520d 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -1,12 +1,22 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables. + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 + #import <SomeKit/SomeKit.h> int main() { A *a; - [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#if SWIFT_VERSION_3_0 + float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A * _Nullable'}} + [a transform: 0 integer: 0]; +#else + float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A *'}} + [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#endif [a setNonnullAInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} [A setNonnullAInstance: 0]; // no warning From 49e71de6b635d32c46de68a91cfc7de00a014198 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 3 Oct 2016 00:39:43 -0700 Subject: [PATCH 095/582] [API Notes] Look for framework API notes in Headers/PrivateHeaders. Rather than relying on the addition of a new "top-level" directory within a framework, have frameworks put API notes alongside the headers. apple-llvm-split-commit: 1237d45515611f604812d85758bf0bf0fea927e1 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesManager.cpp | 16 ++++--- .../Headers/SomeKit.apinotes | 42 +++++++++++++++++++ .../Headers/SomeKitExplicitNullability.h | 5 +++ .../PrivateHeaders/SomeKit_private.apinotes | 15 +++++++ .../Headers/SomeOtherKit.apinotes | 8 ++++ 5 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 1622f8f40675a..5d50d3cd1dd4e 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -396,14 +396,20 @@ bool APINotesManager::loadCurrentModuleAPINotes( }; if (module->IsFramework) { - // For frameworks, we search in the "APINotes" subdirectory. + // For frameworks, we search in the "Headers" or "PrivateHeaders" + // subdirectory. llvm::SmallString<128> path; path += module->Directory->getName(); - llvm::sys::path::append(path, "APINotes"); - if (auto apinotesDir = fileMgr.getDirectory(path)) { + unsigned pathLen = path.size(); + + llvm::sys::path::append(path, "Headers"); + if (auto apinotesDir = fileMgr.getDirectory(path)) tryAPINotes(apinotesDir, /*wantPublic=*/true); - tryAPINotes(apinotesDir, /*wantPublic=*/false); - } + + path.resize(pathLen); + llvm::sys::path::append(path, "PrivateHeaders"); + if (auto privateAPINotesDir = fileMgr.getDirectory(path)) + tryAPINotes(privateAPINotesDir, /*wantPublic=*/false); } else { tryAPINotes(module->Directory, /*wantPublic=*/true); tryAPINotes(module->Directory, /*wantPublic=*/false); diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes new file mode 100644 index 0000000000000..9c855c6dc6ac8 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes @@ -0,0 +1,42 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "transform:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: intValue + PropertyKind: Instance + Availability: none + AvailabilityMsg: "wouldn't work anyway" + - Name: nonnullAInstance + PropertyKind: Instance + Nullability: N + - Name: nonnullAClass + PropertyKind: Class + Nullability: N + - Name: nonnullABoth + Nullability: N + - Name: B + Availability: none + AvailabilityMsg: "just don't" + - Name: C + Methods: + - Selector: "initWithA:" + MethodKind: Instance + DesignatedInit: true +SwiftVersions: + - Version: 3.0 + Classes: + - Name: A + Methods: + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: O + Nullability: [ O, S ] diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h new file mode 100644 index 0000000000000..40be241eb934f --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h @@ -0,0 +1,5 @@ +@interface A(ExplicitNullabilityProperties) +@property (nonatomic, readwrite, retain, nonnull) A *explicitNonnullInstance; +@property (nonatomic, readwrite, retain, nullable) A *explicitNullableInstance; +@end + diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes new file mode 100644 index 0000000000000..28ede9dfa25c0 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes @@ -0,0 +1,15 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "privateTransform:input:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: internalProperty + Nullability: N +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes new file mode 100644 index 0000000000000..2ad546b8f8bcc --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes @@ -0,0 +1,8 @@ +Name: SomeOtherKit +Classes: + - Name: A + Methods: + - Selector: "methodA" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" From e1edc62ef482909c2ddce792f9c5d700dd34aad8 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 3 Oct 2016 11:41:02 -0700 Subject: [PATCH 096/582] [API Notes] Fix serialization of FactoryAsInit data. apple-llvm-split-commit: 8099c09e822b238ae700ab27d8a6d45e2b3adcf3 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesWriter.cpp | 6 +++--- clang/test/APINotes/Inputs/roundtrip.apinotes | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 36c504afc34cd..6debe997bf001 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -721,9 +721,9 @@ namespace { } void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) { - uint8_t payload = info.FactoryAsInit << 2; - payload = (payload | info.DesignatedInit) << 1; - payload = (payload | info.Required); + uint8_t payload = info.FactoryAsInit; + payload = (payload << 1) | info.DesignatedInit; + payload = (payload << 1) | info.Required; endian::Writer<little> writer(out); writer.write<uint8_t>(payload); diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index c0e91f93fde0f..13e08a1126b0b 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -12,6 +12,13 @@ Classes: SwiftBridge: '' NSErrorDomain: '' Methods: + - Selector: 'cellWithImage:' + MethodKind: Class + Availability: available + AvailabilityMsg: '' + SwiftPrivate: false + SwiftName: '' + FactoryAsInit: C - Selector: init MethodKind: Instance NullabilityOfRet: U From 1e92684e88abd8a69aff45fc4a5f5593274a0cae Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 4 Oct 2016 10:41:35 -0700 Subject: [PATCH 097/582] [API Notes] Allow versioned API notes to override information in headers. When a versioned API note is used, allow it to override information explicitly described in a header. This allows a workflow where the headers are considered the "truth" for the latest version of Swift, but version-specific API notes can reset the state of the headers back to a prior version of Swift. At present, this only effectively works for nullability, because most of the Boolean or naming-related API notes fields don't distinguish between "not specified" and "false/empty". Part of rdar://problem/28455809. apple-llvm-split-commit: abcdca4e15477521394af9735db6b04c6f0d5892 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 22 ++ clang/include/clang/AST/AttrIterator.h | 2 + clang/include/clang/Sema/Sema.h | 6 +- clang/lib/APINotes/APINotesReader.cpp | 11 +- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 1 - clang/lib/Sema/SemaAPINotes.cpp | 311 ++++++++++++------ clang/lib/Sema/SemaType.cpp | 37 ++- .../APINotes/SomeKit.apinotes | 7 + .../Headers/SomeKit.apinotes | 7 + .../SomeKit.framework/Headers/SomeKit.h | 2 + .../Headers/VersionedKit.apinotes | 6 + .../Headers/VersionedKit.h | 1 + .../Modules/module.modulemap | 5 + clang/test/APINotes/nullability.m | 12 +- clang/test/APINotes/versioned.m | 15 + 15 files changed, 342 insertions(+), 103 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/versioned.m diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index a09bcc76df181..f96c7b65c9154 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -25,6 +25,20 @@ namespace clang { namespace api_notes { +/// Describes the role of a specific bit of versioned information. +enum class VersionedInfoRole : unsigned { + /// Augment the AST, but do not override information explicitly specified + /// in the source code. + AugmentSource, + + /// Replace information that may have been explicitly specified in the source + /// code. + ReplaceSource, + + /// Describes an alternate version of this information. + Versioned, +}; + /// A class that reads API notes data from a binary file that was written by /// the \c APINotesWriter. class APINotesReader { @@ -80,6 +94,9 @@ class APINotesReader { /// Swift version, or \c Results.size() if nothing matched. unsigned Selected; + /// The role of the selected index. + VersionedInfoRole SelectedRole; + public: /// Form an empty set of versioned information. VersionedInfo(llvm::NoneType) : Selected(0) { } @@ -105,6 +122,11 @@ class APINotesReader { return Selected; } + /// Describes the role of the selected entity. + VersionedInfoRole getSelectedRole() const { + return SelectedRole; + } + /// Return the number of versioned results we know about. unsigned size() const { return Results.size(); } diff --git a/clang/include/clang/AST/AttrIterator.h b/clang/include/clang/AST/AttrIterator.h index fb9b049e5d6b1..5ef250cf81499 100644 --- a/clang/include/clang/AST/AttrIterator.h +++ b/clang/include/clang/AST/AttrIterator.h @@ -108,6 +108,8 @@ class specific_attr_iterator { specific_attr_iterator Right) { return !(Left == Right); } + + Iterator getCurrent() const { return Current; } }; template <typename SpecificAttr, typename Container> diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 06800e3ece627..518aee137db12 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3140,11 +3140,15 @@ class Sema { /// method) or an Objective-C property attribute, rather than as an /// underscored type specifier. /// + /// \param overrideExisting Whether to override an existing, locally-specified + /// nullability specifier rather than complaining about the conflict. + /// /// \returns true if nullability cannot be applied, false otherwise. bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability, SourceLocation nullabilityLoc, bool isContextSensitive, - bool implicit); + bool implicit, + bool overrideExisting = false); /// \brief Stmt attributes - this routine is the top level dispatcher. StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs, diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index dda721e411293..964cc1bc6525a 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -1451,9 +1451,14 @@ APINotesReader::VersionedInfo<T>::VersionedInfo( // Look for an exact version match. Optional<unsigned> unversioned; Selected = Results.size(); + SelectedRole = VersionedInfoRole::Versioned; + for (unsigned i = 0, n = Results.size(); i != n; ++i) { if (Results[i].first == version) { Selected = i; + + if (version) SelectedRole = VersionedInfoRole::ReplaceSource; + else SelectedRole = VersionedInfoRole::AugmentSource; break; } @@ -1465,9 +1470,11 @@ APINotesReader::VersionedInfo<T>::VersionedInfo( // If we didn't find a match but we have an unversioned result, use the // unversioned result. - if (Selected == Results.size() && unversioned) + if (Selected == Results.size() && unversioned) { Selected = *unversioned; -} + SelectedRole = VersionedInfoRole::AugmentSource; + } + } auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> { if (!Impl.ObjCContextIDTable) diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 8f1e5a29d4ebd..868544f6afd00 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -929,7 +929,6 @@ namespace { // Convert the versioned information. for (const auto &versioned : TheModule.SwiftVersions) { convertTopLevelItems(versioned.Items, versioned.Version); - } if (!ErrorOccured) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 4a666ccbd9421..4d4ea6eee8d50 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/APINotes/APINotesReader.h" using namespace clang; +using clang::api_notes::VersionedInfoRole; /// Determine whether this is a multi-level pointer type. static bool isMultiLevelPointerType(QualType type) { @@ -27,7 +28,23 @@ static bool isMultiLevelPointerType(QualType type) { } // Apply nullability to the given declaration. -static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability) { +static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, + VersionedInfoRole role) { + bool overrideExisting; + switch (role) { + case VersionedInfoRole::AugmentSource: + overrideExisting = false; + break; + + case VersionedInfoRole::ReplaceSource: + overrideExisting = true; + break; + + case VersionedInfoRole::Versioned: + // FIXME: Record versioned info? + return; + } + QualType type; // Nullability for a function/method appertains to the retain type. @@ -47,7 +64,8 @@ static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability) { QualType origType = type; S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(), /*isContextSensitive=*/false, - /*implicit=*/true); + /*implicit=*/true, + overrideExisting); if (type.getTypePtr() == origType.getTypePtr()) return; @@ -100,104 +118,196 @@ static StringRef CopyString(ASTContext &ctx, StringRef string) { return StringRef(static_cast<char *>(mem), string.size()); } +namespace { + /// Handle an attribute introduced by API notes. + /// + /// \param shouldAddAttribute Whether we should add a new attribute + /// (otherwise, we might remove an existing attribute). + /// \param createAttr Create the new attribute to be added. + /// \param getExistingAttr Get an existing, matching attribute on the given + /// declaration. + template<typename A> + void handleAPINotedAttribute( + Sema &S, Decl *D, bool shouldAddAttribute, + VersionedInfoRole role, + llvm::function_ref<A *()> createAttr, + llvm::function_ref<specific_attr_iterator<A>(Decl *)> getExistingAttr = + [](Decl *decl) { return decl->specific_attr_begin<A>(); }) { + switch (role) { + case VersionedInfoRole::AugmentSource: + // If we're not adding an attribute, there's nothing to do. + if (!shouldAddAttribute) return; + + // If the attribute is already present, we're done. + if (getExistingAttr(D) != D->specific_attr_end<A>()) return; + + // Add the attribute. + if (auto attr = createAttr()) + D->addAttr(attr); + break; + + case VersionedInfoRole::ReplaceSource: { + auto end = D->specific_attr_end<A>(); + auto existing = getExistingAttr(D); + if (existing != end) { + // Remove the existing attribute. + D->getAttrs().erase(existing.getCurrent()); + } + + // If we're supposed to add a new attribute, do so. + if (shouldAddAttribute) { + if (auto attr = createAttr()) { + D->addAttr(attr); + } + } + break; + } + + case VersionedInfoRole::Versioned: + // FIXME: Retain versioned attributes separately. + break; + } + } +} + static void ProcessAPINotes(Sema &S, Decl *D, - const api_notes::CommonEntityInfo &Info) { + const api_notes::CommonEntityInfo &info, + VersionedInfoRole role) { // Availability - if (Info.Unavailable && !D->hasAttr<UnavailableAttr>()) { - D->addAttr(UnavailableAttr::CreateImplicit(S.Context, - CopyString(S.Context, - Info.UnavailableMsg))); + if (info.Unavailable) { + handleAPINotedAttribute<UnavailableAttr>(S, D, true, role, + [&] { + return UnavailableAttr::CreateImplicit(S.Context, + CopyString(S.Context, + info.UnavailableMsg)); + }); } - if (Info.UnavailableInSwift) { - D->addAttr(AvailabilityAttr::CreateImplicit( - S.Context, - &S.Context.Idents.get("swift"), - VersionTuple(), - VersionTuple(), - VersionTuple(), - /*Unavailable=*/true, - CopyString(S.Context, Info.UnavailableMsg), - /*Strict=*/false, - /*Replacement=*/StringRef())); + if (info.UnavailableInSwift) { + handleAPINotedAttribute<AvailabilityAttr>(S, D, true, role, [&] { + return AvailabilityAttr::CreateImplicit( + S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), + VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + CopyString(S.Context, info.UnavailableMsg), + /*Strict=*/false, + /*Replacement=*/StringRef()); + }, + [](Decl *decl) { + auto existing = decl->specific_attr_begin<AvailabilityAttr>(), + end = decl->specific_attr_end<AvailabilityAttr>(); + while (existing != end) { + if (auto platform = (*existing)->getPlatform()) { + if (platform->isStr("swift")) + break; + } + + ++existing; + } + + return existing; + }); } // swift_private - if (Info.SwiftPrivate && !D->hasAttr<SwiftPrivateAttr>()) { - D->addAttr(SwiftPrivateAttr::CreateImplicit(S.Context)); + if (info.SwiftPrivate) { + handleAPINotedAttribute<SwiftPrivateAttr>(S, D, true, role, [&] { + return SwiftPrivateAttr::CreateImplicit(S.Context); + }); } // swift_name - if (!Info.SwiftName.empty() && !D->hasAttr<SwiftNameAttr>()) { - auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); - - if (!S.DiagnoseSwiftName(D, Info.SwiftName, D->getLocation(), - &APINoteName)) { - return; - } - D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, - CopyString(S.Context, Info.SwiftName))); + if (!info.SwiftName.empty()) { + handleAPINotedAttribute<SwiftNameAttr>(S, D, true, role, + [&]() -> SwiftNameAttr * { + auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); + + if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(), + &APINoteName)) { + return nullptr; + } + + return SwiftNameAttr::CreateImplicit(S.Context, + CopyString(S.Context, + info.SwiftName)); + }); } } static void ProcessAPINotes(Sema &S, Decl *D, - const api_notes::CommonTypeInfo &Info) { + const api_notes::CommonTypeInfo &info, + VersionedInfoRole role) { // swift_bridge - if (!Info.getSwiftBridge().empty() && - !D->getAttr<SwiftBridgeAttr>()) { - D->addAttr( - SwiftBridgeAttr::CreateImplicit(S.Context, - CopyString(S.Context, - Info.getSwiftBridge()))); + if (!info.getSwiftBridge().empty()) { + handleAPINotedAttribute<SwiftBridgeAttr>(S, D, true, role, [&] { + return SwiftBridgeAttr::CreateImplicit(S.Context, + CopyString(S.Context, + info.getSwiftBridge())); + }); } // ns_error_domain - if (!Info.getNSErrorDomain().empty() && - !D->getAttr<NSErrorDomainAttr>()) { - D->addAttr( - NSErrorDomainAttr::CreateImplicit( - S.Context, - &S.Context.Idents.get(Info.getNSErrorDomain()))); + if (!info.getNSErrorDomain().empty()) { + handleAPINotedAttribute<NSErrorDomainAttr>(S, D, true, role, [&] { + return NSErrorDomainAttr::CreateImplicit( + S.Context, + &S.Context.Idents.get(info.getNSErrorDomain())); + }); } - ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), + role); } /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, - const api_notes::VariableInfo &Info) { + const api_notes::VariableInfo &info, + VersionedInfoRole role) { // Nullability. - if (auto Nullability = Info.getNullability()) { - applyNullability(S, D, *Nullability); + if (auto Nullability = info.getNullability()) { + applyNullability(S, D, *Nullability, role); } // Handle common entity information. - ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), + role); } /// Process API notes for a parameter. static void ProcessAPINotes(Sema &S, ParmVarDecl *D, - const api_notes::ParamInfo &Info) { + const api_notes::ParamInfo &info, + VersionedInfoRole role) { // noescape - if (Info.isNoEscape() && !D->getAttr<NoEscapeAttr>()) - D->addAttr(NoEscapeAttr::CreateImplicit(S.Context)); + if (info.isNoEscape()) { + handleAPINotedAttribute<NoEscapeAttr>(S, D, true, role, [&] { + return NoEscapeAttr::CreateImplicit(S.Context); + }); + } // Handle common entity information. - ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), + role); } /// Process API notes for a global variable. static void ProcessAPINotes(Sema &S, VarDecl *D, - const api_notes::GlobalVariableInfo &Info) { + const api_notes::GlobalVariableInfo &info, + VersionedInfoRole role) { // Handle common entity information. - ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), + role); } /// Process API notes for an Objective-C property. static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, - const api_notes::ObjCPropertyInfo &Info) { + const api_notes::ObjCPropertyInfo &info, + VersionedInfoRole role) { // Handle common entity information. - ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), + role); } namespace { @@ -206,7 +316,8 @@ namespace { /// Process API notes for a function or method. static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, - const api_notes::FunctionInfo &Info) { + const api_notes::FunctionInfo &info, + VersionedInfoRole role) { // Find the declaration itself. FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>(); Decl *D = FD; @@ -217,8 +328,8 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, } // Nullability of return type. - if (Info.NullabilityAudited) { - applyNullability(S, D, Info.getReturnTypeInfo()); + if (info.NullabilityAudited) { + applyNullability(S, D, info.getReturnTypeInfo(), role); } // Parameters. @@ -236,48 +347,57 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, Param = MD->param_begin()[I]; // Nullability. - if (Info.NullabilityAudited) - applyNullability(S, Param, Info.getParamTypeInfo(I)); + if (info.NullabilityAudited) + applyNullability(S, Param, info.getParamTypeInfo(I), role); - if (I < Info.Params.size()) { - ProcessAPINotes(S, Param, Info.Params[I]); + if (I < info.Params.size()) { + ProcessAPINotes(S, Param, info.Params[I], role); } } // Handle common entity information. - ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), + role); } /// Process API notes for a global function. static void ProcessAPINotes(Sema &S, FunctionDecl *D, - const api_notes::GlobalFunctionInfo &Info) { + const api_notes::GlobalFunctionInfo &info, + VersionedInfoRole role) { // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), - static_cast<const api_notes::FunctionInfo &>(Info)); + static_cast<const api_notes::FunctionInfo &>(info), role); } /// Process API notes for an enumerator. static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, - const api_notes::EnumConstantInfo &Info) { + const api_notes::EnumConstantInfo &info, + VersionedInfoRole role) { // Handle common information. ProcessAPINotes(S, D, - static_cast<const api_notes::CommonEntityInfo &>(Info)); + static_cast<const api_notes::CommonEntityInfo &>(info), + role); } /// Process API notes for an Objective-C method. static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, - const api_notes::ObjCMethodInfo &Info) { + const api_notes::ObjCMethodInfo &info, + VersionedInfoRole role) { // Designated initializers. - if (Info.DesignatedInit && !D->getAttr<ObjCDesignatedInitializerAttr>()) { - if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { - D->addAttr(ObjCDesignatedInitializerAttr::CreateImplicit(S.Context)); - IFace->setHasDesignatedInitializers(); - } + if (info.DesignatedInit) { + handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, role, [&] { + if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { + IFace->setHasDesignatedInitializers(); + } + return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context); + }); } - if (Info.getFactoryAsInitKind() + // FIXME: This doesn't work well with versioned API notes. + if (role == VersionedInfoRole::AugmentSource && + info.getFactoryAsInitKind() == api_notes::FactoryAsInitKind::AsClassMethod && !D->getAttr<SwiftNameAttr>()) { D->addAttr(SwiftSuppressFactoryAsInitAttr::CreateImplicit(S.Context)); @@ -285,36 +405,43 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), - static_cast<const api_notes::FunctionInfo &>(Info)); + static_cast<const api_notes::FunctionInfo &>(info), role); } /// Process API notes for a tag. static void ProcessAPINotes(Sema &S, TagDecl *D, - const api_notes::TagInfo &Info) { + const api_notes::TagInfo &info, + VersionedInfoRole role) { // Handle common type information. - ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), + role); } /// Process API notes for a typedef. static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, - const api_notes::TypedefInfo &Info) { + const api_notes::TypedefInfo &info, + VersionedInfoRole role) { // Handle common type information. - ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), + role); } /// Process API notes for an Objective-C class or protocol. static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, - const api_notes::ObjCContextInfo &Info) { + const api_notes::ObjCContextInfo &info, + VersionedInfoRole role) { // Handle common type information. - ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info)); + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), + role); } /// Process API notes for an Objective-C class. static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, - const api_notes::ObjCContextInfo &Info) { + const api_notes::ObjCContextInfo &info, + VersionedInfoRole role) { // Handle information common to Objective-C classes and protocols. - ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), Info); + ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info, role); } /// Process API notes that are associated with this declaration, mapping them @@ -329,7 +456,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto VD = dyn_cast<VarDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupGlobalVariable(VD->getName())) { - ::ProcessAPINotes(*this, VD, *Info); + ::ProcessAPINotes(*this, VD, *Info, Info.getSelectedRole()); } } @@ -341,7 +468,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (FD->getDeclName().isIdentifier()) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupGlobalFunction(FD->getName())) { - ::ProcessAPINotes(*this, FD, *Info); + ::ProcessAPINotes(*this, FD, *Info, Info.getSelectedRole()); } } } @@ -353,7 +480,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupObjCClassInfo(Class->getName())) { - ::ProcessAPINotes(*this, Class, *Info); + ::ProcessAPINotes(*this, Class, *Info, Info.getSelectedRole()); } } @@ -364,7 +491,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName())) { - ::ProcessAPINotes(*this, Protocol, *Info); + ::ProcessAPINotes(*this, Protocol, *Info, Info.getSelectedRole()); } } @@ -375,7 +502,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto Tag = dyn_cast<TagDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupTag(Tag->getName())) { - ::ProcessAPINotes(*this, Tag, *Info); + ::ProcessAPINotes(*this, Tag, *Info, Info.getSelectedRole()); } } @@ -386,7 +513,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupTypedef(Typedef->getName())) { - ::ProcessAPINotes(*this, Typedef, *Info); + ::ProcessAPINotes(*this, Typedef, *Info, Info.getSelectedRole()); } } @@ -401,7 +528,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { - ::ProcessAPINotes(*this, EnumConstant, *Info); + ::ProcessAPINotes(*this, EnumConstant, *Info, Info.getSelectedRole()); } } @@ -472,7 +599,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, Method->isInstanceMethod())){ - ::ProcessAPINotes(*this, Method, *Info); + ::ProcessAPINotes(*this, Method, *Info, Info.getSelectedRole()); } } } @@ -488,7 +615,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto Info = Reader->lookupObjCProperty(*Context, Property->getName(), isInstanceProperty)) { - ::ProcessAPINotes(*this, Property, *Info); + ::ProcessAPINotes(*this, Property, *Info, Info.getSelectedRole()); } } } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 620018e94c958..d966f8efeac56 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -5823,11 +5823,31 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State, return false; } +/// Rebuild an attributed type without the nullability attribute on it. +static QualType rebuildAttributedTypeWithoutNullability(ASTContext &ctx, + QualType type) { + auto attributed = dyn_cast<AttributedType>(type.getTypePtr()); + if (!attributed) return type; + + // Skip the nullability attribute; we're done. + if (attributed->getImmediateNullability()) { + return attributed->getModifiedType(); + } + + // Build the modified type. + auto modified = rebuildAttributedTypeWithoutNullability( + ctx, attributed->getModifiedType()); + assert(modified.getTypePtr() != attributed->getModifiedType().getTypePtr()); + return ctx.getAttributedType(attributed->getAttrKind(), modified, + attributed->getEquivalentType()); +} + bool Sema::checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability, SourceLocation nullabilityLoc, bool isContextSensitive, - bool implicit) { + bool implicit, + bool overrideExisting) { if (!implicit) { // We saw a nullability type specifier. If this is the first one for // this file, note that. @@ -5864,11 +5884,16 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, break; } - // Conflicting nullability. - Diag(nullabilityLoc, diag::err_nullability_conflicting) - << DiagNullabilityKind(nullability, isContextSensitive) - << DiagNullabilityKind(*existingNullability, false); - return true; + if (!overrideExisting) { + // Conflicting nullability. + Diag(nullabilityLoc, diag::err_nullability_conflicting) + << DiagNullabilityKind(nullability, isContextSensitive) + << DiagNullabilityKind(*existingNullability, false); + return true; + } + + // Rebuild the attributed type, dropping the existing nullability. + type = rebuildAttributedTypeWithoutNullability(Context, type); } desugared = attributed->getModifiedType(); diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index 9c855c6dc6ac8..aa43d849be1d0 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -40,3 +40,10 @@ SwiftVersions: MethodKind: Instance NullabilityOfRet: O Nullability: [ O, S ] + Properties: + - Name: explicitNonnullInstance + PropertyKind: Instance + Nullability: O + - Name: explicitNullableInstance + PropertyKind: Instance + Nullability: N diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes index 9c855c6dc6ac8..aa43d849be1d0 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes @@ -40,3 +40,10 @@ SwiftVersions: MethodKind: Instance NullabilityOfRet: O Nullability: [ O, S ] + Properties: + - Name: explicitNonnullInstance + PropertyKind: Instance + Nullability: O + - Name: explicitNullableInstance + PropertyKind: Instance + Nullability: N diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h index eb25cc0c7fcc8..60d9afa30509a 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -35,4 +35,6 @@ __attribute__((objc_root_class)) @property (class, nonatomic, readwrite, retain) A *nonnullABoth; @end +#import <SomeKit/SomeKitExplicitNullability.h> + #endif diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes new file mode 100644 index 0000000000000..c4e2bc0b6490f --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -0,0 +1,6 @@ +Name: SomeKit +SwiftVersions: + - Version: 3.0 + Functions: + - Name: moveToPoint + SwiftName: 'moveTo(a:b:)' diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h new file mode 100644 index 0000000000000..8b0c2901cbba4 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -0,0 +1 @@ +void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..6d957fd68009f --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module VersionedKit { + umbrella header "VersionedKit.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index c11cea5bd520d..f70c3634b5997 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -3,7 +3,7 @@ // Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables. -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 -fmodules-ignore-macro=SWIFT_VERSION_3_0 #import <SomeKit/SomeKit.h> @@ -29,6 +29,16 @@ int main() { [a setInternalProperty: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#if SWIFT_VERSION_3_0 + // Version 3 information overrides header information. + [a setExplicitNonnullInstance: 0]; // okay + [a setExplicitNullableInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#else + // Header information overrides unversioned information. + [a setExplicitNonnullInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} + [a setExplicitNullableInstance: 0]; // okay +#endif + return 0; } diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m new file mode 100644 index 0000000000000..c92ccd10e1878 --- /dev/null +++ b/clang/test/APINotes/versioned.m @@ -0,0 +1,15 @@ +// RUN: rm -rf %t && mkdir -p %t + +// Build and check the unversioned module file. +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s + +// Build and check the versioned module file. +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s + +#import <VersionedKit/VersionedKit.h> + +// CHECK-UNVERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); +// CHECK-VERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); + From 679b3f5565685a7122d7b1d015041bd83dbfe3bf Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 4 Oct 2016 11:30:37 -0700 Subject: [PATCH 098/582] [API Notes] Allow removal of explicit attributes via versioned API notes. Teach API notes to distinguish between unspecified API notes and false/empty API notes. When a versioned API note explicitly specifies an empty/false state, *remove* an the corresponding attribute from the AST even when it was explicitly specified in the source. This allows headers to move forward while API notes provide backward compatibility. Another part of rdar://problem/28455809. apple-llvm-split-commit: 1fbea6c310b39c87e1f78e56430569a9dcd75488 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 67 +++++++++++++++---- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 22 +++--- clang/lib/APINotes/APINotesWriter.cpp | 27 ++++++-- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 32 ++++++--- clang/lib/AST/DeclPrinter.cpp | 3 + clang/lib/Sema/SemaAPINotes.cpp | 18 ++--- .../Headers/VersionedKit.apinotes | 12 ++++ .../Headers/VersionedKit.h | 14 ++++ clang/test/APINotes/Inputs/roundtrip.apinotes | 8 --- clang/test/APINotes/versioned.m | 20 +++++- 11 files changed, 168 insertions(+), 57 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 9761510ead2b6..4620bfd496e72 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -127,26 +127,48 @@ class CommonTypeInfo : public CommonEntityInfo { /// The Swift type to which a given type is bridged. /// /// Reflects the swift_bridge attribute. - std::string SwiftBridge; + Optional<std::string> SwiftBridge; /// The NS error domain for this type. - std::string NSErrorDomain; + Optional<std::string> NSErrorDomain; public: CommonTypeInfo() : CommonEntityInfo() { } - const std::string &getSwiftBridge() const { return SwiftBridge; } - void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + const Optional<std::string> &getSwiftBridge() const { return SwiftBridge; } - const std::string &getNSErrorDomain() const { return NSErrorDomain; } - void setNSErrorDomain(const std::string &domain) { NSErrorDomain = domain; } + void setSwiftBridge(const Optional<std::string> &swiftType) { + SwiftBridge = swiftType; + } + + void setSwiftBridge(const Optional<StringRef> &swiftType) { + if (swiftType) + SwiftBridge = *swiftType; + else + SwiftBridge = None; + } + + const Optional<std::string> &getNSErrorDomain() const { + return NSErrorDomain; + } + + void setNSErrorDomain(const Optional<std::string> &domain) { + NSErrorDomain = domain; + } + + void setNSErrorDomain(const Optional<StringRef> &domain) { + if (domain) + NSErrorDomain = *domain; + else + NSErrorDomain = None; + } friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs, const CommonTypeInfo &rhs) { static_cast<CommonEntityInfo &>(lhs) |= rhs; - if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) + if (!lhs.SwiftBridge && rhs.SwiftBridge) lhs.SwiftBridge = rhs.SwiftBridge; - if (lhs.NSErrorDomain.empty() && !rhs.NSErrorDomain.empty()) + if (!lhs.NSErrorDomain && rhs.NSErrorDomain) lhs.NSErrorDomain = rhs.NSErrorDomain; return lhs; } @@ -308,24 +330,41 @@ class ObjCPropertyInfo : public VariableInfo { /// Describes a function or method parameter. class ParamInfo : public VariableInfo { + /// Whether noescape was specified. + unsigned NoEscapeSpecified : 1; + /// Whether the this parameter has the 'noescape' attribute. unsigned NoEscape : 1; public: - ParamInfo() : VariableInfo(), NoEscape(false) { } - - bool isNoEscape() const { return NoEscape; } - void setNoEscape(bool noescape) { NoEscape = noescape; } + ParamInfo() : VariableInfo(), NoEscapeSpecified(false), NoEscape(false) { } + + Optional<bool> isNoEscape() const { + if (!NoEscapeSpecified) return None; + return NoEscape; + } + void setNoEscape(Optional<bool> noescape) { + if (noescape) { + NoEscapeSpecified = true; + NoEscape = *noescape; + } else { + NoEscapeSpecified = false; + NoEscape = false; + } + } friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) { static_cast<VariableInfo &>(lhs) |= rhs; - if (!lhs.NoEscape && rhs.NoEscape) - lhs.NoEscape = true; + if (!lhs.NoEscapeSpecified && rhs.NoEscapeSpecified) { + lhs.NoEscapeSpecified = true; + lhs.NoEscape = rhs.NoEscape; + } return lhs; } friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) { return static_cast<const VariableInfo &>(lhs) == rhs && + lhs.NoEscapeSpecified == rhs.NoEscapeSpecified && lhs.NoEscape == rhs.NoEscape; } diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 9b68cf292386d..6f7734bb7154c 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 16; // versioned API notes. +const uint16_t VERSION_MINOR = 17; // optional NSErrorDomain using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 964cc1bc6525a..0e254094c9d5e 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -126,15 +126,19 @@ namespace { unsigned swiftBridgeLength = endian::readNext<uint16_t, little, unaligned>(data); - info.setSwiftBridge( - StringRef(reinterpret_cast<const char *>(data), swiftBridgeLength)); - data += swiftBridgeLength; + if (swiftBridgeLength > 0) { + info.setSwiftBridge( + std::string(reinterpret_cast<const char *>(data), swiftBridgeLength-1)); + data += swiftBridgeLength-1; + } unsigned errorDomainLength = endian::readNext<uint16_t, little, unaligned>(data); - info.setNSErrorDomain( - StringRef(reinterpret_cast<const char *>(data), errorDomainLength)); - data += errorDomainLength; + if (errorDomainLength > 0) { + info.setNSErrorDomain( + std::string(reinterpret_cast<const char *>(data), errorDomainLength-1)); + data += errorDomainLength-1; + } } /// Used to deserialize the on-disk identifier table. @@ -303,8 +307,10 @@ namespace { if (payload & 0x01) pi.setNullabilityAudited(static_cast<NullabilityKind>(nullabilityValue)); payload >>= 1; - pi.setNoEscape(payload & 0x01); - payload >>= 1; assert(payload == 0 && "Bad API notes"); + if (payload & 0x01) { + pi.setNoEscape(payload & 0x02); + } + payload >>= 2; assert(payload == 0 && "Bad API notes"); info.Params.push_back(pi); --numParams; diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 6debe997bf001..2cd577613cc20 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -343,8 +343,8 @@ namespace { // Retrieve the serialized size of the given CommonTypeInfo, for use // in on-disk hash tables. static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) { - return 2 + info.getSwiftBridge().size() + - 2 + info.getNSErrorDomain().size() + + return 2 + (info.getSwiftBridge() ? info.getSwiftBridge()->size() : 0) + + 2 + (info.getNSErrorDomain() ? info.getNSErrorDomain()->size() : 0) + getCommonEntityInfoSize(info); } @@ -352,10 +352,18 @@ namespace { static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) { emitCommonEntityInfo(out, info); endian::Writer<little> writer(out); - writer.write<uint16_t>(info.getSwiftBridge().size()); - out.write(info.getSwiftBridge().c_str(), info.getSwiftBridge().size()); - writer.write<uint16_t>(info.getNSErrorDomain().size()); - out.write(info.getNSErrorDomain().c_str(), info.getNSErrorDomain().size()); + if (auto swiftBridge = info.getSwiftBridge()) { + writer.write<uint16_t>(swiftBridge->size() + 1); + out.write(swiftBridge->c_str(), swiftBridge->size()); + } else { + writer.write<uint16_t>(0); + } + if (auto nsErrorDomain = info.getNSErrorDomain()) { + writer.write<uint16_t>(nsErrorDomain->size() + 1); + out.write(nsErrorDomain->c_str(), info.getNSErrorDomain()->size()); + } else { + writer.write<uint16_t>(0); + } } /// Used to serialize the on-disk Objective-C context table. @@ -687,7 +695,12 @@ namespace { // Parameters. writer.write<uint16_t>(info.Params.size()); for (const auto &pi : info.Params) { - uint8_t payload = pi.isNoEscape(); + uint8_t payload = 0; + if (auto noescape = pi.isNoEscape()) { + payload |= 0x01; + if (*noescape) + payload |= 0x02; + } auto nullability = pi.getNullability(); payload = (payload << 1) | nullability.hasValue(); diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 868544f6afd00..69ed5425ded89 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -171,7 +171,7 @@ namespace { struct Param { unsigned Position; - bool NoEscape = false; + Optional<bool> NoEscape = false; llvm::Optional<NullabilityKind> Nullability; }; typedef std::vector<Param> ParamsSeq; @@ -208,8 +208,8 @@ namespace { AvailabilityItem Availability; bool SwiftPrivate = false; StringRef SwiftName; - StringRef SwiftBridge; - StringRef NSErrorDomain; + Optional<StringRef> SwiftBridge; + Optional<StringRef> NSErrorDomain; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -248,8 +248,8 @@ namespace { AvailabilityItem Availability; StringRef SwiftName; bool SwiftPrivate = false; - StringRef SwiftBridge; - StringRef NSErrorDomain; + Optional<StringRef> SwiftBridge; + Optional<StringRef> NSErrorDomain; }; typedef std::vector<Tag> TagsSeq; @@ -258,8 +258,8 @@ namespace { AvailabilityItem Availability; StringRef SwiftName; bool SwiftPrivate = false; - StringRef SwiftBridge; - StringRef NSErrorDomain; + Optional<StringRef> SwiftBridge; + Optional<StringRef> NSErrorDomain; }; typedef std::vector<Typedef> TypedefsSeq; @@ -1033,6 +1033,20 @@ namespace { return StringRef(reinterpret_cast<const char *>(ptr), string.size()); } + /// Copy an optional string into allocated memory so it does disappear on us. + Optional<StringRef> maybeCopyString(Optional<StringRef> string) { + if (!string) return None; + + return copyString(*string); + } + + /// Copy an optional string into allocated memory so it does disappear on us. + Optional<StringRef> maybeCopyString(Optional<std::string> string) { + if (!string) return None; + + return copyString(*string); + } + template<typename T> void handleCommon(T &record, const CommonEntityInfo &info) { handleAvailability(record.Availability, info); @@ -1043,8 +1057,8 @@ namespace { template<typename T> void handleCommonType(T &record, const CommonTypeInfo &info) { handleCommon(record, info); - record.SwiftBridge = copyString(info.getSwiftBridge()); - record.NSErrorDomain = copyString(info.getNSErrorDomain()); + record.SwiftBridge = maybeCopyString(info.getSwiftBridge()); + record.NSErrorDomain = maybeCopyString(info.getNSErrorDomain()); } /// Map Objective-C context info. diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 6d47dd7b88376..e599840a05919 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1137,6 +1137,9 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) { return; } bool eolnOut = false; + prettyPrintAttributes(OID); + if (OID->hasAttrs()) Out << "\n"; + Out << "@interface " << I; if (auto TypeParams = OID->getTypeParamListAsWritten()) { diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 4d4ea6eee8d50..89cf5b613981e 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -241,20 +241,22 @@ static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonTypeInfo &info, VersionedInfoRole role) { // swift_bridge - if (!info.getSwiftBridge().empty()) { - handleAPINotedAttribute<SwiftBridgeAttr>(S, D, true, role, [&] { + if (auto swiftBridge = info.getSwiftBridge()) { + handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(), role, + [&] { return SwiftBridgeAttr::CreateImplicit(S.Context, CopyString(S.Context, - info.getSwiftBridge())); + *swiftBridge)); }); } // ns_error_domain - if (!info.getNSErrorDomain().empty()) { - handleAPINotedAttribute<NSErrorDomainAttr>(S, D, true, role, [&] { + if (auto nsErrorDomain = info.getNSErrorDomain()) { + handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(), + role, [&] { return NSErrorDomainAttr::CreateImplicit( S.Context, - &S.Context.Idents.get(info.getNSErrorDomain())); + &S.Context.Idents.get(*nsErrorDomain)); }); } @@ -281,8 +283,8 @@ static void ProcessAPINotes(Sema &S, ParmVarDecl *D, const api_notes::ParamInfo &info, VersionedInfoRole role) { // noescape - if (info.isNoEscape()) { - handleAPINotedAttribute<NoEscapeAttr>(S, D, true, role, [&] { + if (auto noescape = info.isNoEscape()) { + handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, role, [&] { return NoEscapeAttr::CreateImplicit(S.Context); }); } diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index c4e2bc0b6490f..1fc6aa4de620d 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -1,6 +1,18 @@ Name: SomeKit SwiftVersions: - Version: 3.0 + Classes: + - Name: MyReferenceType + SwiftBridge: '' Functions: - Name: moveToPoint SwiftName: 'moveTo(a:b:)' + - Name: acceptClosure + Parameters: + - Position: 0 + NoEscape: false + Tags: + - Name: MyErrorCode + NSErrorDomain: '' + + \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index 8b0c2901cbba4..741bdaaba0cdf 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -1 +1,15 @@ void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); + +void acceptClosure(void (^ __attribute__((noescape)) block)(void)); + +@class NSString; + +extern NSString *MyErrorDomain; + +enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorCode { + MyErrorCodeFailed = 1 +}; + +__attribute__((swift_bridge("MyValueType"))) +@interface MyReferenceType +@end diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 13e08a1126b0b..7e2e68a667cf3 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -9,8 +9,6 @@ Classes: AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' - SwiftBridge: '' - NSErrorDomain: '' Methods: - Selector: 'cellWithImage:' MethodKind: Class @@ -62,7 +60,6 @@ Classes: SwiftPrivate: false SwiftName: '' SwiftBridge: View - NSErrorDomain: '' Methods: - Selector: 'addSubview:' MethodKind: Instance @@ -78,7 +75,6 @@ Classes: - Position: 0 NoEscape: false - Position: 1 - NoEscape: false - Position: 2 NoEscape: true Nullability: [ N, N, O ] @@ -136,14 +132,12 @@ Tags: AvailabilityMsg: '' SwiftPrivate: false SwiftName: SomeEnum - SwiftBridge: '' NSErrorDomain: some_error_domain - Name: NSSomeStruct Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: SomeStruct - SwiftBridge: '' NSErrorDomain: '' Typedefs: - Name: NSTypedef @@ -152,7 +146,6 @@ Typedefs: SwiftPrivate: false SwiftName: Typedef SwiftBridge: '' - NSErrorDomain: '' SwiftVersions: - Version: 3.0 Classes: @@ -162,7 +155,6 @@ SwiftVersions: SwiftPrivate: false SwiftName: NSBox SwiftBridge: '' - NSErrorDomain: '' Methods: - Selector: init MethodKind: Instance diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index c92ccd10e1878..53af3339163e7 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -1,11 +1,11 @@ // RUN: rm -rf %t && mkdir -p %t // Build and check the unversioned module file. -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s // Build and check the versioned module file. -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s #import <VersionedKit/VersionedKit.h> @@ -13,3 +13,19 @@ // CHECK-UNVERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); // CHECK-VERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); +// CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape))); +// CHECK-VERSIONED: void acceptClosure(void (^block)(void)); + +// CHECK-UNVERSIONED: enum MyErrorCode { +// CHECK-UNVERSIONED-NEXT: MyErrorCodeFailed = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((ns_error_domain(MyErrorDomain))); + +// CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType"))) +// CHECK-UNVERSIONED: @interface MyReferenceType + +// CHECK-VERSIONED: enum MyErrorCode { +// CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1 +// CHECK-VERSIONED-NEXT: }; + +// CHECK-VERSIONED-NOT: __attribute__((swift_bridge("MyValueType"))) +// CHECK-VERSIONED: @interface MyReferenceType From 31441f4eb94323e664c3efa98506f0172cfac083 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 4 Oct 2016 14:18:27 -0700 Subject: [PATCH 099/582] [Driver] Propagate -fapinotes-swift-version=XX.YY to -cc1. apple-llvm-split-commit: c24e0dabac8400ab013b96b4ce6fc66840c41824 apple-llvm-split-dir: clang/ --- clang/lib/Driver/Tools.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index d1b203b7db0f5..34414debca24e 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -5435,6 +5435,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, const char Arg[] = "-fapinotes-cache-path="; APINotesCachePath.insert(APINotesCachePath.begin(), Arg, Arg + strlen(Arg)); CmdArgs.push_back(Args.MakeArgString(APINotesCachePath)); + + Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version); } // -fblocks=0 is default. From 7a9a24ecd8ed7c05fb31ab9ea042bce11664e2fc Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 4 Oct 2016 16:25:05 -0700 Subject: [PATCH 100/582] [API Notes] Suppress an unused-variable warning when assertions are disabled. Fixes rdar://problem/28589320. apple-llvm-split-commit: 70cfd267b9ca0057fe0dee1309ba6c6f01309920 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 0e254094c9d5e..2b7ec59b87c73 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -88,7 +88,7 @@ namespace { result.reserve(numElements); for (unsigned i = 0; i != numElements; ++i) { auto version = readVersionTuple(data); - auto dataBefore = data; (void)data; + auto dataBefore = data; (void)dataBefore; auto unversionedData = Derived::readUnversioned(key, data); assert(data != dataBefore && "Unversioned data reader didn't move pointer"); From 3ee59bc91b8a118e8d20d9b880a5d78e95bfc56e Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 4 Oct 2016 16:49:39 -0700 Subject: [PATCH 101/582] [API Notes] Allow removal of SwiftPrivate via versioned API notes. More of rdar://problem/28455809. apple-llvm-split-commit: 9b0bccba91f27449ea2e7d60f5d47c63bcd8d551 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 31 +++++++++++++++++-- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 3 +- clang/lib/APINotes/APINotesWriter.cpp | 15 +++++++-- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 28 ++++++++--------- clang/lib/Sema/SemaAPINotes.cpp | 4 +-- .../Headers/VersionedKit.apinotes | 2 ++ .../Headers/VersionedKit.h | 2 ++ clang/test/APINotes/Inputs/roundtrip.apinotes | 3 -- clang/test/APINotes/versioned.m | 4 +++ 10 files changed, 67 insertions(+), 27 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 4620bfd496e72..79ec22ce8b684 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -70,19 +70,42 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; +private: + /// Whether SwiftPrivate was specified. + unsigned SwiftPrivateSpecified : 1; + /// Whether this entity is considered "private" to a Swift overlay. unsigned SwiftPrivate : 1; +public: /// Swift name of this entity. std::string SwiftName; - CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0), SwiftPrivate(0) { } + CommonEntityInfo() + : Unavailable(0), UnavailableInSwift(0), SwiftPrivateSpecified(0), + SwiftPrivate(0) { } + + Optional<bool> isSwiftPrivate() const { + if (!SwiftPrivateSpecified) return None; + return SwiftPrivate; + } + + void setSwiftPrivate(Optional<bool> swiftPrivate) { + if (swiftPrivate) { + SwiftPrivateSpecified = 1; + SwiftPrivate = *swiftPrivate; + } else { + SwiftPrivateSpecified = 0; + SwiftPrivate = 0; + } + } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && lhs.UnavailableInSwift == rhs.UnavailableInSwift && + lhs.SwiftPrivateSpecified == rhs.SwiftPrivateSpecified && lhs.SwiftPrivate == rhs.SwiftPrivate && lhs.SwiftName == rhs.SwiftName; } @@ -111,8 +134,10 @@ class CommonEntityInfo { } } - if (rhs.SwiftPrivate) - lhs.SwiftPrivate = true; + if (rhs.SwiftPrivateSpecified && !lhs.SwiftPrivateSpecified) { + lhs.SwiftPrivateSpecified = 1; + lhs.SwiftPrivate = rhs.SwiftPrivate; + } if (rhs.SwiftName.length() != 0 && lhs.SwiftName.length() == 0) diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 6f7734bb7154c..4e763efa8a198 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 17; // optional NSErrorDomain +const uint16_t VERSION_MINOR = 18; // three-state SwiftPrivate using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 2b7ec59b87c73..9a87a445c55d4 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -104,7 +104,8 @@ namespace { uint8_t unavailableBits = *data++; info.Unavailable = (unavailableBits >> 1) & 0x01; info.UnavailableInSwift = unavailableBits & 0x01; - info.SwiftPrivate = (unavailableBits >> 2) & 0x01; + if ((unavailableBits >> 2) & 0x01) + info.setSwiftPrivate(static_cast<bool>((unavailableBits >> 3) & 0x01)); unsigned msgLength = endian::readNext<uint16_t, little, unaligned>(data); info.UnavailableMsg diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 2cd577613cc20..cbf76c1af3c10 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -331,9 +331,18 @@ namespace { static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer<little> writer(out); - writer.write<uint8_t>(info.SwiftPrivate << 2 - | info.Unavailable << 1 - | info.UnavailableInSwift); + uint8_t payload = 0; + if (auto swiftPrivate = info.isSwiftPrivate()) { + payload |= 0x01; + if (*swiftPrivate) payload |= 0x02; + } + payload <<= 1; + payload |= info.Unavailable; + payload <<= 1; + payload |= info.UnavailableInSwift; + + writer.write<uint8_t>(payload); + writer.write<uint16_t>(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); writer.write<uint16_t>(info.SwiftName.size()); diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 69ed5425ded89..000ffb58721ec 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -183,7 +183,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; StringRef SwiftName; api_notes::FactoryAsInitKind FactoryAsInit = api_notes::FactoryAsInitKind::Infer; @@ -197,7 +197,7 @@ namespace { llvm::Optional<MethodKind> Kind; llvm::Optional<NullabilityKind> Nullability; AvailabilityItem Availability; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; StringRef SwiftName; }; typedef std::vector<Property> PropertiesSeq; @@ -206,7 +206,7 @@ namespace { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; StringRef SwiftName; Optional<StringRef> SwiftBridge; Optional<StringRef> NSErrorDomain; @@ -221,7 +221,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional<NullabilityKind> NullabilityOfRet; AvailabilityItem Availability; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; StringRef SwiftName; }; typedef std::vector<Function> FunctionsSeq; @@ -230,7 +230,7 @@ namespace { StringRef Name; llvm::Optional<NullabilityKind> Nullability; AvailabilityItem Availability; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; StringRef SwiftName; }; typedef std::vector<GlobalVariable> GlobalVariablesSeq; @@ -238,7 +238,7 @@ namespace { struct EnumConstant { StringRef Name; AvailabilityItem Availability; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; StringRef SwiftName; }; typedef std::vector<EnumConstant> EnumConstantsSeq; @@ -247,7 +247,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; Optional<StringRef> SwiftBridge; Optional<StringRef> NSErrorDomain; }; @@ -257,7 +257,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; - bool SwiftPrivate = false; + Optional<bool> SwiftPrivate; Optional<StringRef> SwiftBridge; Optional<StringRef> NSErrorDomain; }; @@ -652,7 +652,7 @@ namespace { return true; convertAvailability(common.Availability, info, apiName); - info.SwiftPrivate = common.SwiftPrivate; + info.setSwiftPrivate(common.SwiftPrivate); info.SwiftName = common.SwiftName; return false; } @@ -769,7 +769,7 @@ namespace { if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); - pInfo.SwiftPrivate = prop.SwiftPrivate; + pInfo.setSwiftPrivate(prop.SwiftPrivate); pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); @@ -825,7 +825,7 @@ namespace { if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); - info.SwiftPrivate = global.SwiftPrivate; + info.setSwiftPrivate(global.SwiftPrivate); info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); @@ -846,7 +846,7 @@ namespace { if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); - info.SwiftPrivate = function.SwiftPrivate; + info.setSwiftPrivate(function.SwiftPrivate); info.SwiftName = function.SwiftName; convertParams(function.Params, info); convertNullability(function.Nullability, @@ -870,7 +870,7 @@ namespace { if (!isAvailable(enumConstant.Availability)) continue; convertAvailability(enumConstant.Availability, info, enumConstant.Name); - info.SwiftPrivate = enumConstant.SwiftPrivate; + info.setSwiftPrivate(enumConstant.SwiftPrivate); info.SwiftName = enumConstant.SwiftName; Writer->addEnumConstant(enumConstant.Name, info, swiftVersion); } @@ -1050,7 +1050,7 @@ namespace { template<typename T> void handleCommon(T &record, const CommonEntityInfo &info) { handleAvailability(record.Availability, info); - record.SwiftPrivate = info.SwiftPrivate; + record.SwiftPrivate = info.isSwiftPrivate(); record.SwiftName = copyString(info.SwiftName); } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 89cf5b613981e..0bcaaac327102 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -213,8 +213,8 @@ static void ProcessAPINotes(Sema &S, Decl *D, } // swift_private - if (info.SwiftPrivate) { - handleAPINotedAttribute<SwiftPrivateAttr>(S, D, true, role, [&] { + if (auto swiftPrivate = info.isSwiftPrivate()) { + handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, role, [&] { return SwiftPrivateAttr::CreateImplicit(S.Context); }); } diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index 1fc6aa4de620d..e840ed48f0e21 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -11,6 +11,8 @@ SwiftVersions: Parameters: - Position: 0 NoEscape: false + - Name: privateFunc + SwiftPrivate: false Tags: - Name: MyErrorCode NSErrorDomain: '' diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index 741bdaaba0cdf..d90cb8f26a07a 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -13,3 +13,5 @@ enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorCode { __attribute__((swift_bridge("MyValueType"))) @interface MyReferenceType @end + +void privateFunc(void) __attribute__((swift_private)); diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 7e2e68a667cf3..a6f368e09c207 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -81,7 +81,6 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' - SwiftPrivate: false SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance @@ -97,7 +96,6 @@ Classes: Nullability: O Availability: available AvailabilityMsg: '' - SwiftPrivate: false SwiftName: enclosing - Name: makeBackingLayer PropertyKind: Class @@ -111,7 +109,6 @@ Functions: NullabilityOfRet: N Availability: available AvailabilityMsg: '' - SwiftPrivate: false SwiftName: 'availableWindowDepths()' Globals: - Name: NSCalibratedWhiteColorSpace diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 53af3339163e7..bfeacc36102ae 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -23,9 +23,13 @@ // CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType"))) // CHECK-UNVERSIONED: @interface MyReferenceType +// CHECK-UNVERSIONED: void privateFunc() __attribute__((swift_private)); + // CHECK-VERSIONED: enum MyErrorCode { // CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1 // CHECK-VERSIONED-NEXT: }; // CHECK-VERSIONED-NOT: __attribute__((swift_bridge("MyValueType"))) // CHECK-VERSIONED: @interface MyReferenceType + +// CHECK-VERSIONED: void privateFunc(); From b930314f24e7cc5af8c4f8b5ceaa746575d8e53d Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 4 Oct 2016 17:24:26 -0700 Subject: [PATCH 102/582] Fix a bad merge; swift_newtype doesn't imply xray_always_instrument apple-llvm-split-commit: faf50eefe1099c61d10a05b2d7c748ebf7edb775 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclAttr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index b779b51f8bbbb..f062999de0bdd 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6546,6 +6546,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, break; case AttributeList::AT_SwiftNewtype: handleSwiftNewtypeAttr(S, D, Attr); + break; // XRay attributes. case AttributeList::AT_XRayInstrument: handleSimpleAttribute<XRayInstrumentAttr>(S, D, Attr); From a29637f0515df3234b91714f2918d8a826c4a083 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Tue, 4 Oct 2016 17:25:12 -0700 Subject: [PATCH 103/582] [API Notes] Add support for SwiftWrapper via API notes apple-llvm-split-commit: 62a7f71c267753a19e051740643ffffa22b41ad5 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 9 +++++++ clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 6 +++++ clang/lib/APINotes/APINotesWriter.cpp | 23 +++++++++++++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 15 ++++++++++- clang/lib/Sema/SemaAPINotes.cpp | 27 +++++++++++++++++++ .../Headers/VersionedKit.apinotes | 4 +++ .../Headers/VersionedKit.h | 2 ++ clang/test/APINotes/Inputs/roundtrip.apinotes | 1 + clang/test/APINotes/versioned.m | 4 +++ 10 files changed, 90 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 79ec22ce8b684..fae55017e6d0b 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -585,9 +585,18 @@ class TagInfo : public CommonTypeInfo { TagInfo() : CommonTypeInfo() { } }; +/// The kind of a swift_wrapper/swift_newtype. +enum class SwiftWrapperKind { + None, + Struct, + Enum +}; + /// Describes API notes data for a typedef. class TypedefInfo : public CommonTypeInfo { public: + Optional<SwiftWrapperKind> SwiftWrapper; + TypedefInfo() : CommonTypeInfo() { } }; diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 4e763efa8a198..4623773a1f0d4 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 18; // three-state SwiftPrivate +const uint16_t VERSION_MINOR = 19; // SwiftWrapper using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 9a87a445c55d4..9dee73cb43fb3 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -479,6 +479,12 @@ namespace { static TypedefInfo readUnversioned(internal_key_type key, const uint8_t *&data) { TypedefInfo info; + + uint8_t payload = *data++; + if (payload > 0) { + info.SwiftWrapper = static_cast<SwiftWrapperKind>((payload & 0x3) - 1); + } + readCommonTypeInfo(data, info); return info; } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index cbf76c1af3c10..c0527aa483a5e 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -1026,6 +1026,7 @@ namespace { /// Used to serialize the on-disk tag table. class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { }; + } // end anonymous namespace void APINotesWriter::Implementation::writeTagBlock( @@ -1055,7 +1056,27 @@ void APINotesWriter::Implementation::writeTagBlock( namespace { /// Used to serialize the on-disk typedef table. class TypedefTableInfo - : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> { }; + : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> { + + public: + unsigned getUnversionedInfoSize(const TypedefInfo &info) { + return 1 + getCommonTypeInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const TypedefInfo &info) { + endian::Writer<little> writer(out); + + uint8_t payload = 0; + if (auto swiftWrapper = info.SwiftWrapper) { + payload |= static_cast<uint8_t>(*swiftWrapper) + 1; + } + + writer.write<uint8_t>(payload); + + emitCommonTypeInfo(out, info); + } + + }; } // end anonymous namespace void APINotesWriter::Implementation::writeTypedefBlock( diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 000ffb58721ec..d24a63fafef1b 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -260,6 +260,7 @@ namespace { Optional<bool> SwiftPrivate; Optional<StringRef> SwiftBridge; Optional<StringRef> NSErrorDomain; + Optional<api_notes::SwiftWrapperKind> SwiftWrapper; }; typedef std::vector<Typedef> TypedefsSeq; @@ -349,6 +350,15 @@ namespace llvm { } }; + template<> + struct ScalarEnumerationTraits<api_notes::SwiftWrapperKind> { + static void enumeration(IO &io, api_notes::SwiftWrapperKind &value) { + io.enumCase(value, "none", api_notes::SwiftWrapperKind::None); + io.enumCase(value, "struct", api_notes::SwiftWrapperKind::Struct); + io.enumCase(value, "enum", api_notes::SwiftWrapperKind::Enum); + } + }; + template <> struct ScalarTraits<VersionTuple> { static void output(const VersionTuple &value, void*, @@ -489,6 +499,7 @@ namespace llvm { io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); io.mapOptional("NSErrorDomain", t.NSErrorDomain); + io.mapOptional("SwiftWrapper", t.SwiftWrapper); } }; @@ -903,7 +914,8 @@ namespace { TypedefInfo typedefInfo; if (convertCommonType(t, typedefInfo, t.Name)) continue; - + typedefInfo.SwiftWrapper = t.SwiftWrapper; + Writer->addTypedef(t.Name, typedefInfo, swiftVersion); } } @@ -1246,6 +1258,7 @@ namespace { Typedef td; td.Name = name; handleCommonType(td, info); + td.SwiftWrapper = info.SwiftWrapper; auto &items = getTopLevelItems(swiftVersion); items.Typedefs.push_back(td); } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 0bcaaac327102..121a7309a8334 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -423,6 +423,33 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, const api_notes::TypedefInfo &info, VersionedInfoRole role) { + // swift_wrapper + using SwiftWrapperKind = api_notes::SwiftWrapperKind; + + if (auto swiftWrapper = info.SwiftWrapper) { + handleAPINotedAttribute<SwiftNewtypeAttr>(S, D, + *swiftWrapper != SwiftWrapperKind::None, role, + [&] { + SwiftNewtypeAttr::NewtypeKind kind; + switch (*swiftWrapper) { + case SwiftWrapperKind::None: + llvm_unreachable("Shouldn't build an attribute"); + + case SwiftWrapperKind::Struct: + kind = SwiftNewtypeAttr::NK_Struct; + break; + + case SwiftWrapperKind::Enum: + kind = SwiftNewtypeAttr::NK_Enum; + break; + } + return SwiftNewtypeAttr::CreateImplicit( + S.Context, + SwiftNewtypeAttr::GNU_swift_wrapper, + kind); + }); + } + // Handle common type information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), role); diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index e840ed48f0e21..4215fb9d15318 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -16,5 +16,9 @@ SwiftVersions: Tags: - Name: MyErrorCode NSErrorDomain: '' + Typedefs: + - Name: MyDoubleWrapper + SwiftWrapper: none + \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index d90cb8f26a07a..db71d8f576f6d 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -15,3 +15,5 @@ __attribute__((swift_bridge("MyValueType"))) @end void privateFunc(void) __attribute__((swift_private)); + +typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index a6f368e09c207..c6beaf1928f26 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -143,6 +143,7 @@ Typedefs: SwiftPrivate: false SwiftName: Typedef SwiftBridge: '' + SwiftWrapper: struct SwiftVersions: - Version: 3.0 Classes: diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index bfeacc36102ae..d761620b80eb8 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -25,6 +25,8 @@ // CHECK-UNVERSIONED: void privateFunc() __attribute__((swift_private)); +// CHECK-UNVERSIONED: typedef double MyDoubleWrapper __attribute__((swift_wrapper("struct"))); + // CHECK-VERSIONED: enum MyErrorCode { // CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1 // CHECK-VERSIONED-NEXT: }; @@ -33,3 +35,5 @@ // CHECK-VERSIONED: @interface MyReferenceType // CHECK-VERSIONED: void privateFunc(); + +// CHECK-VERSIONED: typedef double MyDoubleWrapper; From 78bf8a182725e7b771222eb08861e11d13c8edfa Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Wed, 5 Oct 2016 15:42:43 -0700 Subject: [PATCH 104/582] [API Notes] Remove comment for nonexistent parameter 'swiftVersion'. NFC apple-llvm-split-commit: a1912f0994dbb3e3f65921149b2aedf964bbbc09 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index f96c7b65c9154..2b985c6ce73b5 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -175,7 +175,6 @@ class APINotesReader { /// \param name The name of the property we're looking for. /// \param isInstance Whether we are looking for an instance property (vs. /// a class property). - /// \param swiftVersion The Swift version to filter for, if any. /// /// \returns Information about the property, if known. VersionedInfo<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID, From bea24bcfdabf3aa8244388ae49599a677b36108d Mon Sep 17 00:00:00 2001 From: Tim Northover <t.p.northover@gmail.com> Date: Thu, 13 Oct 2016 16:06:29 -0700 Subject: [PATCH 105/582] Update internal tool after API change. apple-llvm-split-commit: c841da503fc183567261caa12b05b18e309decac apple-llvm-split-dir: clang/ --- clang/tools/driver/apinotes_main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/tools/driver/apinotes_main.cpp b/clang/tools/driver/apinotes_main.cpp index e4ccc2e7f3244..930dc39c3b726 100644 --- a/clang/tools/driver/apinotes_main.cpp +++ b/clang/tools/driver/apinotes_main.cpp @@ -44,8 +44,7 @@ int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0, "Convert binary format to YAML"), clEnumValN(api_notes::ActionType::Dump, "dump", - "Parse and dump the output"), - clEnumValEnd), + "Parse and dump the output")), cl::cat(APINotesCategory)); static cl::opt<std::string> From 9c2e10a6a77558f617779547f1ca0461118c0a49 Mon Sep 17 00:00:00 2001 From: Manman Ren <mren@apple.com> Date: Fri, 16 Sep 2016 17:15:06 -0700 Subject: [PATCH 106/582] Revert "ObjectiveC Generics: Start using ObjCTypeParamType." This reverts commit c7c63c5b9fd8211c606407cb8b6eef67321f16a3. The series of patches broke swift bot. apple-llvm-split-commit: 1859c18983ee8b0615ad4587b0d52c1440b11ed6 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTContext.cpp | 5 --- clang/lib/AST/DeclObjC.cpp | 8 ++--- clang/lib/AST/Type.cpp | 19 +++-------- clang/lib/Sema/SemaDeclObjC.cpp | 2 +- clang/lib/Sema/SemaType.cpp | 22 +------------ .../Checkers/DynamicTypePropagation.cpp | 2 +- clang/test/SemaObjC/kindof.m | 10 +----- .../SemaObjC/parameterized_classes_subst.m | 33 ------------------- 8 files changed, 10 insertions(+), 91 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 318523eed91df..9488ac436ac92 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3975,11 +3975,6 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, bool allowOnPointerType) const { hasError = false; - if (const ObjCTypeParamType *objT = - dyn_cast<ObjCTypeParamType>(type.getTypePtr())) { - return getObjCTypeParamType(objT->getDecl(), protocols); - } - // Apply protocol qualifiers to ObjCObjectPointerType. if (allowOnPointerType) { if (const ObjCObjectPointerType *objPtr = diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index fdbac00fc9426..d1c77bb0b1b3e 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1324,12 +1324,8 @@ ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc, IdentifierInfo *name, SourceLocation colonLoc, TypeSourceInfo *boundInfo) { - auto *TPDecl = - new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, - nameLoc, name, colonLoc, boundInfo); - QualType TPType = ctx.getObjCTypeParamType(TPDecl, {}); - TPDecl->setTypeForDecl(TPType.getTypePtr()); - return TPDecl; + return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, + nameLoc, name, colonLoc, boundInfo); } ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 3e776e3043850..0a034f6ed4739 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1081,24 +1081,13 @@ QualType QualType::substObjCTypeArgs( // Replace an Objective-C type parameter reference with the corresponding // type argument. - if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) { - if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(OTPTy->getDecl())) { + if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) { + if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) { // If we have type arguments, use them. if (!typeArgs.empty()) { + // FIXME: Introduce SubstObjCTypeParamType ? QualType argType = typeArgs[typeParam->getIndex()]; - if (OTPTy->qual_empty()) - return ctx.getQualifiedType(argType, splitType.Quals); - - // Apply protocol lists if exists. - bool hasError; - SmallVector<ObjCProtocolDecl*, 8> protocolsVec; - protocolsVec.append(OTPTy->qual_begin(), - OTPTy->qual_end()); - ArrayRef<ObjCProtocolDecl *> protocolsToApply = protocolsVec; - QualType resultTy = ctx.applyObjCProtocolQualifiers(argType, - protocolsToApply, hasError, true/*allowOnPointerType*/); - - return ctx.getQualifiedType(resultTy, splitType.Quals); + return ctx.getQualifiedType(argType, splitType.Quals); } switch (context) { diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index cfa34377d288c..527d8ac9fa498 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -2358,7 +2358,7 @@ static bool CheckMethodOverrideParam(Sema &S, } if (S.Context.hasSameUnqualifiedType(ImplTy, IfaceTy)) return true; - + if (!Warn) return false; unsigned DiagID = diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f8ed64ec28aaf..1b74942de9708 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1157,20 +1157,6 @@ TypeResult Sema::actOnObjCTypeArgsAndProtocolQualifiers( ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } - if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) { - // Protocol qualifier information. - if (OTPTL.getNumProtocols() > 0) { - assert(OTPTL.getNumProtocols() == Protocols.size()); - OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc); - OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc); - for (unsigned i = 0, n = Protocols.size(); i != n; ++i) - OTPTL.setProtocolLoc(i, ProtocolLocs[i]); - } - - // We're done. Return the completed type to the parser. - return CreateParsedType(Result, ResultTInfo); - } - auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>(); // Type argument information. @@ -5926,6 +5912,7 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, // For the context-sensitive keywords/Objective-C property // attributes, require that the type be a single-level pointer. if (isContextSensitive) { + // Make sure that the pointee isn't itself a pointer type. QualType pointeeType = desugared->getPointeeType(); if (pointeeType->isAnyPointerType() || pointeeType->isObjCObjectPointerType() || @@ -5949,13 +5936,6 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, } bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) { - if (isa<ObjCTypeParamType>(type)) { - // Build the attributed type to record where __kindof occurred. - type = Context.getAttributedType(AttributedType::attr_objc_kindof, - type, type); - return false; - } - // Find out if it's an Objective-C object or object pointer type; const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>(); const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index ec2153cc8266d..a8f2f3a4a5005 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -626,7 +626,7 @@ static bool isObjCTypeParamDependent(QualType Type) { : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { public: IsObjCTypeParamDependentTypeVisitor() : Result(false) {} - bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { + bool VisitTypedefType(const TypedefType *Type) { if (isa<ObjCTypeParamDecl>(Type->getDecl())) { Result = true; return false; diff --git a/clang/test/SemaObjC/kindof.m b/clang/test/SemaObjC/kindof.m index 9d758d3cfb2a6..63ba18fe89bc0 100644 --- a/clang/test/SemaObjC/kindof.m +++ b/clang/test/SemaObjC/kindof.m @@ -385,7 +385,7 @@ - (void)test:(id)T { @end @interface NSGeneric<ObjectType> : NSObject -- (void)test:(__kindof ObjectType)T; // expected-note{{passing argument to parameter 'T' here}} +- (void)test:(__kindof ObjectType)T; - (void)mapUsingBlock:(id (^)(__kindof ObjectType))block; @end @implementation NSGeneric @@ -395,14 +395,6 @@ - (void)mapUsingBlock:(id (^)(id))block { } @end -void testGeneric(NSGeneric<NSString*> *generic) { - NSObject *NSObject_obj; - // Assign from NSObject_obj to __kindof NSString*. - [generic test:NSObject_obj]; // expected-warning{{incompatible pointer types sending 'NSObject *' to parameter of type '__kindof NSString *'}} - NSString *NSString_str; - [generic test:NSString_str]; -} - // Check that clang doesn't crash when a type parameter is illegal. @interface Array1<T> : NSObject @end diff --git a/clang/test/SemaObjC/parameterized_classes_subst.m b/clang/test/SemaObjC/parameterized_classes_subst.m index da2d56f11bc80..f90ee9093592c 100644 --- a/clang/test/SemaObjC/parameterized_classes_subst.m +++ b/clang/test/SemaObjC/parameterized_classes_subst.m @@ -426,36 +426,3 @@ + (void)useSuperMethod { // warning about likely protocol/class name typos. // -------------------------------------------------------------------------- typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}} - -// rdar://25060179 -@interface MyMutableDictionary<KeyType, ObjectType> : NSObject -- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \ - // expected-note{{passing argument to parameter 'key' here}} -@end - -void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString, - NSNumber *n1, NSNumber *n2) { - // We warn here when the key types do not match. - stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \ - // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}} -} - -@interface MyTest<K, V> : NSObject <NSCopying> -- (V)test:(K)key; -- (V)test2:(K)key; // expected-note{{previous definition is here}} -- (void)mapUsingBlock:(id (^)(V))block; -- (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}} -@end - -@implementation MyTest -- (id)test:(id)key { - return key; -} -- (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}} - return 0; -} -- (void)mapUsingBlock:(id (^)(id))block { -} -- (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}} -} -@end From 411ba51ab0505880d81b86ccd4417486dc65a765 Mon Sep 17 00:00:00 2001 From: Brian Gesiak <modocache@gmail.com> Date: Sun, 23 Oct 2016 22:24:50 -0400 Subject: [PATCH 107/582] [AST] Remove superfluous docblock change Upstream Clang documents the `Message` parameter as simply `\param`, while Swift's fork of Clang documents it as `\param[out]`. Although `\param[out]` may be more correct, the change to the documentation should be made in upstream Clang, not Swift Clang. Furthermore, upstream Clang does not appear to have a strong convention as to the use of `\param[out]`: of 4616 instances of `\param`, only 49 are denoted as `\param[out]`. apple-llvm-split-commit: 75035e4a3a2a80296b0a868e1e6150a860a336fb apple-llvm-split-dir: clang/ --- clang/include/clang/AST/DeclBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 7685241996091..3a80ff5b234e9 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -605,7 +605,7 @@ class LLVM_ALIGNAS(/*alignof(uint64_t)*/ 8) Decl { /// the given declaration (e.g., preferring 'unavailable' to /// 'deprecated'). /// - /// \param[out] Message If non-NULL and the result is not \c + /// \param Message If non-NULL and the result is not \c /// AR_Available, will be set to a (possibly empty) message /// describing why the declaration has not been introduced, is /// deprecated, or is unavailable. From 5165a199b97de802308cb0ca4863866fb6f81280 Mon Sep 17 00:00:00 2001 From: Mehdi Amini <mehdi_amini@Mehdis-MacBook-Pro.local> Date: Mon, 24 Oct 2016 12:12:36 -0700 Subject: [PATCH 108/582] Fix build after incorrect merge conflict fix apple-llvm-split-commit: d636f484881c4fc740f7c0232691af475e2e2e37 apple-llvm-split-dir: clang/ --- clang/lib/Driver/Tools.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index 86f7473cc2c1e..def5bd04c8e62 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -5615,8 +5615,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back( Args.MakeArgString("-fbuild-session-timestamp=" + Twine((uint64_t)Status.getLastModificationTime() - .time_since_epoch() - .count()))); + .toEpochTime()))); } if (Args.getLastArg(options::OPT_fmodules_validate_once_per_build_session)) { From b5e9ed4b9c7abc3201b427a773575b5391608f0b Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 24 Oct 2016 14:12:51 +0100 Subject: [PATCH 109/582] [Sema] Display an objc_subclassing_restricted error for Objective-C implementation declarations when appropriate. rdar://28753587 apple-llvm-split-commit: eb81c6fb87dd0fd5661209e2cf2aef5d77ca56b1 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclObjC.cpp | 12 ++++++++++++ clang/test/SemaObjC/subclassing-restricted-attr.m | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 527d8ac9fa498..7ec3202d7529d 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -3859,6 +3859,18 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, Diag(IDecl->getLocation(), diag::err_objc_root_class_subclass); } + if (const ObjCInterfaceDecl *Super = IDecl->getSuperClass()) { + // An interface can subclass another interface with a + // objc_subclassing_restricted attribute when it has that attribute as + // well (because of interfaces imported from Swift). Therefore we have + // to check if we can subclass in the implementation as well. + if (IDecl->hasAttr<ObjCSubclassingRestrictedAttr>() && + Super->hasAttr<ObjCSubclassingRestrictedAttr>()) { + Diag(IC->getLocation(), diag::err_restricted_superclass_mismatch); + Diag(Super->getLocation(), diag::note_class_declared); + } + } + if (LangOpts.ObjCRuntime.isNonFragile()) { while (IDecl->getSuperClass()) { DiagnoseDuplicateIvars(IDecl, IDecl->getSuperClass()); diff --git a/clang/test/SemaObjC/subclassing-restricted-attr.m b/clang/test/SemaObjC/subclassing-restricted-attr.m index 2e77df8763aab..5edd342acfc99 100644 --- a/clang/test/SemaObjC/subclassing-restricted-attr.m +++ b/clang/test/SemaObjC/subclassing-restricted-attr.m @@ -21,3 +21,16 @@ @interface PlainRoot __attribute__((objc_subclassing_restricted)) @interface Sub2Class : PlainRoot // okay @end + +// rdar://28753587 +__attribute__((objc_subclassing_restricted)) +@interface SuperImplClass // expected-note {{class is declared here}} +@end +@implementation SuperImplClass +@end + +__attribute__((objc_subclassing_restricted)) +@interface SubImplClass : SuperImplClass +@end +@implementation SubImplClass // expected-error {{cannot subclass a class with objc_subclassing_restricted attribute}} +@end From 114ba7ff213988ae9454678e21e68e400cc7ab3d Mon Sep 17 00:00:00 2001 From: Mehdi Amini <mehdi.amini@apple.com> Date: Tue, 25 Oct 2016 14:11:14 -0700 Subject: [PATCH 110/582] Revert "Fix build after incorrect merge conflict fix" This reverts commit d636f484881c4fc740f7c0232691af475e2e2e37. It seems that my LLVM was out-of-sync with clang, probably because the automerger hadn't pushback the LLVM part. apple-llvm-split-commit: 3a3f8258a8f422a3315b86449dbf03ff193ce6aa apple-llvm-split-dir: clang/ --- clang/lib/Driver/Tools.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index def5bd04c8e62..86f7473cc2c1e 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -5615,7 +5615,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back( Args.MakeArgString("-fbuild-session-timestamp=" + Twine((uint64_t)Status.getLastModificationTime() - .toEpochTime()))); + .time_since_epoch() + .count()))); } if (Args.getLastArg(options::OPT_fmodules_validate_once_per_build_session)) { From bd651478911c0711118fb1adad94380ea497d7c9 Mon Sep 17 00:00:00 2001 From: Brian Gesiak <modocache@gmail.com> Date: Sat, 29 Oct 2016 11:50:09 -0400 Subject: [PATCH 111/582] Remove whitespace changes Remove trailing whitespace that was added in swift-clang, which cause unnecessary divergence from Clang trunk. This reduces the number of files that differ from trunk, from 144 files down to 143 files. apple-llvm-split-commit: beff10e95f9db587d0c48aa5e45054aba3d8d5d0 apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGBuiltin.cpp | 2 +- clang/lib/Sema/SemaDeclObjC.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index a22e1c7174556..c73a6e1b061ed 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2250,7 +2250,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, return RValue::get(llvm::ConstantExpr::getBitCast(GV, CGM.Int8PtrTy)); break; } - + case Builtin::BI__builtin_coro_size: { auto & Context = getContext(); auto SizeTy = Context.getSizeType(); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 757b19b5d5da8..59882ccc46e47 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -2358,7 +2358,7 @@ static bool CheckMethodOverrideParam(Sema &S, } if (S.Context.hasSameUnqualifiedType(ImplTy, IfaceTy)) return true; - + if (!Warn) return false; unsigned DiagID = From 15ec27b9be465a3559f0f9abf4c38fc445cbb9af Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 10 Nov 2016 13:38:38 -0800 Subject: [PATCH 112/582] [APINotes] Update for upstream changes to llvm::BitstreamReader. (#37) rdar://problem/29078044 apple-llvm-split-commit: d39a991b0e768818ba5460c43a2c7d2c60ce138f apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesManager.cpp | 17 +++++++++++++++++ clang/lib/APINotes/APINotesReader.cpp | 21 ++++++++------------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 5d50d3cd1dd4e..ebbd73e4e4aa0 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/SetVector.h" #include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" #include <sys/stat.h> using namespace clang; @@ -51,6 +52,20 @@ STATISTIC(NumBinaryCacheMisses, STATISTIC(NumBinaryCacheRebuilds, "binary form cache rebuilds"); +namespace { + /// Prints two successive strings, which much be kept alive as long as the + /// PrettyStackTrace entry. + class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry { + StringRef First, Second; + public: + PrettyStackTraceDoubleString(StringRef first, StringRef second) + : First(first), Second(second) {} + void print(raw_ostream &OS) const override { + OS << First << Second; + } + }; +} + APINotesManager::APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts) : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes), @@ -140,6 +155,8 @@ static void pruneAPINotesCache(StringRef APINotesCachePath) { std::unique_ptr<APINotesReader> APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { FileManager &fileMgr = SourceMgr.getFileManager(); + PrettyStackTraceDoubleString trace("Loading API notes from ", + apiNotesFile->getName()); // If the API notes file is already in the binary form, load it directly. StringRef apiNotesFileName = apiNotesFile->getName(); diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 9dee73cb43fb3..8681d972d679a 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -502,9 +502,6 @@ class APINotesReader::Implementation { /// The Swift version to use for filtering. VersionTuple SwiftVersion; - /// The reader attached to \c InputBuffer. - llvm::BitstreamReader InputReader; - /// The name of the module that we read from the control block. std::string ModuleName; @@ -1278,10 +1275,7 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, Impl.InputBuffer = inputBuffer; Impl.OwnsInputBuffer = ownsInputBuffer; Impl.SwiftVersion = swiftVersion; - Impl.InputReader.init( - reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferStart()), - reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferEnd())); - llvm::BitstreamCursor cursor(Impl.InputReader); + llvm::BitstreamCursor cursor(Impl.InputBuffer->getBuffer()); // Validate signature. for (auto byte : API_NOTES_SIGNATURE) { @@ -1294,11 +1288,14 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, // Look at all of the blocks. bool hasValidControlBlock = false; SmallVector<uint64_t, 64> scratch; - auto topLevelEntry = cursor.advance(); - while (topLevelEntry.Kind == llvm::BitstreamEntry::SubBlock) { + while (!cursor.AtEndOfStream()) { + auto topLevelEntry = cursor.advance(); + if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) + break; + switch (topLevelEntry.ID) { case llvm::bitc::BLOCKINFO_BLOCK_ID: - if (cursor.ReadBlockInfoBlock()) { + if (!cursor.ReadBlockInfoBlock()) { failed = true; break; } @@ -1399,11 +1396,9 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, } break; } - - topLevelEntry = cursor.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); } - if (topLevelEntry.Kind != llvm::BitstreamEntry::EndBlock) { + if (!cursor.AtEndOfStream()) { failed = true; return; } From 3a1210fa3393ab842e412b768fb0a5edd5efd76d Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 10 Nov 2016 14:03:35 -0800 Subject: [PATCH 113/582] [APINotes] Go with a slightly simpler overload. NFC. (#38) apple-llvm-split-commit: df2cd0cde3d5ad1e59ffafdcf3c07a726c013ceb apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 8681d972d679a..11117a4a1680c 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -1275,7 +1275,7 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, Impl.InputBuffer = inputBuffer; Impl.OwnsInputBuffer = ownsInputBuffer; Impl.SwiftVersion = swiftVersion; - llvm::BitstreamCursor cursor(Impl.InputBuffer->getBuffer()); + llvm::BitstreamCursor cursor(*Impl.InputBuffer); // Validate signature. for (auto byte : API_NOTES_SIGNATURE) { From 10eff17ffc1ad1a99c4175363413def4479497d4 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Tue, 25 Oct 2016 17:37:58 -0700 Subject: [PATCH 114/582] [APINotes] Add support for nullability on arrays. Everything just works once we call the method correctly. Part of rdar://problem/25846421. apple-llvm-split-commit: 3c6ec7a509ad01f4e00d8e61c5c6462a7d60c172 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 2 +- clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes | 6 ++++++ clang/test/APINotes/Inputs/Headers/HeaderLib.h | 1 + clang/test/APINotes/availability.m | 4 ++-- clang/test/APINotes/nullability.c | 5 +++++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 121a7309a8334..2257c14965876 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -64,7 +64,7 @@ static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, QualType origType = type; S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(), /*isContextSensitive=*/false, - /*implicit=*/true, + isa<ParmVarDecl>(decl), /*implicit=*/true, overrideExisting); if (type.getTypePtr() == origType.getTypePtr()) return; diff --git a/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes b/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes index f1cd086126862..c822964ad29c7 100644 --- a/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes @@ -10,6 +10,12 @@ Functions: - Name: do_something_with_pointers NullabilityOfRet: O Nullability: [ N, O ] + - Name: do_something_with_arrays + Parameters: + - Position: 0 + Nullability: N + - Position: 1 + Nullability: N - Name: take_pointer_and_int Parameters: - Position: 0 diff --git a/clang/test/APINotes/Inputs/Headers/HeaderLib.h b/clang/test/APINotes/Inputs/Headers/HeaderLib.h index ec66166adb236..8065249607851 100644 --- a/clang/test/APINotes/Inputs/Headers/HeaderLib.h +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.h @@ -9,6 +9,7 @@ int unavailable_function(void); int unavailable_global_int; void do_something_with_pointers(int *ptr1, int *ptr2); +void do_something_with_arrays(int simple[], int nested[][2]); typedef int unavailable_typedef; struct unavailable_struct { int x, y, z; }; diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m index 5537316dcaa01..6a3034c0ceb50 100644 --- a/clang/test/APINotes/availability.m +++ b/clang/test/APINotes/availability.m @@ -13,10 +13,10 @@ int main() { // expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}} unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}} - // expected-note@HeaderLib.h:13{{'unavailable_typedef' has been explicitly marked unavailable here}} + // expected-note@HeaderLib.h:14{{'unavailable_typedef' has been explicitly marked unavailable here}} struct unavailable_struct s; // expected-error{{'unavailable_struct' is unavailable}} - // expected-note@HeaderLib.h:14{{'unavailable_struct' has been explicitly marked unavailable here}} + // expected-note@HeaderLib.h:15{{'unavailable_struct' has been explicitly marked unavailable here}} B *b = 0; // expected-error{{'B' is unavailable: just don't}} // expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}} diff --git a/clang/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c index 36507f1b9724b..1fcd0ee1b42bc 100644 --- a/clang/test/APINotes/nullability.c +++ b/clang/test/APINotes/nullability.c @@ -8,6 +8,11 @@ int main() { int i = 0; do_something_with_pointers(&i, 0); do_something_with_pointers(0, &i); // expected-warning{{null passed to a callee that requires a non-null argument}} + + extern void *p; + do_something_with_arrays(0, p); // expected-warning{{null passed to a callee that requires a non-null argument}} + do_something_with_arrays(p, 0); // expected-warning{{null passed to a callee that requires a non-null argument}} + take_pointer_and_int(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}} float *fp = global_int; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * _Nonnull'}} From 2288a35a530bc537d38d1bbbd5fd4f0deca605cc Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 11 Nov 2016 14:56:11 -0800 Subject: [PATCH 115/582] [APINotes] Add a 'SwiftImportAsAccessors' entry for properties. (#39) If true, Swift will import the property as a getter and setter (or just a getter for read-only properties), rather than as a Swift 'var' property. At least, it will once this lands and the Swift side gets implemented. Part of rdar://problem/28455962 apple-llvm-split-commit: ac91fd5da8be22915b9bf9592e20536df6a18b70 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 33 ++++++++++++++++++- clang/include/clang/Basic/Attr.td | 8 +++++ clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 3 ++ clang/lib/APINotes/APINotesWriter.cpp | 8 ++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 6 ++++ clang/lib/Sema/SemaAPINotes.cpp | 7 ++++ .../Headers/VersionedKit.apinotes | 31 ++++++++++++++++- .../Headers/VersionedKit.h | 11 +++++++ clang/test/APINotes/Inputs/roundtrip.apinotes | 12 +++++++ clang/test/APINotes/properties.m | 31 +++++++++++++++++ 11 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 clang/test/APINotes/properties.m diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index fae55017e6d0b..09fbc8dcecb2e 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -336,8 +336,13 @@ class VariableInfo : public CommonEntityInfo { /// Describes API notes data for an Objective-C property. class ObjCPropertyInfo : public VariableInfo { + unsigned SwiftImportAsAccessorsSpecified : 1; + unsigned SwiftImportAsAccessors : 1; + public: - ObjCPropertyInfo() : VariableInfo() { } + ObjCPropertyInfo() + : VariableInfo(), SwiftImportAsAccessorsSpecified(false), + SwiftImportAsAccessors(false) {} /// Merge class-wide information into the given property. friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs, @@ -351,6 +356,32 @@ class ObjCPropertyInfo : public VariableInfo { return lhs; } + + Optional<bool> getSwiftImportAsAccessors() const { + if (SwiftImportAsAccessorsSpecified) + return SwiftImportAsAccessors; + return None; + } + void setSwiftImportAsAccessors(Optional<bool> value) { + if (value.hasValue()) { + SwiftImportAsAccessorsSpecified = true; + SwiftImportAsAccessors = value.getValue(); + } else { + SwiftImportAsAccessorsSpecified = false; + SwiftImportAsAccessors = false; + } + } + + friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs, + const ObjCPropertyInfo &rhs) { + lhs |= static_cast<const VariableInfo &>(rhs); + if (!lhs.SwiftImportAsAccessorsSpecified && + rhs.SwiftImportAsAccessorsSpecified) { + lhs.SwiftImportAsAccessorsSpecified = true; + lhs.SwiftImportAsAccessors = rhs.SwiftImportAsAccessors; + } + return lhs; + } }; /// Describes a function or method parameter. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 11ee3426e4df1..84a3ef9589cb5 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1475,6 +1475,14 @@ def SwiftSuppressFactoryAsInit : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftImportPropertyAsAccessors : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + def ReqdWorkGroupSize : InheritableAttr { let Spellings = [GNU<"reqd_work_group_size">]; let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">, diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 4623773a1f0d4..b3d2d36c93da4 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 19; // SwiftWrapper +const uint16_t VERSION_MINOR = 20; // ImportPropertyAsAccessors using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 11117a4a1680c..2599830cc105c 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -285,6 +285,9 @@ namespace { const uint8_t *&data) { ObjCPropertyInfo info; readVariableInfo(data, info); + uint8_t flags = *data++; + if (flags & (1 << 0)) + info.setSwiftImportAsAccessors(flags & (1 << 1)); return info; } }; diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index c0527aa483a5e..d696aa604e28c 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -606,11 +606,17 @@ namespace { } unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) { - return getVariableInfoSize(info); + return getVariableInfoSize(info) + 1; } void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) { emitVariableInfo(out, info); + uint8_t flags = 0; + if (Optional<bool> value = info.getSwiftImportAsAccessors()) { + flags |= 1 << 0; + flags |= value.getValue() << 1; + } + out << flags; } }; } // end anonymous namespace diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index d24a63fafef1b..1f3783c6a18a7 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -199,6 +199,7 @@ namespace { AvailabilityItem Availability; Optional<bool> SwiftPrivate; StringRef SwiftName; + Optional<bool> SwiftImportAsAccessors; }; typedef std::vector<Property> PropertiesSeq; @@ -398,6 +399,7 @@ namespace llvm { io.mapOptional("AvailabilityMsg", p.Availability.Msg); io.mapOptional("SwiftPrivate", p.SwiftPrivate); io.mapOptional("SwiftName", p.SwiftName); + io.mapOptional("SwiftImportAsAccessors", p.SwiftImportAsAccessors); } }; @@ -784,6 +786,8 @@ namespace { pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); + if (prop.SwiftImportAsAccessors) + pInfo.setSwiftImportAsAccessors(*prop.SwiftImportAsAccessors); if (prop.Kind) { Writer->addObjCProperty(clID, prop.Name, *prop.Kind == MethodKind::Instance, pInfo, @@ -1197,6 +1201,8 @@ namespace { property.Nullability = *nullability; } + property.SwiftImportAsAccessors = info.getSwiftImportAsAccessors(); + auto &items = getTopLevelItems(swiftVersion); knownContexts[contextID.Value].getContext(swiftVersion, items) .Properties.push_back(property); diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 2257c14965876..73329229ae2d9 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -310,6 +310,13 @@ static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), role); + if (auto asAccessors = info.getSwiftImportAsAccessors()) { + handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D, + *asAccessors, + role, [&] { + return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context); + }); + } } namespace { diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index 4215fb9d15318..c3b70b6e9520b 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -1,9 +1,38 @@ -Name: SomeKit +Name: VersionedKit +Classes: + - Name: TestProperties + Properties: + - Name: accessorsOnly + PropertyKind: Instance + SwiftImportAsAccessors: true + - Name: accessorsOnlyForClass + PropertyKind: Class + SwiftImportAsAccessors: true + - Name: accessorsOnlyExceptInVersion3 + PropertyKind: Instance + SwiftImportAsAccessors: true + - Name: accessorsOnlyForClassExceptInVersion3 + PropertyKind: Class + SwiftImportAsAccessors: true SwiftVersions: - Version: 3.0 Classes: - Name: MyReferenceType SwiftBridge: '' + - Name: TestProperties + Properties: + - Name: accessorsOnlyInVersion3 + PropertyKind: Instance + SwiftImportAsAccessors: true + - Name: accessorsOnlyForClassInVersion3 + PropertyKind: Class + SwiftImportAsAccessors: true + - Name: accessorsOnlyExceptInVersion3 + PropertyKind: Instance + SwiftImportAsAccessors: false + - Name: accessorsOnlyForClassExceptInVersion3 + PropertyKind: Class + SwiftImportAsAccessors: false Functions: - Name: moveToPoint SwiftName: 'moveTo(a:b:)' diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index db71d8f576f6d..ffbc7df7229d2 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -17,3 +17,14 @@ __attribute__((swift_bridge("MyValueType"))) void privateFunc(void) __attribute__((swift_private)); typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); + +@interface TestProperties +@property (nonatomic, readwrite, retain) id accessorsOnly; +@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClass; + +@property (nonatomic, readwrite, retain) id accessorsOnlyInVersion3; +@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassInVersion3; + +@property (nonatomic, readwrite, retain) id accessorsOnlyExceptInVersion3; +@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassExceptInVersion3; +@end diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index c6beaf1928f26..68169c4e4d259 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -104,6 +104,7 @@ Classes: AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' + SwiftImportAsAccessors: false Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N @@ -162,3 +163,14 @@ SwiftVersions: SwiftPrivate: true SwiftName: '' DesignatedInit: true + - Name: NSView + Availability: available + AvailabilityMsg: '' + SwiftName: '' + Properties: + - Name: makeBackingLayer + PropertyKind: Class + Availability: available + AvailabilityMsg: '' + SwiftName: '' + SwiftImportAsAccessors: true diff --git a/clang/test/APINotes/properties.m b/clang/test/APINotes/properties.m new file mode 100644 index 0000000000000..a4c925b67449a --- /dev/null +++ b/clang/test/APINotes/properties.m @@ -0,0 +1,31 @@ +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' | FileCheck -check-prefix=CHECK -check-prefix=CHECK-4 %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' -fapinotes-swift-version=3 | FileCheck -check-prefix=CHECK -check-prefix=CHECK-3 %s + +// I know, FileChecking an AST dump is brittle. However, the attributes being +// tested aren't used for anything by Clang, and don't even have a spelling. + +@import VersionedKit; + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnly 'id' +// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClass 'id' +// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id' +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: {{^$}} + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id' +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: {{^$}} + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id' +// CHECK-3-NEXT: {{^$}} +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 'id' +// CHECK-3-NEXT: {{^$}} +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit From 09cdee995b9c8ef17fdcf350093e992cd83ab563 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Fri, 11 Nov 2016 16:27:19 -0800 Subject: [PATCH 116/582] [API Notes] Add support for expressing the types of entities. Introduces the "Type" key for global variables, properties, and parameters and the "ResultType" key for functions and methods, to describe the (Objective-)C type of that entity. This commit handles YAML, the representation of this information, and round-tripping through the binary API notes. Part of rdar://problem/28943642. apple-llvm-split-commit: ebca3ea28181818c7fe7ba320c9ae7775d8678fa apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 20 ++++++- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 33 ++++++++---- clang/lib/APINotes/APINotesWriter.cpp | 53 ++++++++++++------- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 26 +++++++-- clang/test/APINotes/Inputs/roundtrip.apinotes | 5 ++ 6 files changed, 103 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 09fbc8dcecb2e..6fbd5d809dad6 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -297,6 +297,9 @@ class VariableInfo : public CommonEntityInfo { /// has been audited. unsigned Nullable : 2; + /// The C type of the variable, as a string. + std::string Type; + public: VariableInfo() : CommonEntityInfo(), @@ -315,10 +318,14 @@ class VariableInfo : public CommonEntityInfo { Nullable = static_cast<unsigned>(kind); } + const std::string &getType() const { return Type; } + void setType(const std::string &type) { Type = type; } + friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) { return static_cast<const CommonEntityInfo &>(lhs) == rhs && lhs.NullabilityAudited == rhs.NullabilityAudited && - lhs.Nullable == rhs.Nullable; + lhs.Nullable == rhs.Nullable && + lhs.Type == rhs.Type; } friend bool operator!=(const VariableInfo &lhs, const VariableInfo &rhs) { @@ -330,6 +337,8 @@ class VariableInfo : public CommonEntityInfo { static_cast<CommonEntityInfo &>(lhs) |= rhs; if (!lhs.NullabilityAudited && rhs.NullabilityAudited) lhs.setNullabilityAudited(*rhs.getNullability()); + if (lhs.Type.empty() && !rhs.Type.empty()) + lhs.Type = rhs.Type; return lhs; } }; @@ -347,6 +356,8 @@ class ObjCPropertyInfo : public VariableInfo { /// Merge class-wide information into the given property. friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs, const ObjCContextInfo &rhs) { + static_cast<VariableInfo &>(lhs) |= rhs; + // Merge nullability. if (!lhs.getNullability()) { if (auto nullable = rhs.getDefaultNullability()) { @@ -462,6 +473,9 @@ class FunctionInfo : public CommonEntityInfo { // of the parameters. uint64_t NullabilityPayload = 0; + /// The result type of this function, as a C type. + std::string ResultType; + /// The function parameters. std::vector<ParamInfo> Params; @@ -525,7 +539,9 @@ class FunctionInfo : public CommonEntityInfo { return static_cast<const CommonEntityInfo &>(lhs) == rhs && lhs.NullabilityAudited == rhs.NullabilityAudited && lhs.NumAdjustedNullable == rhs.NumAdjustedNullable && - lhs.NullabilityPayload == rhs.NullabilityPayload; + lhs.NullabilityPayload == rhs.NullabilityPayload && + lhs.ResultType == rhs.ResultType && + lhs.Params == rhs.Params; } friend bool operator!=(const FunctionInfo &lhs, const FunctionInfo &rhs) { diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index b3d2d36c93da4..ef3ef73ab0cdd 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 20; // ImportPropertyAsAccessors +const uint16_t VERSION_MINOR = 21; // Override types using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 2599830cc105c..8fbe2a184541e 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -265,6 +265,11 @@ namespace { info.setNullabilityAudited(static_cast<NullabilityKind>(*data)); } ++data; + + auto typeLen + = endian::readNext<uint16_t, little, unaligned>(data); + info.setType(std::string(data, data + typeLen)); + data += typeLen; } /// Used to deserialize the on-disk Objective-C property table. @@ -292,6 +297,17 @@ namespace { } }; + /// Read serialized ParamInfo. + void readParamInfo(const uint8_t *&data, ParamInfo &info) { + readVariableInfo(data, info); + + uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data); + if (payload & 0x01) { + info.setNoEscape(payload & 0x02); + } + payload >>= 2; assert(payload == 0 && "Bad API notes"); + } + /// Read serialized FunctionInfo. void readFunctionInfo(const uint8_t *&data, FunctionInfo &info) { readCommonEntityInfo(data, info); @@ -304,21 +320,16 @@ namespace { unsigned numParams = endian::readNext<uint16_t, little, unaligned>(data); while (numParams > 0) { - uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data); - ParamInfo pi; - uint8_t nullabilityValue = payload & 0x3; payload >>= 2; - if (payload & 0x01) - pi.setNullabilityAudited(static_cast<NullabilityKind>(nullabilityValue)); - payload >>= 1; - if (payload & 0x01) { - pi.setNoEscape(payload & 0x02); - } - payload >>= 2; assert(payload == 0 && "Bad API notes"); - + readParamInfo(data, pi); info.Params.push_back(pi); --numParams; } + + unsigned resultTypeLen + = endian::readNext<uint16_t, little, unaligned>(data); + info.ResultType = std::string(data, data + resultTypeLen); + data += resultTypeLen; } /// Used to deserialize the on-disk Objective-C method table. diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index d696aa604e28c..86b6abd2a36e6 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -490,7 +490,7 @@ namespace { /// Retrieve the serialized size of the given VariableInfo, for use in /// on-disk hash tables. unsigned getVariableInfoSize(const VariableInfo &info) { - return 2 + getCommonEntityInfoSize(info); + return 2 + getCommonEntityInfoSize(info) + 2 + info.getType().size(); } /// Emit a serialized representation of the variable information. @@ -506,6 +506,10 @@ namespace { } out.write(reinterpret_cast<const char *>(bytes), 2); + + endian::Writer<little> writer(out); + writer.write<uint16_t>(info.getType().size()); + out.write(info.getType().data(), info.getType().size()); } /// On-dish hash table info key base for handling versioned data. @@ -691,11 +695,34 @@ void APINotesWriter::Implementation::writeObjCPropertyBlock( } namespace { + static unsigned getParamInfoSize(const ParamInfo &info) { + return getVariableInfoSize(info) + 1; + } + + static void emitParamInfo(raw_ostream &out, const ParamInfo &info) { + emitVariableInfo(out, info); + + endian::Writer<little> writer(out); + + uint8_t payload = 0; + if (auto noescape = info.isNoEscape()) { + payload |= 0x01; + if (*noescape) + payload |= 0x02; + } + writer.write<uint8_t>(payload); + } + /// Retrieve the serialized size of the given FunctionInfo, for use in /// on-disk hash tables. static unsigned getFunctionInfoSize(const FunctionInfo &info) { - return 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + - 2 + info.Params.size() * 1; + unsigned size = 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + 2; + + for (const auto ¶m : info.Params) + size += getParamInfoSize(param); + + size += 2 + info.ResultType.size(); + return size; } /// Emit a serialized representation of the function information. @@ -709,22 +736,12 @@ namespace { // Parameters. writer.write<uint16_t>(info.Params.size()); - for (const auto &pi : info.Params) { - uint8_t payload = 0; - if (auto noescape = pi.isNoEscape()) { - payload |= 0x01; - if (*noescape) - payload |= 0x02; - } - - auto nullability = pi.getNullability(); - payload = (payload << 1) | nullability.hasValue(); + for (const auto &pi : info.Params) + emitParamInfo(out, pi); - payload = payload << 2; - if (nullability) - payload |= static_cast<uint8_t>(*nullability); - writer.write<uint8_t>(payload); - } + // Result type. + writer.write<uint16_t>(info.ResultType.size()); + out.write(info.ResultType.data(), info.ResultType.size()); } /// Used to serialize the on-disk Objective-C method table. diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 1f3783c6a18a7..0ac0b6d65b767 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -173,6 +173,7 @@ namespace { unsigned Position; Optional<bool> NoEscape = false; llvm::Optional<NullabilityKind> Nullability; + StringRef Type; }; typedef std::vector<Param> ParamsSeq; @@ -189,6 +190,7 @@ namespace { = api_notes::FactoryAsInitKind::Infer; bool DesignatedInit = false; bool Required = false; + StringRef ResultType; }; typedef std::vector<Method> MethodsSeq; @@ -200,6 +202,7 @@ namespace { Optional<bool> SwiftPrivate; StringRef SwiftName; Optional<bool> SwiftImportAsAccessors; + StringRef Type; }; typedef std::vector<Property> PropertiesSeq; @@ -224,6 +227,8 @@ namespace { AvailabilityItem Availability; Optional<bool> SwiftPrivate; StringRef SwiftName; + StringRef Type; + StringRef ResultType; }; typedef std::vector<Function> FunctionsSeq; @@ -233,6 +238,7 @@ namespace { AvailabilityItem Availability; Optional<bool> SwiftPrivate; StringRef SwiftName; + StringRef Type; }; typedef std::vector<GlobalVariable> GlobalVariablesSeq; @@ -385,6 +391,7 @@ namespace llvm { io.mapOptional("Nullability", p.Nullability, AbsentNullability); io.mapOptional("NoEscape", p.NoEscape); + io.mapOptional("Type", p.Type, StringRef("")); } }; @@ -400,6 +407,7 @@ namespace llvm { io.mapOptional("SwiftPrivate", p.SwiftPrivate); io.mapOptional("SwiftName", p.SwiftName); io.mapOptional("SwiftImportAsAccessors", p.SwiftImportAsAccessors); + io.mapOptional("Type", p.Type, StringRef("")); } }; @@ -420,6 +428,7 @@ namespace llvm { api_notes::FactoryAsInitKind::Infer); io.mapOptional("DesignatedInit", m.DesignatedInit, false); io.mapOptional("Required", m.Required, false); + io.mapOptional("ResultType", m.ResultType, StringRef("")); } }; @@ -451,6 +460,7 @@ namespace llvm { io.mapOptional("AvailabilityMsg", f.Availability.Msg); io.mapOptional("SwiftPrivate", f.SwiftPrivate); io.mapOptional("SwiftName", f.SwiftName); + io.mapOptional("ResultType", f.ResultType, StringRef("")); } }; @@ -464,6 +474,7 @@ namespace llvm { io.mapOptional("AvailabilityMsg", v.Availability.Msg); io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); + io.mapOptional("Type", v.Type, StringRef("")); } }; @@ -621,7 +632,7 @@ namespace { if (p.Nullability) pi.setNullabilityAudited(*p.Nullability); pi.setNoEscape(p.NoEscape); - + pi.setType(p.Type); while (outInfo.Params.size() <= p.Position) { outInfo.Params.push_back(ParamInfo()); } @@ -712,6 +723,7 @@ namespace { mInfo.Required = meth.Required; if (meth.FactoryAsInit != FactoryAsInitKind::Infer) mInfo.setFactoryAsInitKind(meth.FactoryAsInit); + mInfo.ResultType = meth.ResultType; // Translate parameter information. convertParams(meth.Params, mInfo); @@ -788,6 +800,7 @@ namespace { pInfo.setNullabilityAudited(*prop.Nullability); if (prop.SwiftImportAsAccessors) pInfo.setSwiftImportAsAccessors(*prop.SwiftImportAsAccessors); + pInfo.setType(prop.Type); if (prop.Kind) { Writer->addObjCProperty(clID, prop.Name, *prop.Kind == MethodKind::Instance, pInfo, @@ -844,6 +857,7 @@ namespace { info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); + info.setType(global.Type); Writer->addGlobalVariable(global.Name, info, swiftVersion); } @@ -867,7 +881,7 @@ namespace { convertNullability(function.Nullability, function.NullabilityOfRet, info, function.Name); - + info.ResultType = function.ResultType; Writer->addGlobalFunction(function.Name, info, swiftVersion); } @@ -1112,6 +1126,7 @@ namespace { p.Position = position++; p.Nullability = pi.getNullability(); p.NoEscape = pi.isNoEscape(); + p.Type = copyString(pi.getType()); params.push_back(p); } } @@ -1181,7 +1196,7 @@ namespace { method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; - + method.ResultType = copyString(info.ResultType); auto &items = getTopLevelItems(swiftVersion); knownContexts[contextID.Value].getContext(swiftVersion, items) .Methods.push_back(method); @@ -1203,6 +1218,8 @@ namespace { property.SwiftImportAsAccessors = info.getSwiftImportAsAccessors(); + property.Type = copyString(info.getType()); + auto &items = getTopLevelItems(swiftVersion); knownContexts[contextID.Value].getContext(swiftVersion, items) .Properties.push_back(property); @@ -1218,7 +1235,7 @@ namespace { if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); - + function.ResultType = copyString(info.ResultType); auto &items = getTopLevelItems(swiftVersion); items.Functions.push_back(function); } @@ -1234,6 +1251,7 @@ namespace { if (auto nullability = info.getNullability()) { global.Nullability = *nullability; } + global.Type = copyString(info.getType()); auto &items = getTopLevelItems(swiftVersion); items.Globals.push_back(global); diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 68169c4e4d259..05599a772d5d6 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -17,6 +17,7 @@ Classes: SwiftPrivate: false SwiftName: '' FactoryAsInit: C + ResultType: id - Selector: init MethodKind: Instance NullabilityOfRet: U @@ -77,6 +78,7 @@ Classes: - Position: 1 - Position: 2 NoEscape: true + Type: id Nullability: [ N, N, O ] NullabilityOfRet: N Availability: available @@ -97,6 +99,7 @@ Classes: Availability: available AvailabilityMsg: '' SwiftName: enclosing + Type: id - Name: makeBackingLayer PropertyKind: Class Nullability: N @@ -111,6 +114,7 @@ Functions: Availability: available AvailabilityMsg: '' SwiftName: 'availableWindowDepths()' + ResultType: NSInteger Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N @@ -118,6 +122,7 @@ Globals: AvailabilityMsg: '' SwiftPrivate: false SwiftName: calibratedWhite + Type: id Enumerators: - Name: NSColorRed Availability: available From 239c40199eb1b9d96df188c16a1e73946871fe50 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Fri, 11 Nov 2016 16:43:52 -0800 Subject: [PATCH 117/582] [API Notes] Enable support for overriding the types of declarations. Lazily parse the types provided in API notes, and apply them to the AST. The lazy parsing is handled via a callback from Sema into the Parser, which parses the type in the current context. We perform some simplistic sanity checking (e.g., the size of the replacement type is the same as the original type), but otherwise this is a dangerous power tool that will need to be used carefully. The specific API notes features this enables are making use of the 'Type' field property/global variable/parameter API notes and the 'ResultType' field of method and function API notes. Finishes rdar://problem/28943642. apple-llvm-split-commit: 4aa4c8bd93b23c4f83ae954a9f187b7c3bdd2b40 apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticParseKinds.td | 3 + .../clang/Basic/DiagnosticSemaKinds.td | 7 ++ clang/include/clang/Lex/Lexer.h | 6 +- clang/include/clang/Parse/Parser.h | 14 ++- clang/include/clang/Sema/Sema.h | 7 ++ clang/lib/Parse/ParseDecl.cpp | 65 ++++++++++ clang/lib/Parse/Parser.cpp | 8 ++ clang/lib/Sema/SemaAPINotes.cpp | 115 +++++++++++++++++- clang/lib/Sema/SemaDecl.cpp | 18 ++- .../APINotes/SomeKit.apinotes | 25 ++++ .../Headers/SomeKit.apinotes | 25 ++++ .../SomeKit.framework/Headers/SomeKit.h | 12 ++ .../Inputs/Headers/BrokenTypes.apinotes | 10 ++ .../APINotes/Inputs/Headers/BrokenTypes.h | 8 ++ .../APINotes/Inputs/Headers/module.modulemap | 4 + clang/test/APINotes/broken_types.m | 19 +++ clang/test/APINotes/types.m | 23 ++++ 17 files changed, 357 insertions(+), 12 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Headers/BrokenTypes.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/BrokenTypes.h create mode 100644 clang/test/APINotes/broken_types.m create mode 100644 clang/test/APINotes/types.m diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index b5b90559b6e9c..be8802e5baf97 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1024,6 +1024,9 @@ def err_pragma_loop_invalid_option : Error< def err_pragma_invalid_keyword : Error< "invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">; +// API notes. +def err_type_unparsed : Error<"unparsed tokens following type">; + // Pragma unroll support. def warn_pragma_unroll_cuda_value_in_parens : Warning< "argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 51f10ba1d34e3..62775c0c1ee87 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8248,6 +8248,13 @@ def ext_opencl_ext_vector_type_rgba_selector: ExtWarn< InGroup<OpenCLUnsupportedRGBA>; } // end of sema category +let CategoryName = "API Notes Issue" in { + +def err_incompatible_replacement_type : Error< + "API notes replacement type %0 has a different size from original type %1">; + +} + let CategoryName = "OpenMP Issue" in { // OpenMP support. def err_omp_expected_var_arg : Error< diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 830c25a2e4d29..6f1f7c6151b5d 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -133,15 +133,17 @@ class Lexer : public PreprocessorLexer { /// from. Currently this is only used by _Pragma handling. SourceLocation getFileLoc() const { return FileLoc; } -private: /// Lex - Return the next token in the file. If this is the end of file, it /// return the tok::eof token. This implicitly involves the preprocessor. bool Lex(Token &Result); -public: /// isPragmaLexer - Returns true if this Lexer is being used to lex a pragma. bool isPragmaLexer() const { return Is_PragmaLexer; } + /// Note that this Lexer is being used to lex a pragma, or something like it + /// that has simple end-of-file behavior. + void setIsPragmaLexer(bool value) { Is_PragmaLexer = value; } + private: /// IndirectLex - An indirect call to 'Lex' that can be invoked via /// the PreprocessorLexer interface. diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 91d3c846cacf6..b5d7e93449bdb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2695,7 +2695,19 @@ class Parser : public CodeCompletionHandler { //===--------------------------------------------------------------------===// // C++11/G++: Type Traits [Type-Traits.html in the GCC manual] ExprResult ParseTypeTrait(); - + + /// Parse the given string as a type. + /// + /// This is a dangerous utility function currently employed only by API notes. + /// It is not a general entry-point for safely parsing types from strings. + /// + /// \param typeStr The string to be parsed as a type. + /// \param context The name of the context in which this string is being + /// parsed, which will be used in diagnostics. + /// \param includeLoc The location at which this parse was triggered. + TypeResult parseTypeFromString(StringRef typeStr, StringRef context, + SourceLocation includeLoc); + //===--------------------------------------------------------------------===// // Embarcadero: Arary and Expression Traits ExprResult ParseArrayTypeTrait(); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 473a1df11995c..4037cf4906ee7 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -54,6 +54,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TinyPtrVector.h" #include <deque> +#include <functional> #include <memory> #include <string> #include <vector> @@ -573,6 +574,10 @@ class Sema { OpaqueParser = P; } + /// \brief Callback to the parser to parse a type expressed as a string. + std::function<TypeResult(StringRef, StringRef, SourceLocation)> + ParseTypeFromStringCallback; + class DelayedDiagnostics; class DelayedDiagnosticsState { @@ -1796,6 +1801,8 @@ class Sema { ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC, SourceLocation Loc, QualType T); + QualType adjustParameterTypeForObjCAutoRefCount(QualType T, + SourceLocation Loc); ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc, SourceLocation NameLoc, IdentifierInfo *Name, QualType T, TypeSourceInfo *TSInfo, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 3d82280d22ab7..89b7a322569c9 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6583,3 +6583,68 @@ bool Parser::TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc, } return false; } + +TypeResult Parser::parseTypeFromString(StringRef typeStr, StringRef context, + SourceLocation includeLoc) { + // Consume (unexpanded) tokens up to the end-of-directive. + SmallVector<Token, 4> tokens; + { + // Create a new buffer from which we will parse the type. + auto &sourceMgr = PP.getSourceManager(); + FileID fileID = sourceMgr.createFileID( + llvm::MemoryBuffer::getMemBufferCopy(typeStr, context), + SrcMgr::C_User, 0, 0, includeLoc); + + // Form a new lexer that references the buffer. + Lexer lexer(fileID, sourceMgr.getBuffer(fileID), PP); + lexer.setParsingPreprocessorDirective(true); + lexer.setIsPragmaLexer(true); + + // Lex the tokens from that buffer. + Token tok; + do { + lexer.Lex(tok); + tokens.push_back(tok); + } while (tok.isNot(tok::eod)); + } + + // Replace the "eod" token with an "eof" token identifying the end of + // the provided string. + Token &endToken = tokens.back(); + endToken.startToken(); + endToken.setKind(tok::eof); + endToken.setLocation(Tok.getLocation()); + endToken.setEofData(typeStr.data()); + + // Add the current token back. + tokens.push_back(Tok); + + // Enter the tokens into the token stream. + PP.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false); + + // Consume the current token so that we'll start parsing the tokens we + // added to the stream. + ConsumeAnyToken(); + + // Enter a new scope. + ParseScope localScope(this, 0); + + // Parse the type. + TypeResult result = ParseTypeName(nullptr); + + // Check if we parsed the whole thing. + if (result.isUsable() && + (Tok.isNot(tok::eof) || Tok.getEofData() != typeStr.data())) { + Diag(Tok.getLocation(), diag::err_type_unparsed); + } + + // There could be leftover tokens (e.g. because of an error). + // Skip through until we reach the 'end of directive' token. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Consume the end token. + if (Tok.is(tok::eof) && Tok.getEofData() == typeStr.data()) + ConsumeAnyToken(); + return result; +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 78eba62695dda..0be9b32f14fa4 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -88,6 +88,11 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) PP.addCommentHandler(CommentSemaHandler.get()); PP.setCodeCompletionHandler(*this); + + Actions.ParseTypeFromStringCallback = + [this](StringRef typeStr, StringRef context, SourceLocation includeLoc) { + return this->parseTypeFromString(typeStr, context, includeLoc); + }; } DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) { @@ -420,6 +425,9 @@ Parser::ParseScopeFlags::~ParseScopeFlags() { //===----------------------------------------------------------------------===// Parser::~Parser() { + // Clear out the parse-type-from-string callback. + Actions.ParseTypeFromStringCallback = nullptr; + // If we still have scopes active, delete the scope tree. delete getCurScope(); Actions.CurScope = nullptr; diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 73329229ae2d9..cf88f735018a7 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -264,10 +264,62 @@ static void ProcessAPINotes(Sema &S, Decl *D, role); } +/// Check that the replacement type provided by API notes is reasonable. +/// +/// This is a very weak form of ABI check. +static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc, + QualType origType, + QualType replacementType) { + if (S.Context.getTypeSize(origType) != + S.Context.getTypeSize(replacementType)) { + S.Diag(loc, diag::err_incompatible_replacement_type) + << replacementType << origType; + return true; + } + + return false; +} + /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &info, VersionedInfoRole role) { + // Type override. + if (role != VersionedInfoRole::Versioned && + !info.getType().empty() && S.ParseTypeFromStringCallback) { + auto parsedType = S.ParseTypeFromStringCallback(info.getType(), + "<API Notes>", + D->getLocation()); + if (parsedType.isUsable()) { + QualType type = Sema::GetTypeFromParser(parsedType.get()); + auto typeInfo = + S.Context.getTrivialTypeSourceInfo(type, D->getLocation()); + + if (auto var = dyn_cast<VarDecl>(D)) { + // Make adjustments to parameter types. + if (isa<ParmVarDecl>(var)) { + type = S.adjustParameterTypeForObjCAutoRefCount(type, + D->getLocation()); + type = S.Context.getAdjustedParameterType(type); + } + + if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(), + type)) { + var->setType(type); + var->setTypeSourceInfo(typeInfo); + } + } else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) { + if (!checkAPINotesReplacementType(S, property->getLocation(), + property->getType(), + type)) { + property->setType(type, typeInfo); + } + } else { + llvm_unreachable("API notes allowed a type on an unknown declaration"); + } + } + } + // Nullability. if (auto Nullability = info.getNullability()) { applyNullability(S, D, *Nullability, role); @@ -347,20 +399,75 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, NumParams = FD->getNumParams(); else NumParams = MD->param_size(); - + + bool anyTypeChanged = false; for (unsigned I = 0; I != NumParams; ++I) { ParmVarDecl *Param; if (FD) Param = FD->getParamDecl(I); else Param = MD->param_begin()[I]; - + + QualType paramTypeBefore = Param->getType(); + + if (I < info.Params.size()) { + ProcessAPINotes(S, Param, info.Params[I], role); + } + // Nullability. if (info.NullabilityAudited) applyNullability(S, Param, info.getParamTypeInfo(I), role); - if (I < info.Params.size()) { - ProcessAPINotes(S, Param, info.Params[I], role); + if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) + anyTypeChanged = true; + } + + // Result type override. + QualType overriddenResultType; + if (role != VersionedInfoRole::Versioned && !info.ResultType.empty() && + S.ParseTypeFromStringCallback) { + auto parsedType = S.ParseTypeFromStringCallback(info.ResultType, + "<API Notes>", + D->getLocation()); + if (parsedType.isUsable()) { + QualType resultType = Sema::GetTypeFromParser(parsedType.get()); + + if (MD) { + if (!checkAPINotesReplacementType(S, D->getLocation(), + MD->getReturnType(), resultType)) { + auto resultTypeInfo = + S.Context.getTrivialTypeSourceInfo(resultType, D->getLocation()); + MD->setReturnType(resultType); + MD->setReturnTypeSourceInfo(resultTypeInfo); + } + } else if (!checkAPINotesReplacementType(S, FD->getLocation(), + FD->getReturnType(), + resultType)) { + overriddenResultType = resultType; + anyTypeChanged = true; + } + } + } + + // If the result type or any of the parameter types changed for a function + // declaration, we have to rebuild the type. + if (FD && anyTypeChanged) { + if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) { + if (overriddenResultType.isNull()) + overriddenResultType = fnProtoType->getReturnType(); + + SmallVector<QualType, 4> paramTypes; + for (auto param : FD->parameters()) { + paramTypes.push_back(param->getType()); + } + FD->setType(S.Context.getFunctionType(overriddenResultType, + paramTypes, + fnProtoType->getExtProtoInfo())); + } else if (!overriddenResultType.isNull()) { + const auto *fnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>(); + FD->setType( + S.Context.getFunctionNoProtoType(overriddenResultType, + fnNoProtoType->getExtInfo())); } } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 5369802ed6bda..9cbe8e1ad78ce 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11257,10 +11257,8 @@ void Sema::DiagnoseSizeOfParametersAndReturnValue( } } -ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, - SourceLocation NameLoc, IdentifierInfo *Name, - QualType T, TypeSourceInfo *TSInfo, - StorageClass SC) { +QualType Sema::adjustParameterTypeForObjCAutoRefCount(QualType T, + SourceLocation Loc) { // In ARC, infer a lifetime qualifier for appropriate parameter types. if (getLangOpts().ObjCAutoRefCount && T.getObjCLifetime() == Qualifiers::OCL_None && @@ -11275,7 +11273,7 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, if (!T.isConstQualified()) { DelayedDiagnostics.add( sema::DelayedDiagnostic::makeForbiddenType( - NameLoc, diag::err_arc_array_param_no_ownership, T, false)); + Loc, diag::err_arc_array_param_no_ownership, T, false)); } lifetime = Qualifiers::OCL_ExplicitNone; } else { @@ -11284,6 +11282,16 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, T = Context.getLifetimeQualifiedType(T, lifetime); } + return T; +} + +ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, + SourceLocation NameLoc, IdentifierInfo *Name, + QualType T, TypeSourceInfo *TSInfo, + StorageClass SC) { + // Perform Objective-C ARC adjustments. + T = adjustParameterTypeForObjCAutoRefCount(T, NameLoc); + ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name, Context.getAdjustedParameterType(T), TSInfo, SC, nullptr); diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index aa43d849be1d0..817af123fc77b 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -31,6 +31,31 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true + - Name: OverriddenTypes + Methods: + - Selector: "methodToMangle:second:" + MethodKind: Instance + ResultType: 'char *' + Parameters: + - Position: 0 + Type: 'SOMEKIT_DOUBLE *' + - Position: 1 + Type: 'float *' + Properties: + - Name: intPropertyToMangle + PropertyKind: Instance + Type: 'double *' +Functions: + - Name: global_int_fun + ResultType: 'char *' + Parameters: + - Position: 0 + Type: 'double *' + - Position: 1 + Type: 'float *' +Globals: + - Name: global_int_ptr + Type: 'double *' SwiftVersions: - Version: 3.0 Classes: diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes index aa43d849be1d0..d2d10037501d7 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes @@ -31,6 +31,31 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true + - Name: OverriddenTypes + Methods: + - Selector: "methodToMangle:second:" + MethodKind: Instance + ResultType: 'char *' + Parameters: + - Position: 0 + Type: 'SOMEKIT_DOUBLE *' + - Position: 1 + Type: 'float *' + Properties: + - Name: intPropertyToMangle + PropertyKind: Instance + Type: 'double *' +Functions: + - Name: global_int_fun + ResultType: 'char *' + Parameters: + - Position: 0 + Type: 'double *' + - Position: 1 + Type: 'float *' +Globals: + - Name: global_int_ptr + Type: 'double (*)(int, int)' SwiftVersions: - Version: 3.0 Classes: diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h index 60d9afa30509a..42a9fbcc2b27b 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -37,4 +37,16 @@ __attribute__((objc_root_class)) #import <SomeKit/SomeKitExplicitNullability.h> +extern int *global_int_ptr; + +int *global_int_fun(int *ptr, int *ptr2); + +#define SOMEKIT_DOUBLE double + +__attribute__((objc_root_class)) +@interface OverriddenTypes +-(int *)methodToMangle:(int *)ptr1 second:(int *)ptr2; +@property int *intPropertyToMangle; +@end + #endif diff --git a/clang/test/APINotes/Inputs/Headers/BrokenTypes.apinotes b/clang/test/APINotes/Inputs/Headers/BrokenTypes.apinotes new file mode 100644 index 0000000000000..00f7b5074e985 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/BrokenTypes.apinotes @@ -0,0 +1,10 @@ +Name: BrokenTypes +Functions: + - Name: break_me_function + ResultType: 'int * with extra junk' + Parameters: + - Position: 0 + Type: 'not_a_type' +Globals: + - Name: break_me_variable + Type: 'double' diff --git a/clang/test/APINotes/Inputs/Headers/BrokenTypes.h b/clang/test/APINotes/Inputs/Headers/BrokenTypes.h new file mode 100644 index 0000000000000..fee054b74cf70 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/BrokenTypes.h @@ -0,0 +1,8 @@ +#ifndef BROKEN_TYPES_H +#define BROKEN_TYPES_H + +char break_me_function(void *ptr); + +extern char break_me_variable; + +#endif // BROKEN_TYPES_H diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index 3e59efcf2c482..54af0f5e76c74 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -1,3 +1,7 @@ module HeaderLib { header "HeaderLib.h" } + +module BrokenTypes { + header "BrokenTypes.h" +} diff --git a/clang/test/APINotes/broken_types.m b/clang/test/APINotes/broken_types.m new file mode 100644 index 0000000000000..164ae795f4c2d --- /dev/null +++ b/clang/test/APINotes/broken_types.m @@ -0,0 +1,19 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2> %t.err +// RUN: FileCheck %s < %t.err + +#include "BrokenTypes.h" + +// CHECK: <API Notes>:1:1: error: unknown type name 'not_a_type' +// CHECK-NEXT: not_a_type +// CHECK-NEXT: ^ + +// CHECK: <API Notes>:1:7: error: unparsed tokens following type +// CHECK-NEXT: int * with extra junk +// CHECK-NEXT: ^ + +// CHECK: BrokenTypes.h:4:6: error: API notes replacement type 'int *' has a different size from original type 'char' + +// CHECK: BrokenTypes.h:6:13: error: API notes replacement type 'double' has a different size from original type 'char' + +// CHECK: 5 errors generated. diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m new file mode 100644 index 0000000000000..fccff8092cf8e --- /dev/null +++ b/clang/test/APINotes/types.m @@ -0,0 +1,23 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +#import <SomeKit/SomeKit.h> + +void test(OverriddenTypes *overridden) { + int *ip1 = global_int_ptr; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double (*)(int, int)'}} + + int *ip2 = global_int_fun( // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}} + ip2, // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'double *'}} + ip2); // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'float *'}} + + int *ip3 = [overridden // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}} + methodToMangle: ip3 // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'double *'}} + second: ip3]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'float *'}} + + int *ip4 = overridden.intPropertyToMangle; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double *'}} +} + +// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr' here}} +// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr2' here}} +// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr1' here}} +// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr2' here}} From 7159fb5b2e75e90efe3782f44a727b0c1d1294c1 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 29 Nov 2016 11:22:57 +0000 Subject: [PATCH 118/582] Add -Wapi-notes-message diagnostic group rdar://8104080 apple-llvm-split-commit: d9265c52ae59472204e0ba0ea176ea727dfd24fa apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticCommonKinds.td | 2 +- clang/test/Misc/warning-flags.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index b9f28a747942f..14715fee6820f 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -222,7 +222,7 @@ def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to b // API notes def err_apinotes_message : Error<"%0">; -def warn_apinotes_message : Warning<"%0">; +def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>; def note_apinotes_message : Note<"%0">; // OpenMP diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c index b756ec5ffd28d..c5294cff8fff0 100644 --- a/clang/test/Misc/warning-flags.c +++ b/clang/test/Misc/warning-flags.c @@ -18,7 +18,7 @@ This test serves two purposes: The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (83): +CHECK: Warnings without flags (82): CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list @@ -39,7 +39,6 @@ CHECK-NEXT: pp_out_of_date_dependency CHECK-NEXT: pp_poisoning_existing_macro CHECK-NEXT: w_asm_qualifier_ignored CHECK-NEXT: warn_accessor_property_type_mismatch -CHECK-NEXT: warn_apinotes_message CHECK-NEXT: warn_arcmt_nsalloc_realloc CHECK-NEXT: warn_asm_label_on_auto_decl CHECK-NEXT: warn_c_kext From 272f5793699a4e8bb0b78db759d5b07c9e33e587 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 29 Nov 2016 11:44:26 +0000 Subject: [PATCH 119/582] Revert "Add -Wapi-notes-message diagnostic group" This reverts commit d9265c52ae59472204e0ba0ea176ea727dfd24fa. The commit message used the wrong name for the new warning group. apple-llvm-split-commit: 2d7d7b2516e13de7b9c4a650644f573677e12611 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticCommonKinds.td | 2 +- clang/test/Misc/warning-flags.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 14715fee6820f..b9f28a747942f 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -222,7 +222,7 @@ def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to b // API notes def err_apinotes_message : Error<"%0">; -def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>; +def warn_apinotes_message : Warning<"%0">; def note_apinotes_message : Note<"%0">; // OpenMP diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c index c5294cff8fff0..b756ec5ffd28d 100644 --- a/clang/test/Misc/warning-flags.c +++ b/clang/test/Misc/warning-flags.c @@ -18,7 +18,7 @@ This test serves two purposes: The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (82): +CHECK: Warnings without flags (83): CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list @@ -39,6 +39,7 @@ CHECK-NEXT: pp_out_of_date_dependency CHECK-NEXT: pp_poisoning_existing_macro CHECK-NEXT: w_asm_qualifier_ignored CHECK-NEXT: warn_accessor_property_type_mismatch +CHECK-NEXT: warn_apinotes_message CHECK-NEXT: warn_arcmt_nsalloc_realloc CHECK-NEXT: warn_asm_label_on_auto_decl CHECK-NEXT: warn_c_kext From ee724f490a93d2c1a8e242d7e3f8ec08fbc57f1d Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 29 Nov 2016 11:22:57 +0000 Subject: [PATCH 120/582] Add -Wapinotes diagnostic group Recommit as the first commit mentioned outdated warning group name in the commit message. rdar://8104080 apple-llvm-split-commit: 6d14fd1cbde56d443076616e1ebcf250d71cdab5 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticCommonKinds.td | 2 +- clang/test/Misc/warning-flags.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index b9f28a747942f..14715fee6820f 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -222,7 +222,7 @@ def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to b // API notes def err_apinotes_message : Error<"%0">; -def warn_apinotes_message : Warning<"%0">; +def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>; def note_apinotes_message : Note<"%0">; // OpenMP diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c index b756ec5ffd28d..c5294cff8fff0 100644 --- a/clang/test/Misc/warning-flags.c +++ b/clang/test/Misc/warning-flags.c @@ -18,7 +18,7 @@ This test serves two purposes: The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (83): +CHECK: Warnings without flags (82): CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list @@ -39,7 +39,6 @@ CHECK-NEXT: pp_out_of_date_dependency CHECK-NEXT: pp_poisoning_existing_macro CHECK-NEXT: w_asm_qualifier_ignored CHECK-NEXT: warn_accessor_property_type_mismatch -CHECK-NEXT: warn_apinotes_message CHECK-NEXT: warn_arcmt_nsalloc_realloc CHECK-NEXT: warn_asm_label_on_auto_decl CHECK-NEXT: warn_c_kext From b99d56f576b1aa1333c68a34ccf3abfffc27c3c8 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 30 Nov 2016 17:22:03 -0800 Subject: [PATCH 121/582] [APINotes] Add missing include. (#49) rdar://problem/29449851 apple-llvm-split-commit: 0d6d326d289b46826d451cc38de8ad774265ad84 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index ebbd73e4e4aa0..832f454c6c897 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -25,6 +25,7 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include <sys/stat.h> From e9c940d506c33e9ad31dbab0c3581693131c6ab4 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Tue, 6 Dec 2016 17:28:36 -0800 Subject: [PATCH 122/582] Fix merge problem from clang r288295. John added the IsForDefinition argument to CGObjCNonFragileABIMac::GetClassGlobal on trunk. This merged badly with the Swift version of the code that previously had a boolean argument named "ForDefinition". In the new version "ForDefinition" is an enum value so this code did not fail to compile and only caused a test failure. rdar://problem/29538073 apple-llvm-split-commit: 6c8b2c7e393fd136eacabbfa6b190953a73aa90e apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCMac.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 53ff137af5504..01793e2ac9d26 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -7148,7 +7148,7 @@ CGObjCNonFragileABIMac::GetClassGlobal(StringRef Name, assert(GV->getLinkage() == L); - if (ForDefinition || + if (IsForDefinition || GV->getValueType() == ObjCTypes.ClassnfABITy) return GV; From 09fd205a6c66c931418055bfb828c3af5faae705 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 8 Dec 2016 20:25:46 -0800 Subject: [PATCH 123/582] Re-apply "ObjectiveC Generics: Start using ObjCTypeParamType." (#51) This re-applies Manman's c7c63c5b9, reverted in 1859c189. rdar://problem/28824900 (orig. rdar://problem/24619481) apple-llvm-split-commit: 765e62603911c714ba3a0437d1afb4be22ed5158 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTContext.cpp | 5 +++ clang/lib/AST/DeclObjC.cpp | 8 +++-- clang/lib/AST/Type.cpp | 19 ++++++++--- clang/lib/Sema/SemaType.cpp | 21 ++++++++++++ .../Checkers/DynamicTypePropagation.cpp | 2 +- clang/test/SemaObjC/kindof.m | 10 +++++- .../SemaObjC/parameterized_classes_subst.m | 33 +++++++++++++++++++ 7 files changed, 90 insertions(+), 8 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index ac99a00bc7610..22be71a7272d6 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4027,6 +4027,11 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, bool allowOnPointerType) const { hasError = false; + if (const ObjCTypeParamType *objT = + dyn_cast<ObjCTypeParamType>(type.getTypePtr())) { + return getObjCTypeParamType(objT->getDecl(), protocols); + } + // Apply protocol qualifiers to ObjCObjectPointerType. if (allowOnPointerType) { if (const ObjCObjectPointerType *objPtr = diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 5d894c5ebc60c..60d05f682e6e0 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1329,8 +1329,12 @@ ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc, IdentifierInfo *name, SourceLocation colonLoc, TypeSourceInfo *boundInfo) { - return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, - nameLoc, name, colonLoc, boundInfo); + auto *TPDecl = + new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, + nameLoc, name, colonLoc, boundInfo); + QualType TPType = ctx.getObjCTypeParamType(TPDecl, {}); + TPDecl->setTypeForDecl(TPType.getTypePtr()); + return TPDecl; } ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index f3724aa85ed87..0d0cd2e305be2 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1081,13 +1081,24 @@ QualType QualType::substObjCTypeArgs( // Replace an Objective-C type parameter reference with the corresponding // type argument. - if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) { - if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) { + if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) { + if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(OTPTy->getDecl())) { // If we have type arguments, use them. if (!typeArgs.empty()) { - // FIXME: Introduce SubstObjCTypeParamType ? QualType argType = typeArgs[typeParam->getIndex()]; - return ctx.getQualifiedType(argType, splitType.Quals); + if (OTPTy->qual_empty()) + return ctx.getQualifiedType(argType, splitType.Quals); + + // Apply protocol lists if exists. + bool hasError; + SmallVector<ObjCProtocolDecl*, 8> protocolsVec; + protocolsVec.append(OTPTy->qual_begin(), + OTPTy->qual_end()); + ArrayRef<ObjCProtocolDecl *> protocolsToApply = protocolsVec; + QualType resultTy = ctx.applyObjCProtocolQualifiers(argType, + protocolsToApply, hasError, true/*allowOnPointerType*/); + + return ctx.getQualifiedType(resultTy, splitType.Quals); } switch (context) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index ed9cbb6733f5f..01b8b3a70d401 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1158,6 +1158,20 @@ TypeResult Sema::actOnObjCTypeArgsAndProtocolQualifiers( ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } + if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) { + // Protocol qualifier information. + if (OTPTL.getNumProtocols() > 0) { + assert(OTPTL.getNumProtocols() == Protocols.size()); + OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc); + OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc); + for (unsigned i = 0, n = Protocols.size(); i != n; ++i) + OTPTL.setProtocolLoc(i, ProtocolLocs[i]); + } + + // We're done. Return the completed type to the parser. + return CreateParsedType(Result, ResultTInfo); + } + auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>(); // Type argument information. @@ -6108,6 +6122,13 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, } bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) { + if (isa<ObjCTypeParamType>(type)) { + // Build the attributed type to record where __kindof occurred. + type = Context.getAttributedType(AttributedType::attr_objc_kindof, + type, type); + return false; + } + // Find out if it's an Objective-C object or object pointer type; const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>(); const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 301a5ce7d5829..a418c82f5a017 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -626,7 +626,7 @@ static bool isObjCTypeParamDependent(QualType Type) { : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { public: IsObjCTypeParamDependentTypeVisitor() : Result(false) {} - bool VisitTypedefType(const TypedefType *Type) { + bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { if (isa<ObjCTypeParamDecl>(Type->getDecl())) { Result = true; return false; diff --git a/clang/test/SemaObjC/kindof.m b/clang/test/SemaObjC/kindof.m index 63ba18fe89bc0..9d758d3cfb2a6 100644 --- a/clang/test/SemaObjC/kindof.m +++ b/clang/test/SemaObjC/kindof.m @@ -385,7 +385,7 @@ - (void)test:(id)T { @end @interface NSGeneric<ObjectType> : NSObject -- (void)test:(__kindof ObjectType)T; +- (void)test:(__kindof ObjectType)T; // expected-note{{passing argument to parameter 'T' here}} - (void)mapUsingBlock:(id (^)(__kindof ObjectType))block; @end @implementation NSGeneric @@ -395,6 +395,14 @@ - (void)mapUsingBlock:(id (^)(id))block { } @end +void testGeneric(NSGeneric<NSString*> *generic) { + NSObject *NSObject_obj; + // Assign from NSObject_obj to __kindof NSString*. + [generic test:NSObject_obj]; // expected-warning{{incompatible pointer types sending 'NSObject *' to parameter of type '__kindof NSString *'}} + NSString *NSString_str; + [generic test:NSString_str]; +} + // Check that clang doesn't crash when a type parameter is illegal. @interface Array1<T> : NSObject @end diff --git a/clang/test/SemaObjC/parameterized_classes_subst.m b/clang/test/SemaObjC/parameterized_classes_subst.m index f90ee9093592c..da2d56f11bc80 100644 --- a/clang/test/SemaObjC/parameterized_classes_subst.m +++ b/clang/test/SemaObjC/parameterized_classes_subst.m @@ -426,3 +426,36 @@ + (void)useSuperMethod { // warning about likely protocol/class name typos. // -------------------------------------------------------------------------- typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}} + +// rdar://25060179 +@interface MyMutableDictionary<KeyType, ObjectType> : NSObject +- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \ + // expected-note{{passing argument to parameter 'key' here}} +@end + +void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString, + NSNumber *n1, NSNumber *n2) { + // We warn here when the key types do not match. + stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \ + // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}} +} + +@interface MyTest<K, V> : NSObject <NSCopying> +- (V)test:(K)key; +- (V)test2:(K)key; // expected-note{{previous definition is here}} +- (void)mapUsingBlock:(id (^)(V))block; +- (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}} +@end + +@implementation MyTest +- (id)test:(id)key { + return key; +} +- (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}} + return 0; +} +- (void)mapUsingBlock:(id (^)(id))block { +} +- (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}} +} +@end From 83be4cf8ce2b12dddffd84b40403d8c043e6da50 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Tue, 13 Dec 2016 15:01:02 -0800 Subject: [PATCH 124/582] Fix MSVC errors building SemaAPINotes with MSVC (#54) apple-llvm-split-commit: 99c41b42e6d0bfcf53c19f029842e78ea296fced apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index cf88f735018a7..6173661d4ec19 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -131,8 +131,7 @@ namespace { Sema &S, Decl *D, bool shouldAddAttribute, VersionedInfoRole role, llvm::function_ref<A *()> createAttr, - llvm::function_ref<specific_attr_iterator<A>(Decl *)> getExistingAttr = - [](Decl *decl) { return decl->specific_attr_begin<A>(); }) { + llvm::function_ref<specific_attr_iterator<A>(Decl *)> getExistingAttr) { switch (role) { case VersionedInfoRole::AugmentSource: // If we're not adding an attribute, there's nothing to do. @@ -168,6 +167,17 @@ namespace { break; } } + + template<typename A> + void handleAPINotedAttribute( + Sema &S, Decl *D, bool shouldAddAttribute, + VersionedInfoRole role, + llvm::function_ref<A *()> createAttr) { + handleAPINotedAttribute<A>(S, D, shouldAddAttribute, role, createAttr, + [](Decl *decl) { + return decl->specific_attr_begin<A>(); + }); + } } static void ProcessAPINotes(Sema &S, Decl *D, From 841c57d6274e7d09aed4a2bf6dfaf4b2ad1b2557 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Tue, 13 Dec 2016 17:29:36 -0800 Subject: [PATCH 125/582] [profile] Add support for the exit-on-signal '%Nx' specifier The exit-on-signal specifier tells the runtime to handle a signal by writing out a profile and then exiting the program. It can be specified in the LLVM_PROFILE_FILE environment variable or by calling __llvm_profile_set_filename. Here is how you might force a program to exit when it's sent SIGTERM (sig 15): LLVM_PROFILE_FILE="default.profraw%15x" <program> When an instance of <program> is sent SIGTERM, it will write out a profile to "default.profraw" (the specifier is stripped out of the filename) and exit with return code 0. It's possible to use multiple exit-on-signal specifiers (up to 16). rdar://problem/24098975 apple-llvm-split-commit: 2cf73dc0fd684e3127e0ec9f3f8c432ae8ce7022 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/profile/InstrProfilingFile.c | 70 +++++++++++++++++-- .../test/profile/instrprof-exit-on-signal.c | 16 +++++ 2 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 compiler-rt/test/profile/instrprof-exit-on-signal.c diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index f82080c98aac4..65393c74690ec 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -14,6 +14,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <signal.h> #ifdef _MSC_VER /* For _alloca. */ #include <malloc.h> @@ -59,6 +60,7 @@ static const char *getPNSStr(ProfileNameSpecifier PNS) { } #define MAX_PID_SIZE 16 +#define MAX_SIGNAL_HANDLERS 16 /* Data structure holding the result of parsed filename pattern. */ typedef struct lprofFilename { /* File name string possibly with %p or %h specifiers. */ @@ -79,11 +81,13 @@ typedef struct lprofFilename { * 2 profile data files. %1m is equivalent to %m. Also %m specifier * can only appear once at the end of the name pattern. */ unsigned MergePoolSize; + char ExitOnSignals[MAX_SIGNAL_HANDLERS]; + unsigned NumExitSignals; ProfileNameSpecifier PNS; } lprofFilename; -COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0}, - 0, 0, 0, PNS_unknown}; +COMPILER_RT_WEAK lprofFilename lprofCurFilename = { + 0, 0, 0, {0}, {0}, 0, 0, 0, {0}, 0, PNS_unknown}; int getpid(void); static int getCurFilenameLength(); @@ -252,6 +256,26 @@ static void truncateCurrentFile(void) { fclose(File); } +static void exitSignalHandler(int sig) { + (void)sig; + exit(0); +} + +static void installExitSignalHandlers(void) { + unsigned I; + struct sigaction sigact; + int err; + for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = exitSignalHandler; + err = sigaction(lprofCurFilename.ExitOnSignals[I], &sigact, NULL); + if (err) + PROF_WARN( + "Unable to install an exit signal handler for %d (errno = %d).\n", + lprofCurFilename.ExitOnSignals[I], err); + } +} + static const char *DefaultProfileName = "default.profraw"; static void resetFilenameToDefault(void) { if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { @@ -262,14 +286,23 @@ static void resetFilenameToDefault(void) { lprofCurFilename.PNS = PNS_default; } +static int isNonZeroDigit(char C) { return C >= '1' && C <= '9'; } + static int containsMergeSpecifier(const char *FilenamePat, int I) { return (FilenamePat[I] == 'm' || - (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' && + (isNonZeroDigit(FilenamePat[I]) && /* If FilenamePat[I] is not '\0', the next byte is guaranteed * to be in-bound as the string is null terminated. */ FilenamePat[I + 1] == 'm')); } +static int containsExitOnSignalSpecifier(const char *FilenamePat, int I) { + if (!isNonZeroDigit(FilenamePat[I])) + return 0; + return (FilenamePat[I + 1] == 'x') || + (isNonZeroDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x'); +} + /* Parses the pattern string \p FilenamePat and stores the result to * lprofcurFilename structure. */ static int parseFilenamePattern(const char *FilenamePat, @@ -278,6 +311,7 @@ static int parseFilenamePattern(const char *FilenamePat, char *PidChars = &lprofCurFilename.PidChars[0]; char *Hostname = &lprofCurFilename.Hostname[0]; int MergingEnabled = 0; + char SignalNo; /* Clean up cached prefix. */ if (lprofCurFilename.ProfilePathPrefix) @@ -327,6 +361,22 @@ static int parseFilenamePattern(const char *FilenamePat, lprofCurFilename.MergePoolSize = FilenamePat[I] - '0'; I++; /* advance to 'm' */ } + } else if (containsExitOnSignalSpecifier(FilenamePat, I)) { + if (lprofCurFilename.NumExitSignals == MAX_SIGNAL_HANDLERS) { + PROF_WARN("%%x specifier has been specified too many times in %s.\n", + FilenamePat); + return -1; + } + /* Grab the signal number. */ + SignalNo = FilenamePat[I] - '0'; + I++; /* advance to either another digit, or 'x' */ + if (FilenamePat[I] != 'x') { + SignalNo = (SignalNo * 10) + (FilenamePat[I] - '0'); + I++; /* advance to 'x' */ + } + lprofCurFilename.ExitOnSignals[lprofCurFilename.NumExitSignals] = + SignalNo; + ++lprofCurFilename.NumExitSignals; } } @@ -370,6 +420,7 @@ static void parseAndSetFilename(const char *FilenamePat, } truncateCurrentFile(); + installExitSignalHandlers(); } /* Return buffer length that is required to store the current profile @@ -378,11 +429,12 @@ static void parseAndSetFilename(const char *FilenamePat, #define SIGLEN 24 static int getCurFilenameLength() { int Len; + unsigned I; if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize)) + lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) return strlen(lprofCurFilename.FilenamePat); Len = strlen(lprofCurFilename.FilenamePat) + @@ -390,6 +442,11 @@ static int getCurFilenameLength() { lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); if (lprofCurFilename.MergePoolSize) Len += SIGLEN; + for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { + Len -= 3; /* Drop the '%', signal number, and the 'x'. */ + if (lprofCurFilename.ExitOnSignals[I] >= 10) + --Len; /* Drop the second digit of the signal number. */ + } return Len; } @@ -405,7 +462,7 @@ static const char *getCurFilename(char *FilenameBuf) { return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize)) + lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) return lprofCurFilename.FilenamePat; PidLength = strlen(lprofCurFilename.PidChars); @@ -431,6 +488,9 @@ static const char *getCurFilename(char *FilenameBuf) { J += S; if (FilenamePat[I] != 'm') I++; + } else if (containsExitOnSignalSpecifier(FilenamePat, I)) { + while (FilenamePat[I] != 'x') + ++I; } /* Drop any unknown substitutions. */ } else diff --git a/compiler-rt/test/profile/instrprof-exit-on-signal.c b/compiler-rt/test/profile/instrprof-exit-on-signal.c new file mode 100644 index 0000000000000..cf23b68003b80 --- /dev/null +++ b/compiler-rt/test/profile/instrprof-exit-on-signal.c @@ -0,0 +1,16 @@ +// RUN: %clang_profgen -o %t %s +// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t +// RUN: llvm-profdata show --all-functions %t.profraw | FileCheck %s +// CHECK: Total functions: 1 + +#include <signal.h> +#include <unistd.h> + +int main() { + kill(getpid(), SIGTERM); + + while (1) { + /* loop forever */ + } + return 1; +} From f8862167e93f65f3ea47e244892efa8a581c2925 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Tue, 13 Dec 2016 17:44:32 -0800 Subject: [PATCH 126/582] [profile] Fix bug in exit-on-signal specifier parsing The parsing logic for the '%Nx' specifier had a bug which made it impossible to specify handling of two-digit signals where the second digit is 0. Fix it and add a test. rdar://problem/24098975 apple-llvm-split-commit: 29107a0e2baacf42e4f4baa0a83d557a3fc7aa88 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/profile/InstrProfilingFile.c | 4 +++- .../test/profile/instrprof-exit-on-signal.c | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index 65393c74690ec..9a11a10c36d94 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -286,6 +286,8 @@ static void resetFilenameToDefault(void) { lprofCurFilename.PNS = PNS_default; } +static int isDigit(char C) { return C >= '0' && C <= '9'; } + static int isNonZeroDigit(char C) { return C >= '1' && C <= '9'; } static int containsMergeSpecifier(const char *FilenamePat, int I) { @@ -300,7 +302,7 @@ static int containsExitOnSignalSpecifier(const char *FilenamePat, int I) { if (!isNonZeroDigit(FilenamePat[I])) return 0; return (FilenamePat[I + 1] == 'x') || - (isNonZeroDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x'); + (isDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x'); } /* Parses the pattern string \p FilenamePat and stores the result to diff --git a/compiler-rt/test/profile/instrprof-exit-on-signal.c b/compiler-rt/test/profile/instrprof-exit-on-signal.c index cf23b68003b80..efe1013f4ba28 100644 --- a/compiler-rt/test/profile/instrprof-exit-on-signal.c +++ b/compiler-rt/test/profile/instrprof-exit-on-signal.c @@ -1,13 +1,23 @@ // RUN: %clang_profgen -o %t %s -// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t -// RUN: llvm-profdata show --all-functions %t.profraw | FileCheck %s -// CHECK: Total functions: 1 +// +// Verify SIGTERM handling. +// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t 15 +// RUN: llvm-profdata show %t.profraw | FileCheck %s +// +// Verify SIGUSR1 handling. +// RUN: %run LLVM_PROFILE_FILE="%30x%t.profraw" %t 30 +// RUN: llvm-profdata show %t.profraw | FileCheck %s +#include <stdlib.h> #include <signal.h> #include <unistd.h> -int main() { - kill(getpid(), SIGTERM); +// CHECK: Total functions: 1 +int main(int argc, char **argv) { + (void)argc; + + int sig = atoi(argv[1]); + kill(getpid(), sig); while (1) { /* loop forever */ From 57046a8edb49919a05a53c19a33a489ac60e7ce7 Mon Sep 17 00:00:00 2001 From: Hugh Bellamy <hughbellars@gmail.com> Date: Thu, 15 Dec 2016 21:11:25 +0000 Subject: [PATCH 127/582] Fix warnings building Swift with swift-llvm with MSVC (#39) MSVC warns: '>=' unsafe use of type 'bool' in operation. apple-llvm-split-commit: facb9960a193c19cd155ca9ec63151cc5bae0453 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Bitcode/RecordLayout.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/llvm/include/llvm/Bitcode/RecordLayout.h b/llvm/include/llvm/Bitcode/RecordLayout.h index ce6c7f8bd869d..2b694753d3513 100644 --- a/llvm/include/llvm/Bitcode/RecordLayout.h +++ b/llvm/include/llvm/Bitcode/RecordLayout.h @@ -97,6 +97,11 @@ class BCFixed : public impl::BCField<> { abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, Width)); } + static void assertValid(const bool &data) { + assert(llvm::isUInt<Width>(data) && + "data value does not fit in the given bit width"); + } + template<typename T> static void assertValid(const T &data) { assert(data >= 0 && "cannot encode signed integers"); From a270e653f7debed0940b880811d002ddc0ed586c Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Tue, 20 Dec 2016 17:51:02 -0800 Subject: [PATCH 128/582] Re-revert "ObjectiveC Generics: Start using ObjCTypeParamType." (#56) Reverts 765e626, originally c7c63c5. The logic isn't quite correct around __kindof types. See rdar://problem/24619481 and rdar://problem/28824900. apple-llvm-split-commit: af52d9b3ae6296e34992cb563d18264e1a102cc7 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTContext.cpp | 5 --- clang/lib/AST/DeclObjC.cpp | 8 ++--- clang/lib/AST/Type.cpp | 19 +++-------- clang/lib/Sema/SemaType.cpp | 21 ------------ .../Checkers/DynamicTypePropagation.cpp | 2 +- clang/test/SemaObjC/kindof.m | 10 +----- .../SemaObjC/parameterized_classes_subst.m | 33 ------------------- 7 files changed, 8 insertions(+), 90 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 4abca4ce5d1a8..79a0ed9302ffe 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4038,11 +4038,6 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, bool allowOnPointerType) const { hasError = false; - if (const ObjCTypeParamType *objT = - dyn_cast<ObjCTypeParamType>(type.getTypePtr())) { - return getObjCTypeParamType(objT->getDecl(), protocols); - } - // Apply protocol qualifiers to ObjCObjectPointerType. if (allowOnPointerType) { if (const ObjCObjectPointerType *objPtr = diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 60d05f682e6e0..5d894c5ebc60c 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1329,12 +1329,8 @@ ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc, IdentifierInfo *name, SourceLocation colonLoc, TypeSourceInfo *boundInfo) { - auto *TPDecl = - new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, - nameLoc, name, colonLoc, boundInfo); - QualType TPType = ctx.getObjCTypeParamType(TPDecl, {}); - TPDecl->setTypeForDecl(TPType.getTypePtr()); - return TPDecl; + return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, + nameLoc, name, colonLoc, boundInfo); } ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 0d0cd2e305be2..f3724aa85ed87 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1081,24 +1081,13 @@ QualType QualType::substObjCTypeArgs( // Replace an Objective-C type parameter reference with the corresponding // type argument. - if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) { - if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(OTPTy->getDecl())) { + if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) { + if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) { // If we have type arguments, use them. if (!typeArgs.empty()) { + // FIXME: Introduce SubstObjCTypeParamType ? QualType argType = typeArgs[typeParam->getIndex()]; - if (OTPTy->qual_empty()) - return ctx.getQualifiedType(argType, splitType.Quals); - - // Apply protocol lists if exists. - bool hasError; - SmallVector<ObjCProtocolDecl*, 8> protocolsVec; - protocolsVec.append(OTPTy->qual_begin(), - OTPTy->qual_end()); - ArrayRef<ObjCProtocolDecl *> protocolsToApply = protocolsVec; - QualType resultTy = ctx.applyObjCProtocolQualifiers(argType, - protocolsToApply, hasError, true/*allowOnPointerType*/); - - return ctx.getQualifiedType(resultTy, splitType.Quals); + return ctx.getQualifiedType(argType, splitType.Quals); } switch (context) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index aedbe39afbffb..9ee6c7c4cff32 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1159,20 +1159,6 @@ TypeResult Sema::actOnObjCTypeArgsAndProtocolQualifiers( ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } - if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) { - // Protocol qualifier information. - if (OTPTL.getNumProtocols() > 0) { - assert(OTPTL.getNumProtocols() == Protocols.size()); - OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc); - OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc); - for (unsigned i = 0, n = Protocols.size(); i != n; ++i) - OTPTL.setProtocolLoc(i, ProtocolLocs[i]); - } - - // We're done. Return the completed type to the parser. - return CreateParsedType(Result, ResultTInfo); - } - auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>(); // Type argument information. @@ -6119,13 +6105,6 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, } bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) { - if (isa<ObjCTypeParamType>(type)) { - // Build the attributed type to record where __kindof occurred. - type = Context.getAttributedType(AttributedType::attr_objc_kindof, - type, type); - return false; - } - // Find out if it's an Objective-C object or object pointer type; const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>(); const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index a418c82f5a017..301a5ce7d5829 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -626,7 +626,7 @@ static bool isObjCTypeParamDependent(QualType Type) { : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { public: IsObjCTypeParamDependentTypeVisitor() : Result(false) {} - bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { + bool VisitTypedefType(const TypedefType *Type) { if (isa<ObjCTypeParamDecl>(Type->getDecl())) { Result = true; return false; diff --git a/clang/test/SemaObjC/kindof.m b/clang/test/SemaObjC/kindof.m index 9d758d3cfb2a6..63ba18fe89bc0 100644 --- a/clang/test/SemaObjC/kindof.m +++ b/clang/test/SemaObjC/kindof.m @@ -385,7 +385,7 @@ - (void)test:(id)T { @end @interface NSGeneric<ObjectType> : NSObject -- (void)test:(__kindof ObjectType)T; // expected-note{{passing argument to parameter 'T' here}} +- (void)test:(__kindof ObjectType)T; - (void)mapUsingBlock:(id (^)(__kindof ObjectType))block; @end @implementation NSGeneric @@ -395,14 +395,6 @@ - (void)mapUsingBlock:(id (^)(id))block { } @end -void testGeneric(NSGeneric<NSString*> *generic) { - NSObject *NSObject_obj; - // Assign from NSObject_obj to __kindof NSString*. - [generic test:NSObject_obj]; // expected-warning{{incompatible pointer types sending 'NSObject *' to parameter of type '__kindof NSString *'}} - NSString *NSString_str; - [generic test:NSString_str]; -} - // Check that clang doesn't crash when a type parameter is illegal. @interface Array1<T> : NSObject @end diff --git a/clang/test/SemaObjC/parameterized_classes_subst.m b/clang/test/SemaObjC/parameterized_classes_subst.m index da2d56f11bc80..f90ee9093592c 100644 --- a/clang/test/SemaObjC/parameterized_classes_subst.m +++ b/clang/test/SemaObjC/parameterized_classes_subst.m @@ -426,36 +426,3 @@ + (void)useSuperMethod { // warning about likely protocol/class name typos. // -------------------------------------------------------------------------- typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}} - -// rdar://25060179 -@interface MyMutableDictionary<KeyType, ObjectType> : NSObject -- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \ - // expected-note{{passing argument to parameter 'key' here}} -@end - -void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString, - NSNumber *n1, NSNumber *n2) { - // We warn here when the key types do not match. - stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \ - // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}} -} - -@interface MyTest<K, V> : NSObject <NSCopying> -- (V)test:(K)key; -- (V)test2:(K)key; // expected-note{{previous definition is here}} -- (void)mapUsingBlock:(id (^)(V))block; -- (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}} -@end - -@implementation MyTest -- (id)test:(id)key { - return key; -} -- (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}} - return 0; -} -- (void)mapUsingBlock:(id (^)(id))block { -} -- (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}} -} -@end From f2a711ea5503a17b143a04affb55159d970ab125 Mon Sep 17 00:00:00 2001 From: Frederic Riss <friss@apple.com> Date: Fri, 23 Dec 2016 10:19:22 -0800 Subject: [PATCH 129/582] Fix previous merge. apple-llvm-split-commit: fd687d16d0bdc786f8d5495f7254a896dfe0a21b apple-llvm-split-dir: llvm/ --- llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 50 +++++++--------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp index 75fc14c755769..501efec7e9e89 100644 --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -573,40 +573,6 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata( return error("Invalid record"); NMD->addOperand(MD); } - case bitc::METADATA_GENERIC_DEBUG: { - if (Record.size() < 4) - return error("Invalid record"); - - IsDistinct = Record[0]; - unsigned Tag = Record[1]; - unsigned Version = Record[2]; - - if (Tag >= 1u << 16 || Version != 0) - return error("Invalid record"); - - // Deprecated internal hack to support serializing MDModule. - // This node has since been deleted. - // Upgrading this node is not officially supported. This code - // may be removed in the future. - if (Tag == dwarf::DW_TAG_module) { - if (Record.size() != 6) - return error("Invalid record"); - - MetadataList.assignValue( - GET_OR_DISTINCT(DIModule, (Context, getMDOrNull(Record[4]), - getMDString(Record[5]), nullptr, nullptr, - nullptr)), - NextMetadataNo++); - break; - } - - auto *Header = getMDString(Record[3]); - SmallVector<Metadata *, 8> DwarfOps; - for (unsigned I = 4, E = Record.size(); I != E; ++I) - DwarfOps.push_back(getMDOrNull(Record[I])); - MetadataList.assignValue( - GET_OR_DISTINCT(GenericDINode, (Context, Tag, Header, DwarfOps)), - NextMetadataNo++); break; } case bitc::METADATA_OLD_FN_NODE: { @@ -713,6 +679,22 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata( if (Tag >= 1u << 16 || Version != 0) return error("Invalid record"); + // Deprecated internal hack to support serializing MDModule. + // This node has since been deleted. + // Upgrading this node is not officially supported. This code + // may be removed in the future. + if (Tag == dwarf::DW_TAG_module) { + if (Record.size() != 6) + return error("Invalid record"); + + MetadataList.assignValue( + GET_OR_DISTINCT(DIModule, (Context, getMDOrNull(Record[4]), + getMDString(Record[5]), nullptr, nullptr, + nullptr)), + NextMetadataNo++); + break; + } + auto *Header = getMDString(Record[3]); SmallVector<Metadata *, 8> DwarfOps; for (unsigned I = 4, E = Record.size(); I != E; ++I) From a2306672d2694cbc1ba35e53add37b285133916b Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 5 Jan 2017 14:04:00 +0000 Subject: [PATCH 130/582] Fix for LLVM Bitcode API change (r291016) This change is swift-llvm specific since RecordLayout.h isn't upstreamed. apple-llvm-split-commit: 6a7a3c612f8601f0e18d5bf1e0689ffd74cdfbf1 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Bitcode/RecordLayout.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/Bitcode/RecordLayout.h b/llvm/include/llvm/Bitcode/RecordLayout.h index 2b694753d3513..c415eeb9d750a 100644 --- a/llvm/include/llvm/Bitcode/RecordLayout.h +++ b/llvm/include/llvm/Bitcode/RecordLayout.h @@ -427,9 +427,9 @@ class BCGenericRecordLayout { /// /// \returns The abbreviation code for the newly-registered record type. static unsigned emitAbbrev(llvm::BitstreamWriter &out) { - auto *abbrev = new llvm::BitCodeAbbrev(); - impl::emitOps<IDField, Fields...>(*abbrev); - return out.EmitAbbrev(abbrev); + auto Abbrev = std::make_shared<llvm::BitCodeAbbrev>(); + impl::emitOps<IDField, Fields...>(*Abbrev); + return out.EmitAbbrev(std::move(Abbrev)); } /// Emit a record identified by \p abbrCode to bitstream reader \p out, using From bca93adf92feaba3538aa4d985f307cc4cd34253 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Mon, 9 Jan 2017 10:04:03 -0800 Subject: [PATCH 131/582] [index] Add Swift symbol language and Swift-specific symbol subkinds. apple-llvm-split-commit: a7af76c80be0a412ae3b136c065d04bd50a3efba apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexSymbol.h | 23 ++++++++++++++++++++ clang/lib/Index/IndexSymbol.cpp | 17 +++++++++++++++ clang/tools/libclang/CXIndexDataConsumer.cpp | 1 + 3 files changed, 41 insertions(+) diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index 559b212b92668..92121759feaf7 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -57,6 +57,7 @@ enum class SymbolLanguage { C, ObjC, CXX, + Swift, }; /// Language specific sub-kinds. @@ -64,6 +65,28 @@ enum class SymbolSubKind { None, CXXCopyConstructor, CXXMoveConstructor, + + // Swift sub-kinds + + SwiftAccessorGetter, + SwiftAccessorSetter, + SwiftAccessorWillSet, + SwiftAccessorDidSet, + SwiftAccessorAddressor, + SwiftAccessorMutableAddressor, + + SwiftExtensionOfStruct, + SwiftExtensionOfClass, + SwiftExtensionOfEnum, + SwiftExtensionOfProtocol, + + SwiftPrefixOperator, + SwiftPostfixOperator, + SwiftInfixOperator, + + SwiftSubscript, + SwiftAssociatedType, + SwiftGenericTypeParam, }; /// Set of properties that provide additional info about a symbol. diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index be847e762091f..e60cfc4435aee 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -375,6 +375,22 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) { case SymbolSubKind::None: return "<none>"; case SymbolSubKind::CXXCopyConstructor: return "cxx-copy-ctor"; case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor"; + case SymbolSubKind::SwiftAccessorGetter: return "acc-get"; + case SymbolSubKind::SwiftAccessorSetter: return "acc-set"; + case SymbolSubKind::SwiftAccessorWillSet: return "acc-willset"; + case SymbolSubKind::SwiftAccessorDidSet: return "acc-didset"; + case SymbolSubKind::SwiftAccessorAddressor: return "acc-addr"; + case SymbolSubKind::SwiftAccessorMutableAddressor: return "acc-mutaddr"; + case SymbolSubKind::SwiftExtensionOfStruct: return "ext-struct"; + case SymbolSubKind::SwiftExtensionOfClass: return "ext-class"; + case SymbolSubKind::SwiftExtensionOfEnum: return "ext-enum"; + case SymbolSubKind::SwiftExtensionOfProtocol: return "ext-protocol"; + case SymbolSubKind::SwiftPrefixOperator: return "prefix-operator"; + case SymbolSubKind::SwiftPostfixOperator: return "postfix-operator"; + case SymbolSubKind::SwiftInfixOperator: return "infix-operator"; + case SymbolSubKind::SwiftSubscript: return "subscript"; + case SymbolSubKind::SwiftAssociatedType: return "associated-type"; + case SymbolSubKind::SwiftGenericTypeParam: return "generic-type-param"; } llvm_unreachable("invalid symbol subkind"); } @@ -384,6 +400,7 @@ StringRef index::getSymbolLanguageString(SymbolLanguage K) { case SymbolLanguage::C: return "C"; case SymbolLanguage::ObjC: return "ObjC"; case SymbolLanguage::CXX: return "C++"; + case SymbolLanguage::Swift: return "Swift"; } llvm_unreachable("invalid symbol language kind"); } diff --git a/clang/tools/libclang/CXIndexDataConsumer.cpp b/clang/tools/libclang/CXIndexDataConsumer.cpp index 1981cabbbe4c1..77a697bdb7c44 100644 --- a/clang/tools/libclang/CXIndexDataConsumer.cpp +++ b/clang/tools/libclang/CXIndexDataConsumer.cpp @@ -1313,6 +1313,7 @@ static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { case SymbolLanguage::C: return CXIdxEntityLang_C; case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC; case SymbolLanguage::CXX: return CXIdxEntityLang_CXX; + case SymbolLanguage::Swift: llvm_unreachable("unexpected Swift symbol language");; } llvm_unreachable("invalid symbol language"); } From 4653f82a995911fdeb15bc440917f4db2a447c04 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 11 Jan 2017 15:02:46 -0800 Subject: [PATCH 132/582] [APINotes] Preserve attributes from inactive versions. (#53) ...by wrapping them in another (new) attribute, SwiftVersionedAttr, or by noting their deletion in a SwiftVersionedRemovalAttr. This doesn't support other kinds of changes that can be made via API notes, such as nullability or wholesale type changes, but it's a place to start, and possibly sufficient for our goals. Part of rdar://problem/28618121. apple-llvm-split-commit: f8c16c2a4e80fedd9df549da57af8b4f8f09aef0 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/Attr.h | 1 + clang/include/clang/Basic/Attr.td | 26 +++ clang/lib/Sema/SemaAPINotes.cpp | 233 ++++++++++++++-------- clang/test/APINotes/properties.m | 22 +- clang/test/APINotes/versioned.m | 13 +- clang/utils/TableGen/ClangAttrEmitter.cpp | 28 +++ 6 files changed, 231 insertions(+), 92 deletions(-) diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index bbe320c28a3b5..58566db60b292 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -24,6 +24,7 @@ #include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/VersionTuple.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 2416ed6ac043e..a4a88d0fc5957 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -195,6 +195,9 @@ class VariadicEnumArgument<string name, string type, list<string> values, list<string> Enums = enums; } +// Represents an attribute wrapped by another attribute. +class AttrArgument<string name, bit opt = 0> : Argument<name, opt>; + // This handles one spelling of an attribute. class Spelling<string name, string variety> { string Name = name; @@ -1500,6 +1503,29 @@ def SwiftImportPropertyAsAccessors : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftVersioned : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">]; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + +def SwiftVersionedRemoval : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">]; + let SemaHandler = 0; + let Documentation = [Undocumented]; + let AdditionalMembers = [{ + attr::Kind getAttrKindToRemove() const { + return static_cast<attr::Kind>(getRawKind()); + } + }]; +} + def ReqdWorkGroupSize : InheritableAttr { let Spellings = [GNU<"reqd_work_group_size">]; let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">, diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 6173661d4ec19..078445e48ad87 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -17,6 +17,21 @@ using namespace clang; using clang::api_notes::VersionedInfoRole; +namespace { + struct VersionedInfoMetadata { + VersionTuple Version; + VersionedInfoRole Role; + + /*implicit*/ VersionedInfoMetadata(VersionedInfoRole role) : Role(role) { + assert(role != VersionedInfoRole::Versioned && + "explicit version required"); + } + + /*implicit*/ VersionedInfoMetadata(VersionTuple version) + : Version(version), Role(VersionedInfoRole::Versioned) {} + }; +} // end anonymous namespace + /// Determine whether this is a multi-level pointer type. static bool isMultiLevelPointerType(QualType type) { QualType pointee = type->getPointeeType(); @@ -29,9 +44,9 @@ static bool isMultiLevelPointerType(QualType type) { // Apply nullability to the given declaration. static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { bool overrideExisting; - switch (role) { + switch (metadata.Role) { case VersionedInfoRole::AugmentSource: overrideExisting = false; break; @@ -119,20 +134,27 @@ static StringRef CopyString(ASTContext &ctx, StringRef string) { } namespace { + template <typename A> + struct AttrKindFor {}; + +#define ATTR(X) \ + template <> struct AttrKindFor<X##Attr> { \ + static const attr::Kind value = attr::X; \ + }; +#include "clang/Basic/AttrList.inc" + /// Handle an attribute introduced by API notes. /// /// \param shouldAddAttribute Whether we should add a new attribute /// (otherwise, we might remove an existing attribute). /// \param createAttr Create the new attribute to be added. - /// \param getExistingAttr Get an existing, matching attribute on the given - /// declaration. template<typename A> void handleAPINotedAttribute( Sema &S, Decl *D, bool shouldAddAttribute, - VersionedInfoRole role, + VersionedInfoMetadata metadata, llvm::function_ref<A *()> createAttr, - llvm::function_ref<specific_attr_iterator<A>(Decl *)> getExistingAttr) { - switch (role) { + llvm::function_ref<specific_attr_iterator<A>(Decl*)> getExistingAttr) { + switch (metadata.Role) { case VersionedInfoRole::AugmentSource: // If we're not adding an attribute, there's nothing to do. if (!shouldAddAttribute) return; @@ -149,8 +171,14 @@ namespace { auto end = D->specific_attr_end<A>(); auto existing = getExistingAttr(D); if (existing != end) { - // Remove the existing attribute. + // Remove the existing attribute, and treat it as a superseded + // non-versioned attribute. + auto *versioned = + SwiftVersionedAttr::CreateImplicit(S.Context, clang::VersionTuple(), + *existing); + D->getAttrs().erase(existing.getCurrent()); + D->addAttr(versioned); } // If we're supposed to add a new attribute, do so. @@ -163,7 +191,24 @@ namespace { } case VersionedInfoRole::Versioned: - // FIXME: Retain versioned attributes separately. + // FIXME: Include the actual version instead of making one up. + if (shouldAddAttribute) { + if (auto attr = createAttr()) { + auto *versioned = + SwiftVersionedAttr::CreateImplicit(S.Context, metadata.Version, + attr); + D->addAttr(versioned); + } + } else { + // FIXME: This isn't preserving enough information for things like + // availability, where we're trying to remove a /specific/ kind of + // attribute. + auto *versioned = + SwiftVersionedRemovalAttr::CreateImplicit(S.Context, + metadata.Version, + AttrKindFor<A>::value); + D->addAttr(versioned); + } break; } } @@ -171,9 +216,9 @@ namespace { template<typename A> void handleAPINotedAttribute( Sema &S, Decl *D, bool shouldAddAttribute, - VersionedInfoRole role, + VersionedInfoMetadata metadata, llvm::function_ref<A *()> createAttr) { - handleAPINotedAttribute<A>(S, D, shouldAddAttribute, role, createAttr, + handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, createAttr, [](Decl *decl) { return decl->specific_attr_begin<A>(); }); @@ -182,10 +227,10 @@ namespace { static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonEntityInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Availability if (info.Unavailable) { - handleAPINotedAttribute<UnavailableAttr>(S, D, true, role, + handleAPINotedAttribute<UnavailableAttr>(S, D, true, metadata, [&] { return UnavailableAttr::CreateImplicit(S.Context, CopyString(S.Context, @@ -194,7 +239,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, } if (info.UnavailableInSwift) { - handleAPINotedAttribute<AvailabilityAttr>(S, D, true, role, [&] { + handleAPINotedAttribute<AvailabilityAttr>(S, D, true, metadata, [&] { return AvailabilityAttr::CreateImplicit( S.Context, &S.Context.Idents.get("swift"), @@ -224,14 +269,15 @@ static void ProcessAPINotes(Sema &S, Decl *D, // swift_private if (auto swiftPrivate = info.isSwiftPrivate()) { - handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, role, [&] { + handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, metadata, + [&] { return SwiftPrivateAttr::CreateImplicit(S.Context); }); } // swift_name if (!info.SwiftName.empty()) { - handleAPINotedAttribute<SwiftNameAttr>(S, D, true, role, + handleAPINotedAttribute<SwiftNameAttr>(S, D, true, metadata, [&]() -> SwiftNameAttr * { auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); @@ -249,11 +295,11 @@ static void ProcessAPINotes(Sema &S, Decl *D, static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonTypeInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // swift_bridge if (auto swiftBridge = info.getSwiftBridge()) { - handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(), role, - [&] { + handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(), + metadata, [&] { return SwiftBridgeAttr::CreateImplicit(S.Context, CopyString(S.Context, *swiftBridge)); @@ -263,7 +309,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, // ns_error_domain if (auto nsErrorDomain = info.getNSErrorDomain()) { handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(), - role, [&] { + metadata, [&] { return NSErrorDomainAttr::CreateImplicit( S.Context, &S.Context.Idents.get(*nsErrorDomain)); @@ -271,7 +317,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, } ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), - role); + metadata); } /// Check that the replacement type provided by API notes is reasonable. @@ -293,9 +339,9 @@ static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc, /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Type override. - if (role != VersionedInfoRole::Versioned && + if (metadata.Role != VersionedInfoRole::Versioned && !info.getType().empty() && S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.getType(), "<API Notes>", @@ -332,50 +378,50 @@ static void ProcessAPINotes(Sema &S, Decl *D, // Nullability. if (auto Nullability = info.getNullability()) { - applyNullability(S, D, *Nullability, role); + applyNullability(S, D, *Nullability, metadata); } // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), - role); + metadata); } /// Process API notes for a parameter. static void ProcessAPINotes(Sema &S, ParmVarDecl *D, const api_notes::ParamInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // noescape if (auto noescape = info.isNoEscape()) { - handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, role, [&] { + handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, metadata, [&] { return NoEscapeAttr::CreateImplicit(S.Context); }); } // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), - role); + metadata); } /// Process API notes for a global variable. static void ProcessAPINotes(Sema &S, VarDecl *D, const api_notes::GlobalVariableInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), - role); + metadata); } /// Process API notes for an Objective-C property. static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, const api_notes::ObjCPropertyInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), - role); + metadata); if (auto asAccessors = info.getSwiftImportAsAccessors()) { handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D, *asAccessors, - role, [&] { + metadata, [&] { return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context); }); } @@ -388,7 +434,7 @@ namespace { /// Process API notes for a function or method. static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, const api_notes::FunctionInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Find the declaration itself. FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>(); Decl *D = FD; @@ -400,7 +446,7 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, // Nullability of return type. if (info.NullabilityAudited) { - applyNullability(S, D, info.getReturnTypeInfo(), role); + applyNullability(S, D, info.getReturnTypeInfo(), metadata); } // Parameters. @@ -421,12 +467,12 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, QualType paramTypeBefore = Param->getType(); if (I < info.Params.size()) { - ProcessAPINotes(S, Param, info.Params[I], role); + ProcessAPINotes(S, Param, info.Params[I], metadata); } // Nullability. if (info.NullabilityAudited) - applyNullability(S, Param, info.getParamTypeInfo(I), role); + applyNullability(S, Param, info.getParamTypeInfo(I), metadata); if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) anyTypeChanged = true; @@ -434,8 +480,8 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, // Result type override. QualType overriddenResultType; - if (role != VersionedInfoRole::Versioned && !info.ResultType.empty() && - S.ParseTypeFromStringCallback) { + if (metadata.Role != VersionedInfoRole::Versioned && + !info.ResultType.empty() && S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.ResultType, "<API Notes>", D->getLocation()); @@ -483,37 +529,38 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), - role); + metadata); } /// Process API notes for a global function. static void ProcessAPINotes(Sema &S, FunctionDecl *D, const api_notes::GlobalFunctionInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), - static_cast<const api_notes::FunctionInfo &>(info), role); + static_cast<const api_notes::FunctionInfo &>(info), metadata); } /// Process API notes for an enumerator. static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, const api_notes::EnumConstantInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), - role); + metadata); } /// Process API notes for an Objective-C method. static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, const api_notes::ObjCMethodInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Designated initializers. if (info.DesignatedInit) { - handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, role, [&] { + handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, metadata, + [&] { if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { IFace->setHasDesignatedInitializers(); } @@ -522,7 +569,7 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, } // FIXME: This doesn't work well with versioned API notes. - if (role == VersionedInfoRole::AugmentSource && + if (metadata.Role == VersionedInfoRole::AugmentSource && info.getFactoryAsInitKind() == api_notes::FactoryAsInitKind::AsClassMethod && !D->getAttr<SwiftNameAttr>()) { @@ -531,28 +578,28 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), - static_cast<const api_notes::FunctionInfo &>(info), role); + static_cast<const api_notes::FunctionInfo &>(info), metadata); } /// Process API notes for a tag. static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common type information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), - role); + metadata); } /// Process API notes for a typedef. static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, const api_notes::TypedefInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // swift_wrapper using SwiftWrapperKind = api_notes::SwiftWrapperKind; if (auto swiftWrapper = info.SwiftWrapper) { handleAPINotedAttribute<SwiftNewtypeAttr>(S, D, - *swiftWrapper != SwiftWrapperKind::None, role, + *swiftWrapper != SwiftWrapperKind::None, metadata, [&] { SwiftNewtypeAttr::NewtypeKind kind; switch (*swiftWrapper) { @@ -576,25 +623,47 @@ static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, // Handle common type information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), - role); + metadata); } /// Process API notes for an Objective-C class or protocol. static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, const api_notes::ObjCContextInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common type information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), - role); + metadata); } /// Process API notes for an Objective-C class. static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, const api_notes::ObjCContextInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle information common to Objective-C classes and protocols. - ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info, role); + ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info, + metadata); +} + +/// Processes all versions of versioned API notes. +/// +/// Just dispatches to the various ProcessAPINotes functions in this file. +template <typename SpecificDecl, typename SpecificInfo> +static void ProcessVersionedAPINotes( + Sema &S, SpecificDecl *D, + const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) { + unsigned Selected = Info.getSelected().getValueOr(Info.size()); + + VersionTuple Version; + SpecificInfo InfoSlice; + for (unsigned i = 0, e = Info.size(); i != e; ++i) { + std::tie(Version, InfoSlice) = Info[i]; + if (i != Selected) { + ProcessAPINotes(S, D, InfoSlice, Version); + } else { + ProcessAPINotes(S, D, InfoSlice, Info.getSelectedRole()); + } + } } /// Process API notes that are associated with this declaration, mapping them @@ -608,9 +677,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Global variables. if (auto VD = dyn_cast<VarDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupGlobalVariable(VD->getName())) { - ::ProcessAPINotes(*this, VD, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupGlobalVariable(VD->getName()); + ProcessVersionedAPINotes(*this, VD, Info); } return; @@ -620,9 +688,8 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto FD = dyn_cast<FunctionDecl>(D)) { if (FD->getDeclName().isIdentifier()) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupGlobalFunction(FD->getName())) { - ::ProcessAPINotes(*this, FD, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupGlobalFunction(FD->getName()); + ProcessVersionedAPINotes(*this, FD, Info); } } @@ -632,9 +699,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C classes. if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCClassInfo(Class->getName())) { - ::ProcessAPINotes(*this, Class, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCClassInfo(Class->getName()); + ProcessVersionedAPINotes(*this, Class, Info); } return; @@ -643,9 +709,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C protocols. if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName())) { - ::ProcessAPINotes(*this, Protocol, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName()); + ProcessVersionedAPINotes(*this, Protocol, Info); } return; @@ -654,9 +719,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Tags if (auto Tag = dyn_cast<TagDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupTag(Tag->getName())) { - ::ProcessAPINotes(*this, Tag, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupTag(Tag->getName()); + ProcessVersionedAPINotes(*this, Tag, Info); } return; @@ -665,9 +729,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Typedefs if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupTypedef(Typedef->getName())) { - ::ProcessAPINotes(*this, Typedef, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupTypedef(Typedef->getName()); + ProcessVersionedAPINotes(*this, Typedef, Info); } return; @@ -680,9 +743,8 @@ void Sema::ProcessAPINotes(Decl *D) { if (D->getDeclContext()->getRedeclContext()->isFileContext()) { if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { - ::ProcessAPINotes(*this, EnumConstant, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupEnumConstant(EnumConstant->getName()); + ProcessVersionedAPINotes(*this, EnumConstant, Info); } return; @@ -750,10 +812,9 @@ void Sema::ProcessAPINotes(Decl *D) { SelectorRef.NumPieces = Sel.getNumArgs(); SelectorRef.Identifiers = SelPieces; - if (auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, - Method->isInstanceMethod())){ - ::ProcessAPINotes(*this, Method, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, + Method->isInstanceMethod()); + ProcessVersionedAPINotes(*this, Method, Info); } } } @@ -765,11 +826,9 @@ void Sema::ProcessAPINotes(Decl *D) { bool isInstanceProperty = (Property->getPropertyAttributesAsWritten() & ObjCPropertyDecl::OBJC_PR_class) == 0; - if (auto Info = Reader->lookupObjCProperty(*Context, - Property->getName(), - isInstanceProperty)) { - ::ProcessAPINotes(*this, Property, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCProperty(*Context, Property->getName(), + isInstanceProperty); + ProcessVersionedAPINotes(*this, Property, Info); } } diff --git a/clang/test/APINotes/properties.m b/clang/test/APINotes/properties.m index a4c925b67449a..b1559b9002c81 100644 --- a/clang/test/APINotes/properties.m +++ b/clang/test/APINotes/properties.m @@ -10,22 +10,36 @@ // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnly 'id' // CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClass 'id' // CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id' // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: {{^$}} +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0 +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id' // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: {{^$}} +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0 +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id' -// CHECK-3-NEXT: {{^$}} +// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}} +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 'id' -// CHECK-3-NEXT: {{^$}} +// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}} +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} +// CHECK-NOT: Attr + +// CHECK-LABEL: Decl diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index d761620b80eb8..38690fe18be86 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -3,16 +3,27 @@ // Build and check the unversioned module file. // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s // Build and check the versioned module file. -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s #import <VersionedKit/VersionedKit.h> // CHECK-UNVERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); // CHECK-VERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); +// CHECK-DUMP-LABEL: Dumping moveToPoint +// CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 0 +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)" +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" +// CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" +// CHECK-DUMP-NOT: Dumping + // CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape))); // CHECK-VERSIONED: void acceptClosure(void (^block)(void)); diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 27ab34c1309d0..8ba2ebc487f63 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1160,6 +1160,32 @@ namespace { } }; + class AttrArgument : public SimpleArgument { + public: + AttrArgument(const Record &Arg, StringRef Attr) + : SimpleArgument(Arg, Attr, "Attr *") + {} + + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " AttrVec vec;\n" + " ReadAttributes(F, vec, Record, Idx);\n" + " assert(vec.size() == 1);\n" + " Attr *" << getLowerName() << " = vec.front();"; + } + + void writePCHWrite(raw_ostream &OS) const override { + OS << " AddAttributes(SA->get" << getUpperName() << "());"; + } + + void writeDump(raw_ostream &OS) const override {} + + void writeDumpChildren(raw_ostream &OS) const override { + OS << " dumpAttr(SA->get" << getUpperName() << "());\n"; + } + + void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } + }; + } // end anonymous namespace static std::unique_ptr<Argument> @@ -1207,6 +1233,8 @@ createArgument(const Record &Arg, StringRef Attr, Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr); else if (ArgName == "VersionArgument") Ptr = llvm::make_unique<VersionArgument>(Arg, Attr); + else if (ArgName == "AttrArgument") + Ptr = llvm::make_unique<AttrArgument>(Arg, Attr); if (!Ptr) { // Search in reverse order so that the most-derived type is handled first. From 3c1a9d7bd8601a3ca84da38e4458b38f2a6272ff Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Wed, 11 Jan 2017 22:28:37 -0800 Subject: [PATCH 133/582] [index] Remove SwiftAccessorGetter/Setter symbol subkinds. Got superseded by the more general ones. apple-llvm-split-commit: 289cf572c9587176be1131e1d55983ef58ffa0ae apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexSymbol.h | 2 -- clang/lib/Index/IndexSymbol.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index 92a4bfcb73c58..16c3026eba8fe 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -70,8 +70,6 @@ enum class SymbolSubKind { // Swift sub-kinds - SwiftAccessorGetter, - SwiftAccessorSetter, SwiftAccessorWillSet, SwiftAccessorDidSet, SwiftAccessorAddressor, diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index 6f8cd0831ff60..f3de472675641 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -389,8 +389,6 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) { case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor"; case SymbolSubKind::AccessorGetter: return "acc-get"; case SymbolSubKind::AccessorSetter: return "acc-set"; - case SymbolSubKind::SwiftAccessorGetter: return "acc-get"; - case SymbolSubKind::SwiftAccessorSetter: return "acc-set"; case SymbolSubKind::SwiftAccessorWillSet: return "acc-willset"; case SymbolSubKind::SwiftAccessorDidSet: return "acc-didset"; case SymbolSubKind::SwiftAccessorAddressor: return "acc-addr"; From ce08f6bb8fe46f73b4e19ed184391ea51bf29962 Mon Sep 17 00:00:00 2001 From: Graydon Hoare <ghoare@apple.com> Date: Thu, 12 Jan 2017 12:21:14 -0800 Subject: [PATCH 134/582] [ASTReader] Add a DeserializationListener callback for IMPORTED_MODULES apple-llvm-split-commit: 19b4e7d2d33b2f01b9c137a578b0b9c6ef943e58 apple-llvm-split-dir: clang/ --- .../include/clang/Serialization/ASTDeserializationListener.h | 4 ++++ clang/lib/Serialization/ASTReader.cpp | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Serialization/ASTDeserializationListener.h b/clang/include/clang/Serialization/ASTDeserializationListener.h index 4b10c39d8fb22..c26f3e0b425e4 100644 --- a/clang/include/clang/Serialization/ASTDeserializationListener.h +++ b/clang/include/clang/Serialization/ASTDeserializationListener.h @@ -26,6 +26,7 @@ class QualType; class MacroDefinitionRecord; class MacroInfo; class Module; +class SourceLocation; class ASTDeserializationListener { public: @@ -52,6 +53,9 @@ class ASTDeserializationListener { MacroDefinitionRecord *MD) {} /// \brief A module definition was read from the AST file. virtual void ModuleRead(serialization::SubmoduleID ID, Module *Mod) {} + /// \brief A module import was read from the AST file. + virtual void ModuleImportRead(serialization::SubmoduleID ID, + SourceLocation ImportLoc) {} }; } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index aa0b6b22a0b50..6e16980c78448 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3245,8 +3245,11 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { for (unsigned I = 0, N = Record.size(); I != N; /**/) { unsigned GlobalID = getGlobalSubmoduleID(F, Record[I++]); SourceLocation Loc = ReadSourceLocation(F, Record, I); - if (GlobalID) + if (GlobalID) { ImportedModules.push_back(ImportedSubmodule(GlobalID, Loc)); + if (DeserializationListener) + DeserializationListener->ModuleImportRead(GlobalID, Loc); + } } } break; From 847e8814275dd20285da0c9953c027894369c976 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Wed, 18 Jan 2017 09:50:43 -0800 Subject: [PATCH 135/582] [API Notes] Consult API notes for implicitly-declared property getter/setter. Fixes rdar://problem/30066544. apple-llvm-split-commit: e74f86c4e3732bd594854bb9898b1537478028c4 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaObjCProperty.cpp | 5 ++++ .../Headers/SomeKit.apinotes | 24 +++++++++++++++++++ .../SomeKit.framework/Headers/SomeKit.h | 8 +++++++ clang/test/APINotes/availability.m | 12 ++++++++++ 4 files changed, 49 insertions(+) diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index a9c3889c53b79..eea44eb3bc5e6 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -2250,6 +2250,8 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { SectionAttr::CreateImplicit(Context, SectionAttr::GNU_section, SA->getName(), Loc)); + ProcessAPINotes(GetterMethod); + if (getLangOpts().ObjCAutoRefCount) CheckARCMethodDecl(GetterMethod); } else @@ -2315,6 +2317,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { SetterMethod->addAttr( SectionAttr::CreateImplicit(Context, SectionAttr::GNU_section, SA->getName(), Loc)); + + ProcessAPINotes(SetterMethod); + // It's possible for the user to have set a very odd custom // setter selector that causes it to have a method family. if (getLangOpts().ObjCAutoRefCount) diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes index d2d10037501d7..ff88fdbaeac83 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes @@ -10,6 +10,30 @@ Classes: MethodKind: Instance NullabilityOfRet: N Nullability: [ N, S ] + - Selector: "implicitGetOnlyInstance" + MethodKind: Instance + Availability: none + AvailabilityMsg: "getter gone" + - Selector: "implicitGetOnlyClass" + MethodKind: Class + Availability: none + AvailabilityMsg: "getter gone" + - Selector: "implicitGetSetInstance" + MethodKind: Instance + Availability: none + AvailabilityMsg: "getter gone" + - Selector: "implicitGetSetClass" + MethodKind: Class + Availability: none + AvailabilityMsg: "getter gone" + - Selector: "setImplicitGetSetInstance:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "setter gone" + - Selector: "setImplicitGetSetClass:" + MethodKind: Class + Availability: none + AvailabilityMsg: "setter gone" Properties: - Name: intValue PropertyKind: Instance diff --git a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h index 42a9fbcc2b27b..1a192f5432fd1 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -49,4 +49,12 @@ __attribute__((objc_root_class)) @property int *intPropertyToMangle; @end +@interface A(ImplicitGetterSetters) +@property (nonatomic, readonly, retain) A *implicitGetOnlyInstance; +@property (class, nonatomic, readonly, retain) A *implicitGetOnlyClass; + +@property (nonatomic, readwrite, retain) A *implicitGetSetInstance; +@property (class, nonatomic, readwrite, retain) A *implicitGetSetClass; +@end + #endif diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m index 6a3034c0ceb50..f9bee1a000012 100644 --- a/clang/test/APINotes/availability.m +++ b/clang/test/APINotes/availability.m @@ -31,6 +31,18 @@ int main() { [a transform:a]; // expected-error{{'transform:' is unavailable: anything but this}} // expected-note@SomeKit/SomeKit.h:6{{'transform:' has been explicitly marked unavailable here}} + [a implicitGetOnlyInstance]; // expected-error{{'implicitGetOnlyInstance' is unavailable: getter gone}} + // expected-note@SomeKit/SomeKit.h:53{{'implicitGetOnlyInstance' has been explicitly marked unavailable here}} + [A implicitGetOnlyClass]; // expected-error{{'implicitGetOnlyClass' is unavailable: getter gone}} + // expected-note@SomeKit/SomeKit.h:54{{'implicitGetOnlyClass' has been explicitly marked unavailable here}} + [a implicitGetSetInstance]; // expected-error{{'implicitGetSetInstance' is unavailable: getter gone}} + // expected-note@SomeKit/SomeKit.h:56{{'implicitGetSetInstance' has been explicitly marked unavailable here}} + [a setImplicitGetSetInstance: a]; // expected-error{{'setImplicitGetSetInstance:' is unavailable: setter gone}} + // expected-note@SomeKit/SomeKit.h:56{{'setImplicitGetSetInstance:' has been explicitly marked unavailable here}} + [A implicitGetSetClass]; // expected-error{{'implicitGetSetClass' is unavailable: getter gone}} + // expected-note@SomeKit/SomeKit.h:57{{'implicitGetSetClass' has been explicitly marked unavailable here}} + [A setImplicitGetSetClass: a]; // expected-error{{'setImplicitGetSetClass:' is unavailable: setter gone}} + // expected-note@SomeKit/SomeKit.h:57{{'setImplicitGetSetClass:' has been explicitly marked unavailable here}} return 0; } From 654e4a76ec35c10c907f9d66c7b4e6e471a504e1 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 18 Jan 2017 13:42:23 -0800 Subject: [PATCH 136/582] [APINotes] Remove FactoryAsInit (superseded by SwiftName) This still leaves behind parsing (to emit an error message) as well as the attribute that used to get generated. Once Swift is no longer checking for the attribute we can take that out as well. apple-llvm-split-commit: 0474bbbe939c82e06933c732975d8664f9d88e47 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 23 ----------- clang/lib/APINotes/APINotesReader.cpp | 2 - clang/lib/APINotes/APINotesWriter.cpp | 2 +- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 39 ++++++++++--------- clang/lib/APINotes/Types.cpp | 2 +- clang/lib/Sema/SemaAPINotes.cpp | 8 ---- clang/test/APINotes/Inputs/roundtrip.apinotes | 1 - 7 files changed, 23 insertions(+), 54 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 6fbd5d809dad6..3f7ab86cf15ea 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -38,16 +38,6 @@ using llvm::StringRef; using llvm::Optional; using llvm::None; -/// Describes whether to classify a factory method as an initializer. -enum class FactoryAsInitKind { - /// Infer based on name and type (the default). - Infer, - /// Treat as a class method. - AsClassMethod, - /// Treat as an initializer. - AsInitializer -}; - /// Opaque context ID used to refer to an Objective-C class or protocol. class ContextID { public: @@ -556,30 +546,17 @@ class ObjCMethodInfo : public FunctionInfo { /// Whether this is a designated initializer of its class. unsigned DesignatedInit : 1; - /// Whether to treat this method as a factory or initializer. - unsigned FactoryAsInit : 2; - /// Whether this is a required initializer. unsigned Required : 1; ObjCMethodInfo() : FunctionInfo(), DesignatedInit(false), - FactoryAsInit(static_cast<unsigned>(FactoryAsInitKind::Infer)), Required(false) { } - FactoryAsInitKind getFactoryAsInitKind() const { - return static_cast<FactoryAsInitKind>(FactoryAsInit); - } - - void setFactoryAsInitKind(FactoryAsInitKind kind) { - FactoryAsInit = static_cast<unsigned>(kind); - } - friend bool operator==(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) { return static_cast<const FunctionInfo &>(lhs) == rhs && lhs.DesignatedInit == rhs.DesignatedInit && - lhs.FactoryAsInit == rhs.FactoryAsInit && lhs.Required == rhs.Required; } diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 8fbe2a184541e..b20bb4373b833 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -353,8 +353,6 @@ namespace { payload >>= 1; info.DesignatedInit = payload & 0x01; payload >>= 1; - info.FactoryAsInit = payload & 0x03; - payload >>= 2; readFunctionInfo(data, info); return info; diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 86b6abd2a36e6..116d23d82b1d0 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -766,7 +766,7 @@ namespace { } void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) { - uint8_t payload = info.FactoryAsInit; + uint8_t payload = 0; payload = (payload << 1) | info.DesignatedInit; payload = (payload << 1) | info.Required; endian::Writer<little> writer(out); diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 0ac0b6d65b767..3621b72fefc93 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -35,11 +35,6 @@ parameter has a nullability value. For 'audited' APIs, we assume the default nullability for any underspecified type. - FactoryAsInit can have the following values: - C - Treat as class method. - I - Treat as initializer. - A - Automatically infer based on the name and type (default). - --- Name: AppKit # The name of the framework @@ -98,8 +93,6 @@ AvailabilityMsg: "" - FactoryAsInit: C # Optional: Specifies if this method is a - # factory initializer (false/true) DesignatedInit: false # Optional: Specifies if this method is a # designated initializer (false/true) @@ -158,6 +151,16 @@ namespace { Instance, }; + /// Old attribute deprecated in favor of SwiftName. + enum class FactoryAsInitKind { + /// Infer based on name and type (the default). + Infer, + /// Treat as a class method. + AsClassMethod, + /// Treat as an initializer. + AsInitializer + }; + struct AvailabilityItem { APIAvailability Mode = APIAvailability::Available; StringRef Msg; @@ -186,8 +189,7 @@ namespace { AvailabilityItem Availability; Optional<bool> SwiftPrivate; StringRef SwiftName; - api_notes::FactoryAsInitKind FactoryAsInit - = api_notes::FactoryAsInitKind::Infer; + FactoryAsInitKind FactoryAsInit = FactoryAsInitKind::Infer; bool DesignatedInit = false; bool Required = false; StringRef ResultType; @@ -330,11 +332,11 @@ namespace llvm { }; template <> - struct ScalarEnumerationTraits<api_notes::FactoryAsInitKind > { - static void enumeration(IO &io, api_notes::FactoryAsInitKind &value) { - io.enumCase(value, "A", api_notes::FactoryAsInitKind::Infer); - io.enumCase(value, "C", api_notes::FactoryAsInitKind::AsClassMethod); - io.enumCase(value, "I", api_notes::FactoryAsInitKind::AsInitializer); + struct ScalarEnumerationTraits<FactoryAsInitKind> { + static void enumeration(IO &io, FactoryAsInitKind &value) { + io.enumCase(value, "A", FactoryAsInitKind::Infer); + io.enumCase(value, "C", FactoryAsInitKind::AsClassMethod); + io.enumCase(value, "I", FactoryAsInitKind::AsInitializer); } }; @@ -425,7 +427,7 @@ namespace llvm { io.mapOptional("SwiftPrivate", m.SwiftPrivate); io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, - api_notes::FactoryAsInitKind::Infer); + FactoryAsInitKind::Infer); io.mapOptional("DesignatedInit", m.DesignatedInit, false); io.mapOptional("Required", m.Required, false); io.mapOptional("ResultType", m.ResultType, StringRef("")); @@ -721,8 +723,10 @@ namespace { // Translate the initializer info. mInfo.DesignatedInit = meth.DesignatedInit; mInfo.Required = meth.Required; - if (meth.FactoryAsInit != FactoryAsInitKind::Infer) - mInfo.setFactoryAsInitKind(meth.FactoryAsInit); + if (meth.FactoryAsInit != FactoryAsInitKind::Infer) { + emitError("'FactoryAsInit' is no longer valid; " + "use 'SwiftName' instead"); + } mInfo.ResultType = meth.ResultType; // Translate parameter information. @@ -1193,7 +1197,6 @@ namespace { handleParameters(method.Params, info); handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); - method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; method.ResultType = copyString(info.ResultType); diff --git a/clang/lib/APINotes/Types.cpp b/clang/lib/APINotes/Types.cpp index 963780fb6f32c..4bbb4a86851cd 100644 --- a/clang/lib/APINotes/Types.cpp +++ b/clang/lib/APINotes/Types.cpp @@ -14,7 +14,7 @@ #include "llvm/Support/raw_ostream.h" void clang::api_notes::ObjCMethodInfo::dump(llvm::raw_ostream &os) { - os << DesignatedInit << " " << FactoryAsInit << " " << Unavailable << " " + os << DesignatedInit << " " << Unavailable << " " << NullabilityAudited << " " << NumAdjustedNullable << " " << NullabilityPayload << " " << UnavailableMsg << "\n"; } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 078445e48ad87..a7b77bc1795c2 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -568,14 +568,6 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, }); } - // FIXME: This doesn't work well with versioned API notes. - if (metadata.Role == VersionedInfoRole::AugmentSource && - info.getFactoryAsInitKind() - == api_notes::FactoryAsInitKind::AsClassMethod && - !D->getAttr<SwiftNameAttr>()) { - D->addAttr(SwiftSuppressFactoryAsInitAttr::CreateImplicit(S.Context)); - } - // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), static_cast<const api_notes::FunctionInfo &>(info), metadata); diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 05599a772d5d6..2937fa6e07bd0 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -16,7 +16,6 @@ Classes: AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' - FactoryAsInit: C ResultType: id - Selector: init MethodKind: Instance From 75d0e02ee2791f81bb8735cd34f94b19ac4fa8b5 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 18 Jan 2017 17:45:46 -0800 Subject: [PATCH 137/582] Remove the attribute used by the FactoryAsInit API note. apple-llvm-split-commit: 7652b982a1b6a2660078cba5a6858680a04a02a2 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 8 -------- 1 file changed, 8 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a88fabb6ac838..40175db3ac632 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1487,14 +1487,6 @@ def SwiftPrivate : InheritableAttr { let Documentation = [Undocumented]; } -def SwiftSuppressFactoryAsInit : InheritableAttr { - // This attribute has no spellings as it is only ever created implicitly - // from API notes. - let Spellings = []; - let SemaHandler = 0; - let Documentation = [Undocumented]; -} - def SwiftImportPropertyAsAccessors : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly // from API notes. From 5ee3434bac9f3493d5bd5b8e22d4f3c3cf4c4a4d Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 19 Jan 2017 11:11:53 -0800 Subject: [PATCH 138/582] [APINotes] Remove VersionedInfoRole. When API notes were first added, they would defer to the header when something was explicitly specified---for example, if the API notes had one set of nullability annotations from before the header was audited. This behavior didn't make sense for versioned API notes, however, where annotations needed for Swift 3 compatibility were intended to override the unversioned information in both the API notes and the header itself. Now that the versioning of API notes is well-established, simplify things by having the API notes always "win"; that is, an annotation in API notes will always override what's in the headers. apple-llvm-split-commit: b0755dd83ebfd484b843b3f63d87161af8c169e0 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 22 ------ clang/lib/APINotes/APINotesReader.cpp | 7 +- clang/lib/Sema/SemaAPINotes.cpp | 70 +++++-------------- 3 files changed, 20 insertions(+), 79 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 2b985c6ce73b5..a71c74da7b8a8 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -25,20 +25,6 @@ namespace clang { namespace api_notes { -/// Describes the role of a specific bit of versioned information. -enum class VersionedInfoRole : unsigned { - /// Augment the AST, but do not override information explicitly specified - /// in the source code. - AugmentSource, - - /// Replace information that may have been explicitly specified in the source - /// code. - ReplaceSource, - - /// Describes an alternate version of this information. - Versioned, -}; - /// A class that reads API notes data from a binary file that was written by /// the \c APINotesWriter. class APINotesReader { @@ -94,9 +80,6 @@ class APINotesReader { /// Swift version, or \c Results.size() if nothing matched. unsigned Selected; - /// The role of the selected index. - VersionedInfoRole SelectedRole; - public: /// Form an empty set of versioned information. VersionedInfo(llvm::NoneType) : Selected(0) { } @@ -122,11 +105,6 @@ class APINotesReader { return Selected; } - /// Describes the role of the selected entity. - VersionedInfoRole getSelectedRole() const { - return SelectedRole; - } - /// Return the number of versioned results we know about. unsigned size() const { return Results.size(); } diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index b20bb4373b833..295e5017c3a8f 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -1471,14 +1471,10 @@ APINotesReader::VersionedInfo<T>::VersionedInfo( // Look for an exact version match. Optional<unsigned> unversioned; Selected = Results.size(); - SelectedRole = VersionedInfoRole::Versioned; for (unsigned i = 0, n = Results.size(); i != n; ++i) { if (Results[i].first == version) { Selected = i; - - if (version) SelectedRole = VersionedInfoRole::ReplaceSource; - else SelectedRole = VersionedInfoRole::AugmentSource; break; } @@ -1492,9 +1488,8 @@ APINotesReader::VersionedInfo<T>::VersionedInfo( // unversioned result. if (Selected == Results.size() && unversioned) { Selected = *unversioned; - SelectedRole = VersionedInfoRole::AugmentSource; - } } +} auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> { if (!Impl.ObjCContextIDTable) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index a7b77bc1795c2..f11f803899ccf 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -15,20 +15,20 @@ #include "clang/AST/DeclObjC.h" #include "clang/APINotes/APINotesReader.h" using namespace clang; -using clang::api_notes::VersionedInfoRole; namespace { + enum IsActive_t : bool { + IsNotActive, + IsActive + }; + struct VersionedInfoMetadata { + /// An empty version refers to unversioned metadata. VersionTuple Version; - VersionedInfoRole Role; - - /*implicit*/ VersionedInfoMetadata(VersionedInfoRole role) : Role(role) { - assert(role != VersionedInfoRole::Versioned && - "explicit version required"); - } + bool IsActive; - /*implicit*/ VersionedInfoMetadata(VersionTuple version) - : Version(version), Role(VersionedInfoRole::Versioned) {} + VersionedInfoMetadata(VersionTuple version, IsActive_t active) + : Version(version), IsActive(active == IsActive_t::IsActive) {} }; } // end anonymous namespace @@ -45,20 +45,8 @@ static bool isMultiLevelPointerType(QualType type) { // Apply nullability to the given declaration. static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, VersionedInfoMetadata metadata) { - bool overrideExisting; - switch (metadata.Role) { - case VersionedInfoRole::AugmentSource: - overrideExisting = false; - break; - - case VersionedInfoRole::ReplaceSource: - overrideExisting = true; - break; - - case VersionedInfoRole::Versioned: - // FIXME: Record versioned info? + if (!metadata.IsActive) return; - } QualType type; @@ -80,7 +68,7 @@ static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(), /*isContextSensitive=*/false, isa<ParmVarDecl>(decl), /*implicit=*/true, - overrideExisting); + /*overrideExisting=*/true); if (type.getTypePtr() == origType.getTypePtr()) return; @@ -154,20 +142,7 @@ namespace { VersionedInfoMetadata metadata, llvm::function_ref<A *()> createAttr, llvm::function_ref<specific_attr_iterator<A>(Decl*)> getExistingAttr) { - switch (metadata.Role) { - case VersionedInfoRole::AugmentSource: - // If we're not adding an attribute, there's nothing to do. - if (!shouldAddAttribute) return; - - // If the attribute is already present, we're done. - if (getExistingAttr(D) != D->specific_attr_end<A>()) return; - - // Add the attribute. - if (auto attr = createAttr()) - D->addAttr(attr); - break; - - case VersionedInfoRole::ReplaceSource: { + if (metadata.IsActive) { auto end = D->specific_attr_end<A>(); auto existing = getExistingAttr(D); if (existing != end) { @@ -187,11 +162,8 @@ namespace { D->addAttr(attr); } } - break; - } - case VersionedInfoRole::Versioned: - // FIXME: Include the actual version instead of making one up. + } else { if (shouldAddAttribute) { if (auto attr = createAttr()) { auto *versioned = @@ -209,7 +181,6 @@ namespace { AttrKindFor<A>::value); D->addAttr(versioned); } - break; } } @@ -341,8 +312,8 @@ static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &info, VersionedInfoMetadata metadata) { // Type override. - if (metadata.Role != VersionedInfoRole::Versioned && - !info.getType().empty() && S.ParseTypeFromStringCallback) { + if (metadata.IsActive && !info.getType().empty() && + S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.getType(), "<API Notes>", D->getLocation()); @@ -480,8 +451,8 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, // Result type override. QualType overriddenResultType; - if (metadata.Role != VersionedInfoRole::Versioned && - !info.ResultType.empty() && S.ParseTypeFromStringCallback) { + if (metadata.IsActive && !info.ResultType.empty() && + S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.ResultType, "<API Notes>", D->getLocation()); @@ -650,11 +621,8 @@ static void ProcessVersionedAPINotes( SpecificInfo InfoSlice; for (unsigned i = 0, e = Info.size(); i != e; ++i) { std::tie(Version, InfoSlice) = Info[i]; - if (i != Selected) { - ProcessAPINotes(S, D, InfoSlice, Version); - } else { - ProcessAPINotes(S, D, InfoSlice, Info.getSelectedRole()); - } + auto Active = (i == Selected) ? IsActive : IsNotActive; + ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active)); } } From 585faeeb56212423dcfccb3bf6e1d2537b787eb1 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Mon, 23 Jan 2017 10:48:39 -0800 Subject: [PATCH 139/582] [APINotes] Add a 'SwiftImportAsNonGeneric' entry for ObjC classes (#70) If true, Swift will ignore any 'lightweight generics' parameters when importing the class, substituting any uses of them with their bounds. At least, it will once this lands and the Swift side gets implemented. Finishes rdar://problem/28455962. apple-llvm-split-commit: 63e3e8227d22f710f16bb6be398176beffab6b81 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 40 +++++++++++++++++-- clang/include/clang/Basic/Attr.td | 8 ++++ clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 4 ++ clang/lib/APINotes/APINotesWriter.cpp | 6 ++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 5 +++ clang/lib/Sema/SemaAPINotes.cpp | 7 ++++ .../Headers/VersionedKit.apinotes | 4 +- .../Headers/VersionedKit.h | 9 ++++- clang/test/APINotes/Inputs/roundtrip.apinotes | 1 + clang/test/APINotes/versioned.m | 16 +++++--- 11 files changed, 89 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 3f7ab86cf15ea..b81d273e9521d 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -212,12 +212,17 @@ class ObjCContextInfo : public CommonTypeInfo { /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; + unsigned SwiftImportAsNonGenericSpecified : 1; + unsigned SwiftImportAsNonGeneric : 1; + public: ObjCContextInfo() : CommonTypeInfo(), HasDefaultNullability(0), DefaultNullability(0), - HasDesignatedInits(0) + HasDesignatedInits(0), + SwiftImportAsNonGenericSpecified(false), + SwiftImportAsNonGeneric(false) { } /// Determine the default nullability for properties and methods of this @@ -240,6 +245,21 @@ class ObjCContextInfo : public CommonTypeInfo { bool hasDesignatedInits() const { return HasDesignatedInits; } void setHasDesignatedInits(bool value) { HasDesignatedInits = value; } + Optional<bool> getSwiftImportAsNonGeneric() const { + if (SwiftImportAsNonGenericSpecified) + return SwiftImportAsNonGeneric; + return None; + } + void setSwiftImportAsNonGeneric(Optional<bool> value) { + if (value.hasValue()) { + SwiftImportAsNonGenericSpecified = true; + SwiftImportAsNonGeneric = value.getValue(); + } else { + SwiftImportAsNonGenericSpecified = false; + SwiftImportAsNonGeneric = false; + } + } + /// Strip off any information within the class information structure that is /// module-local, such as 'audited' flags. void stripModuleLocalInfo() { @@ -249,9 +269,9 @@ class ObjCContextInfo : public CommonTypeInfo { friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { return static_cast<const CommonTypeInfo &>(lhs) == rhs && - lhs.HasDefaultNullability == rhs.HasDefaultNullability && - lhs.DefaultNullability == rhs.DefaultNullability && - lhs.HasDesignatedInits == rhs.HasDesignatedInits; + lhs.getDefaultNullability() == rhs.getDefaultNullability() && + lhs.HasDesignatedInits == rhs.HasDesignatedInits && + lhs.getSwiftImportAsNonGeneric() == rhs.getSwiftImportAsNonGeneric(); } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -270,6 +290,12 @@ class ObjCContextInfo : public CommonTypeInfo { } } + if (!lhs.SwiftImportAsNonGenericSpecified && + rhs.SwiftImportAsNonGenericSpecified) { + lhs.SwiftImportAsNonGenericSpecified = true; + lhs.SwiftImportAsNonGeneric = rhs.SwiftImportAsNonGeneric; + } + lhs.HasDesignatedInits |= rhs.HasDesignatedInits; return lhs; @@ -383,6 +409,12 @@ class ObjCPropertyInfo : public VariableInfo { } return lhs; } + + friend bool operator==(const ObjCPropertyInfo &lhs, + const ObjCPropertyInfo &rhs) { + return static_cast<const VariableInfo &>(lhs) == rhs && + lhs.getSwiftImportAsAccessors() == rhs.getSwiftImportAsAccessors(); + } }; /// Describes a function or method parameter. diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 40175db3ac632..0560f17ca72bd 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1487,6 +1487,14 @@ def SwiftPrivate : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftImportAsNonGeneric : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + def SwiftImportPropertyAsAccessors : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly // from API notes. diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index ef3ef73ab0cdd..1e257f3ada3a5 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 21; // Override types +const uint16_t VERSION_MINOR = 22; // SwiftImportAsNonGeneric using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 295e5017c3a8f..587903fccfb7b 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -253,6 +253,10 @@ namespace { if (payload & 0x4) info.setDefaultNullability(static_cast<NullabilityKind>(payload&0x03)); + payload >>= 3; + + if (payload & (1 << 1)) + info.setSwiftImportAsNonGeneric(payload & 1); return info; } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 116d23d82b1d0..65edaf9a5946c 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -584,8 +584,12 @@ namespace { emitCommonTypeInfo(out, info); uint8_t payload = 0; + if (auto swiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric()) { + payload |= (0x01 << 1) | swiftImportAsNonGeneric.getValue(); + } + payload <<= 3; if (auto nullable = info.getDefaultNullability()) { - payload = (0x01 << 2) | static_cast<uint8_t>(*nullable); + payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable); } payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0); out << payload; diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 3621b72fefc93..c96764617062c 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -216,6 +216,7 @@ namespace { StringRef SwiftName; Optional<StringRef> SwiftBridge; Optional<StringRef> NSErrorDomain; + Optional<bool> SwiftImportAsNonGeneric; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -445,6 +446,7 @@ namespace llvm { io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("NSErrorDomain", c.NSErrorDomain); + io.mapOptional("SwiftImportAsNonGeneric", c.SwiftImportAsNonGeneric); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -752,6 +754,8 @@ namespace { if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); + if (cl.SwiftImportAsNonGeneric) + cInfo.setSwiftImportAsNonGeneric(*cl.SwiftImportAsNonGeneric); ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo, swiftVersion); @@ -1101,6 +1105,7 @@ namespace { record.Name = name; handleCommonType(record, info); + record.SwiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric(); if (info.getDefaultNullability()) { record.AuditedForNullability = true; diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index f11f803899ccf..f58691181f1cd 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -603,6 +603,13 @@ static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, const api_notes::ObjCContextInfo &info, VersionedInfoMetadata metadata) { + if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) { + handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(S, D, *asNonGeneric, + metadata, [&] { + return SwiftImportAsNonGenericAttr::CreateImplicit(S.Context); + }); + } + // Handle information common to Objective-C classes and protocols. ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info, metadata); diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index c3b70b6e9520b..bd09953dcda96 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -19,6 +19,8 @@ SwiftVersions: Classes: - Name: MyReferenceType SwiftBridge: '' + - Name: TestGenericDUMP + SwiftImportAsNonGeneric: true - Name: TestProperties Properties: - Name: accessorsOnlyInVersion3 @@ -34,7 +36,7 @@ SwiftVersions: PropertyKind: Class SwiftImportAsAccessors: false Functions: - - Name: moveToPoint + - Name: moveToPointDUMP SwiftName: 'moveTo(a:b:)' - Name: acceptClosure Parameters: diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index ffbc7df7229d2..8301bd1f10a1c 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -1,4 +1,4 @@ -void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); +void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); void acceptClosure(void (^ __attribute__((noescape)) block)(void)); @@ -28,3 +28,10 @@ typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); @property (nonatomic, readwrite, retain) id accessorsOnlyExceptInVersion3; @property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassExceptInVersion3; @end + +@interface Base +@end + +@interface TestGenericDUMP<Element> : Base +- (Element)element; +@end diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 2937fa6e07bd0..4895060813798 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -9,6 +9,7 @@ Classes: AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' + SwiftImportAsNonGeneric: true Methods: - Selector: 'cellWithImage:' MethodKind: Class diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 38690fe18be86..29800b623c198 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -3,25 +3,31 @@ // Build and check the unversioned module file. // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s // Build and check the versioned module file. // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s #import <VersionedKit/VersionedKit.h> -// CHECK-UNVERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); -// CHECK-VERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); +// CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); +// CHECK-VERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); -// CHECK-DUMP-LABEL: Dumping moveToPoint +// CHECK-DUMP-LABEL: Dumping moveToPointDUMP // CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 0 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)" // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" // CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" + +// CHECK-DUMP-LABEL: Dumping TestGenericDUMP +// CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} Implicit +// CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} Implicit + // CHECK-DUMP-NOT: Dumping // CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape))); From e5c8915c8cb2ecd05f4a28cc031605a7a2d1b33e Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 24 Jan 2017 11:19:17 +0000 Subject: [PATCH 140/582] [TableGen] Fix the attr argument for SwiftVersioned attribute following the changes in r292868. apple-llvm-split-commit: 647ba8e8cc8ff1e6c8ad448f62e339457c2d1266 apple-llvm-split-dir: clang/ --- clang/utils/TableGen/ClangAttrEmitter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 2912ba3b8539b..44daf05c0bd59 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1168,7 +1168,7 @@ namespace { void writePCHReadDecls(raw_ostream &OS) const override { OS << " AttrVec vec;\n" - " ReadAttributes(F, vec, Record, Idx);\n" + " ReadAttributes(Record, vec);\n" " assert(vec.size() == 1);\n" " Attr *" << getLowerName() << " = vec.front();"; } From 5c7d1f9a385d8ecc116fd6a3e49a418c9616228d Mon Sep 17 00:00:00 2001 From: Hugh Bellamy <hughbellars@gmail.com> Date: Sat, 28 Jan 2017 15:30:15 +0000 Subject: [PATCH 141/582] Port swift specific compiler-rt code to Windows https://github.com/apple/swift-compiler-rt/pull/5 apple-llvm-split-commit: ac64ee693098615eaeceb290bc59a83f4455b167 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/profile/InstrProfilingFile.c | 11 ++--------- compiler-rt/lib/profile/InstrProfilingUtil.c | 19 +++++++++++++++++++ compiler-rt/lib/profile/InstrProfilingUtil.h | 2 ++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index 2c3add347d576..7c01e8d2fc475 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -269,16 +269,9 @@ static void exitSignalHandler(int sig) { static void installExitSignalHandlers(void) { unsigned I; - struct sigaction sigact; - int err; for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = exitSignalHandler; - err = sigaction(lprofCurFilename.ExitOnSignals[I], &sigact, NULL); - if (err) - PROF_WARN( - "Unable to install an exit signal handler for %d (errno = %d).\n", - lprofCurFilename.ExitOnSignals[I], err); + lprofInstallSignalHandler(lprofCurFilename.ExitOnSignals[I], + exitSignalHandler); } } diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.c b/compiler-rt/lib/profile/InstrProfilingUtil.c index 321c7192cc60f..f891b041c20fb 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.c +++ b/compiler-rt/lib/profile/InstrProfilingUtil.c @@ -26,6 +26,7 @@ #include <sys/utsname.h> #endif +#include <signal.h> #include <stdlib.h> #include <string.h> @@ -219,3 +220,21 @@ COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) { #endif return Sep; } + +COMPILER_RT_VISIBILITY void lprofInstallSignalHandler(int sig, + void (*handler)(int)) { +#ifdef _WIN32 + void (*err)(int) = signal(sig, handler); + if (err == SIG_ERR) + PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n", + sig, errno); +#else + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = handler; + int err = sigaction(sig, &sigact, NULL); + if (err) + PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n", + sig, err); +#endif +} diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.h b/compiler-rt/lib/profile/InstrProfilingUtil.h index a80fde77e16a2..679e55243dcdb 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.h +++ b/compiler-rt/lib/profile/InstrProfilingUtil.h @@ -51,4 +51,6 @@ int lprofGetHostName(char *Name, int Len); unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV); void *lprofPtrFetchAdd(void **Mem, long ByteIncr); +void lprofInstallSignalHandler(int sig, void(*handler)(int)); + #endif /* PROFILE_INSTRPROFILINGUTIL_H */ From 3ec8379f35272dd3500e372d69c05ae83fd7d397 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 24 Mar 2017 17:17:14 -0700 Subject: [PATCH 142/582] [APINotes] Don't apply API notes to non-definitions of structs. (#69) There are two reasons for this: - It doesn't really seem appropriate for forward-declared structs to pick up attributes. - In a proper definition `struct X { ... }`, the first `struct X` counts as a declaration, which means the API note attributes can get added ahead of time. Later on, when we try to add them /again/, they'll replace the attribute added at the first point rather than whatever the user wrote in source after the close brace. This also applies if there are any forward declarations before the definition. There is a downside: API notes now can't affect tag types that are /never/ defined (for example, structs that are forward-declared for use in pointers). Any annotations one might want to apply to such a type must be written in source. rdar://problem/29981732 apple-llvm-split-commit: 911df2f93842d956d2848d8c10366b742cc4eef2 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDecl.cpp | 1 - .../SimpleKit.framework/Headers/SimpleKit.apinotes | 6 ++++++ .../Frameworks/SimpleKit.framework/Headers/SimpleKit.h | 7 +++++++ .../SimpleKit.framework/Modules/module.modulemap | 5 +++++ clang/test/APINotes/types.m | 7 ++++++- 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Modules/module.modulemap diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a8369e794e873..90721e5ddc535 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13649,7 +13649,6 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, if (Attr) ProcessDeclAttributeList(S, New, Attr); - ProcessAPINotes(New); // If this has an identifier, add it to the scope stack. if (TUK == TUK_Friend) { diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes new file mode 100644 index 0000000000000..336f1685703c6 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes @@ -0,0 +1,6 @@ +Name: SimpleKit +Tags: +- Name: RenamedAgainInAPINotesA + SwiftName: SuccessfullyRenamedA +- Name: RenamedAgainInAPINotesB + SwiftName: SuccessfullyRenamedB diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h new file mode 100644 index 0000000000000..c30a1e75641b8 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h @@ -0,0 +1,7 @@ +struct RenamedAgainInAPINotesA { + int field; +} __attribute__((swift_name("bad"))); + +struct __attribute__((swift_name("bad"))) RenamedAgainInAPINotesB { + int field; +}; diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..2d07e76c0a142 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module SimpleKit { + umbrella header "SimpleKit.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m index fccff8092cf8e..9f8b970cde06f 100644 --- a/clang/test/APINotes/types.m +++ b/clang/test/APINotes/types.m @@ -1,7 +1,12 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s #import <SomeKit/SomeKit.h> +#import <SimpleKit/SimpleKit.h> + +// CHECK: struct __attribute__((swift_name("SuccessfullyRenamedA"))) RenamedAgainInAPINotesA { +// CHECK: struct __attribute__((swift_name("SuccessfullyRenamedB"))) RenamedAgainInAPINotesB { void test(OverriddenTypes *overridden) { int *ip1 = global_int_ptr; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double (*)(int, int)'}} From 3234acd649918f5c5c1544568bf9244d096d9f76 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 3 Apr 2017 10:15:52 -0700 Subject: [PATCH 143/582] [Swift SE-0160] Add swift_objc_members attribute for Objective-C classes. This attribute maps directly to Swift's new @objcMembers attribute; it is unused in Objective-C. Attribute part of rdar://problem/31371251. apple-llvm-split-commit: 6d10ac1c433dff0be863a3be68e5f993c3f831b3 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 6 ++++++ clang/include/clang/Basic/AttrDocs.td | 7 +++++++ clang/lib/Sema/SemaDeclAttr.cpp | 3 +++ clang/test/SemaObjC/attr-swift_objc_members.m | 17 +++++++++++++++++ 4 files changed, 33 insertions(+) create mode 100644 clang/test/SemaObjC/attr-swift_objc_members.m diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 6e4a4d6d2bbc1..0bb2e33d14df2 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1509,6 +1509,12 @@ def SwiftBridge : Attr { let Documentation = [SwiftBridgeDocs]; } +def SwiftObjCMembers : Attr { + let Spellings = [GNU<"swift_objc_members">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [SwiftObjCMembersDocs]; +} + def SwiftError : InheritableAttr { let Spellings = [GCC<"swift_error">]; let Args = [EnumArgument<"Convention", "ConventionKind", diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index dd1f4e91d16f9..e1e292b6c2deb 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2505,6 +2505,13 @@ The ``swift_bridge`` attribute indicates that the type to which the attribute ap }]; } +def SwiftObjCMembersDocs : Documentation { + let Category = SwiftDocs; + let Content = [{ +The ``swift_objc_members`` attribute maps to the Swift ``@objcMembers`` attribute, which indicates that Swift members of this class, its subclasses, and all of the extensions thereof, will implicitly be exposed back to Objective-C. + }]; +} + def SwiftErrorDocs : Documentation { let Category = SwiftDocs; let Heading = "swift_error"; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9624b9f8edb6b..a5d72c5ce72c6 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6950,6 +6950,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SwiftBridge: handleSwiftBridgeAttr(S, D, Attr); break; + case AttributeList::AT_SwiftObjCMembers: + handleSimpleAttribute<SwiftObjCMembersAttr>(S, D, Attr); + break; case AttributeList::AT_SwiftNewtype: handleSwiftNewtypeAttr(S, D, Attr); break; diff --git a/clang/test/SemaObjC/attr-swift_objc_members.m b/clang/test/SemaObjC/attr-swift_objc_members.m new file mode 100644 index 0000000000000..c8781f876da38 --- /dev/null +++ b/clang/test/SemaObjC/attr-swift_objc_members.m @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +#if !__has_attribute(swift_objc_members) +# error Cannot query presence of swift_objc_members attribute. +#endif + +__attribute__((swift_objc_members)) +__attribute__((objc_root_class)) +@interface A +@end + +__attribute__((swift_objc_members)) // expected-error{{'swift_objc_members' attribute only applies to Objective-C interfaces}} +@protocol P +@end + +__attribute__((swift_objc_members)) // expected-error{{'swift_objc_members' attribute only applies to Objective-C interfaces}} +extern void foo(void); From 8a4d5cc26490602152d9aa6d8d04c0e611f95f48 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Mon, 3 Apr 2017 10:54:38 -0700 Subject: [PATCH 144/582] [API Notes] Add support for swift_objc_members attribute as SwiftObjCMembers. Finishes rdar://problem/31371251. apple-llvm-split-commit: 83bd45deb00d73d9b888461bc6cdc2c6af5d1f23 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 26 +++++++++++++++++-- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 4 +++ clang/lib/APINotes/APINotesWriter.cpp | 4 +++ clang/lib/APINotes/APINotesYAMLCompiler.cpp | 5 ++++ clang/lib/Sema/SemaAPINotes.cpp | 7 +++++ .../Headers/VersionedKit.apinotes | 2 ++ clang/test/APINotes/Inputs/roundtrip.apinotes | 2 ++ clang/test/APINotes/versioned.m | 5 ++++ 9 files changed, 54 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index b81d273e9521d..725711ea2e112 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -215,6 +215,9 @@ class ObjCContextInfo : public CommonTypeInfo { unsigned SwiftImportAsNonGenericSpecified : 1; unsigned SwiftImportAsNonGeneric : 1; + unsigned SwiftObjCMembersSpecified : 1; + unsigned SwiftObjCMembers : 1; + public: ObjCContextInfo() : CommonTypeInfo(), @@ -222,7 +225,9 @@ class ObjCContextInfo : public CommonTypeInfo { DefaultNullability(0), HasDesignatedInits(0), SwiftImportAsNonGenericSpecified(false), - SwiftImportAsNonGeneric(false) + SwiftImportAsNonGeneric(false), + SwiftObjCMembersSpecified(false), + SwiftObjCMembers(false) { } /// Determine the default nullability for properties and methods of this @@ -260,6 +265,16 @@ class ObjCContextInfo : public CommonTypeInfo { } } + Optional<bool> getSwiftObjCMembers() const { + if (SwiftObjCMembersSpecified) + return SwiftObjCMembers; + return None; + } + void setSwiftObjCMembers(Optional<bool> value) { + SwiftObjCMembersSpecified = value.hasValue(); + SwiftObjCMembers = value.hasValue() ? *value : false; + } + /// Strip off any information within the class information structure that is /// module-local, such as 'audited' flags. void stripModuleLocalInfo() { @@ -271,7 +286,9 @@ class ObjCContextInfo : public CommonTypeInfo { return static_cast<const CommonTypeInfo &>(lhs) == rhs && lhs.getDefaultNullability() == rhs.getDefaultNullability() && lhs.HasDesignatedInits == rhs.HasDesignatedInits && - lhs.getSwiftImportAsNonGeneric() == rhs.getSwiftImportAsNonGeneric(); + lhs.getSwiftImportAsNonGeneric() == + rhs.getSwiftImportAsNonGeneric() && + lhs.getSwiftObjCMembers() == rhs.getSwiftObjCMembers(); } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -296,6 +313,11 @@ class ObjCContextInfo : public CommonTypeInfo { lhs.SwiftImportAsNonGeneric = rhs.SwiftImportAsNonGeneric; } + if (!lhs.SwiftObjCMembersSpecified && rhs.SwiftObjCMembersSpecified) { + lhs.SwiftObjCMembersSpecified = true; + lhs.SwiftObjCMembers = rhs.SwiftObjCMembers; + } + lhs.HasDesignatedInits |= rhs.HasDesignatedInits; return lhs; diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 1e257f3ada3a5..5a0cdbdd050b7 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 22; // SwiftImportAsNonGeneric +const uint16_t VERSION_MINOR = 23; // SwiftObjCMembers using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 587903fccfb7b..d9a4297d9139b 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -255,6 +255,10 @@ namespace { info.setDefaultNullability(static_cast<NullabilityKind>(payload&0x03)); payload >>= 3; + if (payload & (1 << 1)) + info.setSwiftObjCMembers(payload & 1); + payload >>= 2; + if (payload & (1 << 1)) info.setSwiftImportAsNonGeneric(payload & 1); diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 65edaf9a5946c..2331cd305f123 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -587,6 +587,10 @@ namespace { if (auto swiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric()) { payload |= (0x01 << 1) | swiftImportAsNonGeneric.getValue(); } + payload <<= 2; + if (auto swiftObjCMembers = info.getSwiftObjCMembers()) { + payload |= (0x01 << 1) | swiftObjCMembers.getValue(); + } payload <<= 3; if (auto nullable = info.getDefaultNullability()) { payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable); diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index c96764617062c..02b2ba1e86694 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -217,6 +217,7 @@ namespace { Optional<StringRef> SwiftBridge; Optional<StringRef> NSErrorDomain; Optional<bool> SwiftImportAsNonGeneric; + Optional<bool> SwiftObjCMembers; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -447,6 +448,7 @@ namespace llvm { io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("NSErrorDomain", c.NSErrorDomain); io.mapOptional("SwiftImportAsNonGeneric", c.SwiftImportAsNonGeneric); + io.mapOptional("SwiftObjCMembers", c.SwiftObjCMembers); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -756,6 +758,8 @@ namespace { cInfo.setDefaultNullability(*DefaultNullability); if (cl.SwiftImportAsNonGeneric) cInfo.setSwiftImportAsNonGeneric(*cl.SwiftImportAsNonGeneric); + if (cl.SwiftObjCMembers) + cInfo.setSwiftObjCMembers(*cl.SwiftObjCMembers); ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo, swiftVersion); @@ -1106,6 +1110,7 @@ namespace { handleCommonType(record, info); record.SwiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric(); + record.SwiftObjCMembers = info.getSwiftObjCMembers(); if (info.getDefaultNullability()) { record.AuditedForNullability = true; diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index f58691181f1cd..59016284861a0 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -610,6 +610,13 @@ static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, }); } + if (auto objcMembers = info.getSwiftObjCMembers()) { + handleAPINotedAttribute<SwiftObjCMembersAttr>(S, D, *objcMembers, + metadata, [&] { + return SwiftObjCMembersAttr::CreateImplicit(S.Context); + }); + } + // Handle information common to Objective-C classes and protocols. ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info, metadata); diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index bd09953dcda96..394b15689fe47 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -1,6 +1,7 @@ Name: VersionedKit Classes: - Name: TestProperties + SwiftObjCMembers: true Properties: - Name: accessorsOnly PropertyKind: Instance @@ -22,6 +23,7 @@ SwiftVersions: - Name: TestGenericDUMP SwiftImportAsNonGeneric: true - Name: TestProperties + SwiftObjCMembers: false Properties: - Name: accessorsOnlyInVersion3 PropertyKind: Instance diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 4895060813798..3b493e1ba046d 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -10,6 +10,7 @@ Classes: SwiftPrivate: false SwiftName: '' SwiftImportAsNonGeneric: true + SwiftObjCMembers: false Methods: - Selector: 'cellWithImage:' MethodKind: Class @@ -61,6 +62,7 @@ Classes: SwiftPrivate: false SwiftName: '' SwiftBridge: View + SwiftObjCMembers: true Methods: - Selector: 'addSubview:' MethodKind: Instance diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 29800b623c198..21a783e180233 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -54,3 +54,8 @@ // CHECK-VERSIONED: void privateFunc(); // CHECK-VERSIONED: typedef double MyDoubleWrapper; + +// CHECK-UNVERSIONED: __attribute__((swift_objc_members) +// CHECK-UNVERSIONED-NEXT: @interface TestProperties +// CHECK-VERSIONED-NOT: __attribute__((swift_objc_members) +// CHECK-VERSIONED: @interface TestProperties From bff373f65cb3f76336d1ad22c9375b390db62b80 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Tue, 18 Apr 2017 15:03:22 -0700 Subject: [PATCH 145/582] Update pragma-attribute-supported-attributes-list test for swift attributes. This was added in r300556. Update the test to also check swift-specific attributes. apple-llvm-split-commit: 2b4f2133d3353410da12c969a100b054df66b772 apple-llvm-split-dir: clang/ --- .../Misc/pragma-attribute-supported-attributes-list.test | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index b1f2e66ab3397..1e1c4ce7bdd6e 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 57 attributes: +// CHECK: #pragma clang attribute supports 60 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -52,8 +52,11 @@ // CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) // CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: SwiftError (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: SwiftNewtype (SubjectMatchRule_type_alias) +// CHECK-NEXT: SwiftObjCMembers (SubjectMatchRule_objc_interface) // CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local) // CHECK-NEXT: Target (SubjectMatchRule_function) // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) From 373cda05981dcb3bb4803eeaea55b724d0e68f67 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Tue, 18 Apr 2017 15:46:32 -0700 Subject: [PATCH 146/582] Undo duplicated call after bad merge (82c834e628). The fact that this was *removed* on one side but was still present in the common ancestor (which is pretty unusual) confused me =/ rdar://31692170 apple-llvm-split-commit: 43cb0052daef91970c242a7ff6e5a774926c6227 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclObjC.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 6349c7d700e6b..f7c502946ceaf 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -995,8 +995,6 @@ ActOnStartClassInterface(Scope *S, SourceLocation AtInterfaceLoc, } } - if (AttrList) - ProcessDeclAttributeList(TUScope, IDecl, AttrList); AddPragmaAttributes(TUScope, IDecl); PushOnScopeChains(IDecl, TUScope); From 45050445b4b1681d559659280c8eaaacee3fdeb7 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 13 Apr 2017 20:12:37 -0700 Subject: [PATCH 147/582] [APINotes] Add 'EnumExtensibility' and 'FlagEnum' API notes. These map to the enum_extensibility and flag_enum attributes. EnumExtensibility has to cover three states of "open", "closed", and "none" because the presence of the attribute (with either an "open" or "closed" argument) indicates auditing of the enum. rdar://problem/29201690 apple-llvm-split-commit: 7c7f69ae64654547806e2b925987d82e4d76ce61 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 58 +++++++++++++++- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 11 +++ clang/lib/APINotes/APINotesWriter.cpp | 27 +++++++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 17 +++++ clang/lib/Sema/SemaAPINotes.cpp | 27 ++++++++ .../Headers/VersionedKit.apinotes | 17 +++++ .../Headers/VersionedKit.h | 45 ++++++++++++ clang/test/APINotes/Inputs/roundtrip.apinotes | 2 + clang/test/APINotes/versioned.m | 69 +++++++++++++++++++ 10 files changed, 272 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 725711ea2e112..2213bad29a4d1 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -657,10 +657,54 @@ class EnumConstantInfo : public CommonEntityInfo { EnumConstantInfo() : CommonEntityInfo() { } }; +/// The payload for an enum_extensibility attribute. This is a tri-state rather +/// than just a boolean because the presence of the attribute indicates +/// auditing. +enum class EnumExtensibilityKind { + None, + Open, + Closed, +}; + /// Describes API notes data for a tag. class TagInfo : public CommonTypeInfo { + unsigned HasFlagEnum : 1; + unsigned IsFlagEnum : 1; public: - TagInfo() : CommonTypeInfo() { } + Optional<EnumExtensibilityKind> EnumExtensibility; + + Optional<bool> isFlagEnum() const { + if (HasFlagEnum) + return IsFlagEnum; + return None; + } + void setFlagEnum(Optional<bool> Value) { + if (Value.hasValue()) { + HasFlagEnum = true; + IsFlagEnum = Value.getValue(); + } else { + HasFlagEnum = false; + } + } + + TagInfo() : CommonTypeInfo(), HasFlagEnum(0), IsFlagEnum(0) { } + + friend TagInfo &operator|=(TagInfo &lhs, const TagInfo &rhs) { + lhs |= static_cast<const CommonTypeInfo &>(rhs); + if (!lhs.HasFlagEnum && rhs.HasFlagEnum) { + lhs.HasFlagEnum = true; + lhs.IsFlagEnum = rhs.IsFlagEnum; + } + if (!lhs.EnumExtensibility.hasValue() && rhs.EnumExtensibility.hasValue()) + lhs.EnumExtensibility = rhs.EnumExtensibility; + return lhs; + } + + friend bool operator==(const TagInfo &lhs, const TagInfo &rhs) { + return static_cast<const CommonTypeInfo &>(lhs) == rhs && + lhs.isFlagEnum() == rhs.isFlagEnum() && + lhs.EnumExtensibility == rhs.EnumExtensibility; + } }; /// The kind of a swift_wrapper/swift_newtype. @@ -676,6 +720,18 @@ class TypedefInfo : public CommonTypeInfo { Optional<SwiftWrapperKind> SwiftWrapper; TypedefInfo() : CommonTypeInfo() { } + + friend TypedefInfo &operator|=(TypedefInfo &lhs, const TypedefInfo &rhs) { + lhs |= static_cast<const CommonTypeInfo &>(rhs); + if (!lhs.SwiftWrapper.hasValue() && rhs.SwiftWrapper.hasValue()) + lhs.SwiftWrapper = rhs.SwiftWrapper; + return lhs; + } + + friend bool operator==(const TypedefInfo &lhs, const TypedefInfo &rhs) { + return static_cast<const CommonTypeInfo &>(lhs) == rhs && + lhs.SwiftWrapper == rhs.SwiftWrapper; + } }; /// Descripts a series of options for a module diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 5a0cdbdd050b7..3e1b2d56ec8de 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 23; // SwiftObjCMembers +const uint16_t VERSION_MINOR = 24; // EnumExtensibility+FlagEnum using IdentifierID = PointerEmbeddedInt<unsigned, 31>; using IdentifierIDField = BCVBR<16>; diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index d9a4297d9139b..0b802b487e585 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -482,6 +482,17 @@ namespace { static TagInfo readUnversioned(internal_key_type key, const uint8_t *&data) { TagInfo info; + + uint8_t payload = *data++; + if (payload & 1) { + info.setFlagEnum(payload & 2); + } + payload >>= 2; + if (payload > 0) { + info.EnumExtensibility = + static_cast<EnumExtensibilityKind>((payload & 0x3) - 1); + } + readCommonTypeInfo(data, info); return info; } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 2331cd305f123..285a35efe205a 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -1056,7 +1056,32 @@ namespace { }; /// Used to serialize the on-disk tag table. - class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { }; + class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { + public: + unsigned getUnversionedInfoSize(const TagInfo &info) { + return 1 + getCommonTypeInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const TagInfo &info) { + endian::Writer<little> writer(out); + + uint8_t payload = 0; + if (auto enumExtensibility = info.EnumExtensibility) { + payload |= static_cast<uint8_t>(enumExtensibility.getValue()) + 1; + assert((payload < (1 << 2)) && "must fit in two bits"); + } + + payload <<= 2; + if (Optional<bool> value = info.isFlagEnum()) { + payload |= 1 << 0; + payload |= value.getValue() << 1; + } + + writer.write<uint8_t>(payload); + + emitCommonTypeInfo(out, info); + } + }; } // end anonymous namespace diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 02b2ba1e86694..db71f7f3a0cb7 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -261,6 +261,8 @@ namespace { Optional<bool> SwiftPrivate; Optional<StringRef> SwiftBridge; Optional<StringRef> NSErrorDomain; + Optional<api_notes::EnumExtensibilityKind> EnumExtensibility; + Optional<bool> FlagEnum; }; typedef std::vector<Tag> TagsSeq; @@ -370,6 +372,15 @@ namespace llvm { } }; + template<> + struct ScalarEnumerationTraits<api_notes::EnumExtensibilityKind> { + static void enumeration(IO &io, api_notes::EnumExtensibilityKind &value) { + io.enumCase(value, "none", api_notes::EnumExtensibilityKind::None); + io.enumCase(value, "open", api_notes::EnumExtensibilityKind::Open); + io.enumCase(value, "closed", api_notes::EnumExtensibilityKind::Closed); + } + }; + template <> struct ScalarTraits<VersionTuple> { static void output(const VersionTuple &value, void*, @@ -505,6 +516,8 @@ namespace llvm { io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); io.mapOptional("NSErrorDomain", t.NSErrorDomain); + io.mapOptional("EnumExtensibility", t.EnumExtensibility); + io.mapOptional("FlagEnum", t.FlagEnum); } }; @@ -928,6 +941,8 @@ namespace { TagInfo tagInfo; if (convertCommonType(t, tagInfo, t.Name)) continue; + tagInfo.EnumExtensibility = t.EnumExtensibility; + tagInfo.setFlagEnum(t.FlagEnum); Writer->addTag(t.Name, tagInfo, swiftVersion); } @@ -1286,6 +1301,8 @@ namespace { Tag tag; tag.Name = name; handleCommonType(tag, info); + tag.EnumExtensibility = info.EnumExtensibility; + tag.FlagEnum = info.isFlagEnum(); auto &items = getTopLevelItems(swiftVersion); items.Tags.push_back(tag); } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 59016284861a0..5c171ef027f0a 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -548,6 +548,33 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &info, VersionedInfoMetadata metadata) { + if (auto extensibility = info.EnumExtensibility) { + using api_notes::EnumExtensibilityKind; + bool shouldAddAttribute = (*extensibility != EnumExtensibilityKind::None); + handleAPINotedAttribute<EnumExtensibilityAttr>(S, D, shouldAddAttribute, + metadata, [&] { + EnumExtensibilityAttr::Kind kind; + switch (extensibility.getValue()) { + case EnumExtensibilityKind::None: + llvm_unreachable("remove only"); + case EnumExtensibilityKind::Open: + kind = EnumExtensibilityAttr::Open; + break; + case EnumExtensibilityKind::Closed: + kind = EnumExtensibilityAttr::Closed; + break; + } + return EnumExtensibilityAttr::CreateImplicit(S.Context, kind); + }); + } + + if (auto flagEnum = info.isFlagEnum()) { + handleAPINotedAttribute<FlagEnumAttr>(S, D, flagEnum.getValue(), metadata, + [&] { + return FlagEnumAttr::CreateImplicit(S.Context); + }); + } + // Handle common type information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info), metadata); diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index 394b15689fe47..3aa7b992059a5 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -15,6 +15,13 @@ Classes: - Name: accessorsOnlyForClassExceptInVersion3 PropertyKind: Class SwiftImportAsAccessors: true +Tags: + - Name: APINotedFlagEnum + FlagEnum: true + - Name: APINotedOpenEnum + EnumExtensibility: open + - Name: APINotedClosedEnum + EnumExtensibility: closed SwiftVersions: - Version: 3.0 Classes: @@ -49,6 +56,16 @@ SwiftVersions: Tags: - Name: MyErrorCode NSErrorDomain: '' + - Name: NewlyFlagEnum + FlagEnum: false + - Name: OpenToClosedEnum + EnumExtensibility: open + - Name: ClosedToOpenEnum + EnumExtensibility: closed + - Name: NewlyClosedEnum + EnumExtensibility: none + - Name: NewlyOpenEnum + EnumExtensibility: none Typedefs: - Name: MyDoubleWrapper SwiftWrapper: none diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index 8301bd1f10a1c..634ffddad9769 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -35,3 +35,48 @@ typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); @interface TestGenericDUMP<Element> : Base - (Element)element; @end + + +enum __attribute__((flag_enum)) FlagEnum { + FlagEnumA = 1, + FlagEnumB = 2 +}; + +enum __attribute__((flag_enum)) NewlyFlagEnum { + NewlyFlagEnumA = 1, + NewlyFlagEnumB = 2 +}; + +enum APINotedFlagEnum { + APINotedFlagEnumA = 1, + APINotedFlagEnumB = 2 +}; + + +enum __attribute__((enum_extensibility(open))) OpenEnum { + OpenEnumA = 1, +}; + +enum __attribute__((enum_extensibility(open))) NewlyOpenEnum { + NewlyOpenEnumA = 1, +}; + +enum __attribute__((enum_extensibility(closed))) NewlyClosedEnum { + NewlyClosedEnumA = 1, +}; + +enum __attribute__((enum_extensibility(open))) ClosedToOpenEnum { + ClosedToOpenEnumA = 1, +}; + +enum __attribute__((enum_extensibility(closed))) OpenToClosedEnum { + OpenToClosedEnumA = 1, +}; + +enum APINotedOpenEnum { + APINotedOpenEnumA = 1, +}; + +enum APINotedClosedEnum { + APINotedClosedEnumA = 1, +}; diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 3b493e1ba046d..65e39283cd8d5 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -138,6 +138,8 @@ Tags: SwiftPrivate: false SwiftName: SomeEnum NSErrorDomain: some_error_domain + EnumExtensibility: closed + FlagEnum: false - Name: NSSomeStruct Availability: available AvailabilityMsg: '' diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 21a783e180233..fe79ca4887753 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -59,3 +59,72 @@ // CHECK-UNVERSIONED-NEXT: @interface TestProperties // CHECK-VERSIONED-NOT: __attribute__((swift_objc_members) // CHECK-VERSIONED: @interface TestProperties + +// CHECK-UNVERSIONED-LABEL: enum FlagEnum { +// CHECK-UNVERSIONED-NEXT: FlagEnumA = 1, +// CHECK-UNVERSIONED-NEXT: FlagEnumB = 2 +// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum)); +// CHECK-UNVERSIONED-LABEL: enum NewlyFlagEnum { +// CHECK-UNVERSIONED-NEXT: NewlyFlagEnumA = 1, +// CHECK-UNVERSIONED-NEXT: NewlyFlagEnumB = 2 +// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum)); +// CHECK-UNVERSIONED-LABEL: enum APINotedFlagEnum { +// CHECK-UNVERSIONED-NEXT: APINotedFlagEnumA = 1, +// CHECK-UNVERSIONED-NEXT: APINotedFlagEnumB = 2 +// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum)); +// CHECK-UNVERSIONED-LABEL: enum OpenEnum { +// CHECK-UNVERSIONED-NEXT: OpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-UNVERSIONED-LABEL: enum NewlyOpenEnum { +// CHECK-UNVERSIONED-NEXT: NewlyOpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-UNVERSIONED-LABEL: enum NewlyClosedEnum { +// CHECK-UNVERSIONED-NEXT: NewlyClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-UNVERSIONED-LABEL: enum ClosedToOpenEnum { +// CHECK-UNVERSIONED-NEXT: ClosedToOpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-UNVERSIONED-LABEL: enum OpenToClosedEnum { +// CHECK-UNVERSIONED-NEXT: OpenToClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-UNVERSIONED-LABEL: enum APINotedOpenEnum { +// CHECK-UNVERSIONED-NEXT: APINotedOpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-UNVERSIONED-LABEL: enum APINotedClosedEnum { +// CHECK-UNVERSIONED-NEXT: APINotedClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); + +// CHECK-VERSIONED-LABEL: enum FlagEnum { +// CHECK-VERSIONED-NEXT: FlagEnumA = 1, +// CHECK-VERSIONED-NEXT: FlagEnumB = 2 +// CHECK-VERSIONED-NEXT: } __attribute__((flag_enum)); +// CHECK-VERSIONED-LABEL: enum NewlyFlagEnum { +// CHECK-VERSIONED-NEXT: NewlyFlagEnumA = 1, +// CHECK-VERSIONED-NEXT: NewlyFlagEnumB = 2 +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum APINotedFlagEnum { +// CHECK-VERSIONED-NEXT: APINotedFlagEnumA = 1, +// CHECK-VERSIONED-NEXT: APINotedFlagEnumB = 2 +// CHECK-VERSIONED-NEXT: } __attribute__((flag_enum)); +// CHECK-VERSIONED-LABEL: enum OpenEnum { +// CHECK-VERSIONED-NEXT: OpenEnumA = 1 +// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-VERSIONED-LABEL: enum NewlyOpenEnum { +// CHECK-VERSIONED-NEXT: NewlyOpenEnumA = 1 +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum NewlyClosedEnum { +// CHECK-VERSIONED-NEXT: NewlyClosedEnumA = 1 +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum ClosedToOpenEnum { +// CHECK-VERSIONED-NEXT: ClosedToOpenEnumA = 1 +// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-VERSIONED-LABEL: enum OpenToClosedEnum { +// CHECK-VERSIONED-NEXT: OpenToClosedEnumA = 1 +// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-VERSIONED-LABEL: enum APINotedOpenEnum { +// CHECK-VERSIONED-NEXT: APINotedOpenEnumA = 1 +// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-VERSIONED-LABEL: enum APINotedClosedEnum { +// CHECK-VERSIONED-NEXT: APINotedClosedEnumA = 1 +// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); + From 5a375f34070fd3afe081f7a7d6162352048293a0 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 19 Apr 2017 20:17:59 -0700 Subject: [PATCH 148/582] [APINotes] Add 'EnumKind' as syntactic sugar. These accept camel-cased versions of the macros in Foundation and Core Foundation (plus a hypothetical "NSClosedEnum") as syntactic sugar for the settings FlagEnum and EnumExtensibility, matching the intent behind each macro. More rdar://problem/29201690 apple-llvm-split-commit: a9696c41f62007171574185d98e6ebc047479272 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 66 ++++++++++++++++++- .../Headers/VersionedKit.apinotes | 14 ++++ .../Headers/VersionedKit.h | 23 +++++++ clang/test/APINotes/versioned.m | 22 +++++++ clang/test/APINotes/yaml-reader-errors.c | 21 ++++++ 5 files changed, 144 insertions(+), 2 deletions(-) diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index db71f7f3a0cb7..f0f6cb8f95898 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -160,6 +160,18 @@ namespace { /// Treat as an initializer. AsInitializer }; + + /// Syntactic sugar for EnumExtensibility and FlagEnum + enum class EnumConvenienceAliasKind { + /// EnumExtensibility: none, FlagEnum: false + None, + /// EnumExtensibility: open, FlagEnum: false + CFEnum, + /// EnumExtensibility: open, FlagEnum: true + CFOptions, + /// EnumExtensibility: closed, FlagEnum: false + CFClosedEnum + }; struct AvailabilityItem { APIAvailability Mode = APIAvailability::Available; @@ -263,6 +275,7 @@ namespace { Optional<StringRef> NSErrorDomain; Optional<api_notes::EnumExtensibilityKind> EnumExtensibility; Optional<bool> FlagEnum; + Optional<EnumConvenienceAliasKind> EnumConvenienceKind; }; typedef std::vector<Tag> TagsSeq; @@ -381,6 +394,21 @@ namespace llvm { } }; + template<> + struct ScalarEnumerationTraits<EnumConvenienceAliasKind> { + static void enumeration(IO &io, EnumConvenienceAliasKind &value) { + io.enumCase(value, "none", EnumConvenienceAliasKind::None); + io.enumCase(value, "CFEnum", EnumConvenienceAliasKind::CFEnum); + io.enumCase(value, "NSEnum", EnumConvenienceAliasKind::CFEnum); + io.enumCase(value, "CFOptions", EnumConvenienceAliasKind::CFOptions); + io.enumCase(value, "NSOptions", EnumConvenienceAliasKind::CFOptions); + io.enumCase(value, "CFClosedEnum", + EnumConvenienceAliasKind::CFClosedEnum); + io.enumCase(value, "NSClosedEnum", + EnumConvenienceAliasKind::CFClosedEnum); + } + }; + template <> struct ScalarTraits<VersionTuple> { static void output(const VersionTuple &value, void*, @@ -518,6 +546,7 @@ namespace llvm { io.mapOptional("NSErrorDomain", t.NSErrorDomain); io.mapOptional("EnumExtensibility", t.EnumExtensibility); io.mapOptional("FlagEnum", t.FlagEnum); + io.mapOptional("EnumKind", t.EnumConvenienceKind); } }; @@ -941,8 +970,41 @@ namespace { TagInfo tagInfo; if (convertCommonType(t, tagInfo, t.Name)) continue; - tagInfo.EnumExtensibility = t.EnumExtensibility; - tagInfo.setFlagEnum(t.FlagEnum); + + if (t.EnumConvenienceKind) { + if (t.EnumExtensibility) { + emitError(llvm::Twine( + "cannot mix EnumKind and EnumExtensibility (for ") + t.Name + + ")"); + continue; + } + if (t.FlagEnum) { + emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") + + t.Name + ")"); + continue; + } + switch (t.EnumConvenienceKind.getValue()) { + case EnumConvenienceAliasKind::None: + tagInfo.EnumExtensibility = EnumExtensibilityKind::None; + tagInfo.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFEnum: + tagInfo.EnumExtensibility = EnumExtensibilityKind::Open; + tagInfo.setFlagEnum(false); + break; + case EnumConvenienceAliasKind::CFOptions: + tagInfo.EnumExtensibility = EnumExtensibilityKind::Open; + tagInfo.setFlagEnum(true); + break; + case EnumConvenienceAliasKind::CFClosedEnum: + tagInfo.EnumExtensibility = EnumExtensibilityKind::Closed; + tagInfo.setFlagEnum(false); + break; + } + } else { + tagInfo.EnumExtensibility = t.EnumExtensibility; + tagInfo.setFlagEnum(t.FlagEnum); + } Writer->addTag(t.Name, tagInfo, swiftVersion); } diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index 3aa7b992059a5..502e32ee87c9c 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -22,6 +22,20 @@ Tags: EnumExtensibility: open - Name: APINotedClosedEnum EnumExtensibility: closed + - Name: SoonToBeCFEnum + EnumKind: CFEnum + - Name: SoonToBeNSEnum + EnumKind: NSEnum + - Name: SoonToBeCFOptions + EnumKind: CFOptions + - Name: SoonToBeNSOptions + EnumKind: NSOptions + - Name: SoonToBeCFClosedEnum + EnumKind: CFClosedEnum + - Name: SoonToBeNSClosedEnum + EnumKind: NSClosedEnum + - Name: UndoAllThatHasBeenDoneToMe + EnumKind: none SwiftVersions: - Version: 3.0 Classes: diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index 634ffddad9769..ef094516673af 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -80,3 +80,26 @@ enum APINotedOpenEnum { enum APINotedClosedEnum { APINotedClosedEnumA = 1, }; + + +enum SoonToBeCFEnum { + SoonToBeCFEnumA = 1 +}; +enum SoonToBeNSEnum { + SoonToBeNSEnumA = 1 +}; +enum SoonToBeCFOptions { + SoonToBeCFOptionsA = 1 +}; +enum SoonToBeNSOptions { + SoonToBeNSOptionsA = 1 +}; +enum SoonToBeCFClosedEnum { + SoonToBeCFClosedEnumA = 1 +}; +enum SoonToBeNSClosedEnum { + SoonToBeNSClosedEnumA = 1 +}; +enum UndoAllThatHasBeenDoneToMe { + UndoAllThatHasBeenDoneToMeA = 1 +} __attribute__((flag_enum)) __attribute__((enum_extensibility(closed))); diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index fe79ca4887753..87363e7f20fd3 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -128,3 +128,25 @@ // CHECK-VERSIONED-NEXT: APINotedClosedEnumA = 1 // CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// These don't actually have versioned information, so we just check them once. +// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeCFEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeNSEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFOptions { +// CHECK-UNVERSIONED-NEXT: SoonToBeCFOptionsA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)); +// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSOptions { +// CHECK-UNVERSIONED-NEXT: SoonToBeNSOptionsA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)); +// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFClosedEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeCFClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSClosedEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeNSClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-UNVERSIONED-LABEL: enum UndoAllThatHasBeenDoneToMe { +// CHECK-UNVERSIONED-NEXT: UndoAllThatHasBeenDoneToMeA = 1 +// CHECK-UNVERSIONED-NEXT: }; diff --git a/clang/test/APINotes/yaml-reader-errors.c b/clang/test/APINotes/yaml-reader-errors.c index 3e01eaaef03cb..c02260bcf2510 100644 --- a/clang/test/APINotes/yaml-reader-errors.c +++ b/clang/test/APINotes/yaml-reader-errors.c @@ -63,3 +63,24 @@ AvailabilityMsg: iOSOnly AvailabilityMsg: iOSOnly - Name: globalVar2 Nullability: O +Tags: +# CHECK: cannot mix EnumKind and FlagEnum (for FlagAndEnumKind) + - Name: FlagAndEnumKind + FlagEnum: true + EnumKind: CFOptions +# CHECK: cannot mix EnumKind and FlagEnum (for FlagAndEnumKind2) + - Name: FlagAndEnumKind2 + EnumKind: CFOptions + FlagEnum: false +# CHECK: cannot mix EnumKind and EnumExtensibility (for ExtensibilityAndEnumKind) + - Name: ExtensibilityAndEnumKind + EnumExtensibility: open + EnumKind: CFOptions +# CHECK: cannot mix EnumKind and EnumExtensibility (for ExtensibilityAndEnumKind2) + - Name: ExtensibilityAndEnumKind2 + EnumKind: CFOptions + EnumExtensibility: closed +# CHECK: cannot mix EnumKind and EnumExtensibility (for ExtensibilityAndEnumKind3) + - Name: ExtensibilityAndEnumKind3 + EnumKind: none + EnumExtensibility: none From 6e7169a2d3ee1ed51c1829e8bb601ba0b1672a63 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Tue, 25 Apr 2017 11:23:46 -0700 Subject: [PATCH 149/582] [index] Introduce 'CommentTag' symbol kind This is only going to be used by the Swift compiler currently, for indexing '- Tag' entries in documentation comments. apple-llvm-split-commit: 1716048fc85a4e5035d03987c75bbaf665531692 apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexSymbol.h | 1 + clang/lib/Index/IndexSymbol.cpp | 1 + clang/tools/libclang/CXIndexDataConsumer.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index ea8864d9aa2f5..6989aa356552d 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -53,6 +53,7 @@ enum class SymbolKind : uint8_t { ConversionFunction, Parameter, + CommentTag, }; enum class SymbolLanguage { diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index 98dbc552042bd..524cb43efefd0 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -452,6 +452,7 @@ StringRef index::getSymbolKindString(SymbolKind K) { case SymbolKind::Destructor: return "destructor"; case SymbolKind::ConversionFunction: return "coversion-func"; case SymbolKind::Parameter: return "param"; + case SymbolKind::CommentTag: return "comment-tag"; } llvm_unreachable("invalid symbol kind"); } diff --git a/clang/tools/libclang/CXIndexDataConsumer.cpp b/clang/tools/libclang/CXIndexDataConsumer.cpp index 9cd5ff4f505f6..4a2957f334986 100644 --- a/clang/tools/libclang/CXIndexDataConsumer.cpp +++ b/clang/tools/libclang/CXIndexDataConsumer.cpp @@ -1256,6 +1256,7 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage case SymbolKind::Module: case SymbolKind::Macro: case SymbolKind::ClassProperty: + case SymbolKind::CommentTag: return CXIdxEntity_Unexposed; case SymbolKind::Enum: return CXIdxEntity_Enum; From 64ad67b08af6e704c3bf0c586bbf121f0667269b Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Mon, 8 May 2017 20:39:57 -0700 Subject: [PATCH 150/582] [APINotes] Always provide an unversioned SwiftName of some kind. (#81) If the active API notes set a Swift name for a declaration, but no other versions do, it's assumed that that's the name in every version, because the AST is indistinguishable from just applying the attribute in the header. Fix this by explicitly adding an inactive, unversioned removal attribute for SwiftName in otherwise ambiguous cases. (This really relates to /all/ annotations in the "SwiftVersions" section, and particularly those that are usually not written in headers, but it's particularly bad for SwiftName because the Swift compiler actually looks at the inactive versions for that one. At some point it may be worth trying to generalize this, though.) rdar://problem/31826517 apple-llvm-split-commit: 325fb32cc8b60bafd8d30213a818f96a520436ab apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 46 +++++++++++++++++++ .../Headers/VersionedKit.apinotes | 4 ++ .../Headers/VersionedKit.h | 7 +++ clang/test/APINotes/versioned.m | 20 ++++++++ 4 files changed, 77 insertions(+) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 5c171ef027f0a..6145fef3de268 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -649,6 +649,49 @@ static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, metadata); } +/// If we're applying API notes with an active, non-default version, and the +/// versioned API notes have a SwiftName but the declaration normally wouldn't +/// have one, add a removal attribute to make it clear that the new SwiftName +/// attribute only applies to the active version of \p D, not to all versions. +/// +/// This must be run \em before processing API notes for \p D, because otherwise +/// any existing SwiftName attribute will have been packaged up in a +/// SwiftVersionedAttr. +template <typename SpecificInfo> +static void maybeAttachUnversionedSwiftName( + Sema &S, Decl *D, + const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) { + if (D->hasAttr<SwiftNameAttr>()) + return; + if (!Info.getSelected()) + return; + + // Is the active slice versioned, and does it set a Swift name? + VersionTuple SelectedVersion; + SpecificInfo SelectedInfoSlice; + std::tie(SelectedVersion, SelectedInfoSlice) = Info[*Info.getSelected()]; + if (SelectedVersion.empty()) + return; + if (SelectedInfoSlice.SwiftName.empty()) + return; + + // Does the unversioned slice /not/ set a Swift name? + for (const auto &VersionAndInfoSlice : Info) { + if (!VersionAndInfoSlice.first.empty()) + continue; + if (!VersionAndInfoSlice.second.SwiftName.empty()) + return; + } + + // Then explicitly call that out with a removal attribute. + VersionedInfoMetadata DummyFutureMetadata(VersionTuple(), IsNotActive); + handleAPINotedAttribute<SwiftNameAttr>(S, D, /*add*/false, + DummyFutureMetadata, + []() -> SwiftNameAttr * { + llvm_unreachable("should not try to add an attribute here"); + }); +} + /// Processes all versions of versioned API notes. /// /// Just dispatches to the various ProcessAPINotes functions in this file. @@ -656,6 +699,9 @@ template <typename SpecificDecl, typename SpecificInfo> static void ProcessVersionedAPINotes( Sema &S, SpecificDecl *D, const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) { + + maybeAttachUnversionedSwiftName(S, D, Info); + unsigned Selected = Info.getSelected().getValueOr(Info.size()); VersionTuple Version; diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index 502e32ee87c9c..c02c6cbac9a17 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -58,6 +58,10 @@ SwiftVersions: - Name: accessorsOnlyForClassExceptInVersion3 PropertyKind: Class SwiftImportAsAccessors: false + - Name: Swift3RenamedOnlyDUMP + SwiftName: SpecialSwift3Name + - Name: Swift3RenamedAlsoDUMP + SwiftName: SpecialSwift3Also Functions: - Name: moveToPointDUMP SwiftName: 'moveTo(a:b:)' diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index ef094516673af..61a10034503c7 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -36,6 +36,13 @@ typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); - (Element)element; @end +@interface Swift3RenamedOnlyDUMP +@end + +__attribute__((swift_name("Swift4Name"))) +@interface Swift3RenamedAlsoDUMP +@end + enum __attribute__((flag_enum)) FlagEnum { FlagEnumA = 1, diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 87363e7f20fd3..5657ae17658db 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -22,11 +22,31 @@ // CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" +// CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping TestGenericDUMP // CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} Implicit // CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} Implicit +// CHECK-DUMP-NOT: Attr + +// CHECK-DUMP-LABEL: Dumping Swift3RenamedOnlyDUMP +// CHECK-DUMP: in VersionedKit Swift3RenamedOnlyDUMP +// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 0 {{[0-9]+}} +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Name" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Name" +// CHECK-DUMP-NOT: Attr + +// CHECK-DUMP-LABEL: Dumping Swift3RenamedAlsoDUMP +// CHECK-DUMP: in VersionedKit Swift3RenamedAlsoDUMP +// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 0 +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> "Swift4Name" +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> "Swift4Name" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Also" +// CHECK-DUMP-NOT: Attr // CHECK-DUMP-NOT: Dumping From 6486012b7d48be0464d0a01a38e01af778ff3df4 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Wed, 26 Apr 2017 16:58:05 -0700 Subject: [PATCH 151/582] [Modules] Include error diagnostics to the module hash Add a CC1 option to include error diagnostics to the module hash: -fmodules-hash-error-diagnostics rdar://problem/31796394 apple-llvm-split-commit: 02d1fb6d217fdd18be00c55cd87ef390379c2269 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/CC1Options.td | 2 + .../clang/Frontend/CompilerInvocation.h | 2 +- clang/lib/Frontend/CompilerInstance.cpp | 10 ++-- clang/lib/Frontend/CompilerInvocation.cpp | 30 ++++++++++- clang/test/Modules/hash-werror.m | 54 +++++++++++++++++++ 6 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 clang/test/Modules/hash-werror.m diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index b8bd25d8377dd..3aac98f5d0691 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -153,6 +153,7 @@ COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "requiring declaration of module BENIGN_LANGOPT(ModulesErrorRecovery, 1, 1, "automatically importing modules as needed when performing error recovery") BENIGN_LANGOPT(ImplicitModules, 1, 1, "building modules that are not specified via -fmodule-file") COMPATIBLE_LANGOPT(ModulesLocalVisibility, 1, 0, "local submodule visibility") +COMPATIBLE_LANGOPT(ModulesHashErrorDiags, 1, 0, "hash out diagnostic errors as part of the module hash") COMPATIBLE_LANGOPT(Optimize , 1, 0, "__OPTIMIZE__ predefined macro") COMPATIBLE_LANGOPT(OptimizeSize , 1, 0, "__OPTIMIZE_SIZE__ predefined macro") COMPATIBLE_LANGOPT(Static , 1, 0, "__STATIC__ predefined macro (as opposed to __DYNAMIC__)") diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 1132d1345f605..61127dd5e4961 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -458,6 +458,8 @@ def fmodules_debuginfo : def fmodule_format_EQ : Joined<["-"], "fmodule-format=">, HelpText<"Select the container format for clang modules and PCH. " "Supported options are 'raw' and 'obj'.">; +def fmodules_hash_error_diagnostics : Flag<["-"], "fmodules-hash-error-diagnostics">, + HelpText<"Use a separate module cache for modules compiled with conflicting -Werror options">; def ftest_module_file_extension_EQ : Joined<["-"], "ftest-module-file-extension=">, HelpText<"introduce a module file extension for testing purposes. " diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 1b48f026606dc..4d3449ff551a8 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -174,7 +174,7 @@ class CompilerInvocation : public CompilerInvocationBase { /// \brief Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built. - std::string getModuleHash() const; + std::string getModuleHash(DiagnosticsEngine &Diags) const; /// @} /// @name Option Subgroups diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 3e390c96e11d9..023a321cb52c2 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -471,7 +471,7 @@ std::string CompilerInstance::getSpecificModuleCachePath() { SmallString<256> SpecificModuleCache(getHeaderSearchOpts().ModuleCachePath); if (!SpecificModuleCache.empty() && !getHeaderSearchOpts().DisableModuleHash) llvm::sys::path::append(SpecificModuleCache, - getInvocation().getModuleHash()); + getInvocation().getModuleHash(getDiagnostics())); return SpecificModuleCache.str(); } @@ -1092,9 +1092,11 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance, PPOpts.RetainRemappedFileBuffers = true; Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; - assert(ImportingInstance.getInvocation().getModuleHash() == - Invocation->getModuleHash() && "Module hash mismatch!"); - + assert(ImportingInstance.getInvocation().getModuleHash( + ImportingInstance.getDiagnostics()) == + Invocation->getModuleHash(ImportingInstance.getDiagnostics()) && + "Module hash mismatch!"); + // Construct a compiler instance that will be used to actually create the // module. Since we're sharing a PCMCache, // CompilerInstance::CompilerInstance is responsible for finalizing the diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 0f2dcb1609cd7..777f060cc0c20 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2076,6 +2076,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Args.hasArg(OPT_fmodules_local_submodule_visibility) || Opts.ModulesTS; Opts.ModulesCodegen = Args.hasArg(OPT_fmodules_codegen); Opts.ModulesDebugInfo = Args.hasArg(OPT_fmodules_debuginfo); + Opts.ModulesHashErrorDiags = Args.hasArg(OPT_fmodules_hash_error_diagnostics); Opts.ModulesSearchAll = Opts.Modules && !Args.hasArg(OPT_fno_modules_search_all) && Args.hasArg(OPT_fmodules_search_all); @@ -2656,7 +2657,16 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, return Success; } -std::string CompilerInvocation::getModuleHash() const { +// Some extension diagnostics aren't explicitly mapped and require custom +// logic in the dianognostic engine to be used, track -pedantic-errors +static bool isExtHandlingFromDiagsError(DiagnosticsEngine &Diags) { + diag::Severity Ext = Diags.getExtensionHandlingBehavior(); + if (Ext == diag::Severity::Warning && Diags.getWarningsAsErrors()) + return true; + return Ext >= diag::Severity::Error; +} + +std::string CompilerInvocation::getModuleHash(DiagnosticsEngine &Diags) const { // Note: For QoI reasons, the things we use as a hash here should all be // dumped via the -module-info flag. using llvm::hash_code; @@ -2747,6 +2757,24 @@ std::string CompilerInvocation::getModuleHash() const { if (!SanHash.empty()) code = hash_combine(code, SanHash.Mask); + // Check for a couple things (see checkDiagnosticMappings in ASTReader.cpp): + // -Werror: consider all warnings into the hash + // -Werror=something: consider only the specified into the hash + // -pedantic-error + if (getLangOpts()->ModulesHashErrorDiags) { + bool ConsiderAllWarningsAsErrors = Diags.getWarningsAsErrors(); + code = hash_combine(code, isExtHandlingFromDiagsError(Diags)); + for (auto DiagIDMappingPair : Diags.getDiagnosticMappings()) { + diag::kind DiagID = DiagIDMappingPair.first; + auto CurLevel = Diags.getDiagnosticLevel(DiagID, SourceLocation()); + if (CurLevel < DiagnosticsEngine::Error && !ConsiderAllWarningsAsErrors) + continue; // not significant + code = hash_combine( + code, + Diags.getDiagnosticIDs()->getWarningOptionForDiag(DiagID).str()); + } + } + return llvm::APInt(64, code).toString(36, /*Signed=*/false); } diff --git a/clang/test/Modules/hash-werror.m b/clang/test/Modules/hash-werror.m new file mode 100644 index 0000000000000..85446ce49eb4d --- /dev/null +++ b/clang/test/Modules/hash-werror.m @@ -0,0 +1,54 @@ +// RUN: rm -rf %t +// RUN: mkdir %t + +// (1) Test -Werror +// RUN: echo 'int foo() { return fn(); }' > %t/foo.h +// RUN: echo 'module foo { header "foo.h" export * }' > %t/module.modulemap +// +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \ +// RUN: -I%t -fmodules-cache-path=%t/cache -Rmodule-build \ +// RUN: -fmodules-hash-error-diagnostics 2>%t/out +// +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \ +// RUN: -I%t -Werror -fmodules-cache-path=%t/cache -Rmodule-build \ +// RUN: -Wno-implicit-function-declaration \ +// RUN: -fmodules-hash-error-diagnostics 2>>%t/out + +// RUN: FileCheck --check-prefix=CHECKWERROR %s -input-file %t/out +// CHECKWERROR: remark: building module 'foo' as '/[[PATH:.*]]/foo- +// CHECKWERROR-NOT: remark: building module 'foo' as '/[[PATH]]/foo- + +// (2) Test -Werror= +// RUN: rm -rf %t/out %t/cache +// +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \ +// RUN: -I%t -Werror -fmodules-cache-path=%t/cache -Rmodule-build \ +// RUN: -fmodules-hash-error-diagnostics 2>%t/out +// +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \ +// RUN: -I%t -fmodules-cache-path=%t/cache -Rmodule-build \ +// RUN: -Werror=implicit-function-declaration \ +// RUN: -fmodules-hash-error-diagnostics 2>>%t/out + +// RUN: FileCheck --check-prefix=CHECKWERROREQUALS %s -input-file %t/out +// CHECKWERROREQUALS: remark: building module 'foo' as '/[[PATH:.*]]/foo- +// CHECKWERROREQUALS-NOT: remark: building module 'foo' as '/[[PATH]]/foo- + +// (3) Test -pedantic-errors +// RUN: rm -rf %t/out %t/cache +// RUN: echo '#ifdef foo' > %t/foo.h +// RUN: echo '#endif bad // extension!' >> %t/foo.h + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \ +// RUN: -I%t -fmodules-cache-path=%t/cache -Rmodule-build -x c \ +// RUN: -fmodules-hash-error-diagnostics 2>%t/out +// +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \ +// RUN: -I%t -pedantic-errors -fmodules-cache-path=%t/cache -Rmodule-build -x c \ +// RUN: -fmodules-hash-error-diagnostics 2>>%t/out + +// RUN: FileCheck --check-prefix=CHECKPEDANTICERROR %s -input-file %t/out +// CHECKPEDANTICERROR: remark: building module 'foo' as '/[[PATH:.*]]/foo- +// CHECKPEDANTICERROR-NOT: remark: building module 'foo' as '/[[PATH]]/foo- + +#include "foo.h" From 0361eaf9e53ef3e09ff34b8d2e1bba87ad74939f Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Sat, 10 Jun 2017 17:08:40 -0700 Subject: [PATCH 152/582] Restore changes dropped in bad merge (b76dafda5a) apple-llvm-split-commit: c8ddb417cd12fcbb3854c2cb158543a17ab7064a apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInstance.cpp | 22 ++++++++++++++++++++++ clang/lib/Frontend/FrontendAction.cpp | 7 +++++++ clang/lib/Lex/PPDirectives.cpp | 8 +++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index f68f9464b9f7f..70e99dff9d614 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/CompilerInstance.h" +#include "clang/APINotes/APINotesReader.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -631,6 +632,27 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, CodeCompleteConsumer *CompletionConsumer) { TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); + + // Set up API notes. + TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion); + + // If we're building a module and are supposed to load API notes, + // notify the API notes manager. + if (auto currentModule = getPreprocessor().getCurrentModule()) { + (void)TheSema->APINotes.loadCurrentModuleAPINotes( + currentModule, + getLangOpts().APINotesModules, + getAPINotesOpts().ModuleSearchPaths); + // Check for any attributes we should add to the module + for (auto reader : TheSema->APINotes.getCurrentModuleReaders()) { + // swift_infer_import_as_member + if (reader->getModuleOptions().SwiftInferImportAsMember) { + currentModule->IsSwiftInferImportAsMember = true; + break; + } + } + } + // Attach the external sema source if there is any. if (ExternalSemaSrc) { TheSema->addExternalSource(ExternalSemaSrc.get()); diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index 7c0b854648bdd..66d5688ffcb04 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -855,6 +855,13 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename; } + // Add a module declaration scope so that modules from -fmodule-map-file + // arguments may shadow modules found implicitly in search paths. + CI.getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .finishModuleDeclarationScope(); + // If we were asked to load any module files, do so now. for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles) if (!CI.loadModuleFile(ModuleFile)) diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 89c2ebd00a683..e95686a397523 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1660,12 +1660,18 @@ bool Preprocessor::checkModuleIsAvailable(const LangOptions &LangOpts, DiagnosticsEngine &Diags, Module *M) { Module::Requirement Requirement; Module::UnresolvedHeaderDirective MissingHeader; - if (M->isAvailable(LangOpts, TargetInfo, Requirement, MissingHeader)) + Module *ShadowingModule = nullptr; + if (M->isAvailable(LangOpts, TargetInfo, Requirement, MissingHeader, + ShadowingModule)) return false; if (MissingHeader.FileNameLoc.isValid()) { Diags.Report(MissingHeader.FileNameLoc, diag::err_module_header_missing) << MissingHeader.IsUmbrella << MissingHeader.FileName; + } else if (ShadowingModule) { + Diags.Report(M->DefinitionLoc, diag::err_module_shadowed) << M->Name; + Diags.Report(ShadowingModule->DefinitionLoc, + diag::note_previous_definition); } else { // FIXME: Track the location at which the requirement was specified, and // use it here. From 35bd78f32f4e769e392df08be5e71bd19ada5d20 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Sat, 10 Jun 2017 17:09:02 -0700 Subject: [PATCH 153/582] Temporary hack to allow making progress on master-next build failures. apple-llvm-split-commit: d1cd5fdf4f31d14fb20e40b4d3565fc865076e7b apple-llvm-split-dir: clang/ --- clang/include/clang/Lex/MacroInfo.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index 7da1e7b41ab8d..e5ca15f2e7170 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -266,6 +266,11 @@ class MacroInfo { void setUsedForHeaderGuard(bool Val) { UsedForHeaderGuard = Val; } + // FIXME: hack to get past build failures + unsigned getOwningModuleID() const { + return 0; + } + void dump() const; private: From c51d996fbcd805d53576a7b75b726774df5991c3 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Sat, 10 Jun 2017 22:36:31 -0700 Subject: [PATCH 154/582] NFC: Fix whitespace to use spaces instead of tabs. apple-llvm-split-commit: 740ea3231c74495c6de2eedbd1f503e4b6b4b543 apple-llvm-split-dir: clang/ --- clang/lib/Lex/PPDirectives.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index e95686a397523..183f67759c193 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1662,7 +1662,7 @@ bool Preprocessor::checkModuleIsAvailable(const LangOptions &LangOpts, Module::UnresolvedHeaderDirective MissingHeader; Module *ShadowingModule = nullptr; if (M->isAvailable(LangOpts, TargetInfo, Requirement, MissingHeader, - ShadowingModule)) + ShadowingModule)) return false; if (MissingHeader.FileNameLoc.isValid()) { From 24209b95dc59025a6f529037371e9f84c340c778 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 12 Jun 2017 13:41:59 -0700 Subject: [PATCH 155/582] Move getRequiredQualification to lib/AST to access it from the refactoring engine Preparation commit for the refactoring engine apple-llvm-split-commit: 98936f53f7c58443f6bf70dce856ff7c23ba9ae4 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/NestedNameSpecifier.h | 17 ++++++ clang/lib/AST/NestedNameSpecifier.cpp | 31 ++++++++++ clang/lib/Sema/SemaCodeComplete.cpp | 60 ++----------------- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h index b1ff9bdff5891..3a6a3c3694cec 100644 --- a/clang/include/clang/AST/NestedNameSpecifier.h +++ b/clang/include/clang/AST/NestedNameSpecifier.h @@ -219,6 +219,23 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { /// in debugging. void dump(const LangOptions &LO) const; void dump() const; + + /// \brief Compute the qualification required to get from the current context + /// (\p CurContext) to the target context (\p TargetContext). + /// + /// \param Context the AST context in which the qualification will be used. + /// + /// \param CurContext the context where an entity is being named, which is + /// typically based on the current scope. + /// + /// \param TargetContext the context in which the named entity actually + /// resides. + /// + /// \returns a nested name specifier that refers into the target context, or + /// NULL if no qualification is needed. + static NestedNameSpecifier * + getRequiredQualification(ASTContext &Context, const DeclContext *CurContext, + const DeclContext *TargetContext); }; /// \brief A C++ nested-name-specifier augmented with source location diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp index e2e0dbeec0dd3..88ee526851ce0 100644 --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -691,3 +691,34 @@ NestedNameSpecifierLocBuilder::getWithLocInContext(ASTContext &Context) const { memcpy(Mem, Buffer, BufferSize); return NestedNameSpecifierLoc(Representation, Mem); } + +NestedNameSpecifier *NestedNameSpecifier::getRequiredQualification( + ASTContext &Context, const DeclContext *CurContext, + const DeclContext *TargetContext) { + SmallVector<const DeclContext *, 4> TargetParents; + + for (const DeclContext *CommonAncestor = TargetContext; + CommonAncestor && !CommonAncestor->Encloses(CurContext); + CommonAncestor = CommonAncestor->getLookupParent()) { + if (CommonAncestor->isTransparentContext() || + CommonAncestor->isFunctionOrMethod()) + continue; + + TargetParents.push_back(CommonAncestor); + } + + NestedNameSpecifier *Result = nullptr; + while (!TargetParents.empty()) { + const DeclContext *Parent = TargetParents.pop_back_val(); + + if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent)) { + if (!Namespace->getIdentifier()) + continue; + + Result = NestedNameSpecifier::Create(Context, Result, Namespace); + } else if (const TagDecl *TD = dyn_cast<TagDecl>(Parent)) + Result = NestedNameSpecifier::Create( + Context, Result, false, Context.getTypeDeclType(TD).getTypePtr()); + } + return Result; +} diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index b9349dc06bff3..523fc69937b96 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -432,53 +432,6 @@ ResultBuilder::ShadowMapEntry::end() const { return iterator(DeclOrVector.get<DeclIndexPairVector *>()->end()); } -/// \brief Compute the qualification required to get from the current context -/// (\p CurContext) to the target context (\p TargetContext). -/// -/// \param Context the AST context in which the qualification will be used. -/// -/// \param CurContext the context where an entity is being named, which is -/// typically based on the current scope. -/// -/// \param TargetContext the context in which the named entity actually -/// resides. -/// -/// \returns a nested name specifier that refers into the target context, or -/// NULL if no qualification is needed. -static NestedNameSpecifier * -getRequiredQualification(ASTContext &Context, - const DeclContext *CurContext, - const DeclContext *TargetContext) { - SmallVector<const DeclContext *, 4> TargetParents; - - for (const DeclContext *CommonAncestor = TargetContext; - CommonAncestor && !CommonAncestor->Encloses(CurContext); - CommonAncestor = CommonAncestor->getLookupParent()) { - if (CommonAncestor->isTransparentContext() || - CommonAncestor->isFunctionOrMethod()) - continue; - - TargetParents.push_back(CommonAncestor); - } - - NestedNameSpecifier *Result = nullptr; - while (!TargetParents.empty()) { - const DeclContext *Parent = TargetParents.pop_back_val(); - - if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent)) { - if (!Namespace->getIdentifier()) - continue; - - Result = NestedNameSpecifier::Create(Context, Result, Namespace); - } - else if (const TagDecl *TD = dyn_cast<TagDecl>(Parent)) - Result = NestedNameSpecifier::Create(Context, Result, - false, - Context.getTypeDeclType(TD).getTypePtr()); - } - return Result; -} - /// Determine whether \p Id is a name reserved for the implementation (C99 /// 7.1.3, C++ [lib.global.names]). static bool isReservedName(const IdentifierInfo *Id, @@ -590,9 +543,8 @@ bool ResultBuilder::CheckHiddenResult(Result &R, DeclContext *CurContext, R.QualifierIsInformative = false; if (!R.Qualifier) - R.Qualifier = getRequiredQualification(SemaRef.Context, - CurContext, - R.Declaration->getDeclContext()); + R.Qualifier = NestedNameSpecifier::getRequiredQualification( + SemaRef.Context, CurContext, R.Declaration->getDeclContext()); return false; } @@ -3332,9 +3284,8 @@ static void MaybeAddOverrideCalls(Sema &S, DeclContext *InContext, // If we need a nested-name-specifier, add one now. if (!InContext) { - NestedNameSpecifier *NNS - = getRequiredQualification(S.Context, CurContext, - Overridden->getDeclContext()); + NestedNameSpecifier *NNS = NestedNameSpecifier::getRequiredQualification( + S.Context, CurContext, Overridden->getDeclContext()); if (NNS) { std::string Str; llvm::raw_string_ostream OS(Str); @@ -4196,7 +4147,8 @@ void Sema::CodeCompleteCase(Scope *S) { // If there are no prior enumerators in C++, check whether we have to // qualify the names of the enumerators that we suggest, because they // may not be visible in this scope. - Qualifier = getRequiredQualification(Context, CurContext, Enum); + Qualifier = NestedNameSpecifier::getRequiredQualification(Context, + CurContext, Enum); } // Add any enumerators that have not yet been mentioned. From 5b8aa9250aaf7cd8ef69fcff0d3313a882117e74 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 15 Jun 2017 16:41:58 -0700 Subject: [PATCH 156/582] Add support for refactoring to Clang & libclang This refactoring engine is used in Xcode 9. The following actions are supported: - Rename - Extract function/method. - Extract duplicate expression. - Add missing switch cases. It is also available as a fixit. - [C++] Fill-in method stubs from abstract base classes. - Convert if to switch. - [ObjC] Fill in protocol stubs. It is also available as a fixit. - [ObjC] Wrap in NSLocalizedString - Generate Missing Function Definitions apple-llvm-split-commit: 9890adfbee8f854732d0093bc8b2a32be1be8844 apple-llvm-split-dir: clang/ --- clang/include/clang-c/CXErrorCode.h | 20 +- clang/include/clang-c/Refactor.h | 1288 +++++++++++ clang/include/clang/AST/DeclObjC.h | 42 +- clang/include/clang/AST/DeclTemplate.h | 3 + clang/include/clang/AST/DependentASTVisitor.h | 91 + clang/include/clang/AST/PrettyPrinter.h | 12 +- clang/include/clang/Basic/AllDiagnostics.h | 1 + clang/include/clang/Basic/CMakeLists.txt | 1 + clang/include/clang/Basic/Diagnostic.td | 2 +- clang/include/clang/Basic/DiagnosticIDs.h.rej | 18 + .../clang/Basic/DiagnosticRefactoringKinds.td | 27 + .../clang/Basic/DiagnosticSemaKinds.td | 7 + clang/include/clang/Edit/RefactoringFixits.h | 66 + clang/include/clang/Lex/Lexer.h | 8 + .../Tooling/Core/RefactoringDiagnostic.h | 29 + .../clang/Tooling/Refactor/IndexerQuery.h | 274 +++ .../Refactor/RefactoringActionFinder.h | 60 + .../Tooling/Refactor/RefactoringActions.def | 60 + .../Tooling/Refactor/RefactoringActions.h | 34 + .../Tooling/Refactor/RefactoringOperation.h | 160 ++ .../Refactor/RefactoringOperationState.h | 66 + .../Tooling/Refactor/RefactoringOptionSet.h | 80 + .../Tooling/Refactor/RefactoringOptions.h | 59 + .../Tooling/Refactor/RefactoringReplacement.h | 85 + .../Tooling/Refactor/RenameIndexedFile.h | 83 + .../clang/Tooling/Refactor/RenamedSymbol.h | 125 ++ .../Tooling/Refactor/RenamingOperation.h | 43 + .../clang/Tooling/Refactor/SymbolName.h | 66 + .../Tooling/Refactor/SymbolOccurrenceFinder.h | 37 + .../clang/Tooling/Refactor/SymbolOperation.h | 91 + .../clang/Tooling/Refactor/USRFinder.h | 85 + clang/include/clang/module.modulemap | 5 +- clang/lib/AST/ASTDumper.cpp | 2 + clang/lib/AST/DeclObjC.cpp | 17 +- clang/lib/AST/DeclPrinter.cpp | 23 +- clang/lib/AST/TypePrinter.cpp | 20 +- clang/lib/Basic/DiagnosticIDs.cpp | 4 +- clang/lib/Edit/CMakeLists.txt | 2 + clang/lib/Edit/FillInMissingProtocolStubs.cpp | 461 ++++ .../lib/Edit/FillInMissingSwitchEnumCases.cpp | 189 ++ clang/lib/Lex/Lexer.cpp | 29 + clang/lib/Sema/SemaDeclObjC.cpp | 71 +- clang/lib/Sema/SemaStmt.cpp | 29 +- clang/lib/Serialization/ASTReaderDecl.cpp | 2 + clang/lib/Serialization/ASTWriterDecl.cpp | 2 + clang/lib/Tooling/CMakeLists.txt | 3 + clang/lib/Tooling/CompilationDatabase.cpp | 4 +- clang/lib/Tooling/Refactor/ASTSlice.cpp | 625 ++++++ clang/lib/Tooling/Refactor/ASTSlice.h | 182 ++ .../Refactor/ASTStateSerialization.cpp | 68 + clang/lib/Tooling/Refactor/CMakeLists.txt | 43 + clang/lib/Tooling/Refactor/Extract.cpp | 1916 ++++++++++++++++ .../ExtractRepeatedExpressionIntoVariable.cpp | 369 ++++ .../Refactor/FillInEnumSwitchCases.cpp | 110 + ...nMissingMethodStubsFromAbstractClasses.cpp | 293 +++ .../Refactor/FillInMissingProtocolStubs.cpp | 91 + .../Tooling/Refactor/IfSwitchConversion.cpp | 460 ++++ .../Refactor/ImplementDeclaredMethods.cpp | 414 ++++ clang/lib/Tooling/Refactor/IndexerQueries.cpp | 136 ++ .../Refactor/LocalizeObjCStringLiteral.cpp | 79 + .../Refactor/RefactoringActionFinder.cpp | 57 + .../Tooling/Refactor/RefactoringActions.cpp | 30 + .../Refactor/RefactoringContinuations.h | 351 +++ .../Tooling/Refactor/RefactoringOperation.cpp | 93 + .../Tooling/Refactor/RefactoringOperations.h | 37 + .../Tooling/Refactor/RefactoringOptions.cpp | 65 + .../Tooling/Refactor/RenameIndexedFile.cpp | 562 +++++ clang/lib/Tooling/Refactor/RenamedSymbol.cpp | 41 + .../Tooling/Refactor/RenamingOperation.cpp | 98 + .../Refactor/SourceLocationUtilities.cpp | 260 +++ .../Refactor/SourceLocationUtilities.h | 174 ++ clang/lib/Tooling/Refactor/StmtUtils.cpp | 54 + clang/lib/Tooling/Refactor/StmtUtils.h | 33 + clang/lib/Tooling/Refactor/SymbolName.cpp | 58 + .../Refactor/SymbolOccurrenceFinder.cpp | 412 ++++ .../lib/Tooling/Refactor/SymbolOperation.cpp | 210 ++ .../lib/Tooling/Refactor/SymbolUSRFinder.cpp | 206 ++ clang/lib/Tooling/Refactor/TypeUtils.cpp | 193 ++ clang/lib/Tooling/Refactor/TypeUtils.h | 35 + clang/lib/Tooling/Refactor/USRFinder.cpp | 698 ++++++ clang/lib/Tooling/Refactoring/CMakeLists.txt | 2 +- clang/test/Analysis/misc-ps.m | 2 +- clang/test/CMakeLists.txt | 3 +- .../fixit-fill-in-missing-switch-cases.cpp | 31 + .../fixit-fill-in-protocol-requirements.m | 54 + clang/test/Misc/ast-dump-decl.m | 3 + clang/test/Parser/switch-recovery.cpp | 2 +- .../Extract/captured-variable-block-types.m | 30 + .../captured-variable-function-types.cpp | 17 + .../Extract/captured-variable-lambda-type.cpp | 18 + .../Extract/captured-variable-typedef.cpp | 15 + .../Extract/captured-variable-types.cpp | 223 ++ .../Extract/captured-variable-types.m | 56 + .../Extract/disallowed-expressions.cpp | 50 + .../extract-address-of-captured-variable.cpp | 194 ++ .../extract-address-of-captured-variable.mm | 44 + .../Extract/extract-before-comments.cpp | 64 + .../extract-capture-instance-variable.cpp | 73 + .../Refactor/Extract/extract-capture-self.m | 56 + .../Extract/extract-capture-static-var.cpp | 12 + .../Refactor/Extract/extract-capture-super.m | 42 + .../Refactor/Extract/extract-capture-this.cpp | 66 + .../extract-capture-used-after-extraction.cpp | 214 ++ .../extract-capture-used-after-extraction.m | 25 + .../Refactor/Extract/extract-expression.cpp | 60 + .../Refactor/Extract/extract-from-class.cpp | 45 + .../Refactor/Extract/extract-header-inline.h | 7 + .../Refactor/Extract/extract-initiate.cpp | 150 ++ .../test/Refactor/Extract/extract-initiate.m | 14 + .../test/Refactor/Extract/extract-macros.cpp | 39 + .../test/Refactor/Extract/extract-method.cpp | 153 ++ clang/test/Refactor/Extract/extract-method.m | 57 + .../extract-mutation-of-captured-variable.cpp | 130 ++ .../Refactor/Extract/extract-objc-property.m | 47 + ...extract-reference-of-captured-variable.cpp | 287 +++ .../extract-reference-of-captured-variable.mm | 50 + .../Extract/extract-statement-macros.cpp | 47 + .../Refactor/Extract/extract-statements.cpp | 178 ++ .../Refactor/Extract/extract-statements.m | 57 + .../extract-whole-source-construct.cpp | 120 ++ .../Extract/extracted-declaration-name.mm | 50 + clang/test/Refactor/Extract/return-c-bool.c | 28 + .../Extract/return-correct-stl-type.cpp | 51 + .../test/Refactor/Extract/return-objc-bool.m | 89 + .../extract-repeated-expr-duplicates.cpp | 63 + .../extract-repeated-expr-duplicates.m | 32 + .../extract-repeated-expr-initiate.cpp | 109 + .../extract-repeated-expr-initiate.m | 94 + .../extract-repeated-expr-perform.cpp | 100 + .../extract-repeated-expr-perform.m | 80 + .../fill-in-cases-forward-decl.c | 26 + .../fill-in-cases-initiate.cpp | 150 ++ .../fill-in-cases-neatly-ordered.cpp | 157 ++ .../fill-in-cases-opaque-decl.cpp | 33 + .../fill-in-cases-perform.cpp | 80 + .../fill-in-cases-wrap-in-compound.cpp | 63 + .../initiate-on-enum-constant.c | 12 + ...l-in-missing-abstract-methods-initiate.cpp | 68 + ...missing-abstract-methods-no-attributes.cpp | 10 + ...ll-in-missing-abstract-methods-perform.cpp | 101 + ...n-missing-abstract-methods-with-bodies.cpp | 50 + ...-protocol-stubs-initiate-class-extension.m | 51 + ...in-protocol-stubs-initiate-required-only.m | 146 ++ ...-protocol-stubs-initiate-when-protocoled.m | 97 + .../fill-in-protocol-stubs-initiate.m | 100 + .../fill-in-protocol-stubs-no-attributes.m | 28 + .../fill-in-protocol-stubs-perform.m | 196 ++ .../if-switch-conversion-initiate.cpp | 566 +++++ .../if-switch-conversion-perform.cpp | 307 +++ .../ImplementDeclaredMethods/Inputs/class.cpp | 25 + .../Inputs/classInHeader.cpp | 69 + .../Inputs/classInHeader.h | 31 + .../ImplementDeclaredMethods/Inputs/empty.cpp | 1 + .../Inputs/objcClass.m | 13 + .../Inputs/objcHeader.h | 13 + .../implement-declared-methods-initiate.cpp | 64 + .../implement-declared-methods-initiate.m | 106 + .../implement-declared-methods.cpp | 164 ++ .../implement-declared-methods.m | 55 + .../ImplementDeclaredMethods/local-record.cpp | 30 + .../prohibited-methods.cpp | 19 + .../localize-objc-initiate.m | 26 + .../localize-objc-perform.m | 9 + .../Rename/CanonicalizeInstantiatedDecls.cpp | 86 + .../Rename/ClassAsTemplateArgument.cpp | 20 + .../Refactor/Rename/ClassSimpleRenaming.cpp | 38 + .../Rename/ComplexFunctionOverride.cpp | 76 + .../Refactor/Rename/ComplicatedClassType.cpp | 62 + clang/test/Refactor/Rename/Ctor.cpp | 29 + .../test/Refactor/Rename/CtorInitializer.cpp | 14 + clang/test/Refactor/Rename/DeclRefExpr.cpp | 21 + .../Refactor/Rename/DependentExpressions.cpp | 72 + clang/test/Refactor/Rename/Field.cpp | 12 + clang/test/Refactor/Rename/FunctionMacro.cpp | 17 + .../test/Refactor/Rename/FunctionOverride.cpp | 10 + .../Refactor/Rename/IndexedObjCMessageSend.mm | 882 ++++++++ .../test/Refactor/Rename/IndexedObjCMethod.m | 128 ++ .../Refactor/Rename/IndexedObjCMethodDecl.mm | 748 +++++++ .../Refactor/Rename/IndexedObjCProperty.m | 30 + .../Rename/Inputs/MultiFileTUHeader.h | 6 + ...jCImplementationTURequestsImplementation.m | 3 + .../Rename/Inputs/objc-system-header.h | 5 + .../Rename/Inputs/rename-indexed-file.cpp | 4 + .../Refactor/Rename/Inputs/system-header.h | 11 + clang/test/Refactor/Rename/LocalBlockSymbol.m | 58 + .../Refactor/Rename/LocalBlockSymbolCpp.mm | 24 + clang/test/Refactor/Rename/LocalSymbol.cpp | 232 ++ .../test/Refactor/Rename/MemberExprMacro.cpp | 19 + clang/test/Refactor/Rename/MultiFileTU.cpp | 9 + clang/test/Refactor/Rename/Namespace.cpp | 22 + clang/test/Refactor/Rename/NoNewName.cpp | 4 + clang/test/Refactor/Rename/ObjCClass.m | 95 + .../test/Refactor/Rename/ObjCClassProperty.m | 100 + .../Refactor/Rename/ObjCCompatibilityAlias.m | 44 + .../Rename/ObjCImplementationTURequests.m | 32 + .../Refactor/Rename/ObjCImplicitProperty.m | 31 + clang/test/Refactor/Rename/ObjCMethod.m | 135 ++ clang/test/Refactor/Rename/ObjCMethodMacro.m | 28 + clang/test/Refactor/Rename/ObjCProperty.m | 303 +++ .../Refactor/Rename/ObjCPropertyDynamic.m | 41 + ...ertyIVarInInterfaceWithoutImplementation.m | 28 + .../Refactor/Rename/ObjCPropertyInCategory.m | 45 + .../test/Refactor/Rename/ObjCPropertyMacro.m | 21 + .../Refactor/Rename/ObjCPropertySynthesize.m | 112 + clang/test/Refactor/Rename/ObjCProtocol.m | 67 + .../Rename/ProhibitedDeclarations.cpp | 72 + .../Refactor/Rename/ProhibitedDeclarations.m | 35 + .../Rename/TemplateClassInstantiation.cpp | 39 + .../Refactor/Rename/TemplateParameters.cpp | 25 + .../test/Refactor/Rename/TemplateTypename.cpp | 24 + .../Rename/TemplatedClassFunction.cpp | 19 + .../test/Refactor/Rename/TransparentTypedef.m | 40 + clang/test/Refactor/Rename/TypedefTag.cpp | 21 + clang/test/Refactor/Rename/USRForSymbols.m | 16 + .../Refactor/Rename/UserDefinedConversion.cpp | 23 + clang/test/Refactor/Rename/UsingDecl.cpp | 46 + clang/test/Refactor/Rename/Variable.cpp | 29 + clang/test/Refactor/Rename/VariableMacro.cpp | 69 + .../Refactor/Rename/invalid-indexed-name.m | 37 + clang/test/Refactor/Rename/invalid-name.cpp | 13 + clang/test/Refactor/Rename/invalid-name.m | 23 + .../Refactor/Rename/rename-indexed-file.cpp | 123 ++ .../Refactor/Rename/rename-initiate-usr.cpp | 20 + .../test/Refactor/Rename/rename-initiate.cpp | 27 + .../Refactor/list-refactoring-actions.cpp | 39 + clang/test/Sema/enum-attr.c | 12 +- clang/test/Sema/statements.c | 8 +- clang/test/Sema/switch.c | 10 +- clang/test/SemaCXX/array-bounds.cpp | 2 +- clang/test/SemaCXX/enum-attr.cpp | 12 +- clang/test/SemaCXX/scope-check.cpp | 2 +- clang/test/SemaCXX/typo-correction.cpp | 2 +- clang/tools/CMakeLists.txt | 3 + clang/tools/c-index-test/c-index-test.c | 4 + .../tools/clang-refactor-test/CMakeLists.txt | 23 + .../clang-refactor-test/ClangRefactorTest.cpp | 1371 ++++++++++++ clang/tools/diagtool/DiagnosticNames.cpp | 1 + clang/tools/libclang/CIndexDiagnostic.cpp | 20 +- clang/tools/libclang/CIndexDiagnostic.h | 4 + clang/tools/libclang/CMakeLists.txt | 1 + clang/tools/libclang/CRefactor.cpp | 1917 +++++++++++++++++ clang/tools/libclang/libclang.exports | 53 + clang/unittests/Tooling/RefactoringTest.cpp | 170 ++ clang/unittests/libclang/LibclangTest.cpp | 69 + 244 files changed, 26698 insertions(+), 88 deletions(-) create mode 100644 clang/include/clang-c/Refactor.h create mode 100644 clang/include/clang/AST/DependentASTVisitor.h create mode 100644 clang/include/clang/Basic/DiagnosticIDs.h.rej create mode 100644 clang/include/clang/Basic/DiagnosticRefactoringKinds.td create mode 100644 clang/include/clang/Edit/RefactoringFixits.h create mode 100644 clang/include/clang/Tooling/Core/RefactoringDiagnostic.h create mode 100644 clang/include/clang/Tooling/Refactor/IndexerQuery.h create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringActionFinder.h create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringActions.def create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringActions.h create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringOperation.h create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringOperationState.h create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringOptions.h create mode 100644 clang/include/clang/Tooling/Refactor/RefactoringReplacement.h create mode 100644 clang/include/clang/Tooling/Refactor/RenameIndexedFile.h create mode 100644 clang/include/clang/Tooling/Refactor/RenamedSymbol.h create mode 100644 clang/include/clang/Tooling/Refactor/RenamingOperation.h create mode 100644 clang/include/clang/Tooling/Refactor/SymbolName.h create mode 100644 clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h create mode 100644 clang/include/clang/Tooling/Refactor/SymbolOperation.h create mode 100644 clang/include/clang/Tooling/Refactor/USRFinder.h create mode 100644 clang/lib/Edit/FillInMissingProtocolStubs.cpp create mode 100644 clang/lib/Edit/FillInMissingSwitchEnumCases.cpp create mode 100644 clang/lib/Tooling/Refactor/ASTSlice.cpp create mode 100644 clang/lib/Tooling/Refactor/ASTSlice.h create mode 100644 clang/lib/Tooling/Refactor/ASTStateSerialization.cpp create mode 100644 clang/lib/Tooling/Refactor/CMakeLists.txt create mode 100644 clang/lib/Tooling/Refactor/Extract.cpp create mode 100644 clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp create mode 100644 clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp create mode 100644 clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp create mode 100644 clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp create mode 100644 clang/lib/Tooling/Refactor/IfSwitchConversion.cpp create mode 100644 clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp create mode 100644 clang/lib/Tooling/Refactor/IndexerQueries.cpp create mode 100644 clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp create mode 100644 clang/lib/Tooling/Refactor/RefactoringActionFinder.cpp create mode 100644 clang/lib/Tooling/Refactor/RefactoringActions.cpp create mode 100644 clang/lib/Tooling/Refactor/RefactoringContinuations.h create mode 100644 clang/lib/Tooling/Refactor/RefactoringOperation.cpp create mode 100644 clang/lib/Tooling/Refactor/RefactoringOperations.h create mode 100644 clang/lib/Tooling/Refactor/RefactoringOptions.cpp create mode 100644 clang/lib/Tooling/Refactor/RenameIndexedFile.cpp create mode 100644 clang/lib/Tooling/Refactor/RenamedSymbol.cpp create mode 100644 clang/lib/Tooling/Refactor/RenamingOperation.cpp create mode 100644 clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp create mode 100644 clang/lib/Tooling/Refactor/SourceLocationUtilities.h create mode 100644 clang/lib/Tooling/Refactor/StmtUtils.cpp create mode 100644 clang/lib/Tooling/Refactor/StmtUtils.h create mode 100644 clang/lib/Tooling/Refactor/SymbolName.cpp create mode 100644 clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp create mode 100644 clang/lib/Tooling/Refactor/SymbolOperation.cpp create mode 100644 clang/lib/Tooling/Refactor/SymbolUSRFinder.cpp create mode 100644 clang/lib/Tooling/Refactor/TypeUtils.cpp create mode 100644 clang/lib/Tooling/Refactor/TypeUtils.h create mode 100644 clang/lib/Tooling/Refactor/USRFinder.cpp create mode 100644 clang/test/FixIt/fixit-fill-in-missing-switch-cases.cpp create mode 100644 clang/test/FixIt/fixit-fill-in-protocol-requirements.m create mode 100644 clang/test/Refactor/Extract/captured-variable-block-types.m create mode 100644 clang/test/Refactor/Extract/captured-variable-function-types.cpp create mode 100644 clang/test/Refactor/Extract/captured-variable-lambda-type.cpp create mode 100644 clang/test/Refactor/Extract/captured-variable-typedef.cpp create mode 100644 clang/test/Refactor/Extract/captured-variable-types.cpp create mode 100644 clang/test/Refactor/Extract/captured-variable-types.m create mode 100644 clang/test/Refactor/Extract/disallowed-expressions.cpp create mode 100644 clang/test/Refactor/Extract/extract-address-of-captured-variable.cpp create mode 100644 clang/test/Refactor/Extract/extract-address-of-captured-variable.mm create mode 100644 clang/test/Refactor/Extract/extract-before-comments.cpp create mode 100644 clang/test/Refactor/Extract/extract-capture-instance-variable.cpp create mode 100644 clang/test/Refactor/Extract/extract-capture-self.m create mode 100644 clang/test/Refactor/Extract/extract-capture-static-var.cpp create mode 100644 clang/test/Refactor/Extract/extract-capture-super.m create mode 100644 clang/test/Refactor/Extract/extract-capture-this.cpp create mode 100644 clang/test/Refactor/Extract/extract-capture-used-after-extraction.cpp create mode 100644 clang/test/Refactor/Extract/extract-capture-used-after-extraction.m create mode 100644 clang/test/Refactor/Extract/extract-expression.cpp create mode 100644 clang/test/Refactor/Extract/extract-from-class.cpp create mode 100644 clang/test/Refactor/Extract/extract-header-inline.h create mode 100644 clang/test/Refactor/Extract/extract-initiate.cpp create mode 100644 clang/test/Refactor/Extract/extract-initiate.m create mode 100644 clang/test/Refactor/Extract/extract-macros.cpp create mode 100644 clang/test/Refactor/Extract/extract-method.cpp create mode 100644 clang/test/Refactor/Extract/extract-method.m create mode 100644 clang/test/Refactor/Extract/extract-mutation-of-captured-variable.cpp create mode 100644 clang/test/Refactor/Extract/extract-objc-property.m create mode 100644 clang/test/Refactor/Extract/extract-reference-of-captured-variable.cpp create mode 100644 clang/test/Refactor/Extract/extract-reference-of-captured-variable.mm create mode 100644 clang/test/Refactor/Extract/extract-statement-macros.cpp create mode 100644 clang/test/Refactor/Extract/extract-statements.cpp create mode 100644 clang/test/Refactor/Extract/extract-statements.m create mode 100644 clang/test/Refactor/Extract/extract-whole-source-construct.cpp create mode 100644 clang/test/Refactor/Extract/extracted-declaration-name.mm create mode 100644 clang/test/Refactor/Extract/return-c-bool.c create mode 100644 clang/test/Refactor/Extract/return-correct-stl-type.cpp create mode 100644 clang/test/Refactor/Extract/return-objc-bool.m create mode 100644 clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.cpp create mode 100644 clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.m create mode 100644 clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.cpp create mode 100644 clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m create mode 100644 clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp create mode 100644 clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m create mode 100644 clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-forward-decl.c create mode 100644 clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-initiate.cpp create mode 100644 clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-neatly-ordered.cpp create mode 100644 clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-opaque-decl.cpp create mode 100644 clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp create mode 100644 clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-wrap-in-compound.cpp create mode 100644 clang/test/Refactor/FillInEnumSwitchCases/initiate-on-enum-constant.c create mode 100644 clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-initiate.cpp create mode 100644 clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-no-attributes.cpp create mode 100644 clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp create mode 100644 clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-with-bodies.cpp create mode 100644 clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-class-extension.m create mode 100644 clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-required-only.m create mode 100644 clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-when-protocoled.m create mode 100644 clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate.m create mode 100644 clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-no-attributes.m create mode 100644 clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-perform.m create mode 100644 clang/test/Refactor/IfSwitchConversion/if-switch-conversion-initiate.cpp create mode 100644 clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/Inputs/class.cpp create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.cpp create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.h create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/Inputs/empty.cpp create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcHeader.h create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.cpp create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.m create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/local-record.cpp create mode 100644 clang/test/Refactor/ImplementDeclaredMethods/prohibited-methods.cpp create mode 100644 clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-initiate.m create mode 100644 clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m create mode 100644 clang/test/Refactor/Rename/CanonicalizeInstantiatedDecls.cpp create mode 100644 clang/test/Refactor/Rename/ClassAsTemplateArgument.cpp create mode 100644 clang/test/Refactor/Rename/ClassSimpleRenaming.cpp create mode 100644 clang/test/Refactor/Rename/ComplexFunctionOverride.cpp create mode 100644 clang/test/Refactor/Rename/ComplicatedClassType.cpp create mode 100644 clang/test/Refactor/Rename/Ctor.cpp create mode 100644 clang/test/Refactor/Rename/CtorInitializer.cpp create mode 100644 clang/test/Refactor/Rename/DeclRefExpr.cpp create mode 100644 clang/test/Refactor/Rename/DependentExpressions.cpp create mode 100644 clang/test/Refactor/Rename/Field.cpp create mode 100644 clang/test/Refactor/Rename/FunctionMacro.cpp create mode 100644 clang/test/Refactor/Rename/FunctionOverride.cpp create mode 100644 clang/test/Refactor/Rename/IndexedObjCMessageSend.mm create mode 100644 clang/test/Refactor/Rename/IndexedObjCMethod.m create mode 100644 clang/test/Refactor/Rename/IndexedObjCMethodDecl.mm create mode 100644 clang/test/Refactor/Rename/IndexedObjCProperty.m create mode 100644 clang/test/Refactor/Rename/Inputs/MultiFileTUHeader.h create mode 100644 clang/test/Refactor/Rename/Inputs/ObjCImplementationTURequestsImplementation.m create mode 100644 clang/test/Refactor/Rename/Inputs/objc-system-header.h create mode 100644 clang/test/Refactor/Rename/Inputs/rename-indexed-file.cpp create mode 100644 clang/test/Refactor/Rename/Inputs/system-header.h create mode 100644 clang/test/Refactor/Rename/LocalBlockSymbol.m create mode 100644 clang/test/Refactor/Rename/LocalBlockSymbolCpp.mm create mode 100644 clang/test/Refactor/Rename/LocalSymbol.cpp create mode 100644 clang/test/Refactor/Rename/MemberExprMacro.cpp create mode 100644 clang/test/Refactor/Rename/MultiFileTU.cpp create mode 100644 clang/test/Refactor/Rename/Namespace.cpp create mode 100644 clang/test/Refactor/Rename/NoNewName.cpp create mode 100644 clang/test/Refactor/Rename/ObjCClass.m create mode 100644 clang/test/Refactor/Rename/ObjCClassProperty.m create mode 100644 clang/test/Refactor/Rename/ObjCCompatibilityAlias.m create mode 100644 clang/test/Refactor/Rename/ObjCImplementationTURequests.m create mode 100644 clang/test/Refactor/Rename/ObjCImplicitProperty.m create mode 100644 clang/test/Refactor/Rename/ObjCMethod.m create mode 100644 clang/test/Refactor/Rename/ObjCMethodMacro.m create mode 100644 clang/test/Refactor/Rename/ObjCProperty.m create mode 100644 clang/test/Refactor/Rename/ObjCPropertyDynamic.m create mode 100644 clang/test/Refactor/Rename/ObjCPropertyIVarInInterfaceWithoutImplementation.m create mode 100644 clang/test/Refactor/Rename/ObjCPropertyInCategory.m create mode 100644 clang/test/Refactor/Rename/ObjCPropertyMacro.m create mode 100644 clang/test/Refactor/Rename/ObjCPropertySynthesize.m create mode 100644 clang/test/Refactor/Rename/ObjCProtocol.m create mode 100644 clang/test/Refactor/Rename/ProhibitedDeclarations.cpp create mode 100644 clang/test/Refactor/Rename/ProhibitedDeclarations.m create mode 100644 clang/test/Refactor/Rename/TemplateClassInstantiation.cpp create mode 100644 clang/test/Refactor/Rename/TemplateParameters.cpp create mode 100644 clang/test/Refactor/Rename/TemplateTypename.cpp create mode 100644 clang/test/Refactor/Rename/TemplatedClassFunction.cpp create mode 100644 clang/test/Refactor/Rename/TransparentTypedef.m create mode 100644 clang/test/Refactor/Rename/TypedefTag.cpp create mode 100644 clang/test/Refactor/Rename/USRForSymbols.m create mode 100644 clang/test/Refactor/Rename/UserDefinedConversion.cpp create mode 100644 clang/test/Refactor/Rename/UsingDecl.cpp create mode 100644 clang/test/Refactor/Rename/Variable.cpp create mode 100644 clang/test/Refactor/Rename/VariableMacro.cpp create mode 100644 clang/test/Refactor/Rename/invalid-indexed-name.m create mode 100644 clang/test/Refactor/Rename/invalid-name.cpp create mode 100644 clang/test/Refactor/Rename/invalid-name.m create mode 100644 clang/test/Refactor/Rename/rename-indexed-file.cpp create mode 100644 clang/test/Refactor/Rename/rename-initiate-usr.cpp create mode 100644 clang/test/Refactor/Rename/rename-initiate.cpp create mode 100644 clang/test/Refactor/list-refactoring-actions.cpp create mode 100644 clang/tools/clang-refactor-test/CMakeLists.txt create mode 100644 clang/tools/clang-refactor-test/ClangRefactorTest.cpp create mode 100644 clang/tools/libclang/CRefactor.cpp diff --git a/clang/include/clang-c/CXErrorCode.h b/clang/include/clang-c/CXErrorCode.h index aff73b746763b..9bee50b4b741f 100644 --- a/clang/include/clang-c/CXErrorCode.h +++ b/clang/include/clang-c/CXErrorCode.h @@ -54,7 +54,25 @@ enum CXErrorCode { /** * \brief An AST deserialization error has occurred. */ - CXError_ASTReadError = 4 + CXError_ASTReadError = 4, + + /** + * \brief A refactoring action is not available at the given location + * or in the given source range. + */ + CXError_RefactoringActionUnavailable = 5, + + /** + * \brief A refactoring action is not able to use the given name because + * it contains an unexpected number of strings. + */ + CXError_RefactoringNameSizeMismatch = 6, + + /** + * \brief A name of a symbol is invalid, i.e. it is reserved by the source + * language and can't be used as a name for this symbol. + */ + CXError_RefactoringNameInvalid = 7 }; #ifdef __cplusplus diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h new file mode 100644 index 0000000000000..869f562726b6d --- /dev/null +++ b/clang/include/clang-c/Refactor.h @@ -0,0 +1,1288 @@ +/*==-- clang-c/Refactor.h - Refactoring Public C Interface --------*- C -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a public inferface to a Clang library for performing *| +|* refactoring actions on projects without exposing the full Clang C++ API. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_REFACTOR_H +#define LLVM_CLANG_C_REFACTOR_H + +#include "clang-c/Index.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup CINDEX_REFACTOR Refactoring options. + * + * @{ + */ + +/** + * \brief The refactoring options that can be specified for each refactoring + * action. + */ +enum CXRefactoringOption { + /** + * \brief The refactoring actions like 'rename' will avoid looking for + * occurrences of the renamed symbol in comments if this option is enabled. + */ + CXRefactorOption_AvoidTextualMatches = 1 +}; + +/** + * \brief Opaque pointer representing a set of options that can be given to + * a refactoring action. + */ +typedef void *CXRefactoringOptionSet; + +/** + * \brief Returns a new option set. + */ +CINDEX_LINKAGE +CXRefactoringOptionSet clang_RefactoringOptionSet_create(); + +/** + * \brief Parses and returns a new option set or NULL if the given string is + * invalid. + */ +CINDEX_LINKAGE +CXRefactoringOptionSet +clang_RefactoringOptionSet_createFromString(const char *String); + +/** + * \brief Adds a new option to the given refactoring option set. + */ +CINDEX_LINKAGE +void clang_RefactoringOptionSet_add(CXRefactoringOptionSet Set, + enum CXRefactoringOption Option); + +/** + * \brief Converts the given refactoring option set to a string value. + */ +CINDEX_LINKAGE +CXString clang_RefactoringOptionSet_toString(CXRefactoringOptionSet Set); + +/** + * \brief Free the given option set. + * + * Option sets should be freed by this function only when they were created + * using the \c clang_RefactoringOptionSet_create* methods. + */ +CINDEX_LINKAGE +void clang_RefactoringOptionSet_dispose(CXRefactoringOptionSet Set); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR Refactoring actions. + * + * @{ + */ + +/** + * \brief The refactoring actions that can be performed by libclang. + */ +enum CXRefactoringActionType { + /** + * \brief The 'rename' refactoring action. + */ + CXRefactor_Rename = 0, + + /** + * \brief The local 'rename' refactoring action. + */ + CXRefactor_Rename_Local = 1, + + /** + * \brief The 'extract' refactoring action extracts source code into a + * new function. + */ + CXRefactor_Extract = 2, + + /** + * \brief The sub-action of 'extract' that extracts source code into a new + * method. + */ + CXRefactor_Extract_Method = 3, + + /** + * \brief The action that converts an if/else constructs to a switch block. + */ + CXRefactor_IfSwitchConversion = 4, + + /** + * \brief The action that wraps an Objective-C string literal in an + * NSLocalizedString macro. + */ + CXRefactor_LocalizeObjCStringLiteral = 5, + + /** + * \brief The action that adds missing switch cases to an switch over an enum. + */ + CXRefactor_FillInEnumSwitchCases = 6, + + /** + * \brief The action that adds missing protocol methods to an Objective-C + * class. + */ + CXRefactor_FillInMissingProtocolStubs = 7, + + /** + * \brief The action that extracts an expression that's repeated in a function + * into a new variable. + */ + CXRefactor_ExtractRepeatedExpressionIntoVariable = 8, + + /** + * \brief The action that adds missing abstract class method overrides to a + * class. + */ + CXRefactor_FillInMissingMethodStubsFromAbstractClasses = 9, + + /** + * \brief The action that generates dummy method definitions for method + * declarations without respective definitions. + */ + CXRefactor_ImplementDeclaredMethods = 10, +}; + +/** + * \brief Return the name of the given refactoring action. + */ +CINDEX_LINKAGE +CXString +clang_RefactoringActionType_getName(enum CXRefactoringActionType Action); + +/** + * \brief A set of refactoring actions that can be performed at some specific + * location in a source file. + * + * The actions in the action set are ordered by their priority: most important + * actions are placed before the less important ones. + */ +typedef struct { + const enum CXRefactoringActionType *Actions; + unsigned NumActions; +} CXRefactoringActionSet; + +/** + * \brief Free the given refactoring action set. + */ +CINDEX_LINKAGE void +clang_RefactoringActionSet_dispose(CXRefactoringActionSet *Set); + +typedef struct { + enum CXRefactoringActionType Action; + /** + * \brief The set of diagnostics that describes the reason why this action + * couldn't be initiated. This set of diagnostics is managed by the + * \c CXRefactoringActionSetWithDiagnostics and shouldn't be freed manually. + */ + CXDiagnosticSet Diagnostics; +} CXRefactoringActionWithDiagnostics; + +/** + * \brief A set of refactoring actions that couldn't be initiated at some + * location and their respective diagnostics that describe the reason why + * the initiation failed. + */ +typedef struct { + CXRefactoringActionWithDiagnostics *Actions; + unsigned NumActions; +} CXRefactoringActionSetWithDiagnostics; + +/** + * \brief Free the given refactoring action set with diagnostics. + */ +CINDEX_LINKAGE void clang_RefactoringActionSetWithDiagnostics_dispose( + CXRefactoringActionSetWithDiagnostics *Set); + +/** + * \brief Find the set of refactoring actions that can be performed at the given + * location. + * + * This function examines the AST around the given source range and creates a + * \c CXRefactoringActionSet that contains all of the actions that can be + * performed in the given source range. + * + * \param TU The translation unit which contains the given source range. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param Options The optional refactoring options that might influence the way + * the search is performed. + * + * \param[out] OutSet A non-NULL pointer to store the created + * \c CXRefactoringActionSet. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there are no actions available in the given range, or an error code + * otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_Refactoring_findActionsAt(CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, + CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet); + +/** + * \brief Find the set of refactoring actions that can be performed at the given + * location. + * + * This function examines the AST around the given source range and creates a + * \c CXRefactoringActionSet that contains all of the actions that can be + * performed in the given source range. It also creates a + * \c CXRefactoringActionSetWithDiagnostics that might describe the reason why + * some refactoring actions are not be available. + * + * \param TU The translation unit which contains the given source range. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param Options The optional refactoring options that might influence the way + * the search is performed. + * + * \param[out] OutSet A non-NULL pointer to store the created + * \c CXRefactoringActionSet. + * + * \param[out] OutFailureSet An optional pointer to store the created + * \c CXRefactoringActionSetWithDiagnostics that describes the failures reasons + * for some of the refactoring actions. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there are no actions available in the given range, or an error code + * otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet, + CXRefactoringActionSetWithDiagnostics *OutFailureSet); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_INITIATE Refactoring initiation + * + * @{ + */ + +/** + * \brief Opaque pointer representing the initiated refactoring action. + */ +typedef void *CXRefactoringAction; + +/** + * \brief Free the given refactoring action. + * + * The refactoring action should be freed before the initiation and/or + * implementation translation units. + */ +CINDEX_LINKAGE void clang_RefactoringAction_dispose(CXRefactoringAction Action); + +/** + * \brief Return the source range that's associated with the initiated + * refactoring action. + * + * The returned source range covers the source that will be modified by the + * given refactoring action. If the action has no associated source range, + * then this function will return a null \c CXSourceRange. + */ +CINDEX_LINKAGE CXSourceRange +clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action); + +/** + * \brief Return the type of the initiated action, which might be different + * to the type of the requested action. For an operation 'rename', the action + * could actually initiate the local 'rename' operation. + */ +CINDEX_LINKAGE +enum CXRefactoringActionType +clang_RefactoringAction_getInitiatedActionType(CXRefactoringAction Action); + +/** + * \brief Return a non-zero value when the refactoring action requires access + * to an additional translation unit that contains an implementation of some + * declaration. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +int clang_RefactoringAction_requiresImplementationTU( + CXRefactoringAction Action); + +/** + * \brief Return a USR that corresponds to the declaration whose implementation + * is required in order for the given refactoring action to work correctly. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +CXString clang_RefactoringAction_getUSRThatRequiresImplementationTU( + CXRefactoringAction Action); + +/** + * \brief Set the translation unit that contains the declaration whose + * implementation is required for the given refactoring action to work + * correctly. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringAction_addImplementationTU(CXRefactoringAction Action, + CXTranslationUnit TU); + +/** + * \brief A refactoring candidate determines on which piece of source code the + * action should be applied. + * + * Most refactoring actions have just one candidate, but some actions, like + * 'Extract' can produce multiple candidates. + * + * The candidates are managed by the refactoring action, and their description + * string doesn't need to be freed manually. + */ +typedef struct { CXString Description; } CXRefactoringCandidate; + +/** + * \brief A set of refactoring candidates on which the previously initiatied + * refactoring action can be performed. + * + * The candidates in the candidate set are ordered by their priority: the + * ones that are more likely to be selected are placed before the other ones. + * + * A non-empty refactoring candidate set always has more than one refactoring + * candidate, because when a refactoring action has just one candidate, + * \c clang_RefactoringAction_getRefactoringCandidates will return an empty + * candidate set. + */ +typedef struct { + const CXRefactoringCandidate *Candidates; + unsigned NumCandidates; +} CXRefactoringCandidateSet; + +/** + * \brief Returns the given action's refactoring candidates. + * + * The resulting refactoring candidate set will be empty when the given \c + * CXRefactoringAction has just one refactoring candidate. + * + * \param Action A previously initiated \c CXRefactoringAction. + * + * \param[out] OutRefactoringCandidateSet An pointer to store the action's + * refactoring candidate set. + * + * \returns Zero on success, or an error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_RefactoringAction_getRefactoringCandidates( + CXRefactoringAction Action, + CXRefactoringCandidateSet *OutRefactoringCandidateSet); + +/** + * \brief Tells the given refactoring action that it has to perform the + * operation on the refactoring candidate that's located at \p Index in the \c + * CXRefactoringCandidateSet. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringAction_selectRefactoringCandidate(CXRefactoringAction Action, + unsigned Index); + +// TODO: Remove. +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateActionAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXString *OutFailureReason); + +/** + * \brief Initiate a specific refactoring action at the given location. + * + * This function initiates an \p ActionType refactoring action when it can + * be initiated at the given location and creates a \c CXRefactoringAction + * action that will allow the control. + * + * \param TU The translation unit in which the action should be initiated. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param ActionType The type of action that should be initiated. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutAction A non-NULL pointer to store the created + * \c CXRefactoringAction. + * + * \param[out] OutDiagnostics An optional pointer to store any diagnostics that + * describe why the action wasn't initiated. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * the given refactoring action can't be performed at the given location, or an + * error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateAction( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXDiagnosticSet *OutDiagnostics); + +/** + * \brief Initiate a specific refactoring action on a particular declaration. + * + * This function searches for the declaration that corresponds to \p DeclUSR + * and initiates an \p ActionType a refactoring action on that declaration + * if possible. + * + * \param TU The translation unit in which the declaration is defined. + * + * \param DeclUSR The USR that corresponds to the declaration of interest. + * + * \param ActionType The type of action that should be initiated. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutAction A non-NULL pointer to store the created + * \c CXRefactoringAction. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * the given refactoring action can't be performed on the found declaration, or + * an error code otherwise. + */ +// TODO: Remove (not needed). +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateActionOnDecl( + CXTranslationUnit TU, const char *DeclUSR, + enum CXRefactoringActionType ActionType, CXRefactoringOptionSet Options, + CXRefactoringAction *OutAction, CXString *OutFailureReason); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_REPLACEMENT Refactoring replacement + * + * @{ + */ + +/** + * \brief A source location in a single file that is independent of \c + * CXTranslationUnit. + */ +typedef struct { unsigned Line, Column; } CXFileLocation; + +/** + * \brief A source range in a single file that is independent of \c + * CXTranslationUnit. + */ +typedef struct { CXFileLocation Begin, End; } CXFileRange; + +// TODO: Remove +typedef struct { + CXFileRange Range; + CXString ReplacementString; +} CXRefactoringReplacement_Old; + +// TODO: Remove +typedef struct { + CXString Filename; + const CXRefactoringReplacement_Old *Replacements; + unsigned NumReplacements; +} CXRefactoringFileReplacementSet_Old; + +// TODO: Remove +typedef struct { + const CXRefactoringFileReplacementSet_Old *FileReplacementSets; + unsigned NumFileReplacementSets; +} CXRefactoringReplacements_Old; + +/** + * \brief Identifies a character range in the source code of a single file that + * should be replaced with the replacement string. + * + * Replacements are managed by the result of a specific refactoring action, + * like \c CXRenamingResult, and are invalidated when the refactoring result is + * destroyed. + */ +typedef struct { + CXFileRange Range; + CXString ReplacementString; + void *AssociatedData; +} CXRefactoringReplacement; + +/** +* \brief A set of refactoring replacements that are applicable to a certain + * file. + */ +typedef struct { + CXString Filename; + const CXRefactoringReplacement *Replacements; + unsigned NumReplacements; +} CXRefactoringFileReplacementSet; + +/** + * \brief A set of refactoring replacements that have been produced by a + * refactoring operation. + * + * The refactoring replacements depend on \c CXRefactoringResult, and can't be + * used after the refactoring result is freed. + */ +typedef struct { + const CXRefactoringFileReplacementSet *FileReplacementSets; + unsigned NumFileReplacementSets; +} CXRefactoringReplacements; + +/** + * @} + */ + +/** + * \defgroup CINDEX_SYMBOL_OPERATION Symbol-based refactoring operation + * (e.g. Rename). + * + * @{ + */ + +/** + * \brief The type of a symbol occurrence. + * + * The occurrence kind determines if an occurrence can be renamed automatically + * or if the user has to make the decision whether or not this occurrence + * should be renamed. + */ +enum CXSymbolOccurrenceKind { + /** + * \brief This occurrence is an exact match and can be renamed automatically. + */ + CXSymbolOccurrence_MatchingSymbol = 0, + + /** + * \brief This is an occurrence of a matching selector. It can't be renamed + * automatically unless the indexer proves that this selector refers only + * to the declarations that correspond to the renamed symbol. + */ + CXSymbolOccurrence_MatchingSelector = 1, + + /** + * \brief This is an occurrence of an implicit property that uses the + * renamed method. + */ + CXSymbolOccurrence_MatchingImplicitProperty = 2, + + /** + * \brief This is an occurrence of an symbol name in a comment. + */ + CXSymbolOccurrence_MatchingCommentString = 3, + + /** + * \brief This is an occurrence of an symbol name in a documentation comment. + */ + CXSymbolOccurrence_MatchingDocCommentString = 4, + + /** + * \brief This is an occurrence of an symbol name in a filename in an inclusion + * directive. + */ + CXSymbolOccurrence_MatchingFilename = 5, + + /** + * \brief This is an occurrence of a symbol name that belongs to the extracted + * declaration. Note: this occurrence can be in two replacements as we might + * extract an out-of-line method that will be both declared and defined. + */ + CXSymbolOccurrence_ExtractedDeclaration = 100, + + /** + * \brief This is an occurrence of a symbol name that references the extracted + * declaration. + */ + CXSymbolOccurrence_ExtractedDeclaration_Reference = 101, +}; + +// TODO: Remove +typedef struct { + const CXRefactoringReplacement_Old *Replacements; + unsigned ReplacementCount; + enum CXSymbolOccurrenceKind Kind; + /** + * Whether or not this occurrence is inside a macro. When this is true, the + * replacements of the occurrence contain just a single empty replacement that + * points to the location of the macro expansion. + */ + int IsMacroExpansion; +} CXRenamedSymbolOccurrence; + +/** + * \brief An occurrence of a symbol. + * + * Contains the source ranges that represent the pieces of the name of the + * symbol. The occurrences are managed by \c CXRenamingResult, and are + * invalidated when \c CXRenamingResult is destroyed. + */ +typedef struct { + const CXFileRange *NamePieces; + unsigned NumNamePieces; + enum CXSymbolOccurrenceKind Kind; + /** + * Whether or not this occurrence is inside a macro. When this is true, the + * replacements of the occurrence contain just a single empty replacement that + * points to the location of the macro expansion. + */ + int IsMacroExpansion; + unsigned SymbolIndex; +} CXSymbolOccurrence; + +// TODO: Remove +typedef struct { + CXString Filename; + const CXRenamedSymbolOccurrence *Occurrences; + unsigned NumOccurrences; +} CXFileRenamingResult; // TODO: Remove + +/** +* \brief A set of symbol occurrences that occur in a single file. + */ +typedef struct { + CXString Filename; + /** + * The set of occurrences for each symbol of interest. + */ + const CXSymbolOccurrence *Occurrences; + unsigned NumOccurrences; +} CXSymbolOccurrencesInFile; + +/** + * \brief Opaque pointer representing all of the renames that should take place + * in a single translation unit. + * + * The result of a renaming action is indepedent from \c CXRenamingAction, and + * remains valid after \c CXRenamingAction is destroyed. + */ +typedef void *CXRenamingResult; + +/** + * \brief Opaque pointer representing all of the symbol occurrences from a + * single TU/file. + * + * The result of a symbol search occurrence search operation is indepedent from + * \c CXRefactoringAction, and remains valid after \c CXRefactoringAction is + * destroyed. + */ +typedef void *CXSymbolOccurrencesResult; + +/** + * \brief Find the cursor that's being renamed at the given location. + * + * \param TU The translation unit in which the cursor is present. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there's no suitable cursor at the given location, or an error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findRenamedCursor( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXCursor *OutCursor); + +/** + * \brief Initiates a renaming operation on a previously initiated refactoring + * action. + * + * The initiation process finds the symbols that have to be renamed for a + * previously initiated \c CXRefactor_Rename refactoring action. + * + * \returns Zero on success, or an error code otherwise. + */ +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode +clang_Refactoring_initiateRenamingOperation(CXRefactoringAction Action); + +/** + * \brief Set the new name of the renamed symbol in the given \c + * RenamingAction. + * + * \returns Zero on success, CXError_RefactoringNameInvalid when the new name + * isn't a valid identifier, CXError_RefactoringNameSizeMismatch when the new + * name has an incorrect number of pieces or a different error code otherwise. + */ +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode clang_RenamingOperation_setNewName(CXRefactoringAction Action, + const char *NewName); + +/** + * \brief Return the number of symbols that are renamed by the given renaming + * action. + * + * A renaming action typically works on just one symbol. However, there are + * certain language constructs that require work with more than one symbol in + * order for them to be renamed correctly. Property declarations in Objective-C + * are the perfect example: in addition to the actual property, the action has + * to rename the corresponding getters and setters, as well as the backing ivar. + */ +// TODO: Remove +CINDEX_LINKAGE +unsigned clang_RenamingOperation_getNumSymbols(CXRefactoringAction Action); + +/** + * \brief Return the USR of the declaration that was found for the symbol at the + * given \p Index in the given renaming action. + */ +// TODO: Remove +CINDEX_LINKAGE +CXString clang_RenamingOperation_getUSRForSymbol(CXRefactoringAction Action, + unsigned Index); + +// TODO: Remove +CINDEX_LINKAGE +CXRenamingResult clang_Refactoring_findRenamedOccurrencesInPrimaryTUs( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles); + +/** + * \brief Find all of the occurrences of the symbol that is being searched for + * by the given refactoring action in the translation unit that was used to + * initiate the refactoring action. + * + * This function searches for all of the \c CXSymbolOccurrence in the + * translation units that are referenced by the given \c CXRefactoringAction by + * iterating through the AST of the each translation unit. The occurrences that + * are found don't have to be from the main file in the translation unit, they + * can be from files included in that translation unit. + * + * \param Action The \c CXRefactoringAction operation that was inititated by + * \c clang_Refactoring_initiateActionAt(). + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \returns If successful, a new \c CXSymbolOccurrencesResult structure + * containing the occurrences of the symbol in the initiation translation unit, + * which should eventually be freed with \c clang_SymbolOccurrences_dispose(). + * If symbol search fails, returns NULL. + */ +CINDEX_LINKAGE +CXSymbolOccurrencesResult clang_Refactoring_findSymbolOccurrencesInInitiationTU( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles); + +// TODO: Remove +typedef struct { + CXFileLocation Location; + /** + * The kind of the declaration/expression that was indexed at this location. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following indexed + * occurrences: + * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC method message send: CXCursor_ObjCMessageExpr + * Other occurrences can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; +} CXRenamedIndexedSymbolLocation; + +// TODO: Remove +typedef struct { + /** + * An array of occurrences that represent indexed occurrences of a symbol. + * It's valid to pass-in no indexed locations, the refactoring engine will + * just perform textual search in that case. + */ + const CXRenamedIndexedSymbolLocation *IndexedLocations; + unsigned IndexedLocationCount; + /** + * The kind of the declaration that is being renamed. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following renamed + * declaration: + * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl + * Other declarations can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; + const char *Name; + const char *NewName; +} CXRenamedIndexedSymbol; + +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findRenamedOccurrencesInIndexedFile( + const CXRenamedIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXRenamingResult *OutResult); + +/** + * \brief A location of an already known occurrence of a symbol. + * + * Used for rename-indexed operation where the renaming is performed on an + * already indexed source file. + */ +typedef struct { + CXFileLocation Location; + /** + * The kind of the declaration/expression that was indexed at this location. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following indexed + * occurrences: + * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC method message send: CXCursor_ObjCMessageExpr + * - filename in an #include: CXCursor_InclusionDirective + * Other occurrences can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; +} CXIndexedSymbolLocation; + +/** + * \brief A symbol that should be found the an indexer symbol search operation. + * + * Used for rename-indexed operation where the renaming is performed on an + * already indexed source file. + */ +typedef struct { + /** + * An array of occurrences that represent indexed occurrences of a symbol. + * It's valid to pass-in no indexed locations, the refactoring engine will + * just perform textual search in that case. + */ + const CXIndexedSymbolLocation *IndexedLocations; + unsigned IndexedLocationCount; + /** + * The kind of the declaration that is being renamed. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following renamed + * declaration: + * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl + * Other declarations can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; + /** + * The name of the symbol. Objective-C selector names should be specified + * using the ':' separator for selector pieces. + */ + const char *Name; +} CXIndexedSymbol; + +/** + * \brief Find all of the occurrences of a symbol in an indexed file. + * + * This function searches for all of the \c CXIndexedSymbol in the + * given file by inspecting the source code at the given indexed locations. + * + * The indexed operations are thread-safe and can be performed concurrently. + * + * \param Symbols The information about the symbols that includes the locations + * for a symbol in the file as determined by the indexer. + * + * \param NumSymbols The number of symbols in \p Symbols. + * + * \param CIdx The index object with which the translation unit will be + * associated. + * + * \param Filename The name of the source file that contains the given + * \p Locations. + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * These command-line options will be parsed and will affect how the translation + * unit is parsed. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutResult A non-NULL pointer to store the created + * \c CXSymbolOccurrencesResult. + * + * \returns Zero on success, or a different error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findSymbolOccurrencesInIndexedFile( + const CXIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXSymbolOccurrencesResult *OutResult); + +// TODO: Remove +CINDEX_LINKAGE +unsigned clang_RenamingResult_getNumModifiedFiles(CXRenamingResult Result); + +// TODO: Remove +CINDEX_LINKAGE +void clang_RenamingResult_getResultForFile(CXRenamingResult Result, + unsigned FileIndex, + CXFileRenamingResult *OutResult); + +// TODO: Remove +CINDEX_LINKAGE +void clang_RenamingResult_dispose(CXRenamingResult Result); + +/** + * \brief Return the number of files that have occurrences of the specific + * symbol. + */ +CINDEX_LINKAGE +unsigned clang_SymbolOccurrences_getNumFiles(CXSymbolOccurrencesResult Result); + +/** + * \brief Return the set of symbol occurrences in a single file. + * + * The resulting \c CXSymbolOccurrencesInFile is managed by the + * \c CXSymbolOccurrencesResult and doesn't have to be disposed of manually. + */ +CINDEX_LINKAGE +void clang_SymbolOccurrences_getOccurrencesForFile( + CXSymbolOccurrencesResult Result, unsigned FileIndex, + CXSymbolOccurrencesInFile *OutResult); + +// TODO: Support refactoring continuations for \c CXSymbolOccurrencesResult, +// e.g. for function parameter name rename. + +/** + * \brief Free the given symbol occurrences result. + */ +CINDEX_LINKAGE +void clang_SymbolOccurrences_dispose(CXSymbolOccurrencesResult Result); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_PERFORM Performing refactoring operations. + * + * @{ + */ + +/** + * \brief Opaque pointer representing the results of the refactoring operation. + * + * The result of a refactoring action depends on the \c CXRefactoringAction, and + * is invalidated after \c CXRefactoringAction is destroyed. + */ +typedef void *CXRefactoringResult; + +/** + * \brief Opaque pointer representing a refactoring continuation. + * + * Refactoring continuations allow refactoring operations to run in external + * AST units with some results that were obtained after querying the indexer. + * + * The refactoring continuation is not dependent on the \c CXRefactoringAction + * or \c CXRefactoringResult. It does depend on the initiation + * \c CXTranslationUnit initially, but that dependency can be terminated. + */ +typedef void *CXRefactoringContinuation; + +/** + * \brief Opaque pointer representing a query to the indexer. + */ +typedef void *CXIndexerQuery; + +/** + * \brief Performs the previously initiated refactoring operation. + * + * This function executes the refactoring operation which produces a set of + * candidate source replacements that can be applied to the source files. + * + * \param Action The refactoring action. + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * These command-line options will be parsed and will affect how the translation + * unit is parsed. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \param Options The optional refactoring options that might have an influence + * on the way the particular action will be performed. + * + * \param[out] OutFailureReason An optional pointer to store a message that + * describes why the action wasn't performed. + * + * \returns If successful, a new \c CXRefactoringResult structure containing the + * source replacement candidates, which should eventually be freed with + * \c clang_RefactoringResult_dispose(). If the refactoring operation fails, + * returns NULL. + */ +CINDEX_LINKAGE +CXRefactoringResult clang_Refactoring_performOperation( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXString *OutFailureReason); + +// TODO: Remove. This is the deprecated API. +CINDEX_LINKAGE +void clang_RefactoringResult_getReplacements( + CXRefactoringResult Result, CXRefactoringReplacements_Old *OutReplacements); + +/** + * \brief Return the set of refactoring source replacements. + * + * The resulting \c CXRefactoringReplacements are managed by the + * \c CXRefactoringResult and don't have to be disposed of manually. + */ +CINDEX_LINKAGE +CXRefactoringReplacements +clang_RefactoringResult_getSourceReplacements(CXRefactoringResult Result); + +/** + * \brief Represents a set of symbol occurrences that are associated with a + * single refactoring replacement. + * + * The symbol occurrences depend on \c CXRefactoringResult, and can't be + * used after the refactoring result is freed. + */ +typedef struct { + const CXSymbolOccurrence *AssociatedSymbolOccurrences; + unsigned NumAssociatedSymbolOccurrences; +} CXRefactoringReplacementAssociatedSymbolOccurrences; + +/** + * \brief Return the set of symbol occurrences that are associated with the + * given \p Replacement. + */ +CXRefactoringReplacementAssociatedSymbolOccurrences +clang_RefactoringReplacement_getAssociatedSymbolOccurrences( + CXRefactoringReplacement Replacement); + +/** + * \brief Returns the refactoring continuation associated with this result, or + * NULL if this result has no refactoring continuation. + */ +CINDEX_LINKAGE +CXRefactoringContinuation +clang_RefactoringResult_getContinuation(CXRefactoringResult Result); + +/** + * \brief Free the given refactoring result. + */ +CINDEX_LINKAGE +void clang_RefactoringResult_dispose(CXRefactoringResult Result); + +/** + * \brief Load the indexer query results from a YAML string. + * + * Mainly used for testing. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringContinuation_loadSerializedIndexerQueryResults( + CXRefactoringContinuation Continuation, const char *Source); + +/** + * \brief Return the number of indexer queries that a refactoring continuation + * has. + */ +CINDEX_LINKAGE +unsigned clang_RefactoringContinuation_getNumIndexerQueries( + CXRefactoringContinuation Continuation); + +/** + * \brief Return the indexer query at index \p Index. + */ +CINDEX_LINKAGE +CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery( + CXRefactoringContinuation Continuation, unsigned Index); + +/** + * \brief Terminate the connection between the initiation TU and the refactoring + * continuation. + * + * The continuation converts all the TU-specific state to TU-independent state. + * The indexer queries that are associate with this continuation are also + * invalidated. + */ +CINDEX_LINKAGE +void clang_RefactoringContinuation_finalizeEvaluationInInitationTU( + CXRefactoringContinuation Continuation); + +/** + * \brief Continue performing the previously initiated and performed refactoring + * operation in the given translation unit \p TU. + */ +CINDEX_LINKAGE +CXRefactoringResult clang_RefactoringContinuation_continueOperationInTU( + CXRefactoringContinuation Continuation, CXTranslationUnit TU, + CXString *OutFailureReason); + +/** + * \brief Free the given refactoring continuation. + */ +CINDEX_LINKAGE +void clang_RefactoringContinuation_dispose( + CXRefactoringContinuation Continuation); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_INDEXER_QUERY Indexer Queries. + * + * @{ + */ + +/** + * \brief The types of indexer queries. + */ +enum CXIndexerQueryKind { + CXIndexerQuery_Unknown = 0, + + /** + * \brief The indexer should find the file that contains/should contain the + * implementation of some declaration. + * A file result is expected. + */ + CXIndexerQuery_Decl_FileThatShouldImplement = 1, + + /** + * \brief The indexer should determine if the some declaration is defined. + * An integer result is expected. + */ + CXIndexerQuery_Decl_IsDefined = 2, +}; + +/** + * \brief Return the kind of the indexer query \p Query. + */ +CINDEX_LINKAGE +enum CXIndexerQueryKind clang_IndexerQuery_getKind(CXIndexerQuery Query); + +/** + * \brief Return the number of cursors that the \p Query has. + */ +CINDEX_LINKAGE +unsigned clang_IndexerQuery_getNumCursors(CXIndexerQuery Query); + +/** + * \brief Return the cursor at the given \p CursorIndex. + */ +CINDEX_LINKAGE +CXCursor clang_IndexerQuery_getCursor(CXIndexerQuery Query, + unsigned CursorIndex); + +/** + * \brief The action that the indexer should take after evaluating the query. + */ +enum CXIndexerQueryAction { + /** + * \brief This result requires no further action. + */ + CXIndexerQueryAction_None = 0, + + /** + * \brief The indexer should run the \c CXRefactoringContinuaton in a + * translation unit that contains this file. + */ + CXIndexerQueryAction_RunContinuationInTUThatHasThisFile = 1, +}; + +/** + * \brief Consumes an integer/boolean query result. + */ +CINDEX_LINKAGE +enum CXIndexerQueryAction +clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex, + int Value); + +/** + * \brief Consumes a filename query result. + * + * This function may return + * \c CXIndexerQueryAction_RunContinuationInTUThatHasThisFile which + * should tell the indexer that it has to run the refactoring continuation in + * the TU that contains this file. + */ +CINDEX_LINKAGE +enum CXIndexerQueryAction +clang_IndexerQuery_consumeFileResult(CXIndexerQuery Query, unsigned CursorIndex, + const char *Filename); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LLVM_CLANG_C_REFACTOR_H */ diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 26c0cbe82d176..d287b910a92d0 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -858,6 +858,11 @@ class ObjCPropertyDecl : public NamedDecl { return Assign; } + /// Return true if this property has an explicitly specified getter name. + bool hasExplicitGetterName() const { + return (PropertyAttributes & OBJC_PR_getter); + } + Selector getGetterName() const { return GetterName; } SourceLocation getGetterNameLoc() const { return GetterNameLoc; } void setGetterName(Selector Sel, SourceLocation Loc = SourceLocation()) { @@ -865,6 +870,11 @@ class ObjCPropertyDecl : public NamedDecl { GetterNameLoc = Loc; } + /// Return true if this property has an explicitly specified setter name. + bool hasExplicitSetterName() const { + return (PropertyAttributes & OBJC_PR_setter); + } + Selector getSetterName() const { return SetterName; } SourceLocation getSetterNameLoc() const { return SetterNameLoc; } void setSetterName(Selector Sel, SourceLocation Loc = SourceLocation()) { @@ -2617,14 +2627,23 @@ class ObjCCompatibleAliasDecl : public NamedDecl { void anchor() override; /// Class that this is an alias of. ObjCInterfaceDecl *AliasedClass; + /// The location of the name of the referenced class. + SourceLocation AliasedClassLoc; + /// The location of the '@'. + SourceLocation AtLoc; + + ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation NameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass, + SourceLocation AliasedClassLoc, SourceLocation AtLoc) + : NamedDecl(ObjCCompatibleAlias, DC, NameLoc, Id), + AliasedClass(AliasedClass), AliasedClassLoc(AliasedClassLoc), + AtLoc(AtLoc) {} - ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id, - ObjCInterfaceDecl* aliasedClass) - : NamedDecl(ObjCCompatibleAlias, DC, L, Id), AliasedClass(aliasedClass) {} public: - static ObjCCompatibleAliasDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id, - ObjCInterfaceDecl* aliasedClass); + static ObjCCompatibleAliasDecl * + Create(ASTContext &C, DeclContext *DC, SourceLocation NameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass, + SourceLocation AliasedClassLoc, SourceLocation AtLoc); static ObjCCompatibleAliasDecl *CreateDeserialized(ASTContext &C, unsigned ID); @@ -2633,6 +2652,17 @@ class ObjCCompatibleAliasDecl : public NamedDecl { ObjCInterfaceDecl *getClassInterface() { return AliasedClass; } void setClassInterface(ObjCInterfaceDecl *D) { AliasedClass = D; } + SourceLocation getClassInterfaceLoc() const { return AliasedClassLoc; } + + void setClassInterfaceLoc(SourceLocation Loc) { AliasedClassLoc = Loc; } + + SourceLocation getAtLoc() const { return AtLoc; } + void setAtLoc(SourceLocation Loc) { AtLoc = Loc; } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(AtLoc, AtLoc); + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCCompatibleAlias; } diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 2879452f24046..de5342568114f 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -157,6 +157,9 @@ class TemplateParameterList final return SourceRange(TemplateLoc, RAngleLoc); } + void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy, + unsigned Indentation = 0) const; + friend TrailingObjects; template <size_t N, bool HasRequiresClause> diff --git a/clang/include/clang/AST/DependentASTVisitor.h b/clang/include/clang/AST/DependentASTVisitor.h new file mode 100644 index 0000000000000..4177344f0ae75 --- /dev/null +++ b/clang/include/clang/AST/DependentASTVisitor.h @@ -0,0 +1,91 @@ +//===--- DependentASTVisitor.h - Helper for dependent nodes -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the DependentASTVisitor RecursiveASTVisitor layer, which +// is responsible for visiting unresolved symbol references. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H +#define LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" + +namespace clang { + +// TODO: Use in the indexer. +template <typename Derived> +class DependentASTVisitor : public RecursiveASTVisitor<Derived> { +private: + bool visitDependentReference( + const Type *T, const DeclarationName &Name, SourceLocation Loc, + llvm::function_ref<bool(const NamedDecl *ND)> Filter) { + if (!T) + return true; + const TemplateSpecializationType *TST = + T->getAs<TemplateSpecializationType>(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + if (!RD->hasDefinition()) + return true; + RD = RD->getDefinition(); + std::vector<const NamedDecl *> Symbols = + RD->lookupDependentName(Name, Filter); + // FIXME: Improve overload handling. + if (Symbols.size() != 1) + return true; + if (Loc.isInvalid()) + return true; + return RecursiveASTVisitor<Derived>::getDerived() + .VisitDependentSymbolReference(Symbols[0], Loc); + } + +public: + bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) { + const DeclarationNameInfo &Info = E->getMemberNameInfo(); + return visitDependentReference( + E->getBaseType().getTypePtrOrNull(), Info.getName(), Info.getLoc(), + [](const NamedDecl *D) { return D->isCXXInstanceMember(); }); + } + + bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) { + const DeclarationNameInfo &Info = E->getNameInfo(); + const NestedNameSpecifier *NNS = E->getQualifier(); + return visitDependentReference( + NNS->getAsType(), Info.getName(), Info.getLoc(), + [](const NamedDecl *D) { return !D->isCXXInstanceMember(); }); + } + + bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) { + const DependentNameType *DNT = TL.getTypePtr(); + const NestedNameSpecifier *NNS = DNT->getQualifier(); + DeclarationName Name(DNT->getIdentifier()); + return visitDependentReference( + NNS->getAsType(), Name, TL.getNameLoc(), + [](const NamedDecl *ND) { return isa<TypeDecl>(ND); }); + } + + bool VisitDependentSymbolReference(const NamedDecl *Symbol, + SourceLocation SymbolNameLoc) { + return true; + } +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 274df220e160e..00c40c866f7fa 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -39,7 +39,7 @@ struct PrintingPolicy { /// \brief Create a default printing policy for the specified language. PrintingPolicy(const LangOptions &LO) : Indentation(2), SuppressSpecifiers(false), - SuppressTagKeyword(LO.CPlusPlus), + SupressStorageClassSpecifiers(false), SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false), SuppressScope(false), SuppressUnwrittenScope(false), SuppressInitializers(false), ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), @@ -50,7 +50,8 @@ struct PrintingPolicy { UseVoidForZeroParams(!LO.CPlusPlus), TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), - IncludeNewlines(true), MSVCFormatting(false) { } + IncludeNewlines(true), MSVCFormatting(false), + UseStdFunctionForLambda(false) { } /// \brief Adjust this printing policy for cases where it's known that /// we're printing C++ code (for instance, if AST dumping reaches a @@ -81,6 +82,10 @@ struct PrintingPolicy { /// "const int" type specifier and instead only print the "*y". bool SuppressSpecifiers : 1; + /// \brief Whether we should supress the printing of the actual storage class + /// specifiers for the given declaration. + bool SupressStorageClassSpecifiers : 1; + /// \brief Whether type printing should skip printing the tag keyword. /// /// This is used when printing the inner type of elaborated types, @@ -200,6 +205,9 @@ struct PrintingPolicy { /// prints anonymous namespaces as `anonymous namespace' and does not insert /// spaces after template arguments. bool MSVCFormatting : 1; + + /// \brief Whether we should use std::function<...> for lambda record types. + bool UseStdFunctionForLambda : 1; }; } // end namespace clang diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h index fc861a1952a51..4af321fd23b7c 100644 --- a/clang/include/clang/Basic/AllDiagnostics.h +++ b/clang/include/clang/Basic/AllDiagnostics.h @@ -24,6 +24,7 @@ #include "clang/Parse/ParseDiagnostic.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Serialization/SerializationDiagnostic.h" +#include "clang/Tooling/Core/RefactoringDiagnostic.h" namespace clang { template <size_t SizeOfStr, typename FieldType> diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index 3e0fb8728c483..ced4d63aa1eff 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -15,6 +15,7 @@ clang_diag_gen(Lex) clang_diag_gen(Parse) clang_diag_gen(Sema) clang_diag_gen(Serialization) +clang_diag_gen(Refactoring) clang_tablegen(DiagnosticGroups.inc -gen-clang-diag-groups SOURCE Diagnostic.td TARGET ClangDiagnosticGroups) diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index f25068eca1322..cb87d87837522 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -139,4 +139,4 @@ include "DiagnosticLexKinds.td" include "DiagnosticParseKinds.td" include "DiagnosticSemaKinds.td" include "DiagnosticSerializationKinds.td" - +include "DiagnosticRefactoringKinds.td" diff --git a/clang/include/clang/Basic/DiagnosticIDs.h.rej b/clang/include/clang/Basic/DiagnosticIDs.h.rej new file mode 100644 index 0000000000000..f81c0605abf59 --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticIDs.h.rej @@ -0,0 +1,18 @@ +*************** +*** 37,43 **** + DIAG_START_COMMENT = DIAG_START_AST + 110, + DIAG_START_SEMA = DIAG_START_COMMENT + 100, + DIAG_START_ANALYSIS = DIAG_START_SEMA + 3500, +- DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100 + }; + + class CustomDiagInfo; +--- 37,44 ---- + DIAG_START_COMMENT = DIAG_START_AST + 110, + DIAG_START_SEMA = DIAG_START_COMMENT + 100, + DIAG_START_ANALYSIS = DIAG_START_SEMA + 3500, ++ DIAG_START_REFACTORING = DIAG_START_ANALYSIS + 100, ++ DIAG_UPPER_LIMIT = DIAG_START_REFACTORING + 100 + }; + + class CustomDiagInfo; diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td new file mode 100644 index 0000000000000..6dfd29169923a --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td @@ -0,0 +1,27 @@ +//==--- DiagnosticRefactoringKinds.td - refactoring diagnostics -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Refactoring Diagnostics +//===----------------------------------------------------------------------===// + +let Component = "Refactoring" in { + +let CategoryName = "Rename Issue" in { +def err_rename_builtin_function : Error<"%0 is a builtin function that " + "cannot be renamed">; +def err_rename_sys_header : Error<"%0 cannot be renamed because it is " + "declared in a system header">; +def err_method_rename_override_sys_framework : Error<"method %0 cannot be " + "renamed because it overrides a method declared in a system framework">; +def err_rename_external_source_symbol : Error<"%0 is declared in a %1 file; " + "rename can be initiated in a %1 file only">; +} + +} // end of Refactoring diagnostics diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5b2a8e26baf8d..415288038492f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1175,6 +1175,12 @@ def warn_unimplemented_selector: Warning< InGroup<Selector>, DefaultIgnore; def warn_unimplemented_protocol_method : Warning< "method %0 in protocol %1 not implemented">, InGroup<Protocol>; +def warn_class_does_not_conform_protocol : Warning< + "%select{class|category}0 %1 does not conform to protocol" + "%plural{1: %3|2:s %3 and %4|3:s %3, %4 and %5|:s %3, %4, %5, ...}2">, + InGroup<Protocol>; +def note_add_missing_protocol_stubs : Note< + "add stubs for missing protocol requirements">; def warn_multiple_selectors: Warning< "several methods with selector %0 of mismatched types are found " "for the @selector expression">, @@ -7954,6 +7960,7 @@ def warn_missing_case : Warning<"%plural{" "3:enumeration values %1, %2, and %3 not handled in switch|" ":%0 enumeration values not handled in switch: %1, %2, %3...}0">, InGroup<Switch>; +def note_fill_in_missing_cases : Note<"add missing switch cases">; def warn_unannotated_fallthrough : Warning< "unannotated fall-through between switch labels">, diff --git a/clang/include/clang/Edit/RefactoringFixits.h b/clang/include/clang/Edit/RefactoringFixits.h new file mode 100644 index 0000000000000..bf8adb551e28e --- /dev/null +++ b/clang/include/clang/Edit/RefactoringFixits.h @@ -0,0 +1,66 @@ +//===--- RefactoringFixits.h - Fixit producers for refactorings -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_EDIT_REFACTORING_FIXITS_H +#define LLVM_CLANG_EDIT_REFACTORING_FIXITS_H + +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { + +class ASTContext; +class SwitchStmt; +class EnumDecl; +class ObjCContainerDecl; + +namespace edit { + +/** + * Generates the fix-its that perform the "add missing switch cases" refactoring + * operation. + */ +void fillInMissingSwitchEnumCases( + ASTContext &Context, const SwitchStmt *Switch, const EnumDecl *Enum, + const DeclContext *SwitchContext, + llvm::function_ref<void(const FixItHint &)> Consumer); + +/// Responsible for the fix-its that perform the +/// "add missing protocol requirements" refactoring operation. +namespace fillInMissingProtocolStubs { + +class FillInMissingProtocolStubsImpl; +class FillInMissingProtocolStubs { + std::unique_ptr<FillInMissingProtocolStubsImpl> Impl; + +public: + FillInMissingProtocolStubs(); + ~FillInMissingProtocolStubs(); + FillInMissingProtocolStubs(FillInMissingProtocolStubs &&); + FillInMissingProtocolStubs &operator=(FillInMissingProtocolStubs &&); + + /// Initiate the FillInMissingProtocolStubs edit. + /// + /// \returns true on Error. + bool initiate(ASTContext &Context, const ObjCContainerDecl *Container); + bool hasMissingRequiredMethodStubs(); + void perform(ASTContext &Context, + llvm::function_ref<void(const FixItHint &)> Consumer); +}; + +void addMissingProtocolStubs( + ASTContext &Context, const ObjCContainerDecl *Container, + llvm::function_ref<void(const FixItHint &)> Consumer); + +} // end namespace fillInMissingProtocolStubs + +} // end namespace edit +} // end namespace clang + +#endif // LLVM_CLANG_EDIT_REFACTORING_FIXITS_H diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 02b3855320578..2a229f6c77622 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -462,6 +462,14 @@ class Lexer : public PreprocessorLexer { const LangOptions &LangOpts, bool SkipTrailingWhitespaceAndNewLine); + /// \brief Returns the source location of the token that comes after the + /// token located at the given location \p Loc (excluding any comments and + /// whitespace). The returned source location will be invalid if the location + /// is inside a macro. + static SourceLocation + findNextTokenLocationAfterTokenAt(SourceLocation Loc, const SourceManager &SM, + const LangOptions &LangOpts); + /// \brief Returns true if the given character could appear in an identifier. static bool isIdentifierBodyChar(char c, const LangOptions &LangOpts); diff --git a/clang/include/clang/Tooling/Core/RefactoringDiagnostic.h b/clang/include/clang/Tooling/Core/RefactoringDiagnostic.h new file mode 100644 index 0000000000000..a17926097df0f --- /dev/null +++ b/clang/include/clang/Tooling/Core/RefactoringDiagnostic.h @@ -0,0 +1,29 @@ +//===--- RefactoringDiagnostic.h - ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_CORE_REFACTORINGSTARTDIAGNOSTIC_H +#define LLVM_CLANG_TOOLING_CORE_REFACTORINGSTARTDIAGNOSTIC_H + +#include "clang/Basic/Diagnostic.h" + +namespace clang { +namespace diag { +enum { +#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, CATEGORY) \ + ENUM, +#define REFACTORINGSTART +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +#undef DIAG + NUM_BUILTIN_REFACTORING_DIAGNOSTICS +}; +} // end namespace diag +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_CORE_REFACTORINGSTARTDIAGNOSTIC_H diff --git a/clang/include/clang/Tooling/Refactor/IndexerQuery.h b/clang/include/clang/Tooling/Refactor/IndexerQuery.h new file mode 100644 index 0000000000000..a3b3caa723c44 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/IndexerQuery.h @@ -0,0 +1,274 @@ +//===--- IndexerQuery.h - A set of indexer query interfaces ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the base indexer queries that can be used with +// refactoring continuations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H +#define LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H + +#include "clang/Tooling/Refactor/RefactoringOperationState.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" +#include <vector> + +namespace clang { +namespace tooling { +namespace indexer { + +/// Represents an abstract indexer query. +class IndexerQuery { +public: + const char *BaseUID; + const char *NameUID; + + IndexerQuery(const char *BaseUID, const char *NameUID) + : BaseUID(BaseUID), NameUID(NameUID) {} + virtual ~IndexerQuery() {} + + virtual void invalidateTUSpecificState() = 0; + + // Mainly used for testing. + static llvm::Error loadResultsFromYAML(StringRef Source, + ArrayRef<IndexerQuery *> Queries); + + static bool classof(const IndexerQuery *) { return true; } +}; + +/// An abstract AST query that can produce an AST unit in which the refactoring +/// continuation will run. +class ASTProducerQuery : public IndexerQuery { + static const char *BaseUIDString; + +public: + /// Deriving AST producer queries can redefine this type to generate custom + /// results that are then passed into the refactoring continuations. + using ResultTy = void; + + ASTProducerQuery(const char *NameUID) + : IndexerQuery(BaseUIDString, NameUID) {} + + static bool classof(const IndexerQuery *Q) { + return Q->BaseUID == BaseUIDString; + } +}; + +/// A query that finds a file that contains/should contain the implementation of +/// some declaration. +class ASTUnitForImplementationOfDeclarationQuery final + : public ASTProducerQuery { + static const char *NameUIDString; + + const Decl *D; + PersistentFileID Result; + +public: + ASTUnitForImplementationOfDeclarationQuery(const Decl *D) + : ASTProducerQuery(NameUIDString), D(D), Result("") {} + + using ResultTy = FileID; + + const Decl *getDecl() const { return D; } + + void invalidateTUSpecificState() override { D = nullptr; } + + void setResult(PersistentFileID File) { Result = std::move(File); } + + const PersistentFileID &getResult() const { return Result; } + + static bool classof(const IndexerQuery *D) { + return D->NameUID == NameUIDString; + } +}; + +/// Returns an indexer query that will allow a refactoring continuation to run +/// in an AST unit that contains a file that should contain the implementation +/// of the given declaration \p D. +/// +/// The continuation function will receive \c FileID that corresponds to the +/// implementation file. The indexer can decide which file should be used as an +/// implementation of a declaration based on a number of different heuristics. +/// It does not guarantee that the file will actually have any declarations that +/// correspond to the implementation of \p D yet, as the indexer may decide to +/// point to a file that it thinks will have the implementation declarations in +/// the future. +std::unique_ptr<ASTUnitForImplementationOfDeclarationQuery> +fileThatShouldContainImplementationOf(const Decl *D); + +/// A declaration predicate operates. +struct DeclPredicate { + const char *Name; + + DeclPredicate(const char *Name) : Name(Name) {} + + bool operator==(const DeclPredicate &P) const { + return StringRef(Name) == P.Name; + } + bool operator!=(const DeclPredicate &P) const { + return StringRef(Name) != P.Name; + } +}; + +/// Represents a declaration predicate that will evaluate to either 'true' or +/// 'false' in an indexer query. +struct BoolDeclPredicate { + DeclPredicate Predicate; + bool IsInverted; + + BoolDeclPredicate(DeclPredicate Predicate, bool IsInverted = false) + : Predicate(Predicate), IsInverted(IsInverted) {} + + BoolDeclPredicate operator!() const { + return BoolDeclPredicate(Predicate, /*IsInverted=*/!IsInverted); + } +}; + +namespace detail { + +/// AST-like representation for decl predicates. +class DeclPredicateNode { +public: + const char *NameUID; + DeclPredicateNode(const char *NameUID) : NameUID(NameUID) {} + + static std::unique_ptr<DeclPredicateNode> + create(const DeclPredicate &Predicate); + static std::unique_ptr<DeclPredicateNode> + create(const BoolDeclPredicate &Predicate); + + static bool classof(const DeclPredicateNode *) { return true; } +}; + +class DeclPredicateNodePredicate : public DeclPredicateNode { + static const char *NameUIDString; + + DeclPredicate Predicate; + +public: + DeclPredicateNodePredicate(const DeclPredicate &Predicate) + : DeclPredicateNode(NameUIDString), Predicate(Predicate) {} + + const DeclPredicate &getPredicate() const { return Predicate; } + + static bool classof(const DeclPredicateNode *P) { + return P->NameUID == NameUIDString; + } +}; + +class DeclPredicateNotPredicate : public DeclPredicateNode { + static const char *NameUIDString; + + std::unique_ptr<DeclPredicateNode> Child; + +public: + DeclPredicateNotPredicate(std::unique_ptr<DeclPredicateNode> Child) + : DeclPredicateNode(NameUIDString), Child(std::move(Child)) {} + + const DeclPredicateNode &getChild() const { return *Child; } + + static bool classof(const DeclPredicateNode *P) { + return P->NameUID == NameUIDString; + } +}; + +} // end namespace detail + +/// Transforms one set of declarations into another using some predicate. +class DeclarationsQuery : public IndexerQuery { + static const char *BaseUIDString; + + std::vector<const Decl *> Input; + std::unique_ptr<detail::DeclPredicateNode> Predicate; + +protected: + std::vector<PersistentDeclRef<Decl>> Output; + +public: + DeclarationsQuery(std::vector<const Decl *> Input, + std::unique_ptr<detail::DeclPredicateNode> Predicate) + : IndexerQuery(BaseUIDString, nullptr), Input(std::move(Input)), + Predicate(std::move(Predicate)) { + assert(!this->Input.empty() && "empty declarations list!"); + } + + ArrayRef<const Decl *> getInputs() const { return Input; } + + void invalidateTUSpecificState() override { Input.clear(); } + + void setOutput(std::vector<PersistentDeclRef<Decl>> Output) { + this->Output = Output; + } + + const detail::DeclPredicateNode &getPredicateNode() const { + return *Predicate; + } + + static bool classof(const IndexerQuery *Q) { + return Q->BaseUID == BaseUIDString; + } +}; + +/// The \c DeclEntity class acts as a proxy for the entity that represents a +/// declaration in the indexer. It defines a set of declaration predicates that +/// can be used in indexer queries. +struct DeclEntity { + /// The indexer will evaluate this predicate to 'true' when a certain + /// declaration has a corresponding definition. + BoolDeclPredicate isDefined() const { + return BoolDeclPredicate("decl.isDefined"); + } +}; + +template <typename T> +class ManyToManyDeclarationsQuery final + : public std::enable_if<std::is_base_of<Decl, T>::value, + DeclarationsQuery>::type { +public: + ManyToManyDeclarationsQuery( + ArrayRef<const T *> Input, + std::unique_ptr<detail::DeclPredicateNode> Predicate) + : DeclarationsQuery(std::vector<const Decl *>(Input.begin(), Input.end()), + std::move(Predicate)) {} + + std::vector<PersistentDeclRef<T>> getOutput() const { + std::vector<PersistentDeclRef<T>> Results; + for (const auto &Ref : DeclarationsQuery::Output) + Results.push_back(PersistentDeclRef<T>(Ref.USR)); + return Results; + } +}; + +/// Returns an indexer query that will pass a filtered list of declarations to +/// a refactoring continuation. +/// +/// The filtering is done based on predicates that are available on the \c +/// DeclEntity types. For example, you can use the following invocation to +/// find a set of declarations that are defined in the entire project: +/// +/// \code +/// filter({ MyDeclA, MyDeclB }, [] (const DeclEntity &D) { return D.isDefined() +/// }) +/// \endcode +template <typename T> +std::unique_ptr<ManyToManyDeclarationsQuery<T>> +filter(ArrayRef<const T *> Declarations, + BoolDeclPredicate (*Fn)(const DeclEntity &), + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + return llvm::make_unique<ManyToManyDeclarationsQuery<T>>( + Declarations, detail::DeclPredicateNode::create(Fn(DeclEntity()))); +} + +} // end namespace indexer +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_INDEXER_QUERY_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringActionFinder.h b/clang/include/clang/Tooling/Refactor/RefactoringActionFinder.h new file mode 100644 index 0000000000000..395d78c0f9ac4 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringActionFinder.h @@ -0,0 +1,60 @@ +//===--- RefactoringActionFinder.h - Clang refactoring library ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Provides methods to find the refactoring actions that can be +/// performed at specific locations / source ranges in a translation unit. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactor/RefactoringActions.h" +#include "llvm/ADT/StringSet.h" +#include <vector> + +namespace clang { + +class NamedDecl; +class ASTContext; + +namespace tooling { + +/// Contains a set of a refactoring actions. +struct RefactoringActionSet { + /// A set of refactoring actions that can be performed at some specific + /// location in a source file. + /// + /// The actions in the action set are ordered by their priority: most + /// important actions are placed before the less important ones. + std::vector<RefactoringActionType> Actions; + + RefactoringActionSet() {} + + RefactoringActionSet(RefactoringActionSet &&) = default; + RefactoringActionSet &operator=(RefactoringActionSet &&) = default; +}; + +/// \brief Returns a \c RefactoringActionSet that contains the set of actions +/// that can be performed at the given location. +RefactoringActionSet findActionSetAt(SourceLocation Loc, + SourceRange SelectionRange, + ASTContext &Context); + +/// \brief Returns a set of USRs that correspond to the given declaration. +llvm::StringSet<> findSymbolsUSRSet(const NamedDecl *FoundDecl, + ASTContext &Context); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringActions.def b/clang/include/clang/Tooling/Refactor/RefactoringActions.def new file mode 100644 index 0000000000000..9b7e7e04d037f --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringActions.def @@ -0,0 +1,60 @@ +//===--- RefactoringActions.def - The list of refactoring actions --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef REFACTORING_ACTION +#define REFACTORING_ACTION(Name, Spelling) +#endif + +#ifndef REFACTORING_SUB_ACTION +#define REFACTORING_SUB_ACTION(Name, Parent, Spelling) \ + REFACTORING_ACTION(Parent##_##Name, Spelling) +#endif + +#ifndef REFACTORING_OPERATION_ACTION +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command)\ + REFACTORING_ACTION(Name, Spelling) +#endif + +#ifndef REFACTORING_OPERATION_SUB_ACTION +#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command)\ + REFACTORING_SUB_ACTION(Name, Parent, Spelling) +#endif + +REFACTORING_ACTION(Rename, "Rename") +REFACTORING_SUB_ACTION(Local, Rename, "Rename") + +REFACTORING_OPERATION_ACTION(Extract, "Extract Function", "extract") +REFACTORING_OPERATION_SUB_ACTION(Method, Extract, "Extract Method", + "extract-method") + +REFACTORING_OPERATION_ACTION(IfSwitchConversion, "Convert to Switch", + "if-switch-conversion") +REFACTORING_OPERATION_ACTION(FillInEnumSwitchCases, "Add Missing Switch Cases", + "fill-in-enum-switch-cases") +REFACTORING_OPERATION_ACTION(FillInMissingProtocolStubs, + "Add Missing Protocol Requirements", + "fill-in-missing-protocol-stubs") +REFACTORING_OPERATION_ACTION(LocalizeObjCStringLiteral, + "Wrap in NSLocalizedString", + "localize-objc-string-literal") +REFACTORING_OPERATION_ACTION(ExtractRepeatedExpressionIntoVariable, + "Extract Repeated Expression", + "extract-repeated-expr-into-var") +REFACTORING_OPERATION_ACTION(FillInMissingMethodStubsFromAbstractClasses, + "Add Missing Abstract Class Overrides", + "fill-in-missing-abstract-methods") + // FIXME: For ObjC this should say 'Methods': +REFACTORING_OPERATION_ACTION(ImplementDeclaredMethods, + "Generate Missing Function Definitions", + "implement-declared-methods") + +#undef REFACTORING_OPERATION_SUB_ACTION +#undef REFACTORING_OPERATION_ACTION +#undef REFACTORING_SUB_ACTION +#undef REFACTORING_ACTION diff --git a/clang/include/clang/Tooling/Refactor/RefactoringActions.h b/clang/include/clang/Tooling/Refactor/RefactoringActions.h new file mode 100644 index 0000000000000..f9d9c6c888ab5 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringActions.h @@ -0,0 +1,34 @@ +//===--- RefactoringActions.h - Clang refactoring library -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Contains a list of all the supported refactoring actions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace tooling { + +enum class RefactoringActionType { +#define REFACTORING_ACTION(Name, Spelling) Name, +#include "clang/Tooling/Refactor/RefactoringActions.def" +}; + +StringRef getRefactoringActionTypeName(RefactoringActionType Action); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOperation.h b/clang/include/clang/Tooling/Refactor/RefactoringOperation.h new file mode 100644 index 0000000000000..3332178011912 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOperation.h @@ -0,0 +1,160 @@ +//===--- RefactoringOperations.h - Defines a refactoring operation --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactor/RefactoringActions.h" +#include "clang/Tooling/Refactor/RefactoringOptionSet.h" +#include "clang/Tooling/Refactor/RefactoringReplacement.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "llvm/ADT/None.h" +#include "llvm/Support/Error.h" +#include <string> +#include <vector> + +namespace clang { + +class ASTContext; +class Decl; +class Preprocessor; +class Stmt; + +namespace tooling { + +class RefactoringContinuation; + +/// A refactoring result contains the source replacements produced by the +/// refactoring operation and the optional refactoring continuation. +struct RefactoringResult { + std::vector<RefactoringReplacement> Replacements; + std::vector<std::unique_ptr<RefactoringResultAssociatedSymbol>> + AssociatedSymbols; + std::unique_ptr<RefactoringContinuation> Continuation; + + RefactoringResult( + std::vector<RefactoringReplacement> Replacements, + std::unique_ptr<RefactoringContinuation> Continuation = nullptr) + : Replacements(std::move(Replacements)), + Continuation(std::move(Continuation)) {} + + RefactoringResult(std::unique_ptr<RefactoringContinuation> Continuation) + : Replacements(), Continuation(std::move(Continuation)) {} + + RefactoringResult(RefactoringResult &&) = default; + RefactoringResult &operator=(RefactoringResult &&) = default; +}; + +namespace indexer { + +class IndexerQuery; +class ASTProducerQuery; + +} // end namespace indexer + +/// Refactoring continuations allow refactoring operations to run in external +/// AST units with some results that were obtained after querying the indexer. +/// +/// The state of the refactoring operation is automatically managed by the +/// refactoring engine: +/// - Declaration references are converted to declaration references in +/// an external translation unit. +class RefactoringContinuation { +public: + virtual ~RefactoringContinuation() {} + + virtual indexer::ASTProducerQuery *getASTUnitIndexerQuery() = 0; + + virtual std::vector<indexer::IndexerQuery *> + getAdditionalIndexerQueries() = 0; + + /// Converts the TU-specific state in the continuation to a TU-independent + /// state. + /// + /// This function is called before the initiation AST unit is freed. + virtual void persistTUSpecificState() = 0; + + /// Invokes the continuation with the indexer query results and the state + /// values in the context of another AST unit. + virtual llvm::Expected<RefactoringResult> + runInExternalASTUnit(ASTContext &Context) = 0; +}; + +// TODO: Remove in favour of diagnostics. +class RefactoringOperationError + : public llvm::ErrorInfo<RefactoringOperationError> { +public: + static char ID; + StringRef FailureReason; + + RefactoringOperationError(StringRef FailureReason) + : FailureReason(FailureReason) {} + + void log(raw_ostream &OS) const override; + + std::error_code convertToErrorCode() const override; +}; + +/// Represents an abstract refactoring operation. +class RefactoringOperation { +public: + virtual ~RefactoringOperation() {} + + virtual const Stmt *getTransformedStmt() const { return nullptr; } + + virtual const Stmt *getLastTransformedStmt() const { return nullptr; } + + virtual const Decl *getTransformedDecl() const { return nullptr; } + + virtual const Decl *getLastTransformedDecl() const { return nullptr; } + + virtual std::vector<std::string> getRefactoringCandidates() { return {}; } + + virtual std::vector<RefactoringActionType> getAvailableSubActions() { + return {}; + } + + virtual llvm::Expected<RefactoringResult> + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex = 0) = 0; +}; + +/// A wrapper around a unique pointer to a \c RefactoringOperation or \c +/// SymbolOperation that determines if the operation was successfully initiated +/// or not, even if the operation itself wasn't created. +struct RefactoringOperationResult { + std::unique_ptr<RefactoringOperation> RefactoringOp; + std::unique_ptr<SymbolOperation> SymbolOp; + bool Initiated; + StringRef FailureReason; + + RefactoringOperationResult() : Initiated(false) {} + RefactoringOperationResult(llvm::NoneType) : Initiated(false) {} + explicit RefactoringOperationResult(StringRef FailureReason) + : Initiated(false), FailureReason(FailureReason) {} +}; + +/// Initiate a specific refactoring operation. +RefactoringOperationResult initiateRefactoringOperationAt( + SourceLocation Location, SourceRange SelectionRange, ASTContext &Context, + RefactoringActionType ActionType, bool CreateOperation = true); + +/// Initiate a specific refactoring operation on a declaration that corresponds +/// to the given \p DeclUSR. +RefactoringOperationResult +initiateRefactoringOperationOnDecl(StringRef DeclUSR, ASTContext &Context, + RefactoringActionType ActionType); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOperationState.h b/clang/include/clang/Tooling/Refactor/RefactoringOperationState.h new file mode 100644 index 0000000000000..76ee7d4392cd1 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOperationState.h @@ -0,0 +1,66 @@ +//===--- RefactoringOperationState.h - Serializable operation state -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the refactoring operation state types that represent the +// TU-independent state that is used for refactoring continuations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H + +#include "clang/AST/Decl.h" +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include <string> +#include <type_traits> + +namespace clang { +namespace tooling { + +namespace detail { + +struct PersistentDeclRefBase {}; + +} // end namespace detail + +/// Declaration references are persisted across translation units by using +/// USRs. +template <typename T> +struct PersistentDeclRef : std::enable_if<std::is_base_of<Decl, T>::value, + detail::PersistentDeclRefBase>::type { + std::string USR; + // FIXME: We can improve the efficiency of conversion to Decl * by storing the + // decl kind. + + PersistentDeclRef(std::string USR) : USR(std::move(USR)) {} + PersistentDeclRef(PersistentDeclRef &&Other) = default; + PersistentDeclRef &operator=(PersistentDeclRef &&Other) = default; + PersistentDeclRef(const PersistentDeclRef &Other) = default; + PersistentDeclRef &operator=(const PersistentDeclRef &Other) = default; + + static PersistentDeclRef<T> create(const Decl *D) { + // FIXME: Move the getUSRForDecl method somewhere else. + return PersistentDeclRef<T>(rename::getUSRForDecl(D)); + } +}; + +/// FileIDs are persisted across translation units by using filenames. +struct PersistentFileID { + std::string Filename; + + PersistentFileID(std::string Filename) : Filename(std::move(Filename)) {} + PersistentFileID(PersistentFileID &&Other) = default; + PersistentFileID &operator=(PersistentFileID &&Other) = default; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h b/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h new file mode 100644 index 0000000000000..c3f05ac0f436d --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h @@ -0,0 +1,80 @@ +//===--- RefactoringOptionSet.h - A container for the refactoring options -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace yaml { +class IO; +} // end namespace yaml +} // end namespace llvm + +namespace clang { +namespace tooling { + +struct RefactoringOption { + virtual ~RefactoringOption() = default; + + struct SerializationContext { + llvm::yaml::IO &IO; + + SerializationContext(llvm::yaml::IO &IO) : IO(IO) {} + }; + + virtual void serialize(const SerializationContext &Context); +}; + +/// \brief A set of refactoring options that can be given to a refactoring +/// operation. +class RefactoringOptionSet final { + llvm::StringMap<std::unique_ptr<RefactoringOption>> Options; + +public: + RefactoringOptionSet() {} + template <typename T> RefactoringOptionSet(const T &Option) { add(Option); } + + RefactoringOptionSet(RefactoringOptionSet &&) = default; + RefactoringOptionSet &operator=(RefactoringOptionSet &&) = default; + + RefactoringOptionSet(const RefactoringOptionSet &) = delete; + RefactoringOptionSet &operator=(const RefactoringOptionSet &) = delete; + + template <typename T> void add(const T &Option) { + auto It = Options.try_emplace(StringRef(T::Name), nullptr); + if (It.second) + It.first->getValue().reset(new T(Option)); + } + + template <typename T> const T *get() const { + auto It = Options.find(StringRef(T::Name)); + if (It == Options.end()) + return nullptr; + return static_cast<const T *>(It->getValue().get()); + } + + template <typename T> const T &get(const T &Default) const { + const auto *Ptr = get<T>(); + return Ptr ? *Ptr : Default; + } + + void print(llvm::raw_ostream &OS) const; + + static llvm::Expected<RefactoringOptionSet> parse(StringRef Source); +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOptions.h b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h new file mode 100644 index 0000000000000..f0ce4ba007d0b --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h @@ -0,0 +1,59 @@ +//===--- RefactoringOptions.h - A set of all the refactoring options ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of all possible refactoring options that can be +// given to the refactoring operations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H + +#include "clang/AST/DeclBase.h" +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactor/RefactoringOptionSet.h" + +namespace clang { +namespace tooling { +namespace option { + +namespace detail { + +struct BoolOptionBase : RefactoringOption { +protected: + bool Value = false; + void serializeImpl(const SerializationContext &Context, const char *Name); + +public: + operator bool() const { return Value; } +}; + +template <typename Option> struct BoolOption : BoolOptionBase { + void serialize(const SerializationContext &Context) override { + serializeImpl(Context, Option::Name); + } + + static Option getTrue() { + Option Result; + Result.Value = true; + return Result; + } +}; + +} // end namespace detail + +struct AvoidTextualMatches final : detail::BoolOption<AvoidTextualMatches> { + static constexpr const char *Name = "rename.avoid.textual.matches"; +}; + +} // end namespace option +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h b/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h new file mode 100644 index 0000000000000..4aed8ab37e76a --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h @@ -0,0 +1,85 @@ +//===--- RefactoringReplacement.h - ------------------------*- C++ -*------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactor/SymbolName.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" +#include <string> + +namespace clang { +namespace tooling { + +/// \brief Represent a symbol that can be used for an additional refactoring +/// action that associated. +class RefactoringResultAssociatedSymbol { + SymbolName Name; + +public: + RefactoringResultAssociatedSymbol(SymbolName Name) : Name(std::move(Name)) {} + + const SymbolName &getName() const { return Name; } +}; + +/// \brief A replacement range. +class RefactoringReplacement { +public: + SourceRange Range; + std::string ReplacementString; + + /// \brief Represents a symbol that is contained in the replacement string + /// of this replacement. + struct AssociatedSymbolLocation { + /// These offsets point into the ReplacementString. + llvm::SmallVector<unsigned, 4> Offsets; + bool IsDeclaration; + + AssociatedSymbolLocation(ArrayRef<unsigned> Offsets, + bool IsDeclaration = false) + : Offsets(Offsets.begin(), Offsets.end()), + IsDeclaration(IsDeclaration) {} + }; + llvm::SmallDenseMap<const RefactoringResultAssociatedSymbol *, + AssociatedSymbolLocation> + SymbolLocations; + + RefactoringReplacement(SourceRange Range) : Range(Range) {} + + RefactoringReplacement(SourceRange Range, StringRef ReplacementString) + : Range(Range), ReplacementString(ReplacementString.str()) {} + RefactoringReplacement(SourceRange Range, std::string ReplacementString) + : Range(Range), ReplacementString(std::move(ReplacementString)) {} + + RefactoringReplacement(SourceRange Range, StringRef ReplacementString, + const RefactoringResultAssociatedSymbol *Symbol, + const AssociatedSymbolLocation &Loc) + : Range(Range), ReplacementString(ReplacementString.str()) { + SymbolLocations.insert(std::make_pair(Symbol, Loc)); + } + + RefactoringReplacement(const FixItHint &Hint) { + Range = Hint.RemoveRange.getAsRange(); + ReplacementString = Hint.CodeToInsert; + } + + RefactoringReplacement(RefactoringReplacement &&) = default; + RefactoringReplacement &operator=(RefactoringReplacement &&) = default; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H diff --git a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h new file mode 100644 index 0000000000000..d598259013b4a --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h @@ -0,0 +1,83 @@ +//===--- RenameIndexedFile.h - -----------------------------*- C++ -*------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H +#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H + +#include "clang/Basic/LLVM.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/Refactor/RenamedSymbol.h" +#include "clang/Tooling/Refactor/SymbolName.h" +#include "llvm/ADT/ArrayRef.h" + +namespace clang { +namespace tooling { + +class RefactoringOptionSet; + +namespace rename { + +/// An already known occurrence of the symbol that's being renamed. +struct IndexedOccurrence { + /// The location of this occurrence in the indexed file. + unsigned Line, Column; + enum OccurrenceKind { + IndexedSymbol, + IndexedObjCMessageSend, + InclusionDirective + }; + OccurrenceKind Kind; +}; + +struct IndexedSymbol { + SymbolName Name; + std::vector<IndexedOccurrence> IndexedOccurrences; + /// Whether this symbol is an Objective-C selector. + bool IsObjCSelector; + + IndexedSymbol(SymbolName Name, + std::vector<IndexedOccurrence> IndexedOccurrences, + bool IsObjCSelector) + : Name(std::move(Name)), + IndexedOccurrences(std::move(IndexedOccurrences)), + IsObjCSelector(IsObjCSelector) {} + IndexedSymbol(IndexedSymbol &&Other) = default; + IndexedSymbol &operator=(IndexedSymbol &&Other) = default; +}; + +/// Consumes the \c SymbolOccurrences found by \c IndexedFileOccurrenceProducer. +class IndexedFileOccurrenceConsumer { +public: + virtual ~IndexedFileOccurrenceConsumer() {} + virtual void handleOccurrence(const SymbolOccurrence &Occurrence, + SourceManager &SM, + const LangOptions &LangOpts) = 0; +}; + +/// Finds the renamed \c SymbolOccurrences in an already indexed files. +class IndexedFileOccurrenceProducer final : public PreprocessorFrontendAction { + bool IsMultiPiece; + ArrayRef<IndexedSymbol> Symbols; + IndexedFileOccurrenceConsumer &Consumer; + const RefactoringOptionSet *Options; + +public: + IndexedFileOccurrenceProducer(ArrayRef<IndexedSymbol> Symbols, + IndexedFileOccurrenceConsumer &Consumer, + const RefactoringOptionSet *Options = nullptr); + +private: + void ExecuteAction() override; +}; + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H diff --git a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h new file mode 100644 index 0000000000000..5c5a9061eac72 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h @@ -0,0 +1,125 @@ +//===--- RenamedSymbol.h - ---------------------------------*- C++ -*------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H +#define LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H + +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactor/SymbolName.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" + +namespace clang { + +class NamedDecl; + +namespace tooling { +namespace rename { + +/// \brief A symbol that has to be renamed. +class Symbol { +public: + SymbolName Name; + /// The index of this symbol in a \c SymbolOperation. + unsigned SymbolIndex; + /// The declaration that was used to initiate a refactoring operation for this + /// symbol. May not be the most canonical declaration. + const NamedDecl *FoundDecl; + /// An optional Objective-C selector. + llvm::Optional<Selector> ObjCSelector; + + Symbol(const NamedDecl *FoundDecl, unsigned SymbolIndex, + const LangOptions &LangOpts); + + Symbol(Symbol &&) = default; + Symbol &operator=(Symbol &&) = default; +}; + +/// \brief An occurrence of a renamed symbol. +/// +/// Provides information about an occurrence of symbol that helps renaming tools +/// determine if they can rename this symbol automatically and which source +/// ranges they have to replace. +/// +/// A single occurrence of a symbol can span more than one source range to +/// account for things like Objective-C selectors. +// TODO: Rename +class SymbolOccurrence { +public: + enum OccurrenceKind { + /// \brief This occurrence is an exact match and can be renamed + /// automatically. + MatchingSymbol, + + /// \brief This is an occurrence of a matching selector. It can't be renamed + /// automatically unless the indexer proves that this selector refers only + /// to the declarations that correspond to the renamed symbol. + MatchingSelector, + + /// \brief This is an occurrence of an implicit property that uses the + /// renamed method. + MatchingImplicitProperty, + + /// \brief This is a textual occurrence of a symbol in a comment. + MatchingComment, + + /// \brief This is a textual occurrence of a symbol in a doc comment. + MatchingDocComment, + + /// \brief This is an occurrence of a symbol in an inclusion directive. + MatchingFilename + }; + + OccurrenceKind Kind; + /// Whether or not this occurrence is inside a macro. When this is true, the + /// locations of the occurrence contain just one location that points to + /// the location of the macro expansion. + bool IsMacroExpansion; + /// The index of the symbol stored in a \c SymbolOperation which matches this + /// occurrence. + unsigned SymbolIndex; + /// The source locations that correspond to the occurence of the symbol. + SmallVector<SourceLocation, 4> Locations; + + SymbolOccurrence() + : Kind(MatchingSymbol), IsMacroExpansion(false), SymbolIndex(0) {} + + SymbolOccurrence(OccurrenceKind Kind, bool IsMacroExpansion, + unsigned SymbolIndex, ArrayRef<SourceLocation> Locations) + : Kind(Kind), IsMacroExpansion(IsMacroExpansion), + SymbolIndex(SymbolIndex), + Locations(Locations.begin(), Locations.end()) { + assert(!Locations.empty() && "Renamed occurence without locations!"); + } + + SymbolOccurrence(SymbolOccurrence &&) = default; + SymbolOccurrence &operator=(SymbolOccurrence &&) = default; + + /// Return the source range that corresponds to an individual source location + /// in this occurrence. + SourceRange getLocationRange(SourceLocation Loc, size_t OldNameSize) const { + return SourceRange( + Loc, IsMacroExpansion ? Loc : Loc.getLocWithOffset(OldNameSize)); + } +}; + +/// \brief Less-than operator between the two renamed symbol occurrences. +bool operator<(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS); + +/// \brief Equal-to operator between the two renamed symbol occurrences. +bool operator==(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS); + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H diff --git a/clang/include/clang/Tooling/Refactor/RenamingOperation.h b/clang/include/clang/Tooling/Refactor/RenamingOperation.h new file mode 100644 index 0000000000000..bb360a0dc766f --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenamingOperation.h @@ -0,0 +1,43 @@ +//===--- RenamingOperation.h - -----------------------------*- C++ -*------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H +#define LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H + +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactor/SymbolName.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +class IdentifierTable; + +namespace tooling { + +class SymbolOperation; + +namespace rename { + +/// Return true if the new name is a valid language identifier. +bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector, + IdentifierTable &IDs, const LangOptions &LangOpts); +bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation, + IdentifierTable &IDs, const LangOptions &LangOpts); + +/// \brief Finds the set of new names that apply to the symbols in the given +/// \c SymbolOperation. +void determineNewNames(SymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl<SymbolName> &NewNames, + const LangOptions &LangOpts); + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H diff --git a/clang/include/clang/Tooling/Refactor/SymbolName.h b/clang/include/clang/Tooling/Refactor/SymbolName.h new file mode 100644 index 0000000000000..b2d18720987ae --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/SymbolName.h @@ -0,0 +1,66 @@ +//===--- SymbolName.h - Clang refactoring library ----------*- C++ -*------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_NAME_H +#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_NAME_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include <string> +#include <vector> + +namespace clang { + +class LangOptions; + +namespace tooling { + +/// \brief A name of a declaration that's used in the refactoring process. +/// +/// Names can be composed of multiple string, to account for things like +/// Objective-C selectors. +class SymbolName { +public: + SymbolName() {} + + /// \brief Creates a \c SymbolName by decomposing the given \p Name using + /// language specific logic. + SymbolName(StringRef Name, const LangOptions &LangOpts); + SymbolName(StringRef Name, bool IsObjectiveCSelector); + explicit SymbolName(ArrayRef<StringRef> Name); + + SymbolName(SymbolName &&) = default; + SymbolName &operator=(SymbolName &&) = default; + + SymbolName(const SymbolName &) = default; + SymbolName &operator=(const SymbolName &) = default; + + bool empty() const { return Strings.empty(); } + + /// \brief Returns the number of the strings that make up the given name. + size_t size() const { return Strings.size(); } + + /// \brief Returns the string at the given index. + StringRef operator[](size_t I) const { return Strings[I]; } + + ArrayRef<std::string> strings() const { return Strings; } + + void print(raw_ostream &OS) const; + +private: + std::vector<std::string> Strings; +}; + +raw_ostream &operator<<(raw_ostream &OS, const SymbolName &N); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_NAME_H diff --git a/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h b/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h new file mode 100644 index 0000000000000..6ecc23b310011 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h @@ -0,0 +1,37 @@ +//===--- SymbolOccurrenceFinder.h - Clang refactoring library -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Provides functionality for finding all occurrences of a USR in a +/// given AST. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H +#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H + +#include "clang/AST/AST.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "llvm/ADT/StringRef.h" +#include <string> +#include <vector> + +namespace clang { +namespace tooling { +namespace rename { + +// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree! +std::vector<SymbolOccurrence> +findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl); + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H diff --git a/clang/include/clang/Tooling/Refactor/SymbolOperation.h b/clang/include/clang/Tooling/Refactor/SymbolOperation.h new file mode 100644 index 0000000000000..7616aa36ae1c3 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/SymbolOperation.h @@ -0,0 +1,91 @@ +//===--- SymbolOperation.h - -------------------------------*- C++ -*------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H +#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H + +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactor/RenamedSymbol.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" + +namespace clang { + +class ASTContext; +class NamedDecl; + +namespace tooling { + +/// \brief A refactoring operation that deals with occurrences of symbols. +class SymbolOperation { + /// Contains the symbols that are required for this operation. + SmallVector<rename::Symbol, 4> Symbols; + + /// Maps from a USR to an index in the \c Symbol array. + /// Contains all of the USRs that correspond to the declarations which use + /// the symbols in this operation. + llvm::StringMap<unsigned> USRToSymbol; + + /// True if all the symbols in this operation occur only in the translation + /// unit that defines them. + bool IsLocal; + + /// The declaration whose implementation is needed for the correct initiation + /// of a symbol operation. + const NamedDecl *DeclThatRequiresImplementationTU; + +public: + SymbolOperation(const NamedDecl *FoundDecl, ASTContext &Context); + + SymbolOperation(SymbolOperation &&) = default; + SymbolOperation &operator=(SymbolOperation &&) = default; + + /// Return the symbol that corresponds to the given USR, or null if this USR + /// isn't interesting from the perspective of this operation. + const rename::Symbol *getSymbolForUSR(StringRef USR) const { + auto It = USRToSymbol.find(USR); + if (It != USRToSymbol.end()) + return &Symbols[It->getValue()]; + return nullptr; + } + + /// The symbols that this operation is working on. + /// + /// Symbol operations, like rename, usually just work on just one symbol. + /// However, there are certain language constructs that require more than + /// one symbol in order for them to be renamed correctly. Property + /// declarations in Objective-C are the perfect example: in addition to the + /// actual property, renaming has to rename the corresponding getters and + /// setters, as well as the backing ivar. + ArrayRef<rename::Symbol> symbols() const { return Symbols; } + + /// True if all the symbols in this operation occur only in the translation + /// unit that defines them. + bool isLocal() const { return IsLocal; } + + /// True if the declaration that was found in the initial TU needs to be + /// examined in the TU that implemented it. + bool requiresImplementationTU() const { + return DeclThatRequiresImplementationTU; + } + + /// Returns the declaration whose implementation is needed for the correct + /// initiation of a symbol operation. + const NamedDecl *declThatRequiresImplementationTU() const { + return DeclThatRequiresImplementationTU; + } +}; + +/// Return true if the given declaration corresponds to a local symbol. +bool isLocalSymbol(const NamedDecl *D, const LangOptions &LangOpts); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H diff --git a/clang/include/clang/Tooling/Refactor/USRFinder.h b/clang/include/clang/Tooling/Refactor/USRFinder.h new file mode 100644 index 0000000000000..0a83f3086d16e --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/USRFinder.h @@ -0,0 +1,85 @@ +//===--- USRFinder.h - Clang refactoring library --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Methods for determining the USR of a symbol at a location in source +/// code. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H +#define LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include <string> +#include <vector> + +namespace clang { + +class ASTContext; +class Decl; +class SourceLocation; +class NamedDecl; + +namespace tooling { +namespace rename { + +using llvm::StringRef; +using namespace clang::ast_matchers; + +// Given an AST context and a point, returns a NamedDecl identifying the symbol +// at the point. Returns null if nothing is found at the point. +const NamedDecl *getNamedDeclAt(const ASTContext &Context, + SourceLocation Point); + +/// Returns a \c NamedDecl that corresponds to the given \p USR in the given +/// AST context. Returns null if there's no declaration that matches the given +/// \p USR. +const NamedDecl *getNamedDeclWithUSR(const ASTContext &Context, StringRef USR); + +// Converts a Decl into a USR. +std::string getUSRForDecl(const Decl *Decl); + +// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead. +class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback { +public: + explicit NestedNameSpecifierLocFinder(ASTContext &Context) + : Context(Context) {} + + ArrayRef<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() { + addMatchers(); + Finder.matchAST(Context); + return Locations; + } + +private: + void addMatchers() { + const auto NestedNameSpecifierLocMatcher = + nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc"); + Finder.addMatcher(NestedNameSpecifierLocMatcher, this); + } + + void run(const MatchFinder::MatchResult &Result) override { + const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>( + "nestedNameSpecifierLoc"); + Locations.push_back(*NNS); + } + + ASTContext &Context; + std::vector<NestedNameSpecifierLoc> Locations; + MatchFinder Finder; +}; + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H diff --git a/clang/include/clang/module.modulemap b/clang/include/clang/module.modulemap index 3b42381100260..a8a23ff18177f 100644 --- a/clang/include/clang/module.modulemap +++ b/clang/include/clang/module.modulemap @@ -132,9 +132,12 @@ module Clang_StaticAnalyzer_Frontend { module Clang_Tooling { requires cplusplus umbrella "Tooling" module * { export * } - // FIXME: Exclude this header to avoid pulling all of the AST matchers + // FIXME: Exclude these headers to avoid pulling all of the AST matchers // library into clang-format. Due to inline key functions in the headers, // importing the AST matchers library gives a link dependency on the AST // matchers (and thus the AST), which clang-format should not have. exclude header "Tooling/RefactoringCallbacks.h" + exclude header "Tooling/Refactor/USRFinder.h" + + textual header "Tooling/Refactor/RefactoringActions.def" } diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index d89be0d9e6fa4..19fdf92d23421 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -1690,6 +1690,8 @@ void ASTDumper::VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { void ASTDumper::VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *D) { dumpName(D); dumpDeclRef(D->getClassInterface()); + OS << " "; + dumpLocation(D->getClassInterfaceLoc()); } void ASTDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 8264493c5c82a..b7b6c098b594b 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -2155,18 +2155,19 @@ raw_ostream &clang::operator<<(raw_ostream &OS, void ObjCCompatibleAliasDecl::anchor() { } -ObjCCompatibleAliasDecl * -ObjCCompatibleAliasDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, - IdentifierInfo *Id, - ObjCInterfaceDecl* AliasedClass) { - return new (C, DC) ObjCCompatibleAliasDecl(DC, L, Id, AliasedClass); +ObjCCompatibleAliasDecl *ObjCCompatibleAliasDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation NameLoc, IdentifierInfo *Id, + ObjCInterfaceDecl *AliasedClass, SourceLocation AliasedClassLoc, + SourceLocation AtLoc) { + return new (C, DC) ObjCCompatibleAliasDecl(DC, NameLoc, Id, AliasedClass, + AliasedClassLoc, AtLoc); } ObjCCompatibleAliasDecl * ObjCCompatibleAliasDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) ObjCCompatibleAliasDecl(nullptr, SourceLocation(), - nullptr, nullptr); + return new (C, ID) + ObjCCompatibleAliasDecl(nullptr, SourceLocation(), nullptr, nullptr, + SourceLocation(), SourceLocation()); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 9fab17e103874..d1f53cf0fc2fc 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -119,6 +119,13 @@ void Decl::print(raw_ostream &Out, const PrintingPolicy &Policy, Printer.Visit(const_cast<Decl*>(this)); } +void TemplateParameterList::print(raw_ostream &Out, + const PrintingPolicy &Policy, + unsigned Indentation) const { + DeclPrinter Printer(Out, Policy, Indentation); + Printer.printTemplateParameters(this); +} + static QualType GetBaseType(QualType T) { // FIXME: This should be on the Type class! QualType BaseType = T; @@ -488,13 +495,15 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D); if (!Policy.SuppressSpecifiers) { - switch (D->getStorageClass()) { - case SC_None: break; - case SC_Extern: Out << "extern "; break; - case SC_Static: Out << "static "; break; - case SC_PrivateExtern: Out << "__private_extern__ "; break; - case SC_Auto: case SC_Register: - llvm_unreachable("invalid for functions"); + if (!Policy.SupressStorageClassSpecifiers) { + switch (D->getStorageClass()) { + case SC_None: break; + case SC_Extern: Out << "extern "; break; + case SC_Static: Out << "static "; break; + case SC_PrivateExtern: Out << "__private_extern__ "; break; + case SC_Auto: case SC_Register: + llvm_unreachable("invalid for functions"); + } } if (D->isInlineSpecified()) Out << "inline "; diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 0551340c37a17..12eb8cb6d9c49 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -84,11 +84,12 @@ namespace { unsigned Indentation; bool HasEmptyPlaceHolder; bool InsideCCAttribute; + bool IgnoreFunctionProtoTypeConstQual; public: explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0) - : Policy(Policy), Indentation(Indentation), - HasEmptyPlaceHolder(false), InsideCCAttribute(false) { } + : Policy(Policy), Indentation(Indentation), HasEmptyPlaceHolder(false), + InsideCCAttribute(false), IgnoreFunctionProtoTypeConstQual(false) {} void print(const Type *ty, Qualifiers qs, raw_ostream &OS, StringRef PlaceHolder); @@ -754,8 +755,12 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, OS << "__attribute__((no_caller_saved_registers))"; if (unsigned quals = T->getTypeQuals()) { - OS << ' '; - AppendTypeQualList(OS, quals, Policy.Restrict); + if (IgnoreFunctionProtoTypeConstQual) + quals &= ~unsigned(Qualifiers::Const); + if (quals) { + OS << ' '; + AppendTypeQualList(OS, quals, Policy.Restrict); + } } switch (T->getRefQualifier()) { @@ -1002,6 +1007,13 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) { else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) { assert(Typedef->getIdentifier() && "Typedef without identifier?"); OS << Typedef->getIdentifier()->getName(); + } else if (Policy.UseStdFunctionForLambda && isa<CXXRecordDecl>(D) && + cast<CXXRecordDecl>(D)->isLambda()) { + OS << "std::function<"; + QualType T = cast<CXXRecordDecl>(D)->getLambdaCallOperator()->getType(); + SaveAndRestore<bool> NoConst(IgnoreFunctionProtoTypeConstQual, true); + print(T, OS, ""); + OS << '>'; } else { // Make an unambiguous representation for anonymous types, e.g. // (anonymous enum at /usr/include/string.h:120:9) diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index ce493c1e5caba..73d2821f21571 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -43,7 +43,7 @@ struct StaticDiagInfoRec { unsigned SFINAE : 2; unsigned WarnNoWerror : 1; unsigned WarnShowInSystemHeader : 1; - unsigned Category : 5; + unsigned Category : 6; uint16_t OptionGroupIndex; @@ -88,6 +88,7 @@ static const StaticDiagInfoRec StaticDiagInfo[] = { #include "clang/Basic/DiagnosticCommentKinds.inc" #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" #undef DIAG }; @@ -137,6 +138,7 @@ CATEGORY(AST, PARSE) CATEGORY(COMMENT, AST) CATEGORY(SEMA, COMMENT) CATEGORY(ANALYSIS, SEMA) +CATEGORY(REFACTORING, ANALYSIS) #undef CATEGORY // Avoid out of bounds reads. diff --git a/clang/lib/Edit/CMakeLists.txt b/clang/lib/Edit/CMakeLists.txt index a7fa9c28e1f61..99aff3c3aeb19 100644 --- a/clang/lib/Edit/CMakeLists.txt +++ b/clang/lib/Edit/CMakeLists.txt @@ -5,6 +5,8 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangEdit Commit.cpp EditedSource.cpp + FillInMissingProtocolStubs.cpp + FillInMissingSwitchEnumCases.cpp RewriteObjCFoundationAPI.cpp LINK_LIBS diff --git a/clang/lib/Edit/FillInMissingProtocolStubs.cpp b/clang/lib/Edit/FillInMissingProtocolStubs.cpp new file mode 100644 index 0000000000000..3173a345e56fa --- /dev/null +++ b/clang/lib/Edit/FillInMissingProtocolStubs.cpp @@ -0,0 +1,461 @@ +//===--- FillInMissingProtocolStubs.cpp - --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add methods from protocol(s)" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NSAPI.h" +#include "clang/Edit/RefactoringFixits.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/DenseSet.h" +#include <algorithm> + +using namespace clang; +using namespace edit; +using namespace fillInMissingProtocolStubs; + +// FIXME: This is duplicated with the refactoring lib. +static bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, + const SourceManager &SM) { + return !Loc1.isMacroID() && !Loc2.isMacroID() && + SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); +} + +static bool isSemicolonAtLocation(SourceLocation TokenLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM, + LangOpts) == ";"; +} + +static SourceLocation getLocationOfPrecedingToken(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation Result = Loc; + if (Result.isMacroID()) + Result = SM.getExpansionLoc(Result); + FileID FID = SM.getFileID(Result); + SourceLocation StartOfFile = SM.getLocForStartOfFile(FID); + if (Loc == StartOfFile) + return SourceLocation(); + return Lexer::GetBeginningOfToken(Result.getLocWithOffset(-1), SM, LangOpts); +} + +static SourceLocation +getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(!SpellingLoc.isMacroID() && "Expecting a spelling location"); + SourceLocation NextTokenLoc = + Lexer::findNextTokenLocationAfterTokenAt(SpellingLoc, SM, LangOpts); + if (NextTokenLoc.isValid()) { + bool IsSameLine = areOnSameLine(SpellingLoc, NextTokenLoc, SM); + if (IsSameLine) { + // Could be a ';' on the same line, so try looking after the ';' + if (isSemicolonAtLocation(NextTokenLoc, SM, LangOpts)) + return getLastLineLocationUnlessItHasOtherTokens(NextTokenLoc, SM, + LangOpts); + } else { + SourceLocation LastLoc = SM.translateLineCol( + SM.getFileID(SpellingLoc), SM.getSpellingLineNumber(SpellingLoc), + std::numeric_limits<unsigned>::max()); + if (LastLoc.isValid()) + return LastLoc; + } + } + return Lexer::getLocForEndOfToken(SpellingLoc, 0, SM, LangOpts); +} + +namespace { + +struct ProtocolInfo { + /// The lower the priority, the more important this protocol is considered to + /// be. Typically protocols from the class have lower priority than protocols + /// from superclasses. + int Priority; +}; + +using ProtocolMapTy = llvm::DenseMap<const ObjCProtocolDecl *, ProtocolInfo>; + +/// Contains the set of methods from all the protocols that the class conforms +/// to. +class MethodSet { +public: + struct MethodInfo { + const ObjCMethodDecl *M; + const ObjCProtocolDecl *P; + int ProtocolPriority; + enum MethodPresenceKind { IsDeclared = 0x1, IsImplemented = 0x2 }; + unsigned PresenceKind = 0; + const ObjCMethodDecl *DeclaredOrImplementedMethod = nullptr; + + MethodInfo(const ObjCMethodDecl *M, const ObjCProtocolDecl *P, + int ProtocolPriority) + : M(M), P(P), ProtocolPriority(ProtocolPriority) {} + + bool isRequired() const { + return M->getImplementationControl() == ObjCMethodDecl::Required; + } + void markAs(MethodPresenceKind Kind) { PresenceKind |= Kind; } + bool is(MethodPresenceKind Kind) const { + return (PresenceKind & Kind) == Kind; + } + }; + +private: + llvm::DenseMap<Selector, MethodInfo> InstanceMethods; + llvm::DenseMap<Selector, MethodInfo> ClassMethods; + + void markMethodsFrom(const ObjCContainerDecl *Container, + MethodInfo::MethodPresenceKind Kind) { + for (const ObjCMethodDecl *M : Container->methods()) { + auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods; + auto It = Map.find(M->getSelector()); + if (It != Map.end()) { + It->second.markAs(Kind); + if (!It->second.DeclaredOrImplementedMethod) + It->second.DeclaredOrImplementedMethod = M; + } + } + } + +public: + MethodSet() {} + MethodSet(MethodSet &&Other) = default; + MethodSet &operator=(MethodSet &&Other) = default; + + void gatherMethodsFrom(const ObjCProtocolDecl *P, int Priority) { + for (const ObjCMethodDecl *M : P->methods()) { + if (M->isImplicit()) + continue; + auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods; + Map.insert(std::make_pair(M->getSelector(), MethodInfo(M, P, Priority))); + } + } + + void markImplementedMethods(const ObjCContainerDecl *Container) { + assert(isa<ObjCImplDecl>(Container) && "Not an implementation container"); + markMethodsFrom(Container, MethodInfo::IsImplemented); + if (const auto *ID = dyn_cast<ObjCImplementationDecl>(Container)) { + const auto *I = ID->getClassInterface(); + // Mark declarations from super-classes as implemented to prevent + // redundant implementations. + while ((I = I->getSuperClass())) + markMethodsFrom(I, MethodInfo::IsImplemented); + } + } + + void markDeclaredMethods(const ObjCContainerDecl *Container) { + assert(!isa<ObjCImplDecl>(Container) && "Not an interface container"); + markMethodsFrom(Container, MethodInfo::IsDeclared); + // Mark declarations from super-classes as declared to prevent redundant + // declarations. + if (const auto *I = dyn_cast<ObjCInterfaceDecl>(Container)) { + while ((I = I->getSuperClass())) + markMethodsFrom(I, MethodInfo::IsDeclared); + } + } + + /// Returns true if the given container has missing @required method stubs. + /// + /// For @interfaces, this method returns true when the interface is missing + /// a declaration for any @required method in all of the protocols. + /// For @implementations, this method returns true when the implementation is + /// missing an implementation of any @required method in all of the protocols. + bool hasMissingRequiredMethodStubs(const ObjCContainerDecl *Container) { + MethodInfo::MethodPresenceKind Kind = isa<ObjCImplDecl>(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + for (const auto &I : InstanceMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + return true; + } + for (const auto &I : ClassMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + return true; + } + return false; + } + + std::vector<MethodInfo> + getMissingRequiredMethods(const ObjCContainerDecl *Container) { + MethodInfo::MethodPresenceKind Kind = isa<ObjCImplDecl>(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + std::vector<MethodInfo> Results; + for (const auto &I : InstanceMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + Results.push_back(I.second); + } + for (const auto &I : ClassMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + Results.push_back(I.second); + } + return Results; + } + + SourceLocation findLocationForInsertionForMethodsFromProtocol( + const ObjCProtocolDecl *P, const ObjCContainerDecl *Container, + const SourceManager &SM, const LangOptions &LangOpts) { + MethodInfo::MethodPresenceKind Kind = isa<ObjCImplDecl>(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + llvm::SmallVector<const ObjCMethodDecl *, 4> MethodsFromProtocolInContainer; + for (const ObjCMethodDecl *M : P->methods()) { + if (M->isImplicit()) + continue; + const auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods; + auto It = Map.find(M->getSelector()); + if (It == Map.end()) + continue; + if (!It->second.is(Kind)) + continue; + const ObjCMethodDecl *ContainerMethod = + It->second.DeclaredOrImplementedMethod; + // Ignore method declarations from superclasses. + if (ContainerMethod->getLexicalDeclContext() != Container) + continue; + // This is a method from the given protocol that either declared or + // implemented in the container. + MethodsFromProtocolInContainer.push_back(ContainerMethod); + } + // Find the appropriate source locations by looking + if (MethodsFromProtocolInContainer.empty()) + return SourceLocation(); + SourceLocation Loc = MethodsFromProtocolInContainer[0]->getLocEnd(); + if (Loc.isMacroID()) + Loc = SM.getExpansionRange(Loc).second; + for (const ObjCMethodDecl *M : + makeArrayRef(MethodsFromProtocolInContainer).drop_front()) { + SourceLocation EndLoc = M->getLocEnd(); + if (EndLoc.isMacroID()) + EndLoc = SM.getExpansionRange(EndLoc).second; + if (SM.isBeforeInTranslationUnit(Loc, EndLoc)) + Loc = EndLoc; + } + return getLastLineLocationUnlessItHasOtherTokens(Loc, SM, LangOpts); + } +}; + +} // end anonymous namespace + +namespace clang { +namespace edit { +namespace fillInMissingProtocolStubs { + +class FillInMissingProtocolStubsImpl { +public: + const ObjCContainerDecl *Container; + MethodSet Methods; +}; + +} // end namespace fillInMissingProtocolStubsImpl +} // end namespace edit +} // end namespace clang + +static void gatherProtocols( + llvm::iterator_range<ObjCList<ObjCProtocolDecl>::iterator> Protocols, + NSAPI &API, ProtocolMapTy &Result, int &Priority) { + for (const ObjCProtocolDecl *P : Protocols) { + // Ignore the 'NSObject' protocol. + if (API.getNSClassId(NSAPI::ClassId_NSObject) == P->getIdentifier()) + continue; + gatherProtocols(P->protocols(), API, Result, Priority); + Result.insert(std::make_pair(P, ProtocolInfo{Priority++})); + } +} + +static ProtocolMapTy +gatherSuitableClassProtocols(const ObjCInterfaceDecl *I, + const ObjCContainerDecl *Container, NSAPI &API) { + ProtocolMapTy Result; + // The class of interest should use the protocols from extensions when the + // operation is initiated from the @implementation / extension. + auto ClassProtocols = + Container == I ? I->protocols() : I->all_referenced_protocols(); + int Priority = 0; + gatherProtocols(ClassProtocols, API, Result, Priority); + while ((I = I->getSuperClass())) + gatherProtocols(I->protocols(), API, Result, Priority); + return Result; +} + +static const ObjCContainerDecl * +getInterfaceOrCategory(const ObjCContainerDecl *Container) { + if (const auto *Impl = dyn_cast<ObjCImplementationDecl>(Container)) + return Impl->getClassInterface(); + if (const auto *CategoryImpl = dyn_cast<ObjCCategoryImplDecl>(Container)) + return CategoryImpl->getCategoryDecl(); + return Container; +} + +static bool initiate(FillInMissingProtocolStubsImpl &Dest, ASTContext &Context, + const ObjCContainerDecl *Container) { + const ObjCContainerDecl *ContainerProtocolSource = + getInterfaceOrCategory(Container); + if (!ContainerProtocolSource) + return false; + + // The protocols that are specified in the @interface and/or in the + // superclasses. + ProtocolMapTy Protocols; + NSAPI API(Context); + if (const auto *I = dyn_cast<ObjCInterfaceDecl>(ContainerProtocolSource)) { + if (!I->hasDefinition()) + return false; + Protocols = gatherSuitableClassProtocols(I, Container, API); + if (Protocols.empty()) + return false; + } else if (const auto *I = + dyn_cast<ObjCCategoryDecl>(ContainerProtocolSource)) { + int Priority = 0; + gatherProtocols(I->protocols(), API, Protocols, Priority); + if (Protocols.empty()) + return false; + } + + // Check if there are missing @required methods. + for (const auto &P : Protocols) + Dest.Methods.gatherMethodsFrom(P.first, P.second.Priority); + if (isa<ObjCImplDecl>(Container)) + Dest.Methods.markImplementedMethods(Container); + else + Dest.Methods.markDeclaredMethods(Container); + + Dest.Container = Container; + return true; +} + +FillInMissingProtocolStubs::FillInMissingProtocolStubs() {} +FillInMissingProtocolStubs::~FillInMissingProtocolStubs() {} +FillInMissingProtocolStubs::FillInMissingProtocolStubs( + FillInMissingProtocolStubs &&Other) + : Impl(std::move(Other.Impl)) {} +FillInMissingProtocolStubs &FillInMissingProtocolStubs:: +operator=(FillInMissingProtocolStubs &&Other) { + Impl = std::move(Other.Impl); + return *this; +} + +bool FillInMissingProtocolStubs::initiate(ASTContext &Context, + const ObjCContainerDecl *Container) { + Impl = llvm::make_unique<FillInMissingProtocolStubsImpl>(); + if (!::initiate(*Impl, Context, Container)) + return true; + return false; +} + +bool FillInMissingProtocolStubs::hasMissingRequiredMethodStubs() { + return Impl->Methods.hasMissingRequiredMethodStubs(Impl->Container); +} + +static void perform(MethodSet &Methods, const ObjCContainerDecl *Container, + ASTContext &Context, + llvm::function_ref<void(const FixItHint &)> Consumer) { + auto MissingMethods = Methods.getMissingRequiredMethods(Container); + // Sort the methods by grouping them into protocol clusters and then sorting + // them alphabetically within the same protocol. + std::sort(MissingMethods.begin(), MissingMethods.end(), + [](const MethodSet::MethodInfo &A, const MethodSet::MethodInfo &B) { + if (A.ProtocolPriority == B.ProtocolPriority) + return A.M->getSelector().getAsString() < + B.M->getSelector().getAsString(); + assert(A.P != B.P && "Same protocols should have same priority"); + return A.ProtocolPriority < B.ProtocolPriority; + }); + + SourceLocation InsertionLoc = + isa<ObjCImplDecl>(Container) + ? Container->getLocEnd() + : getLocationOfPrecedingToken(Container->getLocEnd(), + Context.getSourceManager(), + Context.getLangOpts()); + if (InsertionLoc.isInvalid()) + InsertionLoc = Container->getLocEnd(); + + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + + std::string EndInsertionOSStr; + llvm::raw_string_ostream EndInsertionOS(EndInsertionOSStr); + + std::string InsertionGroupStr; + llvm::raw_string_ostream InsertionGroupOS(InsertionGroupStr); + + const ObjCProtocolDecl *CurrentProtocol = nullptr; + SourceLocation CurrentProtocolInsertionLoc; + bool IsImplementation = isa<ObjCImplDecl>(Container); + for (const auto &Method : MissingMethods) { + const ObjCProtocolDecl *P = Method.P; + if (CurrentProtocol != P) { + if (!InsertionGroupOS.str().empty()) { + assert(CurrentProtocolInsertionLoc.isValid()); + Consumer(FixItHint::CreateInsertion(CurrentProtocolInsertionLoc, + InsertionGroupOS.str())); + } + InsertionGroupStr.clear(); + CurrentProtocol = P; + CurrentProtocolInsertionLoc = + Methods.findLocationForInsertionForMethodsFromProtocol( + P, Container, Context.getSourceManager(), Context.getLangOpts()); + } + bool IsInsertingAfterRelatedMethods = CurrentProtocolInsertionLoc.isValid(); + raw_ostream &OS = + IsInsertingAfterRelatedMethods ? InsertionGroupOS : EndInsertionOS; + + std::string MethodDeclStr; + llvm::raw_string_ostream MethodOS(MethodDeclStr); + Method.M->print(MethodOS, PP); + if (IsInsertingAfterRelatedMethods) + OS << "\n\n"; + OS << StringRef(MethodOS.str()).drop_back(); // Drop the ';' + if (IsImplementation) + OS << " { \n <#code#>\n}\n"; + else + OS << ";\n"; + if (!IsInsertingAfterRelatedMethods) + OS << "\n"; + } + if (!InsertionGroupOS.str().empty()) { + assert(CurrentProtocolInsertionLoc.isValid()); + Consumer(FixItHint::CreateInsertion(CurrentProtocolInsertionLoc, + InsertionGroupOS.str())); + } + if (!EndInsertionOS.str().empty()) + Consumer(FixItHint::CreateInsertion(InsertionLoc, EndInsertionOS.str())); +} + +void FillInMissingProtocolStubs::perform( + ASTContext &Context, llvm::function_ref<void(const FixItHint &)> Consumer) { + ::perform(Impl->Methods, Impl->Container, Context, Consumer); +} + +void fillInMissingProtocolStubs::addMissingProtocolStubs( + ASTContext &Context, const ObjCContainerDecl *Container, + llvm::function_ref<void(const FixItHint &)> Consumer) { + FillInMissingProtocolStubsImpl Impl; + if (initiate(Impl, Context, Container)) + perform(Impl.Methods, Impl.Container, Context, Consumer); +} diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp new file mode 100644 index 0000000000000..e2dcdb1f36d65 --- /dev/null +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -0,0 +1,189 @@ +//===--- FillInMissingSwitchEnumCases.cpp - ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/Edit/RefactoringFixits.h" +#include <unordered_map> + +using namespace clang; + +namespace { + +struct CaseInfo { + const SwitchCase *Case, *NextCase; + unsigned Index; +}; +typedef std::unordered_map<int64_t, CaseInfo> CoveredEnumCasesInfoType; + +/// Return true if the ordering of the covered enum cases is similar to the +/// order of the enum case constants that are defined in the enum. +bool useCaseBasedOrdering(const ArrayRef<int64_t> &CoveredEnumCaseValues, + const CoveredEnumCasesInfoType &CoveredEnumCases) { + if (CoveredEnumCaseValues.empty()) + return false; + for (const auto &I : llvm::enumerate(CoveredEnumCaseValues)) { + auto It = CoveredEnumCases.find(I.value()); + if (It == CoveredEnumCases.end()) + return false; + const CaseInfo &Case = It->second; + if (Case.Index != I.index()) + return false; + } + return true; +} + +/// Determine if the inserted cases should be wrapped in braces using a simple +/// heuristic: +/// Wrap only if at least 90% of existing cases use braces. +bool useBraces(const SwitchStmt *S) { + unsigned CaseCount = 0, CompoundCasesCount = 0; + for (const SwitchCase *Case = S->getSwitchCaseList(); Case; + Case = Case->getNextSwitchCase(), ++CaseCount) { + if (isa<CompoundStmt>(Case->getSubStmt())) + ++CompoundCasesCount; + } + return CaseCount && float(CompoundCasesCount) / float(CaseCount) >= 0.9; +} + +} // end anonymous namespace + +void edit::fillInMissingSwitchEnumCases( + ASTContext &Context, const SwitchStmt *Switch, const EnumDecl *Enum, + const DeclContext *SwitchContext, + llvm::function_ref<void(const FixItHint &)> Consumer) { + // Compute the number of cases in the switch. + unsigned CaseCount = 0; + for (const SwitchCase *Case = Switch->getSwitchCaseList(); Case; + Case = Case->getNextSwitchCase()) + ++CaseCount; + + // Compute the set of enum values that are covered by the switch. + CoveredEnumCasesInfoType CoveredEnumCases; + const SwitchCase *DefaultCase = nullptr; + const SwitchCase *FirstCoveredEnumCase = nullptr; + const SwitchCase *NextCase = nullptr; + unsigned CaseIndex = CaseCount; + for (const SwitchCase *Case = Switch->getSwitchCaseList(); Case; + NextCase = Case, Case = Case->getNextSwitchCase()) { + // The cases in the switch are ordered back to front, so the index has + // to be reversed. + --CaseIndex; + if (isa<DefaultStmt>(Case)) { + DefaultCase = Case; + continue; + } + const auto *CS = cast<CaseStmt>(Case); + if (const auto *LHS = CS->getLHS()) { + llvm::APSInt Value; + if (!LHS->EvaluateAsInt(Value, Context)) + continue; + // Only allow constant that fix into 64 bits. + if (Value.getMinSignedBits() > 64) + continue; + CoveredEnumCases[Value.getSExtValue()] = + CaseInfo{Case, NextCase, CaseIndex}; + // The cases in the switch are ordered back to front, so the last + // case is actually the first enum case in the switch. + FirstCoveredEnumCase = Case; + } + } + + // Wrap the inserted cases in braces using a simple heuristic: + // Wrap only if at least 90% of existing cases use braces. + bool WrapInBraces = useBraces(Switch); + auto CreateReplacementForMissingCaseGroup = + [&](ArrayRef<const EnumConstantDecl *> UncoveredEnumCases, + SourceLocation InsertionLoc = SourceLocation()) { + if (UncoveredEnumCases.empty()) + return; + std::string Result; + llvm::raw_string_ostream OS(Result); + for (const auto *EnumCase : UncoveredEnumCases) { + OS << "case "; + if (SwitchContext) { + const auto *NS = NestedNameSpecifier::getRequiredQualification( + Context, SwitchContext, Enum->getLexicalDeclContext()); + if (NS) + NS->print(OS, Context.getPrintingPolicy()); + } + if (Enum->isScoped()) + OS << Enum->getName() << "::"; + OS << EnumCase->getName() << ":"; + if (WrapInBraces) + OS << " {"; + OS << "\n<#code#>\nbreak;\n"; + if (WrapInBraces) + OS << "}\n"; + } + + if (InsertionLoc.isInvalid()) { + // Insert the cases before the 'default' if it's the last case in the + // switch. + // Note: Switch cases are ordered back to front, so the last default + // case would be the first case in the switch statement. + if (DefaultCase && DefaultCase == Switch->getSwitchCaseList()) + InsertionLoc = DefaultCase->getLocStart(); + else + InsertionLoc = Switch->getBody()->getLocEnd(); + } + Consumer(FixItHint::CreateInsertion(InsertionLoc, OS.str())); + }; + + // Determine which enum cases are uncovered. + + llvm::SmallVector<std::pair<const EnumConstantDecl *, int64_t>, 8> EnumCases; + llvm::SmallVector<int64_t, 8> CoveredEnumCaseValues; + for (const auto *EnumCase : Enum->enumerators()) { + if (EnumCase->getInitVal().getMinSignedBits() > 64) + continue; + int64_t Value = EnumCase->getInitVal().getSExtValue(); + EnumCases.push_back(std::make_pair(EnumCase, Value)); + if (CoveredEnumCases.count(Value)) + CoveredEnumCaseValues.push_back(Value); + } + + llvm::SmallVector<const EnumConstantDecl *, 8> UncoveredEnumCases; + // Figure out if the ordering of the covered enum cases is similar to the + // order of enum case values defined in the enum. + if (useCaseBasedOrdering(CoveredEnumCaseValues, CoveredEnumCases)) { + // Start inserting before the first covered case. + SourceLocation InsertionLoc = FirstCoveredEnumCase->getLocStart(); + + for (const auto &EnumCase : EnumCases) { + if (!CoveredEnumCases.count(EnumCase.second)) { + UncoveredEnumCases.push_back(EnumCase.first); + continue; + } + // Create the insertion source replacement for this set of uncovered + // cases. + CreateReplacementForMissingCaseGroup(UncoveredEnumCases, InsertionLoc); + UncoveredEnumCases.clear(); + // Find the insertion location for the next set of uncovered cases. + auto It = CoveredEnumCases.find(EnumCase.second); + assert(It != CoveredEnumCases.end() && "Missing enum case"); + const CaseInfo &Case = It->second; + InsertionLoc = Case.NextCase ? Case.NextCase->getLocStart() + : /*Insert before end*/ SourceLocation(); + } + CreateReplacementForMissingCaseGroup(UncoveredEnumCases, InsertionLoc); + } else { + // Gather all of the uncovered enum cases. + for (const auto &EnumCase : EnumCases) { + if (!CoveredEnumCases.count(EnumCase.second)) + UncoveredEnumCases.push_back(EnumCase.first); + } + assert(!UncoveredEnumCases.empty() && + "Can't fill-in enum cases in a full switch"); + CreateReplacementForMissingCaseGroup(UncoveredEnumCases); + } +} diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 447ff212f06e1..08a27e80ae8de 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -1258,6 +1258,35 @@ SourceLocation Lexer::findLocationAfterToken(SourceLocation Loc, return TokenLoc.getLocWithOffset(Tok.getLength() + NumWhitespaceChars); } +SourceLocation Lexer::findNextTokenLocationAfterTokenAt( + SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts) { + // TODO: Share the code with the function above when upstreaming. + if (Loc.isMacroID()) { + if (!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) + return SourceLocation(); + } + Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); + + // Break down the source location. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + + // Try to load the file buffer. + bool InvalidTemp = false; + StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp); + if (InvalidTemp) + return SourceLocation(); + + const char *TokenBegin = File.data() + LocInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(), + TokenBegin, File.end()); + // Find the token. + Token Tok; + lexer.LexFromRawLexer(Tok); + return Tok.getLocation(); +} + /// getCharAndSizeSlow - Peek a single 'character' from the specified buffer, /// get its size, and return it. This is tricky in several cases: /// 1. If currently at the start of a trigraph, we warn about the trigraph, diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index fe920fd61f6c8..f6a18fd8cb90e 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -20,6 +20,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/SourceManager.h" +#include "clang/Edit/RefactoringFixits.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" @@ -1095,7 +1096,8 @@ Decl *Sema::ActOnCompatibilityAlias(SourceLocation AtLoc, // Everything checked out, instantiate a new alias declaration AST. ObjCCompatibleAliasDecl *AliasDecl = - ObjCCompatibleAliasDecl::Create(Context, CurContext, AtLoc, AliasName, CDecl); + ObjCCompatibleAliasDecl::Create(Context, CurContext, AliasLocation, + AliasName, CDecl, ClassLocation, AtLoc); if (!CheckObjCDeclScope(AliasDecl)) PushOnScopeChains(AliasDecl, TUScope); @@ -2605,14 +2607,12 @@ static void findProtocolsWithExplicitImpls(const ObjCInterfaceDecl *Super, /// CheckProtocolMethodDefs - This routine checks unimplemented methods /// Declared in protocol, and those referenced by it. -static void CheckProtocolMethodDefs(Sema &S, - SourceLocation ImpLoc, - ObjCProtocolDecl *PDecl, - bool& IncompleteImpl, - const Sema::SelectorSet &InsMap, - const Sema::SelectorSet &ClsMap, - ObjCContainerDecl *CDecl, - LazyProtocolNameSet &ProtocolsExplictImpl) { +static void CheckProtocolMethodDefs( + Sema &S, SourceLocation ImpLoc, ObjCProtocolDecl *PDecl, + bool &IncompleteImpl, const Sema::SelectorSet &InsMap, + const Sema::SelectorSet &ClsMap, ObjCContainerDecl *CDecl, + LazyProtocolNameSet &ProtocolsExplictImpl, + llvm::SmallPtrSetImpl<ObjCProtocolDecl *> *MissingRequirements) { ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl); ObjCInterfaceDecl *IDecl = C ? C->getClassInterface() : dyn_cast<ObjCInterfaceDecl>(CDecl); @@ -2672,6 +2672,7 @@ static void CheckProtocolMethodDefs(Sema &S, // protocol. This lookup is slow, but occurs rarely in correct code // and otherwise would terminate in a warning. + bool HasMissingRequirements = false; // check unimplemented instance methods. if (!NSIDecl) for (auto *method : PDecl->instance_methods()) { @@ -2701,8 +2702,11 @@ static void CheckProtocolMethodDefs(Sema &S, continue; unsigned DIAG = diag::warn_unimplemented_protocol_method; if (!S.Diags.isIgnored(DIAG, ImpLoc)) { - WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, - PDecl); + if (MissingRequirements) + HasMissingRequirements = true; + else + WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, + PDecl); } } } @@ -2724,14 +2728,21 @@ static void CheckProtocolMethodDefs(Sema &S, unsigned DIAG = diag::warn_unimplemented_protocol_method; if (!S.Diags.isIgnored(DIAG, ImpLoc)) { - WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, PDecl); + if (MissingRequirements) + HasMissingRequirements = true; + else + WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, PDecl); } } } + if (HasMissingRequirements) { + assert(MissingRequirements != nullptr && "No missing requirements!"); + MissingRequirements->insert(PDecl); + } // Check on this protocols's referenced protocols, recursively. for (auto *PI : PDecl->protocols()) CheckProtocolMethodDefs(S, ImpLoc, PI, IncompleteImpl, InsMap, ClsMap, - CDecl, ProtocolsExplictImpl); + CDecl, ProtocolsExplictImpl, MissingRequirements); } /// MatchAllMethodDeclarations - Check methods declared in interface @@ -2943,23 +2954,49 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, LazyProtocolNameSet ExplicitImplProtocols; + bool UseEditorDiagnostics = !getDiagnostics() + .getDiagnosticOptions() + .DiagnosticSerializationFile.empty() || + getLangOpts().AllowEditorPlaceholders; + llvm::SmallPtrSet<ObjCProtocolDecl *, 4> MissingRequirements; if (ObjCInterfaceDecl *I = dyn_cast<ObjCInterfaceDecl> (CDecl)) { for (auto *PI : I->all_referenced_protocols()) CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), PI, IncompleteImpl, - InsMap, ClsMap, I, ExplicitImplProtocols); + InsMap, ClsMap, I, ExplicitImplProtocols, + UseEditorDiagnostics ? &MissingRequirements + : nullptr); } else if (ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl)) { // For extended class, unimplemented methods in its protocols will // be reported in the primary class. if (!C->IsClassExtension()) { for (auto *P : C->protocols()) - CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), P, - IncompleteImpl, InsMap, ClsMap, CDecl, - ExplicitImplProtocols); + CheckProtocolMethodDefs( + *this, IMPDecl->getLocation(), P, IncompleteImpl, InsMap, ClsMap, + CDecl, ExplicitImplProtocols, + UseEditorDiagnostics ? &MissingRequirements : nullptr); DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, /*SynthesizeProperties=*/false); } } else llvm_unreachable("invalid ObjCContainerDecl type."); + if (!MissingRequirements.empty()) { + { + auto DB = Diag(IMPDecl->getLocation(), + diag::warn_class_does_not_conform_protocol) + << (isa<ObjCCategoryDecl>(CDecl) ? /*category=*/1 : /*class=*/0) + << CDecl << (unsigned)MissingRequirements.size(); + unsigned NumProtocols = 0; + for (const auto *PD : MissingRequirements) { + DB << PD; + if (++NumProtocols > 3) + break; + } + } + auto DB = + Diag(IMPDecl->getLocation(), diag::note_add_missing_protocol_stubs); + edit::fillInMissingProtocolStubs::addMissingProtocolStubs( + Context, IMPDecl, [&](const FixItHint &Hint) { DB << Hint; }); + } } Sema::DeclGroupPtrTy diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index eed10b077eb88..98032a04cc789 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -11,11 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "clang/Sema/SemaInternal.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" -#include "clang/AST/CharUnits.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" @@ -26,11 +25,13 @@ #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Edit/RefactoringFixits.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/SemaInternal.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" @@ -1160,14 +1161,22 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, // Produce a nice diagnostic if multiple values aren't handled. if (!UnhandledNames.empty()) { - DiagnosticBuilder DB = Diag(CondExpr->getExprLoc(), - TheDefaultStmt ? diag::warn_def_missing_case - : diag::warn_missing_case) - << (int)UnhandledNames.size(); - - for (size_t I = 0, E = std::min(UnhandledNames.size(), (size_t)3); - I != E; ++I) - DB << UnhandledNames[I]; + { + DiagnosticBuilder DB = + Diag(CondExpr->getExprLoc(), TheDefaultStmt + ? diag::warn_def_missing_case + : diag::warn_missing_case) + << (int)UnhandledNames.size(); + + for (size_t I = 0, E = std::min(UnhandledNames.size(), (size_t)3); + I != E; ++I) + DB << UnhandledNames[I]; + } + auto DB = + Diag(CondExpr->getExprLoc(), diag::note_fill_in_missing_cases); + edit::fillInMissingSwitchEnumCases( + Context, SS, ED, CurContext, + [&](const FixItHint &Hint) { DB << Hint; }); } if (!hasCasesNotInSwitch) diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9c467055fe551..c9ccc16fa9e7c 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1136,6 +1136,8 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) { VisitNamedDecl(CAD); CAD->setClassInterface(ReadDeclAs<ObjCInterfaceDecl>()); + CAD->setClassInterfaceLoc(ReadSourceLocation()); + CAD->setAtLoc(ReadSourceLocation()); } void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 2d648cb103cbf..8d1428a925257 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -784,6 +784,8 @@ void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { void ASTDeclWriter::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *D) { VisitNamedDecl(D); Record.AddDeclRef(D->getClassInterface()); + Record.AddSourceLocation(D->getClassInterfaceLoc()); + Record.AddSourceLocation(D->getAtLoc()); Code = serialization::DECL_OBJC_COMPATIBLE_ALIAS; } diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt index 7b0c58e7947d6..33214509d450f 100644 --- a/clang/lib/Tooling/CMakeLists.txt +++ b/clang/lib/Tooling/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS ) add_subdirectory(Core) +add_subdirectory(Refactor) add_subdirectory(Refactoring) add_clang_library(clangTooling @@ -30,4 +31,6 @@ add_clang_library(clangTooling clangLex clangRewrite clangToolingCore + clangToolingRefactor + clangToolingRefactoring ) diff --git a/clang/lib/Tooling/CompilationDatabase.cpp b/clang/lib/Tooling/CompilationDatabase.cpp index 77c5b547ca09a..0238b1c8079c3 100644 --- a/clang/lib/Tooling/CompilationDatabase.cpp +++ b/clang/lib/Tooling/CompilationDatabase.cpp @@ -257,8 +257,10 @@ static bool stripPositionalArgs(std::vector<const char *> Args, for (const auto &Cmd : Jobs) { // Collect only for Assemble jobs. If we do all jobs we get duplicates // since Link jobs point to Assemble jobs as inputs. - if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass) + if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass || + Cmd.getSource().getKind() == driver::Action::CompileJobClass) { CompileAnalyzer.run(&Cmd.getSource()); + } } if (CompileAnalyzer.Inputs.empty()) { diff --git a/clang/lib/Tooling/Refactor/ASTSlice.cpp b/clang/lib/Tooling/Refactor/ASTSlice.cpp new file mode 100644 index 0000000000000..5447571caee8c --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTSlice.cpp @@ -0,0 +1,625 @@ +//===--- ASTSlice.cpp - Represents a portion of the AST -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ASTSlice.h" +#include "SourceLocationUtilities.h" +#include "StmtUtils.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/SaveAndRestore.h" +#include <algorithm> + +using namespace clang; +using namespace clang::tooling; + +namespace { + +/// Searches for AST nodes around the given source location and range that can +/// be used to initiate a refactoring operation. +class ASTSliceFinder : public clang::RecursiveASTVisitor<ASTSliceFinder> { +public: + explicit ASTSliceFinder(SourceLocation Location, SourceRange SelectionRange, + const ASTContext &Context) + : Location(Location), SelectionRange(SelectionRange), Context(Context) {} + + bool TraverseDecl(Decl *D) { + if (!D) + return true; + if (isa<DeclContext>(D) && !D->isImplicit()) + collectDeclIfInRange(D); + // TODO: Handle Lambda/Blocks. + if (!isa<FunctionDecl>(D) && !isa<ObjCMethodDecl>(D)) { + RecursiveASTVisitor::TraverseDecl(D); + return true; + } + const Decl *PreviousDecl = CurrentDecl; + CurrentDecl = D; + RecursiveASTVisitor::TraverseDecl(D); + CurrentDecl = PreviousDecl; + return true; + } + + bool TraverseStmt(Stmt *S) { + if (!S) + return true; + // PseudoObjectExpressions don't have to be parents. + if (isa<PseudoObjectExpr>(S)) + return RecursiveASTVisitor::TraverseStmt(S); + llvm::SaveAndRestore<const Stmt *> Parent(ParentStmt, CurrentStmt); + llvm::SaveAndRestore<const Stmt *> Current(CurrentStmt, S); + RecursiveASTVisitor::TraverseStmt(S); + return true; + } + + bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { + // Avoid traversing the getter/setter message sends for property + // expressions. + TraverseStmt(E->getSyntacticForm()); + return true; + } + + bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + RecursiveASTVisitor::TraverseObjCPropertyRefExpr(E); + // Visit the opaque base manually as it won't be traversed by the + // PseudoObjectExpr. + if (E->isObjectReceiver()) { + if (const auto *Opaque = dyn_cast<OpaqueValueExpr>(E->getBase())) + TraverseStmt(Opaque->getSourceExpr()); + } + return true; + } + + // Statement visitors: + + bool VisitStmt(Stmt *S) { + collectStmtIfInRange(S, S->getSourceRange()); + return true; + } + + // Ignore some implicit expressions. + + bool WalkUpFromMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { + return true; + } + + bool WalkUpFromCXXThisExpr(CXXThisExpr *E) { + if (E->isImplicit()) + return true; + return RecursiveASTVisitor::WalkUpFromCXXThisExpr(E); + } + + /// Checks if the given statement and its source range has the location + /// of interest or overlaps with the selection range, and adds this node to + /// the set of statements for the slice that's being constructed. + void collectStmtIfInRange(const Stmt *S, SourceRange Range) { + SourceLocation Start = Range.getBegin(); + const auto &SM = Context.getSourceManager(); + bool IsStartMacroArg = false; + if (Start.isMacroID()) { + if (SM.isMacroArgExpansion(Start)) { + Start = SM.getSpellingLoc(Start); + IsStartMacroArg = true; + } else { + Start = SM.getExpansionLoc(Start); + } + } + SourceLocation End = Range.getEnd(); + if (End.isMacroID() && SM.isMacroArgExpansion(End)) { + // Ignore the node that's span across normal code and a macro argument. + if (IsStartMacroArg) + End = SM.getSpellingLoc(End); + } + End = getPreciseTokenLocEnd(End, SM, Context.getLangOpts()); + if (!isPairOfFileLocations(Start, End)) + return; + if (SelectionRange.isValid()) { + if (!areRangesOverlapping(SelectionRange, SourceRange(Start, End), + Context.getSourceManager())) + return; + } else if (!isPointWithin(Location, Start, End, Context.getSourceManager())) + return; + Matches.emplace_back(S, ParentStmt, CurrentDecl, SourceRange(Start, End)); + } + + void collectDeclIfInRange(const Decl *D) { + SourceLocation Start = D->getSourceRange().getBegin(); + SourceLocation End = getPreciseTokenLocEnd( + getLexicalEndLocForDecl(D, Context.getSourceManager(), + Context.getLangOpts()), + Context.getSourceManager(), Context.getLangOpts()); + if (!isPairOfFileLocations(Start, End)) + return; + if (SelectionRange.isValid()) { + if (!areRangesOverlapping(SelectionRange, SourceRange(Start, End), + Context.getSourceManager())) + return; + } else if (!isPointWithin(Location, Start, End, Context.getSourceManager())) + return; + Matches.emplace_back(D, CurrentDecl, SourceRange(Start, End)); + } + + SmallVector<ASTSlice::Node, 16> Matches; + /// The point of interest. + /// + /// Represents a location at which refactoring should be initiated. + const SourceLocation Location; + const SourceRange SelectionRange; + const ASTContext &Context; + const Decl *CurrentDecl = nullptr; + const Stmt *ParentStmt = nullptr, *CurrentStmt = nullptr; +}; + +} // end anonymous namespace + +ASTSlice::SelectedStmt::SelectedStmt(ASTSlice &Slice, const Stmt *S, + unsigned Index) + : Slice(Slice), S(S), Index(Index) { + assert(S && "No statement given!"); +} + +ASTSlice::SelectedDecl::SelectedDecl(const Decl *D) : D(D) { + assert(D && "No decl given!"); +} + +const Decl *ASTSlice::SelectedStmt::getParentDecl() { + return Slice.parentDeclForIndex(Index); +} + +ASTSlice::ASTSlice(SourceLocation Location, SourceRange SelectionRange, + ASTContext &Context) + : Context(Context), SelectionLocation(Location), + SelectionRange(SelectionRange) { + FileID SearchFile = Context.getSourceManager().getFileID(Location); + ASTSliceFinder Visitor(Location, SelectionRange, Context); + SourceLocation EndLoc; + for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { + if (EndLoc.isValid() && + !Context.getSourceManager().isBeforeInTranslationUnit( + CurrDecl->getLocStart(), EndLoc)) + break; + const SourceLocation FileLoc = + Context.getSourceManager().getSpellingLoc(CurrDecl->getLocStart()); + if (Context.getSourceManager().getFileID(FileLoc) == SearchFile) + Visitor.TraverseDecl(CurrDecl); + // We are only interested in looking at a single top level declaration + // even if our selection range spans across multiple top level declarations. + if (!Visitor.Matches.empty()) { + // Objective-C @implementation declarations might have trailing functions + // that are declared outside of the @implementation, so continue looking + // through them. + if (isa<ObjCImplDecl>(CurrDecl)) { + EndLoc = CurrDecl->getLocEnd(); + continue; + } + break; + } + } + + for (auto I = Visitor.Matches.rbegin(), E = Visitor.Matches.rend(); I != E; + ++I) + NodeTree.push_back(*I); +} + +bool ASTSlice::isSourceRangeSelected(CharSourceRange Range) const { + SourceRange R = Range.getAsRange(); + if (Range.isTokenRange()) + R.setEnd(getPreciseTokenLocEnd(R.getEnd(), Context.getSourceManager(), + Context.getLangOpts())); + if (SelectionRange.isInvalid()) + return isPointWithin(SelectionLocation, R.getBegin(), R.getEnd(), + Context.getSourceManager()); + return areRangesOverlapping(SelectionRange, R, Context.getSourceManager()); +} + +/// Find the 'if' statement that acts as the start of the +/// 'if'/'else if'/'else' construct. +static std::pair<const IfStmt *, unsigned> +findIfStmtStart(const IfStmt *If, unsigned Index, + ArrayRef<ASTSlice::Node> NodeTree) { + if (Index >= NodeTree.size()) + return {If, Index}; // We've reached the top of the tree, return. + const auto *ParentIf = + dyn_cast_or_null<IfStmt>(NodeTree[Index + 1].getStmtOrNull()); + // The current 'if' is actually an 'else if' when the next 'if' has an else + // statement that points to the current 'if'. + if (!ParentIf || ParentIf->getElse() != If) + return {If, Index}; + return findIfStmtStart(ParentIf, Index + 1, NodeTree); +} + +/// Find an expression that best represents the given selected expression. +static std::pair<const Stmt *, unsigned> +canonicalizeSelectedExpr(const Stmt *S, unsigned Index, + ArrayRef<ASTSlice::Node> NodeTree) { + const auto Same = std::make_pair(S, Index); + if (Index + 1 >= NodeTree.size()) + return Same; + const Stmt *Parent = NodeTree[Index + 1].getStmtOrNull(); + if (!Parent) + return Same; + + const auto Next = std::make_pair(Parent, Index + 1); + // The entire pseudo expression is selected when just its syntactic + // form is selected. + if (isa<Expr>(S)) { + if (const auto *POE = dyn_cast_or_null<PseudoObjectExpr>(Parent)) { + if (POE->getSyntacticForm() == S) + return Next; + } + } + // The entire ObjC string literal is selected when just its string + // literal is selected. + if (isa<StringLiteral>(S) && isa<ObjCStringLiteral>(Parent)) + return Next; + // The entire call should be selected when just the member expression + // that refers to the method is selected. + // FIXME: Check if this can be one of the call arguments. + if (isa<MemberExpr>(S) && isa<CXXMemberCallExpr>(Parent)) + return Next; + // The entire call should be selected when just the callee is selected. + if (const auto *DRE = dyn_cast<DeclRefExpr>(S)) { + if (const auto *Call = dyn_cast<CallExpr>(Parent)) { + if (Call->getCalleeDecl() == DRE->getDecl()) + return Next; + } + } + return Same; +} + +Optional<ASTSlice::SelectedStmt> ASTSlice::nearestSelectedStmt( + llvm::function_ref<bool(const Stmt *)> Predicate) { + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Stmt *S = Node.value().getStmtOrNull(); + if (!S || !Predicate(S)) + continue; + + // Found the match. Perform any additional adjustments. + if (isa<Expr>(S)) { + auto CanonicalExpr = canonicalizeSelectedExpr(S, Node.index(), NodeTree); + return SelectedStmt(*this, CanonicalExpr.first, CanonicalExpr.second); + } + switch (S->getStmtClass()) { + case Stmt::IfStmtClass: { + // TODO: Fix findIfStmtStart bug with Index where it will return the + // index of the last statement. + auto If = findIfStmtStart(cast<IfStmt>(S), Node.index(), NodeTree); + return SelectedStmt(*this, If.first, If.second); + } + default: + break; + } + + return SelectedStmt(*this, S, Node.index()); + } + return None; +} + +Optional<ASTSlice::SelectedStmt> +ASTSlice::nearestSelectedStmt(Stmt::StmtClass Class) { + return nearestSelectedStmt( + [Class](const Stmt *S) -> bool { return S->getStmtClass() == Class; }); +} + +const Stmt *ASTSlice::nearestStmt(Stmt::StmtClass Class) { + auto Result = nearestSelectedStmt(Class); + return Result ? Result->getStmt() : nullptr; +} + +Optional<ASTSlice::SelectedDecl> ASTSlice::innermostSelectedDecl( + llvm::function_ref<bool(const Decl *)> Predicate, unsigned Options) { + if (SelectionRange.isValid()) { + if (Options & ASTSlice::InnermostDeclOnly) { + auto Result = getInnermostCompletelySelectedDecl(); + if (!Result) + return None; + if (Predicate(Result->getDecl())) + return Result; + return None; + } + // Traverse down through all of the selected node checking the predicate. + // TODO: Cache the SelectionRangeOverlap kinds properly instead of relying + // on getInnermostCompletelySelectedDecl. + getInnermostCompletelySelectedDecl(); + for (const auto &N : NodeTree) { + const Decl *D = N.getDeclOrNull(); + if (!D) + continue; + if (N.SelectionRangeOverlap != Node::ContainsSelectionRange) + continue; + if (Predicate(D)) + return SelectedDecl(D); + } + return None; + } + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Decl *D = Node.value().getDeclOrNull(); + if (!D) + continue; + if (Predicate(D)) + return SelectedDecl(D); + if (Options & ASTSlice::InnermostDeclOnly) + return None; + } + return None; +} + +Optional<ASTSlice::SelectedDecl> +ASTSlice::innermostSelectedDecl(ArrayRef<Decl::Kind> Classes, + unsigned Options) { + assert(!Classes.empty() && "Expected at least one decl kind"); + return innermostSelectedDecl( + [&](const Decl *D) { + for (Decl::Kind Class : Classes) { + if (D->getKind() == Class) + return true; + } + return false; + }, + Options); +} + +/// Compute the SelectionRangeOverlap kinds for matched AST nodes. +/// +/// The overlap kinds are computed only upto the first node that contains the +/// entire selection range. +static void +computeSelectionRangeOverlapKinds(MutableArrayRef<ASTSlice::Node> NodeTree, + SourceRange SelectionRange, + const SourceManager &SM) { + for (ASTSlice::Node &Node : NodeTree) { + bool HasStart = + isPointWithin(SelectionRange.getBegin(), Node.Range.getBegin(), + Node.Range.getEnd(), SM); + bool HasEnd = isPointWithin(SelectionRange.getEnd(), Node.Range.getBegin(), + Node.Range.getEnd(), SM); + if (HasStart && HasEnd) + Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRange; + else if (HasStart) + Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRangeStart; + else if (HasEnd) + Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRangeEnd; + } +} + +const Stmt *findFirstStatementAfter(const CompoundStmt *CS, SourceLocation Loc, + const SourceManager &SM) { + for (const Stmt *S : CS->body()) { + if (!SM.isBeforeInTranslationUnit(S->getLocStart(), Loc)) + return S; + } + return nullptr; +} + +const Stmt *findLastStatementBefore(const CompoundStmt *CS, SourceLocation Loc, + const Stmt *StartAt, + const SourceManager &SM) { + auto It = std::find(CS->body_begin(), CS->body_end(), StartAt); + assert(It != CS->body_end()); + const Stmt *Last = StartAt; + for (auto E = CS->body_end(); It != E; ++It) { + const Stmt *S = *It; + if (!SM.isBeforeInTranslationUnit(S->getLocStart(), Loc)) + return Last; + Last = S; + } + return Last; +} + +/// Return the source construct that contains the given compound statement. +/// +/// This is useful to find the source construct to which the given compound +/// statement belongs to lexically. For example, if we've selected just the +/// body of an if statement, we ideally want to select the entire if statement. +static std::pair<const Stmt *, unsigned> +findCompoundStatementSourceConstruct(const CompoundStmt *CS, + ArrayRef<ASTSlice::Node> NodeTree) { + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Stmt *S = Node.value().getStmtOrNull(); + if (!S) + continue; + for (const Stmt *Child : S->children()) { + if (Child == CS) { + if (isa<CompoundStmt>(S)) + return {CS, 0}; + if (const auto *If = dyn_cast<IfStmt>(S)) + return findIfStmtStart(If, Node.index(), NodeTree); + return {S, Node.index()}; + } + } + } + // This is the outer compound statement. + return {CS, 0}; +} + +/// Return the source construct that contains the given switch case. +static std::pair<const Stmt *, unsigned> +findSwitchSourceConstruct(const SwitchCase *Case, + ArrayRef<ASTSlice::Node> NodeTree) { + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Stmt *S = Node.value().getStmtOrNull(); + if (!S) + continue; + if (isa<SwitchStmt>(S)) + return {S, Node.index()}; + } + return {Case, 0}; +} + +SelectedStmtSet SelectedStmtSet::createFromEntirelySelected(const Stmt *S, + unsigned Index) { + SelectedStmtSet Result; + Result.containsSelectionRange = S; + Result.containsSelectionRangeIndex = Index; + return Result; +} + +Optional<ASTSlice::SelectedDecl> +ASTSlice::getInnermostCompletelySelectedDecl() { + assert(SelectionRange.isValid() && "No selection range!"); + if (CachedSelectedInnermostDecl) + return *CachedSelectedInnermostDecl; + computeSelectionRangeOverlapKinds(NodeTree, SelectionRange, + Context.getSourceManager()); + Optional<SelectedDecl> Result; + for (const auto &N : llvm::enumerate(NodeTree)) { + const Decl *D = N.value().getDeclOrNull(); + if (!D) + continue; + if (N.value().SelectionRangeOverlap != Node::ContainsSelectionRange) + continue; + Result = SelectedDecl(D); + break; + } + CachedSelectedInnermostDecl = Result; + return Result; +} + +static bool isCaseSelected(const SwitchStmt *S, SourceRange SelectionRange, + const SourceManager &SM) { + for (const SwitchCase *Case = S->getSwitchCaseList(); Case; + Case = Case->getNextSwitchCase()) { + SourceRange Range(Case->getLocStart(), Case->getColonLoc()); + if (areRangesOverlapping(Range, SelectionRange, SM)) + return true; + } + return false; +} + +Optional<SelectedStmtSet> ASTSlice::computeSelectedStmtSet() { + if (SelectionRange.isInvalid()) + return None; + computeSelectionRangeOverlapKinds(NodeTree, SelectionRange, + Context.getSourceManager()); + + SelectedStmtSet Result; + for (const auto &N : llvm::enumerate(NodeTree)) { + const auto *S = N.value().getStmtOrNull(); + if (!S) + continue; + switch (N.value().SelectionRangeOverlap) { + case Node::ContainsSelectionRange: { + Result.containsSelectionRange = S; + Result.containsSelectionRangeIndex = N.index(); + + const auto *CS = dyn_cast<CompoundStmt>(Result.containsSelectionRange); + if (!CS) { + // The entire if should be selected when just the 'else if' overlaps + // with the selection range. + if (const auto *If = dyn_cast<IfStmt>(Result.containsSelectionRange)) { + auto IfConstruct = findIfStmtStart(If, N.index(), NodeTree); + return SelectedStmtSet::createFromEntirelySelected( + IfConstruct.first, IfConstruct.second); + } + // The entire switch should be selected when just a 'case' overlaps + // with the selection range. + if (const auto *Case = + dyn_cast<SwitchCase>(Result.containsSelectionRange)) { + auto Switch = findSwitchSourceConstruct( + Case, makeArrayRef(NodeTree).drop_front(N.index() + 1)); + return SelectedStmtSet::createFromEntirelySelected( + Switch.first, N.index() + Switch.second); + } + + auto CanonicalExpr = canonicalizeSelectedExpr(S, N.index(), NodeTree); + Result.containsSelectionRange = CanonicalExpr.first; + Result.containsSelectionRangeIndex = CanonicalExpr.second; + return Result; + } + + bool IsLBraceSelected = + !Context.getSourceManager().isBeforeInTranslationUnit( + CS->getLBracLoc(), SelectionRange.getBegin()); + bool IsRBraceSelected = + Context.getSourceManager().isBeforeInTranslationUnit( + CS->getRBracLoc(), SelectionRange.getEnd()); + + // Return the entire source construct that has the compound statement + // when one of the braces is selected, or when an actual `case` of the + // switch is selected. + auto Construct = findCompoundStatementSourceConstruct( + CS, makeArrayRef(NodeTree).drop_front(N.index() + 1)); + if (Construct.first != CS && + ((IsLBraceSelected || IsRBraceSelected) || + (isa<SwitchStmt>(Construct.first) && + isCaseSelected(cast<SwitchStmt>(Construct.first), SelectionRange, + Context.getSourceManager())))) + return SelectedStmtSet::createFromEntirelySelected( + Construct.first, N.index() + Construct.second); + + // When both braces are selected the entire compound statement is + // considered to be selected. + if (IsLBraceSelected && IsRBraceSelected) + return Result; + if (IsLBraceSelected) + Result.containsSelectionRangeStart = CS->body_front(); + else if (IsRBraceSelected) + Result.containsSelectionRangeEnd = CS->body_back(); + + if (!Result.containsSelectionRangeStart) + Result.containsSelectionRangeStart = findFirstStatementAfter( + CS, SelectionRange.getBegin(), Context.getSourceManager()); + + // Return an empty set when the compound statements os empty or the + // selection range starts after the last statement or the selection range + // doesn't overlap with any actual statements. + if (!Result.containsSelectionRangeStart || + !Context.getSourceManager().isBeforeInTranslationUnit( + Result.containsSelectionRangeStart->getLocStart(), + SelectionRange.getEnd())) + return None; + + if (!Result.containsSelectionRangeEnd) + Result.containsSelectionRangeEnd = findLastStatementBefore( + CS, SelectionRange.getEnd(), Result.containsSelectionRangeStart, + Context.getSourceManager()); + + return Result; + } + case Node::ContainsSelectionRangeStart: + Result.containsSelectionRangeStart = S; + break; + case Node::ContainsSelectionRangeEnd: + Result.containsSelectionRangeEnd = S; + break; + case Node::UnknownOverlap: + break; + } + } + return Result; +} + +Optional<SelectedStmtSet> ASTSlice::getSelectedStmtSet() { + if (CachedSelectedStmtSet) + return *CachedSelectedStmtSet; + CachedSelectedStmtSet = computeSelectedStmtSet(); + return *CachedSelectedStmtSet; +} + +bool ASTSlice::isContainedInCompoundStmt(unsigned Index) { + assert(Index < NodeTree.size() && "Invalid node index"); + for (unsigned I = Index + 1, E = NodeTree.size(); I != E; ++I) { + const Stmt *S = NodeTree[I].getStmtOrNull(); + if (!S) + continue; + if (isa<CompoundStmt>(S)) + return true; + } + return false; +} + +const Decl *ASTSlice::parentDeclForIndex(unsigned Index) { + return NodeTree[Index].ParentDecl; +} + +const Stmt *ASTSlice::parentStmtForIndex(unsigned Index) { + return NodeTree[Index].ParentStmt; +} diff --git a/clang/lib/Tooling/Refactor/ASTSlice.h b/clang/lib/Tooling/Refactor/ASTSlice.h new file mode 100644 index 0000000000000..f5cb9c6f3afd4 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTSlice.h @@ -0,0 +1,182 @@ +//===--- ASTSlice.h - Represents a portion of the AST ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H + +#include "clang/AST/DeclBase.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +class NamedDecl; + +namespace tooling { + +/// Represents a set of statements that overlap with the selection range. +struct SelectedStmtSet { + /// The outermost statement that contains the start of the selection range. + const Stmt *containsSelectionRangeStart = nullptr; + + /// The outermost statement that contains the end of the selection range. + const Stmt *containsSelectionRangeEnd = nullptr; + + /// The innermost statement that contains the entire selection range. + const Stmt *containsSelectionRange = nullptr; + + /// The index of the innermost statement that contains the entire selection + /// range. The index points into the NodeTree stored in the \c ASTSlice. + Optional<unsigned> containsSelectionRangeIndex; + + static SelectedStmtSet createFromEntirelySelected(const Stmt *S, + unsigned Index); + + /// Returns true if the compound statement is not fully selected. + bool isCompoundStatementPartiallySelected() const { + assert(containsSelectionRange && "No statement selected"); + return isa<CompoundStmt>(containsSelectionRange) && + (containsSelectionRangeStart || containsSelectionRangeEnd); + } +}; + +/// A portion of the AST that is located around the location and/or source +/// range of interest. +class ASTSlice { +public: + struct Node { + enum SelectionRangeOverlapKind { + UnknownOverlap, + ContainsSelectionRangeStart, + ContainsSelectionRangeEnd, + ContainsSelectionRange + }; + llvm::PointerUnion<const Stmt *, const Decl *> StmtOrDecl; + const Stmt *ParentStmt; + const Decl *ParentDecl; + SourceRange Range; + SelectionRangeOverlapKind SelectionRangeOverlap = UnknownOverlap; + + const Stmt *getStmtOrNull() const { + return StmtOrDecl.dyn_cast<const Stmt *>(); + } + + const Decl *getDeclOrNull() const { + return StmtOrDecl.dyn_cast<const Decl *>(); + } + + Node(const Stmt *S, const Stmt *ParentStmt, const Decl *ParentDecl, + SourceRange Range) + : StmtOrDecl(S), ParentStmt(ParentStmt), ParentDecl(ParentDecl), + Range(Range) {} + Node(const Decl *D, const Decl *ParentDecl, SourceRange Range) + : StmtOrDecl(D), ParentStmt(nullptr), ParentDecl(ParentDecl), + Range(Range) {} + }; + + /// Represents a statement that overlaps with the selection range/point. + class SelectedStmt { + ASTSlice &Slice; + const Stmt *S; + unsigned Index; + + friend class ASTSlice; + + SelectedStmt(ASTSlice &Slice, const Stmt *S, unsigned Index); + + public: + const Stmt *getStmt() { return S; } + const Decl *getParentDecl(); + }; + + /// Represents a declaration that overlaps with the selection range/point. + class SelectedDecl { + const Decl *D; + + friend class ASTSlice; + + SelectedDecl(const Decl *D); + + public: + const Decl *getDecl() { return D; } + }; + + ASTSlice(SourceLocation Location, SourceRange SelectionRange, + ASTContext &Context); + + /// Returns true if the given source range overlaps with the selection. + bool isSourceRangeSelected(CharSourceRange Range) const; + + enum SelectionSearchOptions { + /// Search with-in the innermost declaration only, including the declaration + /// itself without inspecting any other outer declarations. + InnermostDeclOnly = 1 + }; + + /// Returns the statement that results in true when passed into \p Predicate + /// that's nearest to the location of interest, or \c None if such statement + /// isn't found. + Optional<SelectedStmt> + nearestSelectedStmt(llvm::function_ref<bool(const Stmt *)> Predicate); + + /// Returns the statement of the given class that's nearest to the location + /// of interest, or \c None if such statement isn't found. + Optional<SelectedStmt> nearestSelectedStmt(Stmt::StmtClass Class); + + /// TODO: Remove in favour of nearestStmt that returns \c SelectedStmt + const Stmt *nearestStmt(Stmt::StmtClass Class); + + /// Returns the declaration that overlaps with the selection range, is + /// nearest to the location of interest and that results in true when passed + /// into \p Predicate, or \c None if such declaration isn't found. + Optional<SelectedDecl> + innermostSelectedDecl(llvm::function_ref<bool(const Decl *)> Predicate, + unsigned Options = 0); + + /// Returns the declaration closest to the location of interest whose decl + /// kind is in \p Classes, or \c None if no such decl can't be found. + Optional<SelectedDecl> innermostSelectedDecl(ArrayRef<Decl::Kind> Classes, + unsigned Options = 0); + + /// Returns the set of statements that overlap with the selection range. + Optional<SelectedStmtSet> getSelectedStmtSet(); + + /// Returns true if the statement with the given index is contained in a + /// compound statement that overlaps with the selection range. + bool isContainedInCompoundStmt(unsigned Index); + + /// Returns the declaration that contains the statement at the given index. + const Decl *parentDeclForIndex(unsigned Index); + + /// Returns the statement that contains the statement at the given index. + const Stmt *parentStmtForIndex(unsigned Index); + +private: + Optional<SelectedStmtSet> computeSelectedStmtSet(); + + /// Returns the innermost declaration that contains both the start and the + /// end of the selection range. + Optional<SelectedDecl> getInnermostCompletelySelectedDecl(); + + /// The lowest element is the top of the hierarchy + SmallVector<Node, 16> NodeTree; + ASTContext &Context; + SourceLocation SelectionLocation; + SourceRange SelectionRange; + Optional<Optional<SelectedStmtSet>> CachedSelectedStmtSet; + Optional<Optional<SelectedDecl>> CachedSelectedInnermostDecl; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H diff --git a/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp new file mode 100644 index 0000000000000..3fa2e606e1f79 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp @@ -0,0 +1,68 @@ +//===-- ASTStateSerialization.cpp - Persists TU-specific state across TUs -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringContinuations.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace clang::tooling; +using namespace clang::tooling::detail; + +namespace { + +class USRToDeclConverter + : public clang::RecursiveASTVisitor<USRToDeclConverter> { + llvm::StringMap<const Decl *> &USRs; + unsigned NumFound = 0; + +public: + USRToDeclConverter(llvm::StringMap<const Decl *> &USRs) : USRs(USRs) {} + + bool isDone() const { return NumFound == USRs.size(); } + + bool VisitNamedDecl(const NamedDecl *D) { + std::string USR = rename::getUSRForDecl(D); + auto It = USRs.find(USR); + if (It == USRs.end() || It->second) + return true; + It->second = D; + ++NumFound; + return NumFound != USRs.size(); + } +}; + +} // end anonymous namespace + +const Decl *PersistentToASTSpecificStateConverter::lookupDecl(StringRef USR) { + auto It = ConvertedDeclRefs.find(USR); + if (It != ConvertedDeclRefs.end()) + return It->second; + // FIXME: If we ever need to convert a PersistentDeclRef through the ASTQuery, + // we have to support conversion without coalesced conversion. + assert(false && "Persistent decl refs should be converted all at once"); + return nullptr; +} + +void PersistentToASTSpecificStateConverter::runCoalescedConversions() { + USRToDeclConverter Converter(ConvertedDeclRefs); + for (Decl *D : Context.getTranslationUnitDecl()->decls()) { + Converter.TraverseDecl(D); + if (Converter.isDone()) + break; + } +} + +FileID +PersistentToASTSpecificStateConverter::convert(const PersistentFileID &Ref) { + FileManager &FM = Context.getSourceManager().getFileManager(); + const FileEntry *Entry = FM.getFile(Ref.Filename); + if (!Entry) + return FileID(); + return Context.getSourceManager().translateFile(Entry); +} diff --git a/clang/lib/Tooling/Refactor/CMakeLists.txt b/clang/lib/Tooling/Refactor/CMakeLists.txt new file mode 100644 index 0000000000000..b65f794de4062 --- /dev/null +++ b/clang/lib/Tooling/Refactor/CMakeLists.txt @@ -0,0 +1,43 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangToolingRefactor + ASTSlice.cpp + ASTStateSerialization.cpp + Extract.cpp + ExtractRepeatedExpressionIntoVariable.cpp + FillInEnumSwitchCases.cpp + FillInMissingMethodStubsFromAbstractClasses.cpp + FillInMissingProtocolStubs.cpp + IfSwitchConversion.cpp + ImplementDeclaredMethods.cpp + IndexerQueries.cpp + LocalizeObjCStringLiteral.cpp + RefactoringActions.cpp + RefactoringActionFinder.cpp + RefactoringOperation.cpp + RefactoringOptions.cpp + RenamingOperation.cpp + RenameIndexedFile.cpp + RenamedSymbol.cpp + SourceLocationUtilities.cpp + StmtUtils.cpp + SymbolOperation.cpp + SymbolOccurrenceFinder.cpp + SymbolName.cpp + SymbolUSRFinder.cpp + TypeUtils.cpp + USRFinder.cpp + + DEPENDS + ClangDriverOptions + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangEdit + clangIndex + clangLex + clangToolingCore + clangRewrite + ) diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp new file mode 100644 index 0000000000000..ac381065709c9 --- /dev/null +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -0,0 +1,1916 @@ +//===--- Extract.cpp - ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "extract" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "StmtUtils.h" +#include "TypeUtils.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Refactor/RefactoringOptions.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/Path.h" +#include <algorithm> + +using namespace clang; +using namespace clang::tooling; + +namespace { + +struct CompoundStatementRange { + CompoundStmt::const_body_iterator First, Last; + + const Stmt *getFirst() const { + // We must have selected just the child of the case, since a selection that + // includes the case is treated like a selection of the entire switch. + if (const auto *Case = dyn_cast<SwitchCase>(*First)) { + if (const Stmt *S = Case->getSubStmt()) + return S; + } + return *First; + } + + const Stmt *getLast() const { return *Last; } + + // TODO: We might not want to iterate over the switch case if we've just + // selected its child. We should switch over to an array of nodes instead of + // an iterator pair instead. + CompoundStmt::const_body_iterator begin() const { return First; } + CompoundStmt::const_body_iterator end() const { return Last + 1; } +}; + +class ExtractOperation : public RefactoringOperation { +public: + struct CandidateInfo { + CandidateInfo(SourceRange Range, StringRef PreInsertedText = "", + const Stmt *AnalyzedStatement = nullptr) + : Range(Range), PreInsertedText(PreInsertedText), + AnalyzedStatement(AnalyzedStatement) {} + + /// The candidate token range, i.e. the end location is the starting + /// location of the last token. + SourceRange Range; + /// The text that should be inserted before the call to the extracted + /// function. + StringRef PreInsertedText; + /// The expression that should be analyzed for captured variables and the + /// return value. + const Stmt *AnalyzedStatement; + }; + + ExtractOperation(const Stmt *S, const Stmt *ParentStmt, + const Decl *FunctionLikeParentDecl, + std::vector<std::string> Candidates, + Optional<CompoundStatementRange> ExtractedStmtRange, + Optional<CandidateInfo> FirstCandidateInfo, + bool IsMethodExtraction) + : S(S), ParentStmt(ParentStmt), + FunctionLikeParentDecl(FunctionLikeParentDecl), + Candidates(std::move(Candidates)), + ExtractedStmtRange(ExtractedStmtRange), + IsMethodExtraction(IsMethodExtraction) { + if (FirstCandidateInfo) + CandidateExtractionInfo.push_back(*FirstCandidateInfo); + } + + const Stmt *getTransformedStmt() const override { + if (ExtractedStmtRange) + return ExtractedStmtRange->getFirst(); + return S; + } + + const Stmt *getLastTransformedStmt() const override { + if (ExtractedStmtRange) + return ExtractedStmtRange->getLast(); + return nullptr; + } + + std::vector<std::string> getRefactoringCandidates() override { + return Candidates; + } + + std::vector<RefactoringActionType> getAvailableSubActions() override { + if (isa<CXXMethodDecl>(FunctionLikeParentDecl) || + isa<ObjCMethodDecl>(FunctionLikeParentDecl)) + return {RefactoringActionType::Extract_Method}; + return {}; + } + + llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const Stmt *S, *ParentStmt; + const Decl *FunctionLikeParentDecl; + std::vector<std::string> Candidates; + /// A set of extraction candidates that correspond to the extracted code. + SmallVector<CandidateInfo, 2> CandidateExtractionInfo; + Optional<CompoundStatementRange> ExtractedStmtRange; + bool IsMethodExtraction; +}; + +} // end anonymous namespace + +bool isSimpleExpression(const Expr *E) { + switch (E->IgnoreParenCasts()->getStmtClass()) { + case Stmt::DeclRefExprClass: + case Stmt::PredefinedExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::StringLiteralClass: + return true; + default: + return false; + } +} + +static bool isMultipleCandidateBinOp(BinaryOperatorKind Op) { + return Op == BO_Add || Op == BO_Sub; +} + +/// Returns the first and the last statements that should be extracted from a +/// compound statement. +Optional<CompoundStatementRange> getExtractedStatements(const CompoundStmt *CS, + const Stmt *Begin, + const Stmt *End) { + if (CS->body_empty()) + return None; + assert(Begin && End); + CompoundStatementRange Result; + Result.First = std::find(CS->body_begin(), CS->body_end(), Begin); + assert(Result.First != CS->body_end()); + Result.Last = std::find(Result.First, CS->body_end(), End); + assert(Result.Last != CS->body_end()); + return Result; +} + +static RefactoringOperationResult +initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, + SourceLocation Location, SourceRange SelectionRange, + bool CreateOperation, + bool IsMethodExtraction = false) { + auto SelectedStmtsOpt = Slice.getSelectedStmtSet(); + if (!SelectedStmtsOpt) + return None; + SelectedStmtSet Stmts = *SelectedStmtsOpt; + // The selection range is contained entirely within this statement (without + // taking leading/trailing comments and whitespace into account). + const Stmt *Selected = Stmts.containsSelectionRange; + + // We only want to perform the extraction if the selection range is entirely + // within a body of a function or method. + if (!Selected) + return None; + const Decl *ParentDecl = + Slice.parentDeclForIndex(*Stmts.containsSelectionRangeIndex); + + if (!ParentDecl || + (!Stmts.isCompoundStatementPartiallySelected() && + !Slice.isContainedInCompoundStmt(*Stmts.containsSelectionRangeIndex))) + return RefactoringOperationResult( + "the selected expression is not in a function"); + + if (isa<Expr>(Selected) && isSimpleExpression(cast<Expr>(Selected))) + return RefactoringOperationResult("the selected expression is too simple"); + if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Selected)) { + if (!PRE->isMessagingGetter()) + return RefactoringOperationResult("property setter can't be extracted"); + } + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + + Optional<CompoundStatementRange> ExtractedStmtRange; + + // Check if there are multiple candidates that can be extracted. + std::vector<std::string> Candidates; + Optional<ExtractOperation::CandidateInfo> FirstCandidateInfo; + if (const auto *BinOp = dyn_cast<BinaryOperator>(Selected)) { + // Binary '+' and '-' operators allow multiple candidates when the + // selection range starts after the LHS expression but still overlaps + // with the RHS. + if (isMultipleCandidateBinOp(BinOp->getOpcode()) && + (!Stmts.containsSelectionRangeStart || + getPreciseTokenLocEnd( + BinOp->getLHS()->getLocEnd(), Context.getSourceManager(), + Context.getLangOpts()) == SelectionRange.getBegin()) && + Stmts.containsSelectionRangeEnd) { + SourceRange FirstCandidateRange = + SourceRange(SelectionRange.getBegin(), BinOp->getLocEnd()); + if (FirstCandidateRange.getEnd().isMacroID()) + FirstCandidateRange.setEnd(Context.getSourceManager().getExpansionLoc( + FirstCandidateRange.getEnd())); + FirstCandidateInfo = ExtractOperation::CandidateInfo( + FirstCandidateRange, "+ ", + /*AnalyzedStatement=*/BinOp->getRHS()); + Candidates.push_back( + Lexer::getSourceText( + CharSourceRange::getTokenRange(FirstCandidateRange), + Context.getSourceManager(), Context.getLangOpts()) + .trim()); + Candidates.push_back(Lexer::getSourceText( + CharSourceRange::getTokenRange(BinOp->getSourceRange()), + Context.getSourceManager(), Context.getLangOpts())); + } + } else if (const auto *CS = dyn_cast<CompoundStmt>(Selected)) { + // We want to extract some child statements from a compound statement unless + // we've selected the entire compound statement including the opening and + // closing brace. + if (Stmts.containsSelectionRangeStart) + ExtractedStmtRange = + getExtractedStatements(CS, Stmts.containsSelectionRangeStart, + Stmts.containsSelectionRangeEnd); + } + + auto Operation = llvm::make_unique<ExtractOperation>( + Selected, Slice.parentStmtForIndex(*Stmts.containsSelectionRangeIndex), + ParentDecl, std::move(Candidates), ExtractedStmtRange, FirstCandidateInfo, + IsMethodExtraction); + auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo; + SourceRange Range; + if (ExtractedStmtRange) + Range = SourceRange(ExtractedStmtRange->getFirst()->getLocStart(), + ExtractedStmtRange->getLast()->getLocEnd()); + else + Range = Selected->getSourceRange(); + bool IsBeginMacroArgument = false; + if (Range.getBegin().isMacroID()) { + if (Context.getSourceManager().isMacroArgExpansion(Range.getBegin())) { + Range.setBegin( + Context.getSourceManager().getSpellingLoc(Range.getBegin())); + IsBeginMacroArgument = true; + } else { + Range.setBegin( + Context.getSourceManager().getExpansionLoc(Range.getBegin())); + } + } + if (Range.getEnd().isMacroID()) { + if (IsBeginMacroArgument && + Context.getSourceManager().isMacroArgExpansion(Range.getEnd())) + Range.setEnd(Context.getSourceManager().getSpellingLoc(Range.getEnd())); + else + Range.setEnd( + Context.getSourceManager().getExpansionRange(Range.getEnd()).second); + } + CandidateExtractionInfo.push_back(ExtractOperation::CandidateInfo(Range)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +RefactoringOperationResult clang::tooling::initiateExtractOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, + CreateOperation); +} + +RefactoringOperationResult clang::tooling::initiateExtractMethodOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + // TODO: Verify that method extraction is actually possible. + return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, + CreateOperation, + /*IsMethodExtraction=*/true); +} + +using ReferencedEntity = + llvm::PointerUnion<const DeclRefExpr *, const FieldDecl *>; + +/// Iterate over the entities (variables/instance variables) that are directly +/// referenced by the given expression \p E. +/// +/// Note: Objective-C ivars are always captured via 'self'. +static void findEntitiesDirectlyReferencedInExpr( + const Expr *E, + llvm::function_ref<void(const ReferencedEntity &Entity)> Handler) { + E = E->IgnoreParenCasts(); + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) + return Handler(DRE); + + if (const auto *ME = dyn_cast<MemberExpr>(E)) { + if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenCasts())) { + if (const auto *FD = dyn_cast_or_null<FieldDecl>(ME->getMemberDecl())) + Handler(FD); + return; + } + if (const auto *MD = ME->getMemberDecl()) { + if (isa<FieldDecl>(MD) || isa<IndirectFieldDecl>(MD)) + findEntitiesDirectlyReferencedInExpr(ME->getBase(), Handler); + } + return; + } + + if (const auto *CO = dyn_cast<ConditionalOperator>(E)) { + findEntitiesDirectlyReferencedInExpr(CO->getTrueExpr(), Handler); + findEntitiesDirectlyReferencedInExpr(CO->getFalseExpr(), Handler); + return; + } + + if (const auto *BO = dyn_cast<BinaryOperator>(E)) { + if (BO->getOpcode() == BO_Comma) + return findEntitiesDirectlyReferencedInExpr(BO->getRHS(), Handler); + } +} + +template <typename T, typename Matcher> +static void +findMatchingParameters(Matcher &ParameterMatcher, const Stmt *S, + ASTContext &Context, StringRef Node, + llvm::function_ref<void(const T *E)> Handler) { + using namespace clang::ast_matchers; + auto Matches = match(findAll(callExpr(ParameterMatcher)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.template getNodeAs<T>(Node)); + Matches = match(findAll(cxxConstructExpr(ParameterMatcher)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.template getNodeAs<T>(Node)); +} + +static void +findUseOfConstThis(const Stmt *S, ASTContext &Context, + llvm::function_ref<void(const CXXThisExpr *E)> Handler) { + using namespace clang::ast_matchers; + // Check the receiver in method call and member operator calls. + auto This = cxxThisExpr().bind("this"); + auto ThisReceiver = ignoringParenCasts( + anyOf(This, unaryOperator(hasOperatorName("*"), + hasUnaryOperand(ignoringParenCasts(This))))); + auto ConstMethodCallee = callee(cxxMethodDecl(isConst())); + auto Matches = match( + findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(ThisReceiver)), + cxxOperatorCallExpr(ConstMethodCallee, + hasArgument(0, ThisReceiver))))), + *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs<CXXThisExpr>("this")); + // Check parameters in calls. + auto ConstPointee = pointee(qualType(isConstQualified())); + auto RefParameter = forEachArgumentWithParam( + ThisReceiver, + parmVarDecl(hasType(qualType(referenceType(ConstPointee))))); + findMatchingParameters(RefParameter, S, Context, "this", Handler); + auto PtrParameter = forEachArgumentWithParam( + ignoringParenCasts(This), + parmVarDecl(hasType(qualType(pointerType(ConstPointee))))); + findMatchingParameters(PtrParameter, S, Context, "this", Handler); +} + +static void findArgumentsPassedByNonConstReference( + const Stmt *S, ASTContext &Context, + llvm::function_ref<void(const Expr *E)> Handler) { + using namespace clang::ast_matchers; + // Check the receiver in method call and member operator calls. + auto NonPointerReceiver = + expr(unless(hasType(qualType(pointerType())))).bind("arg"); + auto NonConstMethodCallee = callee(cxxMethodDecl(unless(isConst()))); + auto Matches = + match(findAll(expr(anyOf( + cxxMemberCallExpr(NonConstMethodCallee, on(NonPointerReceiver)), + cxxOperatorCallExpr(NonConstMethodCallee, + hasArgument(0, NonPointerReceiver))))), + *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs<Expr>("arg")); + // Check parameters in calls. + auto RefParameter = forEachArgumentWithParam( + expr().bind("arg"), parmVarDecl(hasType(qualType(referenceType(unless( + pointee(qualType(isConstQualified())))))))); + Matches = match(findAll(callExpr(RefParameter)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs<Expr>("arg")); + Matches = match(findAll(cxxConstructExpr(RefParameter)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs<Expr>("arg")); +} + +static void findAddressExpressionsPassedByConstPointer( + const Stmt *S, ASTContext &Context, + llvm::function_ref<void(const UnaryOperator *E)> Handler) { + using namespace clang::ast_matchers; + auto ConstPtrParameter = forEachArgumentWithParam( + ignoringParenImpCasts(unaryOperator(hasOperatorName("&")).bind("arg")), + parmVarDecl(hasType( + qualType(pointerType(pointee(qualType(isConstQualified()))))))); + auto Matches = match(findAll(callExpr(ConstPtrParameter)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs<UnaryOperator>("arg")); + Matches = match(findAll(cxxConstructExpr(ConstPtrParameter)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs<UnaryOperator>("arg")); +} + +static bool isImplicitInitializer(const VarDecl *VD) { + assert(VD->hasInit()); + const auto *E = VD->getInit(); + if (isa<ExprWithCleanups>(E)) + return false; + const auto *Construct = dyn_cast<CXXConstructExpr>(E); + if (!Construct) + return E->getLocStart() == VD->getLocation(); + return Construct->getParenOrBraceRange().isInvalid(); +} + +static const Expr *getInitializerExprWithLexicalRange(const Expr *E) { + if (const auto *EWC = dyn_cast<ExprWithCleanups>(E)) { + if (const auto *Construct = dyn_cast<CXXConstructExpr>(EWC->getSubExpr())) { + if (Construct->getNumArgs() == 1) { + if (const auto *ME = + dyn_cast<MaterializeTemporaryExpr>(Construct->getArg(0))) + return ME; + } + } + } + return E; +} + +namespace { + +class ExtractedCodeVisitor : public RecursiveASTVisitor<ExtractedCodeVisitor> { + int DefineOrdering = 0; + +public: + struct CaptureInfo { + bool IsMutated = false; + bool IsDefined = false; + bool IsAddressTaken = false; + bool IsConstAddressTaken = false; + bool IsFieldCapturedWithThis = false; + bool IsUsed = false; + int DefineOrderingPriority = 0; + + bool isPassedByRefOrPtr() const { + return IsMutated || IsAddressTaken || IsConstAddressTaken; + } + bool isRefOrPtrConst() const { + return IsConstAddressTaken && !IsMutated && !IsAddressTaken; + } + }; + + const ImplicitParamDecl *SelfDecl; + + ExtractedCodeVisitor(const ImplicitParamDecl *SelfDecl) + : SelfDecl(SelfDecl) {} + + bool HasReturnInExtracted = false; + + CaptureInfo &captureVariable(const VarDecl *VD) { + CaptureInfo &Result = CapturedVariables[VD]; + Result.IsUsed = true; + return Result; + } + + CaptureInfo &captureField(const FieldDecl *FD) { return CapturedFields[FD]; } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); + if (!VD) + return true; + if (VD == SelfDecl) { + CaptureSelf = true; + SelfType = VD->getType(); + return true; + } + if (!VD->isLocalVarDeclOrParm()) + return true; + captureVariable(VD); + return true; + } + + void captureThisWithoutConstConcerns(const CXXThisExpr *E) { + CaptureThis = true; + ThisRecordType = E->getType()->getPointeeType(); + } + + bool VisitCXXThisExpr(const CXXThisExpr *E) { + captureThisWithoutConstConcerns(E); + ThisUsesWithUnknownConstness.insert(E); + return true; + } + + bool TraverseMemberExpr(MemberExpr *E) { + const auto *Base = dyn_cast<CXXThisExpr>(E->getBase()->IgnoreParenCasts()); + if (!Base) + return RecursiveASTVisitor::TraverseMemberExpr(E); + const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(E->getMemberDecl()); + if (!FD) + return RecursiveASTVisitor::TraverseMemberExpr(E); + CaptureInfo &Info = captureField(FD); + // Don't capture the implicit 'this' for private fields as we don't want to + // capture this if we only use the private fields. + if (FD->getAccess() == AS_public || !Base->isImplicit()) { + Info.IsFieldCapturedWithThis = true; + // The member might have an effect on the constness of the captured 'this' + // but this is checked via mutation/const tracking for the field itself, + // so we just capture 'this' without worrying about checking if it's used + // in a 'const' manner here. + captureThisWithoutConstConcerns(Base); + } + return true; + } + + void captureSuper(QualType T) { + if (CaptureSuper) + return; + SuperType = T; + CaptureSuper = true; + } + + bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + if (E->isSuperReceiver()) + captureSuper(E->getSuperReceiverType()); + // Base might be an opaque expression, so we have to visit it manually as + // we don't necessarily visit the setter/getter message sends if just the + // property was selected. + if (E->isObjectReceiver()) { + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E->getBase())) + TraverseStmt(OVE->getSourceExpr()); + } + return RecursiveASTVisitor::TraverseObjCPropertyRefExpr(E); + } + + bool TraverseBinAssign(BinaryOperator *S) { + // RHS might be an opaque expression, if this is a property assignment. We + // have to visit it manually as we don't necessarily visit the setter/getter + // message sends if just the property was selected. + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(S->getRHS())) + TraverseStmt(OVE->getSourceExpr()); + return RecursiveASTVisitor::TraverseBinAssign(S); + } + + void findCapturedVariableOrFieldsInExpression( + const Expr *E, llvm::function_ref<void(CaptureInfo &)> Handler) { + findEntitiesDirectlyReferencedInExpr( + E, [&Handler, this](const ReferencedEntity &Entity) { + if (const auto *DRE = Entity.dyn_cast<const DeclRefExpr *>()) { + const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (!VD || !VD->isLocalVarDeclOrParm() || VD->isImplicit()) + return; + return Handler(captureVariable(VD)); + } + return Handler(captureField(Entity.get<const FieldDecl *>())); + }); + } + + void + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(const Expr *E) { + findCapturedVariableOrFieldsInExpression( + E, [](CaptureInfo &Capture) { Capture.IsMutated = true; }); + } + + bool VisitBinaryOperator(const BinaryOperator *E) { + if (E->isAssignmentOp()) + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getLHS()); + return true; + } + + bool VisitUnaryPreInc(const UnaryOperator *E) { + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); + return true; + } + + bool VisitUnaryPostInc(const UnaryOperator *E) { + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); + return true; + } + + bool VisitUnaryPreDec(const UnaryOperator *E) { + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); + return true; + } + + bool VisitUnaryPostDec(const UnaryOperator *E) { + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); + return true; + } + + /// If the given expression refers to a local/instance variable or a + /// a member of such variable that variable is marked as captured by + /// reference. + void captureVariableOrFieldInExpressionByReference(const Expr *E) { + findCapturedVariableOrFieldsInExpression( + E, [](CaptureInfo &Capture) { Capture.IsAddressTaken = true; }); + } + + bool VisitUnaryAddrOf(const UnaryOperator *E) { + // Capture the entity with 'const' reference/pointer when its address is + // passed into a function that takes a 'const' pointer and no other + // mutations or non-const address/reference acquisitions occur. + if (AddressExpressionsPassedToConstPointerParameter.count(E)) + findCapturedVariableOrFieldsInExpression( + E->getSubExpr(), + [](CaptureInfo &Capture) { Capture.IsConstAddressTaken = true; }); + else + captureVariableOrFieldInExpressionByReference(E->getSubExpr()); + return true; + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *E) { + if (E->getSuperLoc().isValid()) + captureSuper(E->getSuperType()); + const ObjCMethodDecl *MD = E->getMethodDecl(); + if (!MD) + return true; + for (const auto &Param : llvm::enumerate(MD->parameters())) { + QualType T = Param.value()->getType(); + if (Param.index() >= E->getNumArgs()) + break; + if (T->isReferenceType() && !T->getPointeeType().isConstQualified()) + captureVariableOrFieldInExpressionByReference(E->getArg(Param.index())); + if (T->isPointerType() && T->getPointeeType().isConstQualified()) { + // Check if this is an '&' passed into a const pointer parameter. + const Expr *Arg = E->getArg(Param.index()); + if (const auto *Op = + dyn_cast<UnaryOperator>(Arg->IgnoreParenImpCasts())) { + if (Op->getOpcode() == UO_AddrOf) + AddressExpressionsPassedToConstPointerParameter.insert(Op); + } + } + } + return true; + } + + bool VisitVarDecl(const VarDecl *VD) { + // Don't capture using the captureVariable method as we don't want to mark + // the declaration as a 'use'. This allows us to avoid passing in variables + // that are defined in extracted code, used afterwards, but never actually + // used in the extracted code. + CaptureInfo &Capture = CapturedVariables[VD]; + Capture.IsDefined = true; + Capture.DefineOrderingPriority = ++DefineOrdering; + // Ensure the capture is marked as 'used' when the variable declaration has + // an explicit initialization expression. This allows us to pass it by + // reference when it's defined in extracted code, used afterwards, but never + // actually used in the extracted code. The main reason why we want to try + // to keep this initialization in the extracted code is to preserve + // semantics as the initialization expression might have side-effects. + if (!Capture.IsUsed && VD->hasInit() && !isImplicitInitializer(VD)) + Capture.IsUsed = true; + QualType T = VD->getType(); + if (T->isReferenceType() && !T->getPointeeType().isConstQualified() && + VD->hasInit()) + captureVariableOrFieldInExpressionByReference(VD->getInit()); + return true; + } + + bool VisitReturnStmt(const ReturnStmt *S) { + HasReturnInExtracted = true; + return true; + } + + void InspectExtractedStmt(Stmt *S, ASTContext &Context) { + findAddressExpressionsPassedByConstPointer( + S, Context, [this](const UnaryOperator *Arg) { + AddressExpressionsPassedToConstPointerParameter.insert(Arg); + }); + TraverseStmt(S); + findArgumentsPassedByNonConstReference(S, Context, [this](const Expr *Arg) { + captureVariableOrFieldInExpressionByReference(Arg); + }); + if (CaptureThis && !ThisUsesWithUnknownConstness.empty()) { + // Compare the definite 'const' uses of 'this' to all the seen uses + // (except for the known field uses). + findUseOfConstThis(S, Context, [this](const CXXThisExpr *Arg) { + ThisUsesWithUnknownConstness.erase(Arg); + }); + IsThisConstForNonCapturedFieldUses = ThisUsesWithUnknownConstness.empty(); + } + } + + llvm::DenseMap<const VarDecl *, CaptureInfo> CapturedVariables; + llvm::DenseMap<const FieldDecl *, CaptureInfo> CapturedFields; + llvm::SmallPtrSet<const UnaryOperator *, 8> + AddressExpressionsPassedToConstPointerParameter; + llvm::SmallPtrSet<const CXXThisExpr *, 16> ThisUsesWithUnknownConstness; + bool CaptureThis = false; + bool IsThisConstForNonCapturedFieldUses = true; + QualType ThisRecordType; + bool CaptureSelf = false, CaptureSuper = false; + QualType SelfType, SuperType; +}; + +/// Traverses the extracted code and finds the uses of captured variables +/// that are passed into the extracted function using a pointer. +class VariableDefinedInExtractedCodeUseAfterExtractionFinder + : public RecursiveASTVisitor< + VariableDefinedInExtractedCodeUseAfterExtractionFinder> { + bool IsAfterExtracted = false; + +public: + const Stmt *LastExtractedStmt; + const llvm::SmallPtrSetImpl<const VarDecl *> &VariablesDefinedInExtractedCode; + llvm::SmallPtrSet<const VarDecl *, 4> VariablesUsedAfterExtraction; + + VariableDefinedInExtractedCodeUseAfterExtractionFinder( + const Stmt *LastExtractedStmt, + const llvm::SmallPtrSetImpl<const VarDecl *> + &VariablesDefinedInExtractedCode) + : LastExtractedStmt(LastExtractedStmt), + VariablesDefinedInExtractedCode(VariablesDefinedInExtractedCode) {} + + bool TraverseStmt(Stmt *S) { + RecursiveASTVisitor::TraverseStmt(S); + if (S == LastExtractedStmt) + IsAfterExtracted = true; + return true; + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (!IsAfterExtracted) + return true; + const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); + if (!VD) + return true; + if (VariablesDefinedInExtractedCode.count(VD)) + VariablesUsedAfterExtraction.insert(VD); + return true; + } +}; + +class PossibleShadowingVariableFinder + : public RecursiveASTVisitor<PossibleShadowingVariableFinder> { + const VarDecl *TargetVD; + + PossibleShadowingVariableFinder(const VarDecl *TargetVD) + : TargetVD(TargetVD) {} + +public: + bool VisitVarDecl(const VarDecl *VD) { + if (VD == TargetVD || VD->getName() != TargetVD->getName()) + return true; + return false; + } + + /// Returns true if the given statement \p S has a variable declaration whose + /// name is identical to the given variable declaration \p VD. + static bool hasShadowingVar(const VarDecl *VD, const Stmt *S) { + return !PossibleShadowingVariableFinder(VD).TraverseStmt( + const_cast<Stmt *>(S)); + } +}; + +/// Traverses the extracted code and rewrites the 'return' statements to ensure +/// that they now return some value. +class ReturnRewriter : public RecursiveASTVisitor<ReturnRewriter> { + Rewriter &SourceRewriter; + std::string Text; + +public: + ReturnRewriter(Rewriter &SourceRewriter, StringRef Text) + : SourceRewriter(SourceRewriter), Text(std::string(" ") + Text.str()) {} + + bool VisitReturnStmt(const ReturnStmt *S) { + SourceRewriter.InsertText( + getPreciseTokenLocEnd(S->getLocEnd(), SourceRewriter.getSourceMgr(), + SourceRewriter.getLangOpts()), + Text); + return true; + } +}; + +/// Prints the given initializer expression using the original source code if +/// possible. +static void printInitializerExpressionUsingOriginalSyntax( + const VarDecl *VD, const Expr *E, bool IsDeclaration, const ASTContext &Ctx, + llvm::raw_ostream &OS, const PrintingPolicy &PP) { + E = getInitializerExprWithLexicalRange(E); + SourceRange Range = E->getSourceRange(); + bool UseEquals = true; + bool UseTypeName = false; + if (const auto *Construct = dyn_cast<CXXConstructExpr>(E)) { + SourceRange SubRange = Construct->getParenOrBraceRange(); + if (SubRange.isValid()) { + UseEquals = false; + UseTypeName = true; + Range = SubRange; + } + } + if (Range.getBegin().isMacroID()) + Range.setBegin(Ctx.getSourceManager().getExpansionLoc(Range.getBegin())); + if (Range.getEnd().isMacroID()) + Range.setEnd(Ctx.getSourceManager().getExpansionLoc(Range.getEnd())); + bool IsInvalid = false; + StringRef Text = Lexer::getSourceText(CharSourceRange::getTokenRange(Range), + Ctx.getSourceManager(), + Ctx.getLangOpts(), &IsInvalid); + if (IsDeclaration && UseEquals) + OS << " = "; + else if (!IsDeclaration && UseTypeName) + VD->getType().print(OS, PP); + if (IsInvalid) + E->printPretty(OS, nullptr, PP); + else + OS << Text; +}; + +/// Traverses the extracted code and rewrites the declaration statements that +/// declare variables that are used after the extracted code. +class DefinedInExtractedCodeDeclStmtRewriter + : public RecursiveASTVisitor<DefinedInExtractedCodeDeclStmtRewriter> { +public: + Rewriter &SourceRewriter; + const llvm::SmallPtrSetImpl<const VarDecl *> &VariablesUsedAfterExtraction; + const PrintingPolicy &PP; + + DefinedInExtractedCodeDeclStmtRewriter( + Rewriter &SourceRewriter, const llvm::SmallPtrSetImpl<const VarDecl *> + &VariablesUsedAfterExtraction, + const PrintingPolicy &PP) + : SourceRewriter(SourceRewriter), + VariablesUsedAfterExtraction(VariablesUsedAfterExtraction), PP(PP) {} + + /// When a declaration statement declares variables that are all used + /// after extraction, we can rewrite it completely into a set of assignments + /// while still preserving the original initializer expressions when we + /// can. + void rewriteAllVariableDeclarationsToAssignments(const DeclStmt *S) { + SourceLocation StartLoc = S->getLocStart(); + for (const Decl *D : S->decls()) { + const auto *VD = dyn_cast<VarDecl>(D); + if (!VD || !VariablesUsedAfterExtraction.count(VD)) + continue; + if (!VD->hasInit() || isImplicitInitializer(VD)) { + // Remove the variable declarations without explicit initializers. + // This can affect the semantics of the program if the implicit + // initialization expression has side effects. + SourceRange Range = SourceRange( + StartLoc, S->isSingleDecl() ? S->getLocEnd() : VD->getLocation()); + SourceRewriter.RemoveText(Range); + continue; + } + std::string Str; + llvm::raw_string_ostream OS(Str); + if (StartLoc != S->getLocStart()) + OS << "; "; + const ASTContext &Ctx = D->getASTContext(); + // Dereference the variable unless the source uses C++. + if (!Ctx.getLangOpts().CPlusPlus) + OS << '*'; + OS << VD->getName() << " = "; + const Expr *Init = getInitializerExprWithLexicalRange(VD->getInit()); + SourceLocation End = Init->getLocStart(); + if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) { + SourceRange SubRange = Construct->getParenOrBraceRange(); + if (SubRange.isValid()) { + End = SubRange.getBegin(); + VD->getType().print(OS, PP); + } + } + if (End.isMacroID()) + End = Ctx.getSourceManager().getExpansionLoc(End); + auto Range = CharSourceRange::getCharRange(StartLoc, End); + SourceRewriter.ReplaceText(StartLoc, SourceRewriter.getRangeSize(Range), + OS.str()); + StartLoc = getPreciseTokenLocEnd(D->getLocEnd(), Ctx.getSourceManager(), + Ctx.getLangOpts()); + } + } + + /// When a declaration statement has variables that are both used after + /// extraction and not used after extraction, we create new declaration + /// statements that declare the unused variables, while creating assignment + /// statements that "initialize" the variables that are used after the + /// extraction. This way we can preserve the order of + /// initialization/assignment from the original declaration statement. + void rewriteMixedDeclarations(const DeclStmt *S) { + // Completely rewrite the declaration statement. + std::string Str; + llvm::raw_string_ostream OS(Str); + for (const Decl *D : S->decls()) { + const ASTContext &Ctx = D->getASTContext(); + const VarDecl *VD = dyn_cast<VarDecl>(D); + bool IsLast = D == S->decl_end()[-1]; + if (!VD) { + OS << "<<unsupported declaration>>;"; + continue; + } + + auto PrintInit = [&](bool IsDeclaration) { + printInitializerExpressionUsingOriginalSyntax( + VD, VD->getInit(), IsDeclaration, Ctx, OS, PP); + }; + if (!VariablesUsedAfterExtraction.count(VD)) { + VD->getType().print(OS, PP); + OS << " " << VD->getName(); + if (VD->hasInit() && !isImplicitInitializer(VD)) + PrintInit(/*IsDeclaration=*/true); + OS << ";"; + if (!IsLast) + OS << ' '; + continue; + } + if (VD->hasInit() && !isImplicitInitializer(VD)) { + // Dereference the variable unless the source uses C++. + if (!Ctx.getLangOpts().CPlusPlus) + OS << '*'; + OS << VD->getName() << " = "; + PrintInit(/*IsDeclaration=*/false); + OS << ";"; + if (!IsLast) + OS << ' '; + } + } + SourceRewriter.ReplaceText(S->getSourceRange(), OS.str()); + } + + bool VisitDeclStmt(const DeclStmt *S) { + bool AreAllUsed = true; + bool AreNoneUsed = true; + for (const Decl *D : S->decls()) { + const auto *VD = dyn_cast<VarDecl>(D); + if (!VD || !VariablesUsedAfterExtraction.count(VD)) { + AreAllUsed = false; + continue; + } + AreNoneUsed = false; + // Exit early when both flags were set in the loop. + if (!AreAllUsed) + break; + } + if (AreNoneUsed) + return true; + + if (AreAllUsed) + rewriteAllVariableDeclarationsToAssignments(S); + else + rewriteMixedDeclarations(S); + return true; + } +}; + +/// Takes care of pseudo object expressions and Objective-C properties to avoid +/// duplicate rewrites and missing rewrites. +template <typename T> +class PseudoObjectRewriter : public RecursiveASTVisitor<T> { + typedef RecursiveASTVisitor<T> Base; + +public: + bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { + return Base::TraverseStmt(E->getSyntacticForm()); + } + + bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + // Base might be an opaque expression, so we have to visit it manually as + // we don't necessarily visit the setter/getter message sends if just the + // property was selected. + if (E->isObjectReceiver()) { + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E->getBase())) + Base::TraverseStmt(OVE->getSourceExpr()); + } + return Base::TraverseObjCPropertyRefExpr(E); + } + + bool TraverseBinAssign(BinaryOperator *S) { + // RHS might be an opaque expression, if this is a property assignment. We + // have to visit it manually as we don't necessarily visit the setter/getter + // message sends if just the property was selected. + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(S->getRHS())) + Base::TraverseStmt(OVE->getSourceExpr()); + return Base::TraverseBinAssign(S); + } +}; + +/// Traverses the extracted code and rewrites the uses of captured variables +/// that are passed into the extracted function using a pointer. +class CapturedVariableCaptureByPointerRewriter + : public PseudoObjectRewriter<CapturedVariableCaptureByPointerRewriter> { +public: + const VarDecl *TargetVD; + Rewriter &SourceRewriter; + + CapturedVariableCaptureByPointerRewriter(const VarDecl *VD, + Rewriter &SourceRewriter) + : TargetVD(VD), SourceRewriter(SourceRewriter) {} + + bool isTargetDeclRefExpr(const Expr *E) { + const auto *DRE = dyn_cast<DeclRefExpr>(E); + if (!DRE) + return false; + return dyn_cast<VarDecl>(DRE->getDecl()) == TargetVD; + } + + void dereferenceTargetVar(const Expr *E, bool WrapInParens = false) { + SourceRewriter.InsertTextBefore(E->getLocStart(), + WrapInParens ? "(*" : "*"); + if (WrapInParens) + SourceRewriter.InsertTextAfterToken(E->getLocEnd(), ")"); + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); + if (VD != TargetVD) + return true; + dereferenceTargetVar(E); + return true; + } + + bool TraverseUnaryAddrOf(UnaryOperator *E) { + if (const auto *DRE = + dyn_cast<DeclRefExpr>(E->getSubExpr()->IgnoreParenCasts())) { + const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (VD == TargetVD) { + // Remove the '&' as the variable is now a pointer. + SourceRewriter.RemoveText( + CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart())); + return true; + } + } + return RecursiveASTVisitor::TraverseUnaryAddrOf(E); + } + + bool TraverseMemberExpr(MemberExpr *E) { + if (!E->isArrow()) { + if (const auto *DRE = + dyn_cast<DeclRefExpr>(E->getBase()->IgnoreParenCasts())) { + const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (VD == TargetVD) { + // Replace '.' with '->'. + SourceRewriter.ReplaceText(E->getOperatorLoc(), 1, "->"); + return true; + } + } + } else if (isTargetDeclRefExpr(E->getBase()->IgnoreImpCasts())) { + // Ensure the variable is wrapped in parenthesis when it's the base of + // '->' operator. + dereferenceTargetVar(E->getBase(), /*WrapInParens=*/true); + return true; + } + return RecursiveASTVisitor::TraverseMemberExpr(E); + } +}; + +/// Traverses the extracted code and rewrites the uses of 'this' that can be +/// rewritten as references. +class CapturedThisReferenceRewriter + : public PseudoObjectRewriter<CapturedThisReferenceRewriter> { +public: + Rewriter &SourceRewriter; + llvm::SmallPtrSet<const CXXThisExpr *, 8> RewrittenExpressions; + + CapturedThisReferenceRewriter(Rewriter &SourceRewriter) + : SourceRewriter(SourceRewriter) {} + + void rewriteThis(const CXXThisExpr *E) { + RewrittenExpressions.insert(E); + if (!E->isImplicit()) + SourceRewriter.ReplaceText(E->getLocStart(), 4, "object"); + else + SourceRewriter.InsertText(E->getLocStart(), "object"); + } + + bool VisitMemberExpr(const MemberExpr *E) { + const auto *This = + dyn_cast<CXXThisExpr>(E->getBase()->IgnoreParenImpCasts()); + if (This) { + rewriteThis(This); + if (!This->isImplicit() && E->isArrow()) + SourceRewriter.ReplaceText(E->getOperatorLoc(), 2, "."); + else + SourceRewriter.InsertText(E->getBase()->getLocEnd(), "."); + } + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'this' into '&object'. +class CapturedThisPointerRewriter + : public PseudoObjectRewriter<CapturedThisPointerRewriter> { +public: + Rewriter &SourceRewriter; + const llvm::SmallPtrSetImpl<const CXXThisExpr *> &RewrittenExpressions; + + CapturedThisPointerRewriter( + Rewriter &SourceRewriter, + const llvm::SmallPtrSetImpl<const CXXThisExpr *> &RewrittenExpressions) + : SourceRewriter(SourceRewriter), + RewrittenExpressions(RewrittenExpressions) {} + + void replace(const CXXThisExpr *E, StringRef Text) { + SourceRewriter.ReplaceText(E->getLocStart(), 4, Text); + } + + bool VisitCXXThisExpr(const CXXThisExpr *E) { + if (RewrittenExpressions.count(E)) + return true; + if (!E->isImplicit()) + replace(E, "&object"); + return true; + } + + bool TraverseUnaryDeref(UnaryOperator *E) { + if (const auto *This = + dyn_cast<CXXThisExpr>(E->getSubExpr()->IgnoreParenImpCasts())) { + if (!This->isImplicit()) { + // Remove the '*' as the variable is now a reference. + SourceRewriter.RemoveText( + CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart())); + replace(This, "object"); + return true; + } + } + return RecursiveASTVisitor::TraverseUnaryAddrOf(E); + } +}; + +/// Traverses the extracted code and rewrites the uses of 'self' into 'object'. +class CapturedSelfRewriter : public PseudoObjectRewriter<CapturedSelfRewriter> { +public: + Rewriter &SourceRewriter; + const ImplicitParamDecl *SelfDecl; + + CapturedSelfRewriter(Rewriter &SourceRewriter, + const ImplicitParamDecl *SelfDecl) + : SourceRewriter(SourceRewriter), SelfDecl(SelfDecl) { + assert(SelfDecl); + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); + if (!VD || VD != SelfDecl) + return true; + if (E->getLocStart().isInvalid()) + return true; + SourceRewriter.ReplaceText(E->getLocStart(), 4, "object"); + return true; + } + + void insertObjectForImplicitSelf(const Expr *E, SourceLocation Loc, + StringRef Text) { + const auto *DRE = dyn_cast<DeclRefExpr>(E); + if (!DRE) + return; + const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (!VD || VD != SelfDecl || DRE->getLocStart().isValid()) + return; + SourceRewriter.InsertText(Loc, Text); + } + + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { + insertObjectForImplicitSelf(E->getBase()->IgnoreImpCasts(), + E->getLocStart(), "object->"); + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'self' into the name +/// of the class. +class CapturedClassSelfRewriter + : public PseudoObjectRewriter<CapturedClassSelfRewriter> { +public: + Rewriter &SourceRewriter; + StringRef ClassName; + const ImplicitParamDecl *SelfDecl; + + CapturedClassSelfRewriter(Rewriter &SourceRewriter, StringRef ClassName, + const ImplicitParamDecl *SelfDecl) + : SourceRewriter(SourceRewriter), ClassName(ClassName), + SelfDecl(SelfDecl) { + + assert(SelfDecl); + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); + if (!VD || VD != SelfDecl || E->getLocStart().isInvalid()) + return true; + SourceRewriter.ReplaceText(E->getLocStart(), 4, ClassName); + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'super' into +/// 'superObject' or the name of the super class. +class CapturedSuperRewriter + : public PseudoObjectRewriter<CapturedSuperRewriter> { +public: + Rewriter &SourceRewriter; + StringRef ReplacementString; + + CapturedSuperRewriter(Rewriter &SourceRewriter, StringRef ReplacementString) + : SourceRewriter(SourceRewriter), ReplacementString(ReplacementString) {} + + void rewriteSuper(SourceLocation Loc) { + SourceRewriter.ReplaceText(Loc, strlen("super"), ReplacementString); + } + + bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *E) { + if (E->isSuperReceiver()) + rewriteSuper(E->getReceiverLocation()); + return true; + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *E) { + if (E->getSuperLoc().isValid()) + rewriteSuper(E->getSuperLoc()); + return true; + } +}; + +struct ExtractionSemicolonPolicy { + bool IsNeededInExtractedFunction; + bool IsNeededInOriginalFunction; + + static ExtractionSemicolonPolicy neededInExtractedFunction() { + return {true, false}; + } + static ExtractionSemicolonPolicy neededInOriginalFunction() { + return {false, true}; + } + static ExtractionSemicolonPolicy neededInBoth() { return {true, true}; } +}; + +} // end anonymous namespace + +ExtractionSemicolonPolicy +computeSemicolonExtractionPolicy(const Stmt *S, SourceRange &ExtractedRange, + const SourceManager &SM, + const LangOptions &LangOpts) { + if (isa<Expr>(S)) + return ExtractionSemicolonPolicy::neededInExtractedFunction(); + bool NeedsSemi = isSemicolonRequiredAfter(S); + if (!NeedsSemi) + return ExtractionSemicolonPolicy::neededInOriginalFunction(); + SourceLocation End = ExtractedRange.getEnd(); + if (isSemicolonAtLocation(End, SM, LangOpts)) + return ExtractionSemicolonPolicy::neededInOriginalFunction(); + SourceLocation NextTokenLoc = + Lexer::findNextTokenLocationAfterTokenAt(End, SM, LangOpts); + if (NextTokenLoc.isValid() && + isSemicolonAtLocation(NextTokenLoc, SM, LangOpts) && + areOnSameLine(NextTokenLoc, End, SM)) { + ExtractedRange.setEnd(NextTokenLoc); + return ExtractionSemicolonPolicy::neededInOriginalFunction(); + } + return ExtractionSemicolonPolicy::neededInBoth(); +} + +PrintingPolicy getPrintingPolicy(const ASTContext &Context, + const Preprocessor &PP) { + PrintingPolicy Policy = Context.getPrintingPolicy(); + // Our printing policy is copied over the ASTContext printing policy whenever + // a diagnostic is emitted, so recompute it. + Policy.Bool = Context.getLangOpts().Bool; + // FIXME: This is duplicated with Sema.cpp. When upstreaming this should be + // cleaned up. + if (!Policy.Bool) { + if (const MacroInfo *BoolMacro = PP.getMacroInfo(Context.getBoolName())) { + Policy.Bool = BoolMacro->isObjectLike() && + BoolMacro->getNumTokens() == 1 && + BoolMacro->getReplacementToken(0).is(tok::kw__Bool); + } + } + return Policy; +} + +static bool isAssignmentOperator(const Stmt *S) { + if (const auto *PseudoExpr = dyn_cast<PseudoObjectExpr>(S)) + return isAssignmentOperator(PseudoExpr->getSyntacticForm()); + if (const auto *BO = dyn_cast<BinaryOperator>(S)) + return BO->isAssignmentOp(); + return false; +} + +static QualType getFunctionLikeParentDeclReturnType(const Decl *D) { + // FIXME: might need to handle ObjC blocks in the future. + if (const auto *M = dyn_cast<ObjCMethodDecl>(D)) + return M->getReturnType(); + return cast<FunctionDecl>(D)->getReturnType(); +} + +static const Stmt *getEnclosingDeclBody(const Decl *D) { + // FIXME: might need to handle ObjC blocks in the future. + if (const auto *M = dyn_cast<ObjCMethodDecl>(D)) + return M->getBody(); + return cast<FunctionDecl>(D)->getBody(); +} + +static bool isEnclosingMethodConst(const Decl *D) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) + return MD->isConst(); + return false; +} + +static bool isEnclosingMethodStatic(const Decl *D) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) + return MD->isStatic(); + return false; +} + +static bool isEnclosingMethodOutOfLine(const Decl *D) { + const auto *MD = dyn_cast<CXXMethodDecl>(D); + if (!MD) + return false; + return MD->isOutOfLine(); +} + +static void printEnclosingMethodScope(const Decl *D, llvm::raw_ostream &OS, + const PrintingPolicy &PP) { + const auto *MD = dyn_cast<CXXMethodDecl>(D); + if (!MD) + return; + if (!MD->isOutOfLine() || !MD->getQualifier()) + return; + MD->getQualifier()->print(OS, PP); +} + +static SourceLocation +computeFunctionExtractionLocation(const Decl *D, bool IsMethodExtraction) { + if (!IsMethodExtraction && isa<CXXMethodDecl>(D)) { + // Code from methods that defined in class bodies should be extracted to a + // function defined just before the class. + while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext())) + D = RD; + } + return D->getLocStart(); +} + +namespace { +enum class MethodDeclarationPlacement { After, Before }; + +/// \brief Represents an entity captured from the original function that's +/// passed into the new function/method. +struct CapturedVariable { + const VarDecl *VD; + const FieldDecl *FD; + QualType ThisType; + bool PassByRefOrPtr; + bool IsRefOrPtrConst; + bool IsThisSelf = false; + bool IsThisSuper = false; + bool TakeAddress = false; + QualType ParameterType; + + CapturedVariable(const VarDecl *VD, bool PassByRefOrPtr, bool IsRefOrPtrConst) + : VD(VD), FD(nullptr), PassByRefOrPtr(PassByRefOrPtr), + IsRefOrPtrConst(IsRefOrPtrConst) {} + CapturedVariable(const FieldDecl *FD, bool PassByRefOrPtr, + bool IsRefOrPtrConst) + : VD(nullptr), FD(FD), PassByRefOrPtr(PassByRefOrPtr), + IsRefOrPtrConst(IsRefOrPtrConst) {} + CapturedVariable(QualType ThisType, bool PassByRefOrPtr, bool IsConst) + : VD(nullptr), FD(nullptr), ThisType(ThisType), + PassByRefOrPtr(PassByRefOrPtr), IsRefOrPtrConst(IsConst) {} + + static CapturedVariable getThis(QualType T, bool IsConst) { + return CapturedVariable(T, /*PassByRefOrPtr=*/true, /*IsConst*/ IsConst); + } + + static CapturedVariable getSelf(QualType T) { + auto Result = + CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false); + Result.IsThisSelf = true; + return Result; + } + + static CapturedVariable getSuper(QualType T) { + auto Result = + CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false); + Result.IsThisSuper = true; + return Result; + } + + StringRef getName() const { + return VD ? VD->getName() + : FD ? FD->getName() : IsThisSuper ? "superObject" : "object"; + } + StringRef getExpr() const { + return ThisType.isNull() + ? getName() + : IsThisSelf ? "self" : IsThisSuper ? "super.self" : "*this"; + } + QualType getType() const { + return VD ? VD->getType() : FD ? FD->getType() : ThisType; + } +}; +} // end anonymous namespace + +static std::pair<SourceLocation, MethodDeclarationPlacement> +computeAppropriateExtractionLocationForMethodDeclaration( + const CXXMethodDecl *D) { + const CXXRecordDecl *RD = D->getParent(); + // Try to put the new declaration after the last method, or just before the + // end of the class. + SourceLocation Loc; + for (const CXXMethodDecl *M : RD->methods()) { + if (M->isImplicit()) + continue; + Loc = M->getLocEnd(); + } + return Loc.isValid() ? std::make_pair(Loc, MethodDeclarationPlacement::After) + : std::make_pair(RD->getLocEnd(), + MethodDeclarationPlacement::Before); +} + +static bool isInHeader(SourceLocation Loc, const SourceManager &SM) { + // Base the header decision on the filename. + StringRef Extension = llvm::sys::path::extension(SM.getFilename(Loc)); + if (Extension.empty()) + return false; + return llvm::StringSwitch<bool>(Extension.drop_front()) + .Case("h", true) + .Case("hpp", true) + .Case("hh", true) + .Case("h++", true) + .Case("hxx", true) + .Case("inl", true) + .Case("def", true) + .Default(false); +} + +llvm::Expected<RefactoringResult> ExtractOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector<RefactoringReplacement> Replacements; + SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + Rewriter SourceRewriter(SM, LangOpts); + PrintingPolicy PP = getPrintingPolicy(Context, ThePreprocessor); + PP.UseStdFunctionForLambda = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + + const Stmt *S = + CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement + ? CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement + : this->S; + + const auto *EnclosingObjCMethod = + dyn_cast<ObjCMethodDecl>(FunctionLikeParentDecl); + + // Find the variables that are captured by the extracted code. + ExtractedCodeVisitor Visitor(/*SelfDecl=*/EnclosingObjCMethod + ? EnclosingObjCMethod->getSelfDecl() + : nullptr); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + Visitor.InspectExtractedStmt(const_cast<Stmt *>(S), Context); + } else + Visitor.InspectExtractedStmt(const_cast<Stmt *>(S), Context); + // Compute the return type. + bool IsExpr = isa<Expr>(S); + // Assignment operators should be treated as statements unless they are a part + // of an expression. + if (IsExpr && isAssignmentOperator(S) && + (!ParentStmt || !isa<Expr>(ParentStmt))) + IsExpr = false; + QualType ReturnType; + if (IsExpr || Visitor.HasReturnInExtracted) { + if (const auto *E = dyn_cast<Expr>(S)) { + assert(!ExtractedStmtRange); + ReturnType = findExpressionLexicalType(FunctionLikeParentDecl, E, + E->getType(), PP, Context); + } else + ReturnType = getFunctionLikeParentDeclReturnType(FunctionLikeParentDecl); + } else + ReturnType = Context.VoidTy; + // Sort the captured variables. + std::vector<CapturedVariable> CapturedVariables; + llvm::SmallPtrSet<const VarDecl *, 4> VariablesDefinedInExtractedCode; + CapturedVariables.reserve(Visitor.CapturedVariables.size() + + Visitor.CapturedFields.size()); + for (const auto &I : Visitor.CapturedVariables) { + if (I.getSecond().IsDefined) { + VariablesDefinedInExtractedCode.insert(I.getFirst()); + continue; + } + CapturedVariables.push_back( + CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(), + I.getSecond().isRefOrPtrConst())); + } + // Take a look at the variables that are defined in the extracted code. + VariableDefinedInExtractedCodeUseAfterExtractionFinder + UsedAfterExtractionFinder(ExtractedStmtRange ? *ExtractedStmtRange->Last + : S, + VariablesDefinedInExtractedCode); + UsedAfterExtractionFinder.TraverseStmt( + const_cast<Stmt *>(getEnclosingDeclBody(FunctionLikeParentDecl))); + struct RedeclaredVariable { + const VarDecl *VD; + int OrderingPriority; + }; + llvm::SmallVector<RedeclaredVariable, 4> RedeclaredVariables; + bool CanUseReturnForVariablesUsedAfterwards = + !isa<Expr>(S) && ReturnType->isVoidType() && + UsedAfterExtractionFinder.VariablesUsedAfterExtraction.size() == 1; + if (CanUseReturnForVariablesUsedAfterwards) { + // Avoid using the return value for the variable that's used afterwards as + // another variable might shadow it at the point of a 'return' that we + // have to rewrite to 'return var'. + const VarDecl *VD = + *UsedAfterExtractionFinder.VariablesUsedAfterExtraction.begin(); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) { + if (PossibleShadowingVariableFinder::hasShadowingVar(VD, S)) { + CanUseReturnForVariablesUsedAfterwards = false; + break; + } + } + } else + CanUseReturnForVariablesUsedAfterwards = + !PossibleShadowingVariableFinder::hasShadowingVar(VD, S); + } + if (CanUseReturnForVariablesUsedAfterwards) { + for (const auto &I : Visitor.CapturedVariables) { + if (!I.getSecond().IsDefined || + !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count( + I.getFirst())) + continue; + RedeclaredVariables.push_back( + {I.getFirst(), I.getSecond().DefineOrderingPriority}); + ReturnType = I.getFirst()->getType(); + // Const qualifier can be dropped as we don't want to declare the return + // type as 'const'. + if (ReturnType.isConstQualified()) + ReturnType.removeLocalConst(); + break; + } + if (Visitor.HasReturnInExtracted) { + ReturnRewriter ReturnsRewriter(SourceRewriter, + RedeclaredVariables.front().VD->getName()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + ReturnsRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + ReturnsRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } + } else { + for (const auto &I : Visitor.CapturedVariables) { + if (!I.getSecond().IsDefined || + !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count( + I.getFirst())) + continue; + RedeclaredVariables.push_back( + {I.getFirst(), I.getSecond().DefineOrderingPriority}); + if (!I.getSecond().IsUsed) + continue; + // Pass the variable that's defined in the extracted code but used + // afterwards as a parameter only when it's actually used in the extracted + // code. + CapturedVariables.push_back(CapturedVariable(I.getFirst(), + /*PassByRefOrPtr=*/true, + /*IsRefOrPtrConst=*/false)); + } + std::sort(RedeclaredVariables.begin(), RedeclaredVariables.end(), + [](const RedeclaredVariable &X, const RedeclaredVariable &Y) { + return X.OrderingPriority < Y.OrderingPriority; + }); + DefinedInExtractedCodeDeclStmtRewriter DeclRewriter( + SourceRewriter, UsedAfterExtractionFinder.VariablesUsedAfterExtraction, + PP); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + DeclRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + DeclRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } + // Capture any fields if necessary. + bool IsThisConstInCapturedFieldUses = true; + if (!IsMethodExtraction) { + for (const auto &I : Visitor.CapturedFields) { + if (I.getSecond().isPassedByRefOrPtr() && + !I.getSecond().isRefOrPtrConst()) + IsThisConstInCapturedFieldUses = false; + // Private fields that use explicit 'this' should be captured using 'this' + // even if they might end up being inaccessible in the extracted function. + if (I.getSecond().IsFieldCapturedWithThis) + continue; + CapturedVariables.push_back( + CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(), + I.getSecond().isRefOrPtrConst())); + } + } + std::sort(CapturedVariables.begin(), CapturedVariables.end(), + [](const CapturedVariable &X, const CapturedVariable &Y) { + return X.getName() < Y.getName(); + }); + // 'This'/'self' should be passed-in first. + if (!IsMethodExtraction && Visitor.CaptureThis) { + CapturedVariables.insert( + CapturedVariables.begin(), + CapturedVariable::getThis( + Visitor.ThisRecordType, + IsThisConstInCapturedFieldUses && + Visitor.IsThisConstForNonCapturedFieldUses)); + CapturedThisReferenceRewriter ThisRewriter(SourceRewriter); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + ThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + ThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); + CapturedThisPointerRewriter PtrThisRewriter( + SourceRewriter, ThisRewriter.RewrittenExpressions); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else if (!IsMethodExtraction && Visitor.CaptureSelf && + EnclosingObjCMethod) { + if (EnclosingObjCMethod->isInstanceMethod()) { + // Instance methods rewrite 'self' into an 'object' parameter. + CapturedVariables.insert(CapturedVariables.begin(), + CapturedVariable::getSelf(Visitor.SelfType)); + CapturedSelfRewriter SelfRewriter(SourceRewriter, + EnclosingObjCMethod->getSelfDecl()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else { + // Class methods rewrite 'self' into the class name and don't pass 'self' + // as a parameter. + CapturedClassSelfRewriter SelfRewriter( + SourceRewriter, EnclosingObjCMethod->getClassInterface()->getName(), + EnclosingObjCMethod->getSelfDecl()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } + } + if (!IsMethodExtraction && Visitor.CaptureSuper && EnclosingObjCMethod) { + if (EnclosingObjCMethod->isInstanceMethod()) + // Instance methods rewrite 'super' into an 'superObject' parameter. + CapturedVariables.insert(Visitor.CaptureSelf + ? CapturedVariables.begin() + 1 + : CapturedVariables.begin(), + CapturedVariable::getSuper(Visitor.SuperType)); + CapturedSuperRewriter SuperRewriter( + SourceRewriter, EnclosingObjCMethod->isInstanceMethod() + ? "superObject" + : EnclosingObjCMethod->getClassInterface() + ->getSuperClass() + ->getName()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + SuperRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + SuperRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } + + // Compute the parameter types. + for (auto &Var : CapturedVariables) { + QualType T = Var.getType(); + + // Array types are passed into the extracted function using a pointer. + if (const auto *AT = Context.getAsArrayType(T)) + T = Context.getPointerType(AT->getElementType()); + + // Captured records and other mutated variables are passed into the + // extracted function either using a reference (C++) or a pointer. + if ((T->isRecordType() || Var.PassByRefOrPtr) && !T->isReferenceType()) { + // Add a 'const' qualifier to the record when it's not mutated in the + // extracted code or when we are taking the address of the captured + // variable for just a 'const' use. + if (!Var.PassByRefOrPtr || Var.IsRefOrPtrConst) + T.addConst(); + + if (LangOpts.CPlusPlus) + T = Context.getLValueReferenceType(T); + else { + T = Context.getPointerType(T); + CapturedVariableCaptureByPointerRewriter UseRewriter(Var.VD, + SourceRewriter); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + UseRewriter.TraverseStmt(const_cast<Stmt *>(S)); + } else + UseRewriter.TraverseStmt(const_cast<Stmt *>(S)); + Var.TakeAddress = true; + } + } + // Const qualifier can be dropped as we don't want to declare the parameter + // as 'const'. + else if (T.isLocalConstQualified()) + T.removeLocalConst(); + + Var.ParameterType = T; + } + + // TODO: Choose a better name if there are collisions. + StringRef ExtractedName = "extracted"; + llvm::SmallVector<StringRef, 4> ExtractedNamePieces; + ExtractedNamePieces.push_back(ExtractedName); + if (IsMethodExtraction && EnclosingObjCMethod && !CapturedVariables.empty()) { + for (const auto &Var : llvm::makeArrayRef(CapturedVariables).drop_front()) + ExtractedNamePieces.push_back(Var.getName()); + } + std::unique_ptr<RefactoringResultAssociatedSymbol> CreatedSymbol = + llvm::make_unique<RefactoringResultAssociatedSymbol>( + SymbolName(ExtractedNamePieces)); + + SourceLocation FunctionExtractionLoc = computeFunctionExtractionLocation( + FunctionLikeParentDecl, IsMethodExtraction); + FunctionExtractionLoc = + getLocationOfPrecedingComment(FunctionExtractionLoc, SM, LangOpts); + + // Create the replacement that contains the new function. + auto PrintFunctionHeader = + [&](llvm::raw_string_ostream &OS, + bool IsDefinition = + true) -> RefactoringReplacement::AssociatedSymbolLocation { + if (IsMethodExtraction && EnclosingObjCMethod) { + OS << (EnclosingObjCMethod->isClassMethod() ? '+' : '-') << " ("; + ReturnType.print(OS, PP); + OS << ')'; + llvm::SmallVector<unsigned, 4> NameOffsets; + NameOffsets.push_back(OS.str().size()); + OS << ExtractedName; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) { + OS << ' '; + NameOffsets.push_back(OS.str().size()); + OS << Var.getName(); + } + IsFirst = false; + OS << ":("; + Var.ParameterType.print(OS, PP); + OS << ')' << Var.getName(); + } + return RefactoringReplacement::AssociatedSymbolLocation( + NameOffsets, /*IsDeclaration=*/true); + } + auto *FD = dyn_cast<FunctionDecl>(FunctionLikeParentDecl); + if (IsMethodExtraction && IsDefinition && + !FD->getDescribedFunctionTemplate()) { + // Print the class template parameter lists for an out-of-line method. + for (unsigned I = 0, + NumTemplateParams = FD->getNumTemplateParameterLists(); + I < NumTemplateParams; ++I) { + FD->getTemplateParameterList(I)->print(OS, PP); + OS << "\n"; + } + } + if (IsMethodExtraction && isEnclosingMethodStatic(FunctionLikeParentDecl)) + OS << "static "; + else if (!IsMethodExtraction) + OS << (isInHeader(FunctionExtractionLoc, SM) ? "inline " : "static "); + ReturnType.print(OS, PP); + OS << ' '; + if (IsMethodExtraction && IsDefinition) + printEnclosingMethodScope(FunctionLikeParentDecl, OS, PP); + unsigned NameOffset = OS.str().size(); + OS << ExtractedName << '('; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) + OS << ", "; + IsFirst = false; + Var.ParameterType.print(OS, PP, /*PlaceHolder=*/Var.getName()); + } + OS << ')'; + if (IsMethodExtraction && isEnclosingMethodConst(FunctionLikeParentDecl)) + OS << " const"; + return RefactoringReplacement::AssociatedSymbolLocation( + NameOffset, /*IsDeclaration=*/true); + ; + }; + + if (IsMethodExtraction && + isEnclosingMethodOutOfLine(FunctionLikeParentDecl)) { + // The location of the declaration should be either before the original + // declararation, or, if this method has not declaration, somewhere + // appropriate in the class. + MethodDeclarationPlacement Placement; + SourceLocation DeclarationLoc; + if (FunctionLikeParentDecl->getCanonicalDecl() != FunctionLikeParentDecl) { + DeclarationLoc = computeFunctionExtractionLocation( + FunctionLikeParentDecl->getCanonicalDecl(), IsMethodExtraction); + Placement = MethodDeclarationPlacement::Before; + } else { + auto LocAndPlacement = + computeAppropriateExtractionLocationForMethodDeclaration( + cast<CXXMethodDecl>(FunctionLikeParentDecl)); + DeclarationLoc = LocAndPlacement.first; + Placement = LocAndPlacement.second; + } + if (Placement == MethodDeclarationPlacement::Before) + DeclarationLoc = + getLocationOfPrecedingComment(DeclarationLoc, SM, LangOpts); + else + DeclarationLoc = getLastLineLocationUnlessItHasOtherTokens( + getPreciseTokenLocEnd(DeclarationLoc, SM, LangOpts), SM, LangOpts); + // Add a replacement for the method declaration if necessary. + std::string DeclarationString; + llvm::raw_string_ostream OS(DeclarationString); + if (Placement == MethodDeclarationPlacement::After) + OS << "\n\n"; + RefactoringReplacement::AssociatedSymbolLocation SymbolLoc = + PrintFunctionHeader(OS, /*IsDefinition=*/false); + OS << ";\n"; + if (Placement == MethodDeclarationPlacement::Before) + OS << "\n"; + Replacements.push_back(RefactoringReplacement( + SourceRange(DeclarationLoc, DeclarationLoc), std::move(OS.str()), + CreatedSymbol.get(), SymbolLoc)); + } + std::string ExtractedCode; + llvm::raw_string_ostream ExtractedOS(ExtractedCode); + RefactoringReplacement::AssociatedSymbolLocation SymbolLoc = + PrintFunctionHeader(ExtractedOS); + ExtractedOS << " {\n"; + if (IsExpr && !ReturnType->isVoidType()) + ExtractedOS << "return "; + SourceRange ExtractedTokenRange = + CandidateExtractionInfo[SelectedCandidateIndex].Range; + auto Semicolons = computeSemicolonExtractionPolicy( + ExtractedStmtRange ? *(ExtractedStmtRange->Last) : S, ExtractedTokenRange, + SM, LangOpts); + ExtractedOS << SourceRewriter.getRewrittenText(ExtractedTokenRange); + if (Semicolons.IsNeededInExtractedFunction) + ExtractedOS << ';'; + if (CanUseReturnForVariablesUsedAfterwards) + ExtractedOS << "\nreturn " << RedeclaredVariables.front().VD->getName() + << ";"; + ExtractedOS << "\n}\n\n"; + Replacements.push_back(RefactoringReplacement( + SourceRange(FunctionExtractionLoc, FunctionExtractionLoc), + std::move(ExtractedOS.str()), CreatedSymbol.get(), SymbolLoc)); + + // Create a replacements that removes the extracted code in favor of the + // function call. + std::string InsertedCode; + llvm::raw_string_ostream InsertedOS(InsertedCode); + // We might have to declare variables that were declared in the extracted code + // but still used afterwards. + if (CanUseReturnForVariablesUsedAfterwards) { + const auto &Var = RedeclaredVariables.front(); + Var.VD->getType().print(InsertedOS, PP); + InsertedOS << ' ' << Var.VD->getName() << " = "; + } else { + for (const auto &Var : RedeclaredVariables) { + Var.VD->getType().print(InsertedOS, PP); + InsertedOS << ' ' << Var.VD->getName() << ";\n"; + } + } + InsertedOS << CandidateExtractionInfo[SelectedCandidateIndex].PreInsertedText; + llvm::SmallVector<unsigned, 4> NameOffsets; + if (IsMethodExtraction && EnclosingObjCMethod) { + InsertedOS << "[self "; + NameOffsets.push_back(InsertedOS.str().size()); + InsertedOS << ExtractedName; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) { + InsertedOS << ' '; + NameOffsets.push_back(InsertedOS.str().size()); + InsertedOS << Var.getName(); + } + IsFirst = false; + InsertedOS << ':'; + if (Var.TakeAddress) + InsertedOS << '&'; + InsertedOS << Var.getExpr(); + } + InsertedOS << ']'; + } else { + NameOffsets.push_back(InsertedOS.str().size()); + InsertedOS << ExtractedName << '('; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) + InsertedOS << ", "; + IsFirst = false; + if (Var.TakeAddress) + InsertedOS << '&'; + InsertedOS << Var.getExpr(); + } + InsertedOS << ')'; + } + if (Semicolons.IsNeededInOriginalFunction) + InsertedOS << ';'; + SourceRange ExtractedCharRange = SourceRange( + ExtractedTokenRange.getBegin(), + getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(), SM, LangOpts)); + Replacements.push_back(RefactoringReplacement( + ExtractedCharRange, std::move(InsertedOS.str()), CreatedSymbol.get(), + llvm::makeArrayRef(NameOffsets))); + + RefactoringResult Result(std::move(Replacements)); + Result.AssociatedSymbols.push_back(std::move(CreatedSymbol)); + return std::move(Result); +} diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp new file mode 100644 index 0000000000000..87b65a325c28f --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -0,0 +1,369 @@ +//===--- ExtractRepeatedExpressionIntoVariable.cpp - ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "Extract repeated expression into variable" refactoring +// operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class ExtractRepeatedExpressionIntoVariableOperation + : public RefactoringOperation { +public: + ExtractRepeatedExpressionIntoVariableOperation( + const Expr *E, ArrayRef<const Expr *> Duplicates, const Decl *ParentDecl) + : E(E), DuplicateExpressions(Duplicates.begin(), Duplicates.end()), + ParentDecl(ParentDecl) {} + + const Stmt *getTransformedStmt() const override { return E; } + + llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const Expr *E; + SmallVector<const Expr *, 4> DuplicateExpressions; + const Decl *ParentDecl; +}; + +using UseOfDeclaration = std::pair<const Decl *, unsigned>; + +bool shouldIgnoreParens(const ParenExpr *E) { + if (!E) + return false; + const Expr *Child = E->getSubExpr(); + // Ignore the parens unless they are around an expression that + // really needs them. + if (isa<UnaryOperator>(Child) || isa<BinaryOperator>(Child) || + isa<AbstractConditionalOperator>(Child) || + isa<CXXOperatorCallExpr>(Child)) + return false; + return true; +} + +/// Builds up a list of declarations that are used in an expression. +class DuplicateExprSemanticProfiler + : public RecursiveASTVisitor<DuplicateExprSemanticProfiler> { + unsigned Index = 0; + llvm::SmallVectorImpl<UseOfDeclaration> &DeclRefs; + +public: + DuplicateExprSemanticProfiler( + llvm::SmallVectorImpl<UseOfDeclaration> &DeclRefs) + : DeclRefs(DeclRefs) { + DeclRefs.clear(); + } + + bool VisitStmt(const Stmt *S) { + if (!shouldIgnoreParens(dyn_cast<ParenExpr>(S))) + ++Index; + return true; + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (E->getDecl()) + DeclRefs.emplace_back(E->getDecl(), Index); + return true; + } +}; + +class DuplicateExprFinder : public RecursiveASTVisitor<DuplicateExprFinder>, + PrinterHelper { + const Expr *Target; + const PrintingPolicy &PP; + Stmt::StmtClass ExprKind; + QualType T; + std::string ExprString, OSString; + llvm::SmallVector<UseOfDeclaration, 8> ExprDecls, DeclUses; + + void printExpr(std::string &Str, const Expr *E) { + llvm::raw_string_ostream OS(Str); + E->printPretty(OS, /*Helper=*/this, PP); + } + +public: + SmallVector<const Expr *, 4> DuplicateExpressions; + + DuplicateExprFinder(const Expr *E, const PrintingPolicy &PP) + : Target(E), PP(PP), ExprKind(E->getStmtClass()), T(E->getType()) { + printExpr(ExprString, E); + DuplicateExprSemanticProfiler(ExprDecls).TraverseStmt( + const_cast<Expr *>(E)); + } + + bool handledStmt(Stmt *E, raw_ostream &OS) final override { + if (const auto *Paren = dyn_cast<ParenExpr>(E)) { + if (!shouldIgnoreParens(Paren)) + return false; + Paren->getSubExpr()->printPretty(OS, /*Helper=*/this, PP); + return true; + } + return false; + } + + bool VisitStmt(const Stmt *S) { + if (S->getStmtClass() != ExprKind) + return true; + const auto *E = cast<Expr>(S); + if (E == Target) { + DuplicateExpressions.push_back(E); + return true; + } + // The expression types should match. + if (E->getType() != T) + return true; + // Check if the expression is a duplicate by comparing their lexical + // representations. + OSString.clear(); + printExpr(OSString, E); + if (OSString == ExprString) { + DuplicateExprSemanticProfiler(DeclUses).TraverseStmt( + const_cast<Expr *>(E)); + // Check if they're semantically equivalent. + if (ExprDecls.size() == DeclUses.size() && + std::equal(ExprDecls.begin(), ExprDecls.end(), DeclUses.begin())) + DuplicateExpressions.push_back(E); + } + return true; + } +}; + +} // end anonymous namespace + +static QualType returnTypeOfCall(const Expr *E) { + if (const auto *Call = dyn_cast<CallExpr>(E)) { + if (const auto *Fn = Call->getDirectCallee()) + return Fn->getReturnType(); + } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) { + if (const auto *M = Msg->getMethodDecl()) + return M->getReturnType(); + } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { + if (PRE->isImplicitProperty()) { + if (const auto *M = PRE->getImplicitPropertyGetter()) + return M->getReturnType(); + } else if (const auto *Prop = PRE->getExplicitProperty()) + return Prop->getType(); + } + return QualType(); +} + +static bool isRepeatableExpression(const Stmt *S) { + if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(S)) + return Op->getOperator() == OO_Call || Op->getOperator() == OO_Subscript; + return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S) || + isa<ObjCPropertyRefExpr>(S); +} + +RefactoringOperationResult +clang::tooling::initiateExtractRepeatedExpressionIntoVariableOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + const Stmt *S; + const Decl *ParentDecl; + if (SelectionRange.isValid()) { + auto SelectedStmt = Slice.getSelectedStmtSet(); + if (!SelectedStmt) + return None; + if (!SelectedStmt->containsSelectionRange) + return None; + if (!isRepeatableExpression(SelectedStmt->containsSelectionRange)) + return None; + S = SelectedStmt->containsSelectionRange; + ParentDecl = + Slice.parentDeclForIndex(*SelectedStmt->containsSelectionRangeIndex); + } else { + auto SelectedStmt = Slice.nearestSelectedStmt(isRepeatableExpression); + if (!SelectedStmt) + return None; + S = SelectedStmt->getStmt(); + ParentDecl = SelectedStmt->getParentDecl(); + } + + const Expr *E = cast<Expr>(S); + // Check if the function/method returns a reference/pointer. + QualType T = returnTypeOfCall(E); + if (!T.getTypePtrOrNull() || + (!T->isAnyPointerType() && !T->isReferenceType())) + return None; + + DuplicateExprFinder DupFinder(E, Context.getPrintingPolicy()); + DupFinder.TraverseDecl(const_cast<Decl *>(ParentDecl)); + if (DupFinder.DuplicateExpressions.size() < 2) + return None; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = + llvm::make_unique<ExtractRepeatedExpressionIntoVariableOperation>( + E, DupFinder.DuplicateExpressions, ParentDecl); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +namespace { + +/// Checks if a set of expressions is directly contained in some AST region. +class StmtReachabilityChecker + : public RecursiveASTVisitor<StmtReachabilityChecker> { + const llvm::SmallPtrSetImpl<const Stmt *> &Expressions; + unsigned Count = 0; + + StmtReachabilityChecker( + const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) + : Expressions(Expressions) {} + + bool areAllExpressionsReached() const { return Count == Expressions.size(); } + +public: + bool VisitStmt(const Stmt *S) { + if (Expressions.count(S)) { + ++Count; + if (areAllExpressionsReached()) + return false; + } + return true; + } + + static bool areAllExpressionsReachableFrom( + CompoundStmt *S, const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) { + StmtReachabilityChecker Checker(Expressions); + Checker.TraverseStmt(S); + return Checker.areAllExpressionsReached(); + } +}; + +/// Figures out where the extracted variable should go. +class ExtractedVariableInsertionLocFinder + : public RecursiveASTVisitor<ExtractedVariableInsertionLocFinder> { + llvm::SmallPtrSet<const Stmt *, 4> Expressions; + llvm::SmallVector<std::pair<CompoundStmt *, const Stmt *>, 4> + InsertionCandidateStack; + bool IsPrevCompoundStmt = false; + +public: + SourceLocation Loc; + + /// Initializes the insertion location finder using the set of duplicate + /// \p Expressions from one function. + ExtractedVariableInsertionLocFinder(ArrayRef<const Expr *> Expressions) { + for (const Expr *E : Expressions) + this->Expressions.insert(E); + } + + bool TraverseStmt(Stmt *S) { + if (!S) + return RecursiveASTVisitor::TraverseStmt(S); + if (IsPrevCompoundStmt && !InsertionCandidateStack.empty()) + InsertionCandidateStack.back().second = S; + llvm::SaveAndRestore<bool> IsPrevCompoundStmtTracker(IsPrevCompoundStmt, + false); + if (auto *CS = dyn_cast<CompoundStmt>(S)) { + IsPrevCompoundStmt = true; + InsertionCandidateStack.emplace_back(CS, nullptr); + return RecursiveASTVisitor::TraverseStmt(S); + } + return RecursiveASTVisitor::TraverseStmt(S); + } + + bool VisitStmt(const Stmt *S) { + if (Expressions.count(S)) { + // The insertion location should be in the first compound statement that + // includes all of the expressions as descendants as we want the new + // variable to be visible to all uses. + for (auto I = InsertionCandidateStack.rbegin(), + E = InsertionCandidateStack.rend(); + I != E; ++I) { + if (StmtReachabilityChecker::areAllExpressionsReachableFrom( + I->first, Expressions) && + I->second) { + Loc = I->second->getLocStart(); + break; + } + } + return false; + } + return true; + } +}; + +} // end anonymous namespace + +static StringRef nameForExtractedVariable(const Expr *E) { + if (const auto *Call = dyn_cast<CallExpr>(E)) { + if (const auto *Fn = Call->getDirectCallee()) + return Fn->getName(); + } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) { + if (const auto *M = Msg->getMethodDecl()) { + if (M->getSelector().isUnarySelector()) + return M->getSelector().getNameForSlot(0); + } + } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { + if (PRE->isImplicitProperty()) { + if (const auto *M = PRE->getImplicitPropertyGetter()) + return M->getSelector().getNameForSlot(0); + } else if (const auto *Prop = PRE->getExplicitProperty()) + return Prop->getName(); + } + return "duplicate"; +} + +llvm::Expected<RefactoringResult> +ExtractRepeatedExpressionIntoVariableOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector<RefactoringReplacement> Replacements; + + const SourceManager &SM = Context.getSourceManager(); + ExtractedVariableInsertionLocFinder LocFinder(DuplicateExpressions); + LocFinder.TraverseDecl(const_cast<Decl *>(ParentDecl)); + if (LocFinder.Loc.isInvalid()) + return llvm::make_error<RefactoringOperationError>( + "no appropriate insertion location found"); + + StringRef Name = nameForExtractedVariable(E); + + // Create the variable that will hold the value of the duplicate expression. + std::string VariableDeclarationString; + llvm::raw_string_ostream OS(VariableDeclarationString); + QualType T = returnTypeOfCall(E); + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + T.print(OS, PP, /*PlaceHolder*/ Name); + OS << " = "; + E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy()); + OS << ";\n"; + Replacements.emplace_back(SourceRange(LocFinder.Loc, LocFinder.Loc), + OS.str()); + + // Replace the duplicates with a reference to the variable. + for (const Expr *E : DuplicateExpressions) + Replacements.emplace_back( + SourceRange( + E->getLocStart(), + getPreciseTokenLocEnd(E->getLocEnd(), SM, Context.getLangOpts())), + Name); + + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp b/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp new file mode 100644 index 0000000000000..2cab0ee182c2c --- /dev/null +++ b/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp @@ -0,0 +1,110 @@ +//===--- FillInEnumSwitchCases.cpp - -------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add missing switch cases" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/Edit/RefactoringFixits.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class FillInEnumSwitchCasesOperation : public RefactoringOperation { +public: + FillInEnumSwitchCasesOperation(const EnumDecl *Enum, const SwitchStmt *Switch, + const DeclContext *SwitchContext) + : Enum(Enum), Switch(Switch), SwitchContext(SwitchContext) {} + + const Stmt *getTransformedStmt() const override { return Switch; } + + llvm::Expected<RefactoringResult> + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const EnumDecl *Enum; + const SwitchStmt *Switch; + const DeclContext *SwitchContext; +}; + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateFillInEnumSwitchCasesOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + const SwitchStmt *Switch; + const Decl *ParentDecl; + if (SelectionRange.isValid()) { + auto SelectedSet = Slice.getSelectedStmtSet(); + if (!SelectedSet) + return None; + Switch = dyn_cast_or_null<SwitchStmt>(SelectedSet->containsSelectionRange); + // FIXME: Improve the interface for this to make it similar to SelectedStmt + if (SelectedSet->containsSelectionRange) + ParentDecl = + Slice.parentDeclForIndex(*SelectedSet->containsSelectionRangeIndex); + } else { + auto SelectedStmt = Slice.nearestSelectedStmt(Stmt::SwitchStmtClass); + if (!SelectedStmt) + return None; + Switch = cast<SwitchStmt>(SelectedStmt->getStmt()); + ParentDecl = SelectedStmt->getParentDecl(); + } + if (!Switch) + return None; + + // Ensure that the type is an enum. + const Expr *Cond = Switch->getCond()->IgnoreImpCasts(); + const EnumDecl *ED = nullptr; + if (const auto *ET = Cond->getType()->getAs<EnumType>()) + ED = ET->getDecl(); + else { + // Enum literals are 'int' in C. + if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) { + if (const auto *EC = dyn_cast<EnumConstantDecl>(DRE->getDecl())) + ED = dyn_cast<EnumDecl>(EC->getDeclContext()); + } + } + + if (!ED) + return RefactoringOperationResult("The switch doesn't operate on an enum"); + if (!ED->isCompleteDefinition()) + return RefactoringOperationResult("The enum type is incomplete"); + + if (Switch->isAllEnumCasesCovered()) + return RefactoringOperationResult("All enum cases are already covered"); + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = llvm::make_unique<FillInEnumSwitchCasesOperation>( + ED, Switch, dyn_cast<DeclContext>(ParentDecl)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected<RefactoringResult> +FillInEnumSwitchCasesOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector<RefactoringReplacement> Replacements; + edit::fillInMissingSwitchEnumCases( + Context, Switch, Enum, SwitchContext, + [&](const FixItHint &Hint) { Replacements.push_back(Hint); }); + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp new file mode 100644 index 0000000000000..42497907ee936 --- /dev/null +++ b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp @@ -0,0 +1,293 @@ +//===--- FillInMissingMethodStubsFromAbstractClasses.cpp - ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add missing abstract class method overrides" refactoring +// operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "llvm/ADT/DenseSet.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class FillInMissingMethodStubsFromAbstractClassesOperation + : public RefactoringOperation { +public: + FillInMissingMethodStubsFromAbstractClassesOperation( + const CXXRecordDecl *Class) + : Class(Class) {} + + const Decl *getTransformedDecl() const override { return Class; } + + llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const CXXRecordDecl *Class; +}; + +} // end anonymous namespace + +static bool hasAbstractBases(const CXXRecordDecl *Class) { + for (const CXXBaseSpecifier &Base : Class->bases()) { + if (const auto *RD = Base.getType()->getAsCXXRecordDecl()) { + if (RD->isAbstract()) + return true; + } + } + return false; +} + +RefactoringOperationResult +clang::tooling::initiateFillInMissingMethodStubsFromAbstractClassesOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + auto SelectedDecl = Slice.innermostSelectedDecl( + llvm::makeArrayRef(Decl::CXXRecord), ASTSlice::InnermostDeclOnly); + if (!SelectedDecl) + return None; + const auto *Class = cast<CXXRecordDecl>(SelectedDecl->getDecl()); + if (Class->isUnion() || !Class->isThisDeclarationADefinition()) + return None; + if (!hasAbstractBases(Class)) + return RefactoringOperationResult("The class has no abstract bases"); + if (!Class->isDependentType() && !Class->isAbstract()) + return RefactoringOperationResult( + "The class has no missing abstract class methods"); + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = + llvm::make_unique<FillInMissingMethodStubsFromAbstractClassesOperation>( + Class); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +namespace { + +class PureMethodSet { + llvm::DenseMap<const CXXMethodDecl *, int> Methods; + + void addPureMethodsFromAbstractClasses(const CXXRecordDecl *Class, + int &Priority) { + for (const CXXBaseSpecifier &Base : Class->bases()) { + const auto *RD = Base.getType()->getAsCXXRecordDecl(); + if (!RD || !RD->isAbstract()) + continue; + for (const CXXMethodDecl *M : RD->methods()) { + if (M->isPure()) + Methods.insert(std::make_pair(M->getCanonicalDecl(), Priority++)); + } + addPureMethodsFromAbstractClasses(RD, Priority); + } + } + + void addPureMethodsFromAbstractClasses(const CXXRecordDecl *Class) { + int Priority = 0; + addPureMethodsFromAbstractClasses(Class, Priority); + } + + void subtractImplementedPureMethods(const CXXRecordDecl *Class) { + for (const CXXMethodDecl *M : Class->methods()) { + if (!M->isVirtual() || M->isPure()) + continue; + for (const CXXMethodDecl *OM : M->overridden_methods()) { + OM = OM->getCanonicalDecl(); + if (OM->isPure()) + Methods.erase(OM); + } + } + for (const CXXBaseSpecifier &Base : Class->bases()) { + const auto *RD = Base.getType()->getAsCXXRecordDecl(); + if (!RD || !RD->isAbstract()) + continue; + subtractImplementedPureMethods(RD); + } + } + +public: + static std::vector<const CXXMethodDecl *> + gatherMissingMethods(const CXXRecordDecl *Class) { + PureMethodSet MethodSet; + MethodSet.addPureMethodsFromAbstractClasses(Class); + MethodSet.subtractImplementedPureMethods(Class); + // Sort the missing methods. That will place methods from the same abstract + // class together in the order in which they were declared. + struct MethodInfo { + const CXXMethodDecl *M; + int Priority; + }; + std::vector<MethodInfo> MissingMethods; + for (const auto &M : MethodSet.Methods) + MissingMethods.push_back({M.first, M.second}); + std::sort(MissingMethods.begin(), MissingMethods.end(), + [](const MethodInfo &LHS, const MethodInfo &RHS) { + return LHS.Priority < RHS.Priority; + }); + std::vector<const CXXMethodDecl *> Result; + Result.reserve(MissingMethods.size()); + for (const auto &M : MissingMethods) + Result.push_back(M.M); + return Result; + } +}; + +} // end anonymous namespace + +static SourceLocation findInsertionLocationForMethodsFromAbstractClass( + const CXXRecordDecl *AbstractClass, const CXXRecordDecl *Class, + const SourceManager &SM, const LangOptions &LangOpts) { + SourceLocation Loc; + for (const CXXMethodDecl *M : Class->methods()) { + if (!M->isVirtual() || M->isPure()) + continue; + for (const CXXMethodDecl *OM : M->overridden_methods()) { + OM = OM->getCanonicalDecl(); + if (OM->getLexicalDeclContext() == AbstractClass) { + SourceLocation EndLoc = M->getLocEnd(); + if (EndLoc.isMacroID()) + EndLoc = SM.getExpansionRange(EndLoc).second; + if (Loc.isInvalid()) + Loc = EndLoc; + else if (SM.isBeforeInTranslationUnit(Loc, EndLoc)) + Loc = EndLoc; + break; + } + } + } + if (Loc.isInvalid()) + return Loc; + return getLastLineLocationUnlessItHasOtherTokens(Loc, SM, LangOpts); +} + +/// Returns true if the given \p Class implements the majority of declared +/// methods in the class itself. +static bool shouldImplementMethodsInClass(const CXXRecordDecl *Class) { + // Check if this class implements the methods in the class itself. + unsigned NumMethods = 0, NumImplementedMethods = 0; + for (const CXXMethodDecl *M : Class->methods()) { + if (M->isImplicit()) + continue; + // Only look at methods/operators. + if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M)) + continue; + ++NumMethods; + if (M->hasBody()) + ++NumImplementedMethods; + } + if (!NumMethods) + return false; + // Use the following arbitrary heuristic: + // If the number of method declarations is less than 4, then all of the + // methods must have bodies. Otherwise, at least 75% of the methods must + // have bodies. + return NumMethods < 4 + ? NumMethods == NumImplementedMethods + : float(NumImplementedMethods) / float(NumMethods) > 0.75; +} + +llvm::Expected<RefactoringResult> +FillInMissingMethodStubsFromAbstractClassesOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector<RefactoringReplacement> Replacements; + + std::vector<const CXXMethodDecl *> MissingMethods = + PureMethodSet::gatherMissingMethods(Class); + + bool GenerateBodyDummies = shouldImplementMethodsInClass(Class); + + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + + std::string EndInsertionOSStr; + llvm::raw_string_ostream EndInsertionOS(EndInsertionOSStr); + + std::string InsertionGroupOSStr; + llvm::raw_string_ostream InsertionGroupOS(InsertionGroupOSStr); + + SourceLocation InsertionLoc = Class->getLocEnd(); + const CXXRecordDecl *CurrentAbstractClass = nullptr; + SourceLocation CurrentGroupInsertionLoc; + for (const auto &I : llvm::enumerate(MissingMethods)) { + const CXXMethodDecl *Method = I.value(); + const CXXRecordDecl *AbstractClass = Method->getParent(); + if (CurrentAbstractClass != AbstractClass) { + if (!InsertionGroupOS.str().empty()) { + assert(CurrentGroupInsertionLoc.isValid()); + Replacements.emplace_back( + SourceRange(CurrentGroupInsertionLoc, CurrentGroupInsertionLoc), + InsertionGroupOS.str()); + } + InsertionGroupOSStr.clear(); + CurrentAbstractClass = AbstractClass; + CurrentGroupInsertionLoc = + findInsertionLocationForMethodsFromAbstractClass( + CurrentAbstractClass, Class, Context.getSourceManager(), + Context.getLangOpts()); + } + bool IsInsertingAfterRelatedMethods = CurrentGroupInsertionLoc.isValid(); + raw_ostream &OS = + IsInsertingAfterRelatedMethods ? InsertionGroupOS : EndInsertionOS; + + if (IsInsertingAfterRelatedMethods && InsertionGroupOS.str().empty()) + OS << "\n\n"; + // Print the method without the 'virtual' specifier and the pure '= 0' + // annotation. + auto *MD = const_cast<CXXMethodDecl *>(Method); + bool IsVirtual = MD->isVirtualAsWritten(); + MD->setVirtualAsWritten(false); + bool IsPure = MD->isPure(); + MD->setPure(false); + MD->print(OS, PP); + MD->setVirtualAsWritten(IsVirtual); + MD->setPure(IsPure); + + OS << " override"; + if (GenerateBodyDummies) + OS << " { \n <#code#>\n}\n"; + else + OS << ";\n"; + // Avoid an additional newline for the last method in an insertion group. + if (IsInsertingAfterRelatedMethods) { + const CXXRecordDecl *NextAbstractClass = + (I.index() + 1) != MissingMethods.size() + ? MissingMethods[I.index() + 1]->getParent() + : nullptr; + if (NextAbstractClass == CurrentAbstractClass) + OS << "\n"; + } else + OS << "\n"; + } + if (!InsertionGroupOS.str().empty()) { + assert(CurrentGroupInsertionLoc.isValid()); + Replacements.emplace_back( + SourceRange(CurrentGroupInsertionLoc, CurrentGroupInsertionLoc), + InsertionGroupOS.str()); + } + if (!EndInsertionOS.str().empty()) + Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc), + EndInsertionOS.str()); + + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp b/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp new file mode 100644 index 0000000000000..de8cfbe4ce7c7 --- /dev/null +++ b/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp @@ -0,0 +1,91 @@ +//===--- FillInMissingProtocolStubs.cpp - --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add methods from protocol(s)" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "clang/AST/AST.h" +#include "clang/Edit/RefactoringFixits.h" + +using namespace clang; +using namespace clang::tooling; +using namespace edit::fillInMissingProtocolStubs; + +namespace { + +class FillInMissingProtocolStubsOperation : public RefactoringOperation { +public: + FillInMissingProtocolStubsOperation(const ObjCContainerDecl *Container, + FillInMissingProtocolStubs Impl) + : Container(Container), Impl(std::move(Impl)) {} + + const Decl *getTransformedDecl() const override { return Container; } + + llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const ObjCContainerDecl *Container; + FillInMissingProtocolStubs Impl; +}; + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateFillInMissingProtocolStubsOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + auto SelectedDecl = Slice.innermostSelectedDecl( + {Decl::ObjCImplementation, Decl::ObjCCategoryImpl, Decl::ObjCInterface, + Decl::ObjCCategory}, + ASTSlice::InnermostDeclOnly); + if (!SelectedDecl) + return None; + const auto *Container = cast<ObjCContainerDecl>(SelectedDecl->getDecl()); + + // If this in a class extension, initiate the operation on the @implementation + // if it's in the same TU. + if (const auto *Category = dyn_cast<ObjCCategoryDecl>(Container)) { + if (Category->IsClassExtension()) { + const ObjCInterfaceDecl *I = Category->getClassInterface(); + if (I && I->getImplementation()) + Container = I->getImplementation(); + else + return RefactoringOperationResult( + "Class extension without suitable @implementation"); + } + } + + FillInMissingProtocolStubs Impl; + if (Impl.initiate(Context, Container)) + return None; + if (!Impl.hasMissingRequiredMethodStubs()) + return RefactoringOperationResult("All of the @required methods are there"); + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = llvm::make_unique<FillInMissingProtocolStubsOperation>( + Container, std::move(Impl)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected<RefactoringResult> +FillInMissingProtocolStubsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector<RefactoringReplacement> Replacements; + Impl.perform(Context, + [&](const FixItHint &Hint) { Replacements.push_back(Hint); }); + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp new file mode 100644 index 0000000000000..1a284b8d49814 --- /dev/null +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -0,0 +1,460 @@ +//===--- IfSwitchConversion.cpp - ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "convert to switch" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class IfSwitchConversionOperation : public RefactoringOperation { +public: + IfSwitchConversionOperation(const IfStmt *If) : If(If) {} + + const Stmt *getTransformedStmt() const override { return If; } + + llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const IfStmt *If; +}; + +class ValidIfBodyVerifier : public RecursiveASTVisitor<ValidIfBodyVerifier> { + bool CheckBreaks = true; + +public: + bool IsValid = true; + + bool VisitBreakStmt(const BreakStmt *S) { + if (!CheckBreaks) + return true; + IsValid = false; + return false; + } + bool VisitDefaultStmt(const DefaultStmt *S) { + IsValid = false; + return false; + } + bool VisitCaseStmt(const CaseStmt *S) { + IsValid = false; + return false; + } + +// Handle nested loops: + +#define TRAVERSE_LOOP(STMT) \ + bool Traverse##STMT(STMT *S) { \ + bool Prev = CheckBreaks; \ + CheckBreaks = false; \ + RecursiveASTVisitor::Traverse##STMT(S); \ + CheckBreaks = Prev; \ + return true; \ + } + + TRAVERSE_LOOP(ForStmt) + TRAVERSE_LOOP(WhileStmt) + TRAVERSE_LOOP(DoStmt) + TRAVERSE_LOOP(CXXForRangeStmt) + TRAVERSE_LOOP(ObjCForCollectionStmt) + +#undef TRAVERSE_LOOP + + // Handle switches: + + bool TraverseSwitchStmt(SwitchStmt *S) { + // Don't visit the body as 'break'/'case'/'default' are all allowed inside + // switches. + return true; + } +}; + +} // end anonymous namespace + +/// Returns true if any of the if statements in the given if construct have +/// conditions that aren't allowed by the "convert to switch" operation. +static bool checkIfsHaveConditionExpression(const IfStmt *If) { + for (; If; If = dyn_cast_or_null<IfStmt>(If->getElse())) { + if (If->getConditionVariable() || If->getInit() || !If->getCond()) + return true; + } + return false; +} + +static Optional<std::pair<const Expr *, const Expr *>> +matchBinOp(const Expr *E, BinaryOperator::Opcode Kind) { + const auto *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens()); + if (!BinOp || BinOp->getOpcode() != Kind) + return None; + return std::pair<const Expr *, const Expr *>( + BinOp->getLHS()->IgnoreParenImpCasts(), BinOp->getRHS()->IgnoreParens()); +} + +typedef llvm::SmallDenseSet<int64_t, 4> RHSValueSet; + +/// Returns true if the conditional expression of an 'if' statement allows +/// the "convert to switch" refactoring action. +static bool isConditionValid(const Expr *E, ASTContext &Context, + Optional<llvm::FoldingSetNodeID> &MatchedLHSNodeID, + RHSValueSet &RHSValues) { + auto Equals = matchBinOp(E, BO_EQ); + if (!Equals.hasValue()) { + auto LogicalOr = matchBinOp(E, BO_LOr); + if (!LogicalOr.hasValue()) + return false; + return isConditionValid(LogicalOr.getValue().first, Context, + MatchedLHSNodeID, RHSValues) && + isConditionValid(LogicalOr.getValue().second, Context, + MatchedLHSNodeID, RHSValues); + } + const Expr *LHS = Equals.getValue().first; + const Expr *RHS = Equals.getValue().second; + if (!LHS->getType()->isIntegralOrEnumerationType() || + !RHS->getType()->isIntegralOrEnumerationType()) + return false; + + // RHS must be a constant and unique. + llvm::APSInt Value; + if (!RHS->EvaluateAsInt(Value, Context)) + return false; + // Only allow constant that fix into 64 bits. + if (Value.getMinSignedBits() > 64 || + !RHSValues.insert(Value.getExtValue()).second) + return false; + + // LHS must be identical to the other LHS expressions. + llvm::FoldingSetNodeID LHSNodeID; + LHS->Profile(LHSNodeID, Context, /*Canonical=*/false); + if (MatchedLHSNodeID.hasValue()) { + if (MatchedLHSNodeID.getValue() != LHSNodeID) + return false; + } else + MatchedLHSNodeID = std::move(LHSNodeID); + return true; +} + +RefactoringOperationResult clang::tooling::initiateIfSwitchConversionOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + // FIXME: Add support for selections. + const auto *If = cast_or_null<IfStmt>(Slice.nearestStmt(Stmt::IfStmtClass)); + if (!If) + return None; + + // Don't allow if statements without any 'else' or 'else if'. + if (!If->getElse()) + return None; + + // Don't allow ifs with variable declarations in conditions or C++17 + // initializer statements. + if (checkIfsHaveConditionExpression(If)) + return None; + + // Find the ranges in which initiation can be performed and verify that the + // ifs don't have any initialization expressions or condition variables. + SmallVector<SourceRange, 4> Ranges; + SourceLocation RangeStart = If->getLocStart(); + const IfStmt *CurrentIf = If; + const SourceManager &SM = Context.getSourceManager(); + while (true) { + const Stmt *Then = CurrentIf->getThen(); + Ranges.emplace_back(RangeStart, + findLastLocationOfSourceConstruct( + CurrentIf->getCond()->getLocEnd(), Then, SM)); + const auto *Else = CurrentIf->getElse(); + if (!Else) + break; + RangeStart = + findFirstLocationOfSourceConstruct(CurrentIf->getElseLoc(), Then, SM); + if (const auto *If = dyn_cast<IfStmt>(Else)) { + CurrentIf = If; + continue; + } + Ranges.emplace_back(RangeStart, findLastLocationOfSourceConstruct( + CurrentIf->getElseLoc(), Else, SM)); + break; + } + + if (!isLocationInAnyRange(Location, Ranges, SM)) + return None; + + // Verify that the bodies don't have any 'break'/'default'/'case' statements. + ValidIfBodyVerifier BodyVerifier; + BodyVerifier.TraverseStmt(const_cast<IfStmt *>(If)); + if (!BodyVerifier.IsValid) + return RefactoringOperationResult( + "if's body contains a 'break'/'default'/'case' statement"); + + // FIXME: Use ASTMatchers if possible. + Optional<llvm::FoldingSetNodeID> MatchedLHSNodeID; + RHSValueSet RHSValues; + for (const IfStmt *CurrentIf = If; CurrentIf; + CurrentIf = dyn_cast_or_null<IfStmt>(CurrentIf->getElse())) { + if (!isConditionValid(CurrentIf->getCond(), Context, MatchedLHSNodeID, + RHSValues)) + return RefactoringOperationResult("unsupported conditional expression"); + } + + RefactoringOperationResult Result; + Result.Initiated = true; + if (CreateOperation) + Result.RefactoringOp.reset(new IfSwitchConversionOperation(If)); + return Result; +} + +/// Returns the first LHS expression in the if's condition. +const Expr *getConditionFirstLHS(const Expr *E) { + auto Equals = matchBinOp(E, BO_EQ); + if (!Equals.hasValue()) { + auto LogicalOr = matchBinOp(E, BO_LOr); + if (!LogicalOr.hasValue()) + return nullptr; + return getConditionFirstLHS(LogicalOr.getValue().first); + } + return Equals.getValue().first; +} + +/// Gathers all of the RHS operands of the == expressions in the if's condition. +void gatherCaseValues(const Expr *E, + SmallVectorImpl<const Expr *> &CaseValues) { + auto Equals = matchBinOp(E, BO_EQ); + if (Equals.hasValue()) { + CaseValues.push_back(Equals.getValue().second); + return; + } + auto LogicalOr = matchBinOp(E, BO_LOr); + if (!LogicalOr.hasValue()) + return; + gatherCaseValues(LogicalOr.getValue().first, CaseValues); + gatherCaseValues(LogicalOr.getValue().second, CaseValues); +} + +/// Return true iff the given body should be terminated with a 'break' statement +/// when used inside of a switch. +static bool isBreakNeeded(const Stmt *Body) { + const auto *CS = dyn_cast<CompoundStmt>(Body); + if (!CS) + return !isa<ReturnStmt>(Body); + return CS->body_empty() ? true : isBreakNeeded(CS->body_back()); +} + +/// Returns true if the given statement declares a variable. +static bool isVarDeclaringStatement(const Stmt *S) { + const auto *DS = dyn_cast<DeclStmt>(S); + if (!DS) + return false; + for (const Decl *D : DS->decls()) { + if (isa<VarDecl>(D)) + return true; + } + return false; +} + +/// Return true if the body of an if/else if/else needs to be wrapped in braces +/// when put in a switch. +static bool areBracesNeeded(const Stmt *Body) { + const auto *CS = dyn_cast<CompoundStmt>(Body); + if (!CS) + return isVarDeclaringStatement(Body); + for (const Stmt *S : CS->body()) { + if (isVarDeclaringStatement(S)) + return true; + } + return false; +} + +namespace { + +/// Information about the replacement that replaces 'if'/'else' with a 'case' or +/// a 'default'. +struct CasePlacement { + /// The location of the 'case' or 'default'. + SourceLocation CaseStartLoc; + /// True when this 'case' or 'default' statement needs a newline. + bool NeedsNewLine; + /// True if this the first 'if' in the source construct. + bool IsFirstIf; + /// True if we need to insert a 'break' to terminate the previous body + /// before the 'case' or 'default'. + bool IsBreakNeeded; + /// True if we need to insert a '}' before the case. + bool ArePreviousBracesNeeded; + + CasePlacement(SourceLocation Loc) + : CaseStartLoc(Loc), NeedsNewLine(false), IsFirstIf(true), + IsBreakNeeded(false), ArePreviousBracesNeeded(false) {} + + CasePlacement(const IfStmt *If, const SourceManager &SM, + bool AreBracesNeeded) { + CaseStartLoc = isa<CompoundStmt>(If->getThen()) ? If->getThen()->getLocEnd() + : If->getElseLoc(); + SourceLocation BodyEndLoc = findLastNonCompoundLocation(If->getThen()); + NeedsNewLine = BodyEndLoc.isValid() + ? areOnSameLine(CaseStartLoc, BodyEndLoc, SM) + : false; + IsFirstIf = false; + IsBreakNeeded = isBreakNeeded(If->getThen()); + ArePreviousBracesNeeded = AreBracesNeeded; + } + + std::string getCaseReplacementString(bool IsDefault = false, + bool AreNextBracesNeeded = false) const { + if (IsFirstIf) + return ") {\ncase "; + std::string Result; + llvm::raw_string_ostream OS(Result); + if (NeedsNewLine) + OS << '\n'; + if (IsBreakNeeded) + OS << "break;\n"; + if (ArePreviousBracesNeeded) + OS << "}\n"; + OS << (IsDefault ? "default:" : "case "); + if (IsDefault && AreNextBracesNeeded) + OS << " {"; + return std::move(OS.str()); + } +}; + +} // end anonymous namespace + +static llvm::Error +addCaseReplacements(const IfStmt *If, const CasePlacement &CaseInfo, + bool &AreBracesNeeded, + std::vector<RefactoringReplacement> &Replacements, + const SourceManager &SM, const LangOptions &LangOpts) { + SmallVector<const Expr *, 2> CaseValues; + gatherCaseValues(If->getCond(), CaseValues); + assert(!CaseValues.empty()); + Replacements.emplace_back( + SourceRange(CaseInfo.CaseStartLoc, CaseValues[0]->getLocStart()), + CaseInfo.getCaseReplacementString()); + + SourceLocation PrevCaseEnd = + getPreciseTokenLocEnd(CaseValues[0]->getLocEnd(), SM, LangOpts); + for (const Expr *CaseValue : llvm::makeArrayRef(CaseValues).drop_front()) { + Replacements.emplace_back( + SourceRange(PrevCaseEnd, CaseValue->getLocStart()), + StringRef(":\ncase ")); + PrevCaseEnd = getPreciseTokenLocEnd(CaseValue->getLocEnd(), SM, LangOpts); + } + + AreBracesNeeded = areBracesNeeded(If->getThen()); + StringRef ColonReplacement = AreBracesNeeded ? ": {" : ":"; + if (isa<CompoundStmt>(If->getThen())) { + Replacements.emplace_back( + SourceRange( + PrevCaseEnd, + getPreciseTokenLocEnd(If->getThen()->getLocStart(), SM, LangOpts)), + ColonReplacement); + } else { + // Find the location of the if's ')' + SourceLocation End = + findClosingParenLocEnd(If->getCond()->getLocEnd(), SM, LangOpts); + if (!End.isValid()) + return llvm::make_error<RefactoringOperationError>( + "couldn't find the location of ')'"); + Replacements.emplace_back(SourceRange(PrevCaseEnd, End), ColonReplacement); + } + return llvm::Error::success(); +} + +llvm::Expected<RefactoringResult> +IfSwitchConversionOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector<RefactoringReplacement> Replacements; + const SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + + // The first if should be replaced with a 'switch' and the text for first LHS + // should be preserved. + const Expr *LHS = getConditionFirstLHS(If->getCond()); + assert(LHS && "Missing == expression"); + Replacements.emplace_back(SourceRange(If->getLocStart(), LHS->getLocStart()), + StringRef("switch (")); + + bool AreBracesNeeded = false; + if (auto Error = addCaseReplacements( + If, + CasePlacement(getPreciseTokenLocEnd(LHS->getLocEnd(), SM, LangOpts)), + AreBracesNeeded, Replacements, SM, LangOpts)) + return std::move(Error); + + // Convert the remaining ifs to 'case' statements. + const IfStmt *CurrentIf = If; + while (true) { + const IfStmt *NextIf = dyn_cast_or_null<IfStmt>(CurrentIf->getElse()); + if (!NextIf) + break; + if (auto Error = addCaseReplacements( + NextIf, CasePlacement(CurrentIf, SM, AreBracesNeeded), + AreBracesNeeded, Replacements, SM, LangOpts)) + return std::move(Error); + CurrentIf = NextIf; + } + + // Convert the 'else' to 'default' + if (const Stmt *Else = CurrentIf->getElse()) { + CasePlacement DefaultInfo(CurrentIf, SM, AreBracesNeeded); + AreBracesNeeded = areBracesNeeded(Else); + SourceLocation EndLoc = getPreciseTokenLocEnd( + isa<CompoundStmt>(Else) ? Else->getLocStart() : CurrentIf->getElseLoc(), + SM, LangOpts); + Replacements.emplace_back(SourceRange(DefaultInfo.CaseStartLoc, EndLoc), + DefaultInfo.getCaseReplacementString( + /*IsDefault=*/true, AreBracesNeeded)); + } + + // Add the trailing break and one or two '}' if needed. + const Stmt *LastBody = + CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen(); + bool IsLastBreakNeeded = isBreakNeeded(LastBody); + SourceLocation TerminatingReplacementLoc; + std::string TerminatingReplacement; + llvm::raw_string_ostream OS(TerminatingReplacement); + if (!isa<CompoundStmt>(LastBody)) { + TerminatingReplacementLoc = LastBody->getLocEnd(); + // Try to adjust the location in order to preserve any trailing comments on + // the last line of the last body. + if (!TerminatingReplacementLoc.isMacroID()) + TerminatingReplacementLoc = getLastLineLocationUnlessItHasOtherTokens( + TerminatingReplacementLoc, SM, LangOpts); + if (IsLastBreakNeeded) + OS << "\nbreak;"; + OS << "\n}"; + if (AreBracesNeeded) + OS << "\n}"; + } else { + TerminatingReplacementLoc = LastBody->getLocEnd(); + if (IsLastBreakNeeded) + OS << "break;\n"; + if (AreBracesNeeded) + OS << "}\n"; + } + + if (!OS.str().empty()) + Replacements.emplace_back( + SourceRange(TerminatingReplacementLoc, TerminatingReplacementLoc), + std::move(OS.str())); + + // TODO: verify replacements (no macro locs + ordered). + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp new file mode 100644 index 0000000000000..5055aac32d158 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -0,0 +1,414 @@ +//===--- ImplementDeclaredMethods.cpp - ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "Generate missing method definitions" refactoring +// operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringContinuations.h" +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +template <typename ClassType, typename MethodType, typename Derived> +class ImplementDeclaredMethodsOperation : public RefactoringOperation { +public: + ImplementDeclaredMethodsOperation( + const ClassType *Container, ArrayRef<const MethodType *> SelectedMethods) + : Container(Container), + SelectedMethods(SelectedMethods.begin(), SelectedMethods.end()) {} + + const Decl *getTransformedDecl() const override { + return SelectedMethods.front(); + } + + const Decl *getLastTransformedDecl() const override { + return SelectedMethods.back(); + } + + static RefactoringOperationResult + initiate(const ClassType *Container, ArrayRef<const MethodType *> Methods, + bool CreateOperation) { + if (Methods.empty()) + return None; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = llvm::make_unique<Derived>(Container, Methods); + Result.RefactoringOp = std::move(Operation); + return Result; + } + + const ClassType *Container; + llvm::SmallVector<const MethodType *, 8> SelectedMethods; +}; + +class ImplementDeclaredCXXMethodsOperation + : public ImplementDeclaredMethodsOperation< + CXXRecordDecl, CXXMethodDecl, ImplementDeclaredCXXMethodsOperation> { +public: + ImplementDeclaredCXXMethodsOperation( + const CXXRecordDecl *Container, + ArrayRef<const CXXMethodDecl *> SelectedMethods) + : ImplementDeclaredMethodsOperation(Container, SelectedMethods) {} + + llvm::Expected<RefactoringResult> + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + static void addInlineBody(const CXXMethodDecl *MD, const ASTContext &Context, + std::vector<RefactoringReplacement> &Replacements); + + static llvm::Expected<RefactoringResult> + runInImplementationAST(ASTContext &Context, const FileID &File, + const CXXRecordDecl *Class, + ArrayRef<const CXXMethodDecl *> SelectedMethods); +}; + +class ImplementDeclaredObjCMethodsOperation + : public ImplementDeclaredMethodsOperation< + ObjCContainerDecl, ObjCMethodDecl, + ImplementDeclaredObjCMethodsOperation> { +public: + ImplementDeclaredObjCMethodsOperation( + const ObjCContainerDecl *Container, + ArrayRef<const ObjCMethodDecl *> SelectedMethods) + : ImplementDeclaredMethodsOperation(Container, SelectedMethods) {} + + llvm::Expected<RefactoringResult> + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + static llvm::Expected<RefactoringResult> + runInImplementationAST(ASTContext &Context, const FileID &File, + const ObjCContainerDecl *Container, + ArrayRef<const ObjCMethodDecl *> SelectedMethods); +}; + +/// Returns true if the given Objective-C method has an implementation. +bool isImplemented(const ObjCMethodDecl *M) { + if (M->hasBody() || M->isDefined()) + return true; + return false; +} + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateImplementDeclaredMethodsOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + // Find the selected Class. + auto SelectedDecl = Slice.innermostSelectedDecl([](const Decl *D) { + return isa<CXXRecordDecl>(D) || isa<ObjCInterfaceDecl>(D) || + isa<ObjCCategoryDecl>(D); + }); + if (!SelectedDecl) + return None; + // Look at the set of methods that intersect with the selection. + if (const auto *CXXClass = dyn_cast<CXXRecordDecl>(SelectedDecl->getDecl())) { + if (CXXClass->isDependentType()) + return RefactoringOperationResult("templates are unsupported"); + llvm::SmallVector<const CXXMethodDecl *, 8> SelectedMethods; + for (const CXXMethodDecl *M : CXXClass->methods()) { + if (M->isImplicit() || M->hasBody() || M->isPure() || M->isDefaulted() || + M->isDeletedAsWritten() || M->getDescribedFunctionTemplate()) + continue; + if (Slice.isSourceRangeSelected( + CharSourceRange::getTokenRange(M->getSourceRange()))) + SelectedMethods.push_back(M); + } + return ImplementDeclaredCXXMethodsOperation::initiate( + CXXClass, SelectedMethods, CreateOperation); + } + const ObjCContainerDecl *Container = + cast<ObjCContainerDecl>(SelectedDecl->getDecl()); + llvm::SmallVector<const ObjCMethodDecl *, 8> SelectedMethods; + for (const ObjCMethodDecl *M : Container->methods()) { + if (M->isImplicit() || isImplemented(M)) + continue; + if (Slice.isSourceRangeSelected( + CharSourceRange::getTokenRange(M->getSourceRange()))) + SelectedMethods.push_back(M); + } + return ImplementDeclaredObjCMethodsOperation::initiate( + Container, SelectedMethods, CreateOperation); +} + +llvm::Expected<RefactoringResult> +ImplementDeclaredCXXMethodsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + if (Container->isLexicallyWithinFunctionOrMethod()) { + // Local methods can be implemented inline. + std::vector<RefactoringReplacement> Replacements; + for (const CXXMethodDecl *MD : SelectedMethods) + addInlineBody(MD, Context, Replacements); + return std::move(Replacements); + } + using namespace indexer; + return continueInExternalASTUnit( + fileThatShouldContainImplementationOf(Container), runInImplementationAST, + Container, filter(llvm::makeArrayRef(SelectedMethods), + [](const DeclEntity &D) { return !D.isDefined(); })); +} + +void ImplementDeclaredCXXMethodsOperation::addInlineBody( + const CXXMethodDecl *MD, const ASTContext &Context, + std::vector<RefactoringReplacement> &Replacements) { + SourceLocation EndLoc = MD->getLocEnd(); + SourceRange SemiRange = getRangeOfNextToken( + EndLoc, tok::semi, Context.getSourceManager(), Context.getLangOpts()); + if (SemiRange.isValid()) { + Replacements.push_back(RefactoringReplacement(SemiRange)); + EndLoc = SemiRange.getEnd(); + } + SourceLocation InsertionLoc = getLastLineLocationUnlessItHasOtherTokens( + EndLoc, Context.getSourceManager(), Context.getLangOpts()); + Replacements.push_back( + RefactoringReplacement(SourceRange(InsertionLoc, InsertionLoc), + StringRef(" { \n <#code#>;\n}"))); +} + +static const RecordDecl *findOutermostRecord(const RecordDecl *RD) { + const RecordDecl *Result = RD; + for (const DeclContext *DC = Result->getLexicalDeclContext(); + isa<RecordDecl>(DC); DC = Result->getLexicalDeclContext()) + Result = cast<RecordDecl>(DC); + return Result; +} + +static bool containsUsingOf(const NamespaceDecl *ND, + const ASTContext &Context) { + for (const Decl *D : Context.getTranslationUnitDecl()->decls()) { + if (const auto *UDD = dyn_cast<UsingDirectiveDecl>(D)) { + if (UDD->getNominatedNamespace() == ND) + return true; + } + } + return false; +} + +llvm::Expected<RefactoringResult> +ImplementDeclaredCXXMethodsOperation::runInImplementationAST( + ASTContext &Context, const FileID &File, const CXXRecordDecl *Class, + ArrayRef<const CXXMethodDecl *> SelectedMethods) { + if (!Class) + return llvm::make_error<RefactoringOperationError>( + "the target class is not defined in the continuation AST unit"); + + SourceManager &SM = Context.getSourceManager(); + + // Find the defined methods of the class. + llvm::SmallVector<const CXXMethodDecl *, 8> DefinedOutOfLineMethods; + for (const CXXMethodDecl *M : Class->methods()) { + if (M->isImplicit()) + continue; + if (const FunctionDecl *MD = M->getDefinition()) { + if (!MD->isOutOfLine()) + continue; + SourceLocation Loc = SM.getExpansionLoc(MD->getLocStart()); + if (SM.getFileID(Loc) == File) + DefinedOutOfLineMethods.push_back(cast<CXXMethodDecl>(MD)); + } + } + + std::vector<RefactoringReplacement> Replacements; + std::string MethodString; + llvm::raw_string_ostream OS(MethodString); + + // Pick a good insertion location. + SourceLocation InsertionLoc; + const CXXMethodDecl *InsertAfterMethod = nullptr; + NestedNameSpecifier *NamePrefix = nullptr; + if (DefinedOutOfLineMethods.empty()) { + const RecordDecl *OutermostRecord = findOutermostRecord(Class); + InsertionLoc = SM.getExpansionRange(OutermostRecord->getLocEnd()).second; + if (SM.getFileID(InsertionLoc) == File) { + // We can insert right after the class. Compute the appropriate + // qualification. + NamePrefix = NestedNameSpecifier::getRequiredQualification( + Context, OutermostRecord->getLexicalDeclContext(), + Class->getLexicalDeclContext()); + } else { + // We can't insert after the end of the class, since the indexer told us + // that some file should have the implementation of it, even when there + // are no methods here. We should try to insert at the end of the file. + InsertionLoc = SM.getLocForEndOfFile(File); + NamePrefix = NestedNameSpecifier::getRequiredQualification( + Context, Context.getTranslationUnitDecl(), + Class->getLexicalDeclContext()); + llvm::SmallVector<const NamespaceDecl *, 4> Namespaces; + for (const NestedNameSpecifier *Qualifier = NamePrefix; Qualifier; + Qualifier = Qualifier->getPrefix()) { + if (const NamespaceDecl *ND = Qualifier->getAsNamespace()) + Namespaces.push_back(ND); + } + // When the class is in a namespace, add a 'using' declaration if it's + // needed and adjust the out-of-line qualification. + if (!Namespaces.empty()) { + const NamespaceDecl *InnermostNamespace = Namespaces[0]; + if (!containsUsingOf(InnermostNamespace, Context)) { + std::string NamespaceString; + llvm::raw_string_ostream NamespaceOS(NamespaceString); + for (const NamespaceDecl *ND : llvm::reverse(Namespaces)) { + if (!NamespaceOS.str().empty()) + NamespaceOS << "::"; + NamespaceOS << ND->getDeclName(); + } + OS << "\nusing namespace " << NamespaceOS.str() << ";"; + } + // Re-compute the name qualifier without the namespace. + NamePrefix = NestedNameSpecifier::getRequiredQualification( + Context, InnermostNamespace, Class->getLexicalDeclContext()); + } + } + } else { + // Insert at the end of the defined methods. + for (const CXXMethodDecl *M : DefinedOutOfLineMethods) { + SourceLocation EndLoc = SM.getExpansionRange(M->getLocEnd()).second; + if (InsertionLoc.isInvalid() || + SM.isBeforeInTranslationUnit(InsertionLoc, EndLoc)) { + InsertionLoc = EndLoc; + InsertAfterMethod = M; + } + } + } + InsertionLoc = getLastLineLocationUnlessItHasOtherTokens( + InsertionLoc, SM, Context.getLangOpts()); + + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SupressStorageClassSpecifiers = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + OS << "\n"; + for (const CXXMethodDecl *MD : SelectedMethods) { + // Check if the method is already defined. + if (!MD) + continue; + + // Drop the 'virtual' specifier. + bool IsVirtual = MD->isVirtualAsWritten(); + const_cast<CXXMethodDecl *>(MD)->setVirtualAsWritten(false); + + // Drop the default arguments. + llvm::SmallVector<std::pair<ParmVarDecl *, Expr *>, 4> DefaultArgs; + for (const ParmVarDecl *P : MD->parameters()) { + if (!P->hasDefaultArg()) + continue; + Expr *E = const_cast<ParmVarDecl *>(P)->getDefaultArg(); + const_cast<ParmVarDecl *>(P)->setDefaultArg(nullptr); + DefaultArgs.emplace_back(const_cast<ParmVarDecl *>(P), E); + } + + // Add the nested name specifiers that are appropriate for an out-of-line + // method. + auto *Qualifier = + InsertAfterMethod + ? InsertAfterMethod->getQualifier() + : NestedNameSpecifier::Create( + Context, /*Prefix=*/NamePrefix, /*Template=*/false, + Context.getRecordType(Class).getTypePtr()); + NestedNameSpecifierLoc PrevQualifierInfo = MD->getQualifierLoc(); + const_cast<CXXMethodDecl *>(MD)->setQualifierInfo( + NestedNameSpecifierLoc(Qualifier, /*Loc=*/nullptr)); + + OS << "\n"; + MD->print(OS, PP); + OS << " { \n <#code#>;\n}\n"; + + // Restore the original method + for (const auto &DefaultArg : DefaultArgs) + DefaultArg.first->setDefaultArg(DefaultArg.second); + const_cast<CXXMethodDecl *>(MD)->setVirtualAsWritten(IsVirtual); + const_cast<CXXMethodDecl *>(MD)->setQualifierInfo(PrevQualifierInfo); + } + + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str()))); + + return std::move(Replacements); +} + +llvm::Expected<RefactoringResult> +ImplementDeclaredObjCMethodsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + using namespace indexer; + return continueInExternalASTUnit( + fileThatShouldContainImplementationOf(Container), runInImplementationAST, + Container, filter(llvm::makeArrayRef(SelectedMethods), + [](const DeclEntity &D) { return !D.isDefined(); })); +} + +static const ObjCImplDecl * +getImplementationContainer(const ObjCContainerDecl *Container) { + if (!Container) + return nullptr; + if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(Container)) + return ID->getImplementation(); + if (const auto *CD = dyn_cast<ObjCCategoryDecl>(Container)) + return CD->getImplementation(); + return nullptr; +} + +llvm::Expected<RefactoringResult> +ImplementDeclaredObjCMethodsOperation::runInImplementationAST( + ASTContext &Context, const FileID &File, const ObjCContainerDecl *Container, + ArrayRef<const ObjCMethodDecl *> SelectedMethods) { + const ObjCImplDecl *ImplementationContainer = + getImplementationContainer(Container); + if (!ImplementationContainer) + return llvm::make_error<RefactoringOperationError>( + "the target @interface is not implemented in the continuation AST " + "unit"); + + std::vector<RefactoringReplacement> Replacements; + + std::string MethodString; + llvm::raw_string_ostream OS(MethodString); + + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + for (const ObjCMethodDecl *MD : SelectedMethods) { + // Skip methods that are already defined. + if (!MD) + continue; + + std::string MethodDeclStr; + llvm::raw_string_ostream MethodOS(MethodDeclStr); + MD->print(MethodOS, PP); + + OS << StringRef(MethodOS.str()).drop_back(); // Drop the ';' + OS << " { \n <#code#>;\n}\n\n"; + } + SourceLocation InsertionLoc = ImplementationContainer->getLocEnd(); + + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str()))); + + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/IndexerQueries.cpp b/clang/lib/Tooling/Refactor/IndexerQueries.cpp new file mode 100644 index 0000000000000..063a27be3b83a --- /dev/null +++ b/clang/lib/Tooling/Refactor/IndexerQueries.cpp @@ -0,0 +1,136 @@ +//===--- IndexerQueries.cpp - Indexer queries -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/IndexerQuery.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace clang; +using namespace clang::tooling; +using namespace clang::tooling::indexer; +using namespace clang::tooling::indexer::detail; +using namespace llvm::yaml; + +const char *ASTProducerQuery::BaseUIDString = "ast.producer.query"; +const char *DeclarationsQuery::BaseUIDString = "decl.query"; +const char *ASTUnitForImplementationOfDeclarationQuery::NameUIDString = + "file.for.impl.of.decl"; + +const char *DeclPredicateNodePredicate::NameUIDString = "decl.predicate"; +const char *DeclPredicateNotPredicate::NameUIDString = "not.decl.predicate"; + +std::unique_ptr<DeclPredicateNode> +DeclPredicateNode::create(const DeclPredicate &Predicate) { + return llvm::make_unique<DeclPredicateNodePredicate>(Predicate); +} + +std::unique_ptr<DeclPredicateNode> +DeclPredicateNode::create(const BoolDeclPredicate &Predicate) { + if (Predicate.IsInverted) + return llvm::make_unique<DeclPredicateNotPredicate>( + create(Predicate.Predicate)); + return create(Predicate.Predicate); +} + +std::unique_ptr<ASTUnitForImplementationOfDeclarationQuery> +clang::tooling::indexer::fileThatShouldContainImplementationOf(const Decl *D) { + return llvm::make_unique<ASTUnitForImplementationOfDeclarationQuery>(D); +} + +namespace { + +struct QueryPredicateNode { + std::string Name; + std::vector<int> IntegerValues; +}; + +struct QueryYAMLNode { + std::string Name; + std::vector<QueryPredicateNode> PredicateResults; + std::string FilenameResult; +}; + +} // end anonymous namespace + +LLVM_YAML_IS_SEQUENCE_VECTOR(int) +LLVM_YAML_IS_SEQUENCE_VECTOR(QueryPredicateNode) +LLVM_YAML_IS_SEQUENCE_VECTOR(QueryYAMLNode) + +namespace llvm { +namespace yaml { + +template <> struct MappingTraits<QueryPredicateNode> { + static void mapping(IO &Yaml, QueryPredicateNode &Predicate) { + Yaml.mapRequired("name", Predicate.Name); + Yaml.mapRequired("intValues", Predicate.IntegerValues); + } +}; + +template <> struct MappingTraits<QueryYAMLNode> { + static void mapping(IO &Yaml, QueryYAMLNode &Query) { + Yaml.mapRequired("name", Query.Name); + Yaml.mapOptional("predicateResults", Query.PredicateResults); + Yaml.mapOptional("filenameResult", Query.FilenameResult); + // FIXME: Report an error if no results are provided at all. + } +}; + +} // end namespace yaml +} // end namespace llvm + +llvm::Error +IndexerQuery::loadResultsFromYAML(StringRef Source, + ArrayRef<IndexerQuery *> Queries) { + std::vector<QueryYAMLNode> QueryResults; + Input YamlIn(Source); + YamlIn >> QueryResults; + if (YamlIn.error()) + return llvm::make_error<llvm::StringError>("Failed to parse query results", + YamlIn.error()); + if (QueryResults.size() != Queries.size()) + return llvm::make_error<llvm::StringError>("Mismatch in query results size", + llvm::errc::invalid_argument); + for (const auto &QueryTuple : llvm::zip(Queries, QueryResults)) { + IndexerQuery *Query = std::get<0>(QueryTuple); + const QueryYAMLNode &Result = std::get<1>(QueryTuple); + if ((Query->NameUID && Query->NameUID != Result.Name) && + (Query->BaseUID && Query->BaseUID != Result.Name)) + continue; + if (auto *DQ = dyn_cast<DeclarationsQuery>(Query)) { + const DeclPredicateNode &Predicate = DQ->getPredicateNode(); + DeclPredicate ActualPredicate(""); + bool IsNot = false; + if (const auto *Not = dyn_cast<DeclPredicateNotPredicate>(&Predicate)) { + ActualPredicate = + cast<DeclPredicateNodePredicate>(Not->getChild()).getPredicate(); + IsNot = true; + } else + ActualPredicate = + cast<DeclPredicateNodePredicate>(Predicate).getPredicate(); + for (const auto &PredicateResult : Result.PredicateResults) { + if (PredicateResult.Name != ActualPredicate.Name) + continue; + std::vector<PersistentDeclRef<Decl>> Output; + for (const auto &ResultTuple : + zip(DQ->getInputs(), PredicateResult.IntegerValues)) { + const Decl *D = std::get<0>(ResultTuple); + int Result = std::get<1>(ResultTuple); + Output.push_back(PersistentDeclRef<Decl>::create( + (IsNot ? !Result : !!Result) ? D : nullptr)); + } + DQ->setOutput(std::move(Output)); + break; + } + } else if (auto *AQ = + dyn_cast<ASTUnitForImplementationOfDeclarationQuery>(Query)) + AQ->setResult(Result.FilenameResult); + } + return llvm::Error::success(); +} diff --git a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp new file mode 100644 index 0000000000000..d2ad11ec15366 --- /dev/null +++ b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp @@ -0,0 +1,79 @@ +//===--- LocalizeObjCString.cpp - ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the "Wrap in NSLocalizedString" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class LocalizeObjCStringLiteralOperation : public RefactoringOperation { +public: + LocalizeObjCStringLiteralOperation(const ObjCStringLiteral *E) : E(E) {} + + const Stmt *getTransformedStmt() const override { return E; } + + llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const ObjCStringLiteral *E; +}; + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateLocalizeObjCStringLiteralOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + const ObjCStringLiteral *E; + if (SelectionRange.isValid()) { + auto SelectedSet = Slice.getSelectedStmtSet(); + if (!SelectedSet) + return None; + E = dyn_cast_or_null<ObjCStringLiteral>( + SelectedSet->containsSelectionRange); + } else + E = cast_or_null<ObjCStringLiteral>( + Slice.nearestStmt(Stmt::ObjCStringLiteralClass)); + if (!E) + return None; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = llvm::make_unique<LocalizeObjCStringLiteralOperation>(E); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected<RefactoringResult> +LocalizeObjCStringLiteralOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector<RefactoringReplacement> Replacements; + SourceLocation LocStart = E->getLocStart(); + Replacements.emplace_back(SourceRange(LocStart, LocStart), + StringRef("NSLocalizedString(")); + SourceLocation LocEnd = getPreciseTokenLocEnd( + E->getLocEnd(), Context.getSourceManager(), Context.getLangOpts()); + Replacements.emplace_back(SourceRange(LocEnd, LocEnd), StringRef(", @\"\")")); + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/RefactoringActionFinder.cpp b/clang/lib/Tooling/Refactor/RefactoringActionFinder.cpp new file mode 100644 index 0000000000000..2fd7cd5c137a1 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringActionFinder.cpp @@ -0,0 +1,57 @@ +//===--- RefactoringActionFinder.cpp - Clang refactoring library ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implements methods that find the refactoring actions that can be +/// performed at specific locations / source ranges in a translation unit. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringActionFinder.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "clang/Tooling/Refactor/USRFinder.h" + +namespace clang { +namespace tooling { + +RefactoringActionSet findActionSetAt(SourceLocation Location, + SourceRange SelectionRange, + ASTContext &Context) { + RefactoringActionSet Result; + if (const auto *ND = rename::getNamedDeclAt(Context, Location)) + Result.Actions.push_back(isLocalSymbol(ND, Context.getLangOpts()) + ? RefactoringActionType::Rename_Local + : RefactoringActionType::Rename); + + // FIXME: We can avoid checking if some actions can be initiated when they're + // not allowed in the current language mode. + RefactoringActionType Actions[] = { +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \ + RefactoringActionType::Name, +#include "clang/Tooling/Refactor/RefactoringActions.def" + }; + + for (auto Action : Actions) { + auto Op = initiateRefactoringOperationAt(Location, SelectionRange, Context, + Action, + /*CreateOperation=*/true); + if (Op.Initiated) { + Result.Actions.push_back(Action); + if (Op.RefactoringOp) { + for (const auto &SubAction : Op.RefactoringOp->getAvailableSubActions()) + Result.Actions.push_back(SubAction); + } + } + } + + return Result; +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RefactoringActions.cpp b/clang/lib/Tooling/Refactor/RefactoringActions.cpp new file mode 100644 index 0000000000000..4b2b735b3d54c --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringActions.cpp @@ -0,0 +1,30 @@ +//===--- RefactoringActions.cpp - Clang refactoring library ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Contains a list of all the supported refactoring actions. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringActions.h" + +namespace clang { +namespace tooling { + +StringRef getRefactoringActionTypeName(RefactoringActionType Action) { + switch (Action) { +#define REFACTORING_ACTION(Name, Spelling) \ + case RefactoringActionType::Name: \ + return Spelling; +#include "clang/Tooling/Refactor/RefactoringActions.def" + } +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RefactoringContinuations.h b/clang/lib/Tooling/Refactor/RefactoringContinuations.h new file mode 100644 index 0000000000000..25c3a39f89428 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringContinuations.h @@ -0,0 +1,351 @@ +//===--- RefactoringContinuations.h - Defines refactoring continuations ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H + +#include "clang/AST/Decl.h" +#include "clang/Tooling/Refactor/IndexerQuery.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "llvm/ADT/StringMap.h" +#include <tuple> + +namespace clang { +namespace tooling { + +namespace detail { + +struct ValidBase {}; + +/// The ContinuationPassType determine which type is passed into the refactoring +/// continuation. +template <typename T> struct ContinuationPassType { using Type = T; }; + +template <typename T> struct ContinuationPassType<std::vector<T>> { + using Type = ArrayRef<T>; +}; + +/// Refactoring operations can pass state to the continuations. Valid state +/// values should have a corresponding \c StateTraits specialization. +template <typename T> struct StateTraits { + /// Specializations should define the following types: + /// + /// StoredResultType: The TU-specific type which is then passed into the + /// continuation function. The continuation receives the result whose type is + /// \c ContinuationPassType<StoredResultType>::Type. + /// + /// PersistentType: The TU-independent type that's persisted even after the + /// TU in which the continuation was created is disposed. +}; + +template <typename T> +struct StateTraits<const T *> + : std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type { + using StoredResultType = const T *; + using PersistentType = PersistentDeclRef<T>; +}; + +template <typename T> +struct StateTraits<ArrayRef<const T *>> + : std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type { + using StoredResultType = std::vector<const T *>; + using PersistentType = std::vector<PersistentDeclRef<T>>; +}; + +template <typename T> +struct StateTraits<std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>>> + : std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type { + using StoredResultType = std::vector<const T *>; + using PersistentType = std::vector<PersistentDeclRef<T>>; +}; + +/// Conversion functions convert the TU-specific state to a TU independent +/// state and vice-versa. +template <typename T> +PersistentDeclRef<T> convertToPersistentRepresentation( + const T *Declaration, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + return PersistentDeclRef<T>::create(Declaration); +} + +template <typename T> +std::vector<PersistentDeclRef<T>> convertToPersistentRepresentation( + ArrayRef<const T *> Declarations, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + std::vector<PersistentDeclRef<T>> Result; + Result.reserve(Declarations.size()); + for (const T *D : Declarations) + Result.push_back(PersistentDeclRef<T>::create(D)); + return Result; +} + +template <typename T> +std::vector<PersistentDeclRef<T>> convertToPersistentRepresentation( + std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>> &Query, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + Query->invalidateTUSpecificState(); + return Query->getOutput(); +} + +/// Converts the TU-independent state to the TU-specific state. +class PersistentToASTSpecificStateConverter { + ASTContext &Context; + llvm::StringMap<const Decl *> ConvertedDeclRefs; + + const Decl *lookupDecl(StringRef USR); + +public: + // FIXME: We can hide the addConvertible/convert interface so that + // the continuation will just invoke one conversion function for the entire + // tuple. + PersistentToASTSpecificStateConverter(ASTContext &Context) + : Context(Context) {} + + template <typename T> + bool addConvertible( + const PersistentDeclRef<T> &Ref, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + ConvertedDeclRefs[Ref.USR] = nullptr; + return true; + } + + template <typename T> + const T * + convert(const PersistentDeclRef<T> &Ref, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + return dyn_cast_or_null<T>(lookupDecl(Ref.USR)); + } + + template <typename T> + bool addConvertible( + const std::vector<PersistentDeclRef<T>> &Refs, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + for (const auto &Ref : Refs) + ConvertedDeclRefs[Ref.USR] = nullptr; + return true; + } + + template <typename T> + std::vector<const T *> + convert(const std::vector<PersistentDeclRef<T>> &Refs, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + std::vector<const T *> Results; + Results.reserve(Refs.size()); + // Allow nulls in the produced array, the continuation will have to deal + // with them by itself. + for (const auto &Ref : Refs) + Results.push_back(dyn_cast_or_null<T>(lookupDecl(Ref.USR))); + return Results; + } + + bool addConvertible(const PersistentFileID &) { + // Do nothing since FileIDs are converted one-by-one. + return true; + } + + FileID convert(const PersistentFileID &Ref); + + /// Converts the added persistent state into TU-specific state using one + /// efficient operation. + void runCoalescedConversions(); +}; + +template <typename T, typename ASTQueryType, typename... QueryOrState> +struct ContinuationFunction { + using Type = llvm::Expected<RefactoringResult> (*)( + ASTContext &, const T &, + typename ContinuationPassType< + typename StateTraits<QueryOrState>::StoredResultType>::Type...); + + template <size_t... Is> + static llvm::Expected<RefactoringResult> dispatch( + Type Fn, detail::PersistentToASTSpecificStateConverter &Converter, + ASTContext &Context, const ASTQueryType &Query, + const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...> + &Arguments, + llvm::index_sequence<Is...>) { + auto ASTQueryResult = Converter.convert(Query.getResult()); + return Fn(Context, ASTQueryResult, std::get<Is>(Arguments)...); + } +}; + +template <typename ASTQueryType, typename... QueryOrState> +struct ContinuationFunction<void, ASTQueryType, QueryOrState...> { + using Type = llvm::Expected<RefactoringResult> (*)( + ASTContext &, + typename ContinuationPassType< + typename StateTraits<QueryOrState>::StoredResultType>::Type...); + + template <size_t... Is> + static llvm::Expected<RefactoringResult> dispatch( + Type Fn, detail::PersistentToASTSpecificStateConverter &, + ASTContext &Context, const ASTQueryType &, + const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...> + &Arguments, + llvm::index_sequence<Is...>) { + return Fn(Context, std::get<Is>(Arguments)...); + } +}; + +/// The refactoring contination contains a set of structures that implement +/// the refactoring operation continuation mechanism. +template <typename ASTQueryType, typename... QueryOrState> +class SpecificRefactoringContinuation final : public RefactoringContinuation { +public: + static_assert(std::is_base_of<indexer::ASTProducerQuery, ASTQueryType>::value, + "Invalid AST Query"); + // TODO: Validate the QueryOrState types. + + /// The consumer function is the actual continuation. It receives the state + /// that was passed-in in the request or the results of the indexing queries + /// that were passed-in in the request. + using ConsumerFn = + typename ContinuationFunction<typename ASTQueryType::ResultTy, + ASTQueryType, QueryOrState...>::Type; + +private: + ConsumerFn Consumer; + std::unique_ptr<ASTQueryType> ASTQuery; + /// Inputs store state that's dependent on the original TU. + llvm::Optional<std::tuple<QueryOrState...>> Inputs; + /// State contains TU-independent values. + llvm::Optional< + std::tuple<typename StateTraits<QueryOrState>::PersistentType...>> + State; + + /// Converts a tuple that contains the TU dependent state to a tuple with + /// TU independent state. + template <size_t... Is> + std::tuple<typename StateTraits<QueryOrState>::PersistentType...> + convertToPersistentImpl(llvm::index_sequence<Is...>) { + assert(Inputs && "TU-dependent state is already converted"); + return std::make_tuple( + detail::convertToPersistentRepresentation(std::get<Is>(*Inputs))...); + } + + template <typename T> + bool gatherQueries( + std::vector<indexer::IndexerQuery *> &Queries, + const std::unique_ptr<T> &Query, + typename std::enable_if< + std::is_base_of<indexer::IndexerQuery, T>::value>::type * = nullptr) { + Queries.push_back(Query.get()); + return true; + } + + template <typename T> + bool gatherQueries(std::vector<indexer::IndexerQuery *> &, const T &) { + // This input element is not a query. + return true; + } + + template <size_t... Is> + std::vector<indexer::IndexerQuery *> + gatherQueries(llvm::index_sequence<Is...>) { + assert(Inputs && "TU-dependent state is already converted"); + std::vector<indexer::IndexerQuery *> Queries; + std::make_tuple(gatherQueries(Queries, std::get<Is>(*Inputs))...); + return Queries; + } + + /// Calls the consumer function with the given \p Context and the state + /// whose values are converted from the TU-independent to TU-specific values. + template <size_t... Is> + llvm::Expected<RefactoringResult> dispatch(ASTContext &Context, + llvm::index_sequence<Is...> Seq) { + assert(State && "TU-independent state is not yet produced"); + detail::PersistentToASTSpecificStateConverter Converter(Context); + (void)std::make_tuple(Converter.addConvertible(std::get<Is>(*State))...); + Converter.runCoalescedConversions(); + auto Results = std::make_tuple(Converter.convert(std::get<Is>(*State))...); + // TODO: Check for errors? + return detail::ContinuationFunction< + typename ASTQueryType::ResultTy, ASTQueryType, + QueryOrState...>::dispatch(Consumer, Converter, Context, *ASTQuery, + Results, Seq); + } + +public: + SpecificRefactoringContinuation(ConsumerFn Consumer, + std::unique_ptr<ASTQueryType> ASTQuery, + QueryOrState... Inputs) + : Consumer(Consumer), ASTQuery(std::move(ASTQuery)), + Inputs(std::make_tuple(std::move(Inputs)...)) {} + + SpecificRefactoringContinuation(SpecificRefactoringContinuation &&) = default; + SpecificRefactoringContinuation & + operator=(SpecificRefactoringContinuation &&) = default; + + indexer::ASTProducerQuery *getASTUnitIndexerQuery() override { + return ASTQuery.get(); + } + + std::vector<indexer::IndexerQuery *> getAdditionalIndexerQueries() override { + return gatherQueries(llvm::index_sequence_for<QueryOrState...>()); + } + + /// Query results are fetched. State is converted to a persistent + /// representation. + void persistTUSpecificState() override { + ASTQuery->invalidateTUSpecificState(); + State = + convertToPersistentImpl(llvm::index_sequence_for<QueryOrState...>()); + Inputs = None; + } + + /// The state is converted to the AST representation in the given ASTContext + /// and the continuation is dispatched. + llvm::Expected<RefactoringResult> + runInExternalASTUnit(ASTContext &Context) override { + return dispatch(Context, llvm::index_sequence_for<QueryOrState...>()); + } +}; + +} // end namespace detail + +/// Returns a refactoring continuation that will run within the context of a +/// single external AST unit. +/// +/// The indexer determines which AST unit should receive the continuation by +/// evaluation the AST query operation \p ASTQuery. +/// +/// \param ASTQuery The query that will determine which AST unit should the +/// continuation run in. +/// +/// \param Consumer The continuation function that will be called once the +/// external AST unit is loaded. +/// +/// \param Inputs Each individiual input element can contain either some +/// state value that will be passed into the \p Consumer function or an +/// indexer query whose results will be passed into the \p Consumer function. +template <typename ASTQueryType, typename... QueryOrState> +typename std::enable_if< + std::is_base_of<indexer::ASTProducerQuery, ASTQueryType>::value, + std::unique_ptr<RefactoringContinuation>>::type +continueInExternalASTUnit( + std::unique_ptr<ASTQueryType> ASTQuery, + typename detail::SpecificRefactoringContinuation< + ASTQueryType, QueryOrState...>::ConsumerFn Consumer, + QueryOrState... Inputs) { + return llvm::make_unique< + detail::SpecificRefactoringContinuation<ASTQueryType, QueryOrState...>>( + Consumer, std::move(ASTQuery), std::move(Inputs)...); +} + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H diff --git a/clang/lib/Tooling/Refactor/RefactoringOperation.cpp b/clang/lib/Tooling/Refactor/RefactoringOperation.cpp new file mode 100644 index 0000000000000..a9f431f98d0a6 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringOperation.cpp @@ -0,0 +1,93 @@ +//===--- RefactoringOperation.cpp - Defines a refactoring operation -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "ASTSlice.h" +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include "llvm/Support/Errc.h" + +using namespace clang; +using namespace clang::tooling; + +char RefactoringOperationError::ID; + +void RefactoringOperationError::log(raw_ostream &OS) const { + OS << "Refactoring operation failed: " << FailureReason; +} + +std::error_code RefactoringOperationError::convertToErrorCode() const { + return make_error_code(llvm::errc::operation_not_permitted); +} + +RefactoringOperationResult clang::tooling::initiateRefactoringOperationAt( + SourceLocation Location, SourceRange SelectionRange, ASTContext &Context, + RefactoringActionType ActionType, bool CreateOperation) { + if (Location.isInvalid()) + return None; + if (ActionType == RefactoringActionType::Rename || + ActionType == RefactoringActionType::Rename_Local) { + const NamedDecl *FoundDecl = rename::getNamedDeclAt(Context, Location); + if (!FoundDecl) + return None; + RefactoringOperationResult Result; + Result.Initiated = true; + if (CreateOperation) + Result.SymbolOp = llvm::make_unique<SymbolOperation>(FoundDecl, Context); + return Result; + } + SourceManager &SM = Context.getSourceManager(); + if (Location.isMacroID()) + Location = SM.getSpellingLoc(Location); + assert(Location.isFileID() && "Invalid location"); + + // TODO: Don't perform duplicate work when initiateRefactoringOperationAt is + // called from findRefactoringActionsAt. + if (SelectionRange.isValid()) { + if (SelectionRange.getBegin().isMacroID() || + SelectionRange.getEnd().isMacroID()) + SelectionRange = SourceRange(SM.getSpellingLoc(SelectionRange.getBegin()), + SM.getSpellingLoc(SelectionRange.getEnd())); + SelectionRange = trimSelectionRange( + SelectionRange, Context.getSourceManager(), Context.getLangOpts()); + } + ASTSlice Slice(Location, SelectionRange, Context); + + switch (ActionType) { +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \ + case RefactoringActionType::Name: \ + return initiate##Name##Operation(Slice, Context, Location, SelectionRange, \ + CreateOperation); +#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \ + case RefactoringActionType::Parent##_##Name: \ + return initiate##Parent##Name##Operation(Slice, Context, Location, \ + SelectionRange, CreateOperation); +#include "clang/Tooling/Refactor/RefactoringActions.def" + default: + break; + } + return RefactoringOperationResult(); +} + +RefactoringOperationResult clang::tooling::initiateRefactoringOperationOnDecl( + StringRef DeclUSR, ASTContext &Context, RefactoringActionType ActionType) { + if (ActionType != RefactoringActionType::Rename) + return None; + const NamedDecl *FoundDecl = rename::getNamedDeclWithUSR(Context, DeclUSR); + if (!FoundDecl) + return None; + RefactoringOperationResult Result; + Result.Initiated = true; + Result.SymbolOp = llvm::make_unique<SymbolOperation>(FoundDecl, Context); + return Result; +} diff --git a/clang/lib/Tooling/Refactor/RefactoringOperations.h b/clang/lib/Tooling/Refactor/RefactoringOperations.h new file mode 100644 index 0000000000000..e02a3cf0b3df4 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringOperations.h @@ -0,0 +1,37 @@ +//===--- RefactoringOperations.h - The supported refactoring operations ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H + +#include "ASTSlice.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" + +namespace clang { + +class Expr; +class IfStmt; +class VarDecl; + +namespace tooling { + +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \ + RefactoringOperationResult initiate##Name##Operation( \ + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, \ + SourceRange SelectionRange, bool CreateOperation = true); +#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \ + RefactoringOperationResult initiate##Parent##Name##Operation( \ + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, \ + SourceRange SelectionRange, bool CreateOperation = true); +#include "clang/Tooling/Refactor/RefactoringActions.def" + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H diff --git a/clang/lib/Tooling/Refactor/RefactoringOptions.cpp b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp new file mode 100644 index 0000000000000..fd2d8d1583a6a --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp @@ -0,0 +1,65 @@ +//===--- RefactoringOptions.cpp - A set of all the refactoring options ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringOptions.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace clang; +using namespace clang::tooling; +using namespace clang::tooling::option; +using namespace llvm::yaml; + +void RefactoringOptionSet::print(llvm::raw_ostream &OS) const { + Output YamlOut(OS); + if (YamlOut.preflightDocument(0)) { + YamlOut.beginFlowMapping(); + for (const auto &Option : Options) + Option.getValue()->serialize(YamlOut); + YamlOut.endFlowMapping(); + YamlOut.postflightDocument(); + } +} + +template <> struct CustomMappingTraits<RefactoringOptionSet> { + static void inputOne(IO &YamlIn, StringRef Key, + RefactoringOptionSet &Result) { +#define HANDLE(Type) \ + if (Key == Type::Name) { \ + Type Value; \ + Value.serialize(YamlIn); \ + Result.add(Value); \ + return; \ + } + HANDLE(AvoidTextualMatches) +#undef HANDLE + YamlIn.setError(Twine("Unknown refactoring option ") + Key); + } + static void output(IO &, RefactoringOptionSet &) { + llvm_unreachable("Output is done without mapping traits"); + } +}; + +llvm::Expected<RefactoringOptionSet> +RefactoringOptionSet::parse(StringRef Source) { + Input YamlIn(Source); + // FIXME: Don't dump errors to stderr. + RefactoringOptionSet Result; + YamlIn >> Result; + if (YamlIn.error()) + return llvm::make_error<llvm::StringError>("Failed to parse the option set", + YamlIn.error()); + return std::move(Result); +} + +void RefactoringOption::serialize(const SerializationContext &) {} + +void clang::tooling::option::detail::BoolOptionBase::serializeImpl( + const SerializationContext &Context, const char *Name) { + Context.IO.mapRequired(Name, Value); +} diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp new file mode 100644 index 0000000000000..5ec491c557b6b --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -0,0 +1,562 @@ +//===--- RenameIndexedFile.cpp - ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RenameIndexedFile.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Tooling/Refactor/RefactoringOptions.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Path.h" + +using namespace clang; + +namespace clang { +namespace tooling { +namespace rename { + +IndexedFileOccurrenceProducer::IndexedFileOccurrenceProducer( + ArrayRef<IndexedSymbol> Symbols, IndexedFileOccurrenceConsumer &Consumer, + const RefactoringOptionSet *Options) + : Symbols(Symbols), Consumer(Consumer), Options(Options) { + IsMultiPiece = false; + for (const auto &Symbol : Symbols) { + if (Symbol.Name.size() > 1) { + IsMultiPiece = true; + break; + } + } + if (IsMultiPiece) { + for (const auto &Symbol : Symbols) { + (void)Symbol; + assert(Symbol.Name.size() > 1 && + "Mixed multi-piece and single piece symbols " + "are unsupported"); + } + } +} + +namespace { + +enum class MatchKind { SourceMatch, MacroExpansion, None }; + +} // end anonymous namespace + +static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence, + const IndexedSymbol &Symbol, + const SourceManager &SM, + const LangOptions &LangOpts, + SourceLocation &BeginLoc) { + if (!Occurrence.Line || !Occurrence.Column) + return MatchKind::None; // Ignore any invalid indexed locations. + + // Ensure that the first string in the name is present at the given + // location. + BeginLoc = SM.translateLineCol(SM.getMainFileID(), Occurrence.Line, + Occurrence.Column); + if (BeginLoc.isInvalid()) + return MatchKind::None; + StringRef SymbolNameStart = Symbol.Name[0]; + SourceLocation EndLoc = BeginLoc.getLocWithOffset(SymbolNameStart.size()); + if (!SM.isBeforeInTranslationUnit(BeginLoc, EndLoc)) { + // Ignore any invalid source ranges. This can occur if the indexed + // location is invalid. + return MatchKind::None; + } + // Extract the token at the location. + auto DecomposedLoc = SM.getDecomposedLoc(BeginLoc); + const llvm::MemoryBuffer *File = SM.getBuffer(DecomposedLoc.first); + Lexer RawLex( + BeginLoc, LangOpts, File->getBufferStart() + DecomposedLoc.second, + File->getBufferStart() + DecomposedLoc.second, File->getBufferEnd()); + Token Tok; + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::raw_identifier) || Tok.getLocation() != BeginLoc) + return MatchKind::None; + return Tok.getRawIdentifier() == SymbolNameStart ? MatchKind::SourceMatch + : MatchKind::MacroExpansion; +} + +static void +findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, + ArrayRef<IndexedSymbol> Symbols, + IndexedFileOccurrenceConsumer &Consumer); + +namespace { + +struct TextualMatchOccurrence { + SourceLocation Location; + unsigned SymbolIndex; +}; + +/// Finds '@selector' expressions by looking at tokens one-by-one. +class SelectorParser { + enum ParseState { + None, + At, + Selector, + ExpectingSelectorPiece, + ExpectingColon, + ExpectingRParenOrColon, + ExpectingRParen, + Success + }; + ParseState State = None; + const SymbolName &Name; + + ParseState stateForToken(const Token &RawTok); + +public: + unsigned SymbolIndex; + llvm::SmallVector<SourceLocation, 8> SelectorLocations; + + SelectorParser(const SymbolName &Name, unsigned SymbolIndex) + : Name(Name), SymbolIndex(SymbolIndex) {} + + /// Returns true if the parses has found a '@selector' expression. + bool handleToken(const Token &RawTok); +}; + +class InclusionLexer final : public Lexer { +public: + InclusionLexer(SourceLocation FileLoc, const LangOptions &LangOpts, + const char *BufStart, const char *BufEnd) + : Lexer(FileLoc, LangOpts, BufStart, BufStart, BufEnd) {} + + void IndirectLex(Token &Result) override { LexFromRawLexer(Result); } +}; + +} // end anonymous namespace + +SelectorParser::ParseState SelectorParser::stateForToken(const Token &RawTok) { + assert(RawTok.isNot(tok::comment) && "unexpected comment token"); + switch (State) { + case None: + break; + case At: + if (RawTok.is(tok::raw_identifier) && + RawTok.getRawIdentifier() == "selector") + return Selector; + break; + case Selector: + if (RawTok.isNot(tok::l_paren)) + break; + SelectorLocations.clear(); + return ExpectingSelectorPiece; + case ExpectingSelectorPiece: + assert(SelectorLocations.size() < Name.size() && + "Expecting invalid selector piece"); + if (RawTok.isNot(tok::raw_identifier) || + RawTok.getRawIdentifier() != Name[SelectorLocations.size()]) + break; + SelectorLocations.push_back(RawTok.getLocation()); + if (SelectorLocations.size() == Name.size()) { + // We found the selector that we were looking for, now check for ')'. + return ExpectingRParenOrColon; + } + return ExpectingColon; + case ExpectingColon: + if (RawTok.is(tok::colon)) + return ExpectingSelectorPiece; + break; + case ExpectingRParenOrColon: + if (RawTok.is(tok::colon)) + return ExpectingRParen; + // Fallthrough + case ExpectingRParen: + if (RawTok.is(tok::r_paren)) { + // We found the selector that we were looking for. + return Success; + } + break; + case Success: + llvm_unreachable("should not get here"); + } + // Look for the start of the selector expression. + return RawTok.is(tok::at) ? At : None; +} + +bool SelectorParser::handleToken(const Token &RawTok) { + State = stateForToken(RawTok); + if (State != Success) + return false; + State = None; + return true; +} + +static void collectTextualMatchesInComment( + ArrayRef<IndexedSymbol> Symbols, SourceLocation CommentLoc, + StringRef Comment, llvm::SmallVectorImpl<TextualMatchOccurrence> &Result) { + for (const auto &Symbol : llvm::enumerate(Symbols)) { + size_t Offset = 0; + while (true) { + Offset = Comment.find(Symbol.value().Name[0], /*From=*/Offset); + if (Offset == StringRef::npos) + break; + Result.push_back( + {CommentLoc.getLocWithOffset(Offset), (unsigned)Symbol.index()}); + Offset += Symbol.value().Name[0].size(); + } + } +} + +/// Lex the comment to figure out if textual matches in a comment are standalone +/// tokens. +static void findTextualMatchesInComment( + const SourceManager &SM, const LangOptions &LangOpts, + ArrayRef<IndexedSymbol> Symbols, + ArrayRef<TextualMatchOccurrence> TextualMatches, SourceRange CommentRange, + llvm::function_ref<void(SymbolOccurrence::OccurrenceKind, + ArrayRef<SourceLocation> Locations, + unsigned SymbolIndex)> + MatchHandler) { + std::string Source = + Lexer::getSourceText(CharSourceRange::getCharRange(CommentRange), SM, + LangOpts) + .str(); + SymbolOccurrence::OccurrenceKind Kind = + RawComment(SM, CommentRange, /*Merged=*/false, /*ParseAllComments=*/false) + .isDocumentation() + ? SymbolOccurrence::MatchingDocComment + : SymbolOccurrence::MatchingComment; + // Replace some special characters with ' ' to avoid comments and literals. + std::replace_if( + Source.begin(), Source.end(), + [](char c) -> bool { return c == '/' || c == '"' || c == '\''; }, ' '); + Lexer RawLex(CommentRange.getBegin(), LangOpts, Source.c_str(), + Source.c_str(), Source.c_str() + Source.size()); + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + auto It = std::find_if(TextualMatches.begin(), TextualMatches.end(), + [&](const TextualMatchOccurrence &Match) { + return Match.Location == RawTok.getLocation(); + }); + if (It != TextualMatches.end()) { + StringRef TokenName = + Lexer::getSourceText(CharSourceRange::getCharRange( + RawTok.getLocation(), RawTok.getEndLoc()), + SM, LangOpts); + // Only report matches that are identical to the symbol. When dealing with + // multi-piece selectors we only look for the first selector piece as we + // assume that textual matches correspond to a match of the first selector + // piece. + if (TokenName == Symbols[It->SymbolIndex].Name[0]) + MatchHandler(Kind, It->Location, It->SymbolIndex); + } + RawLex.LexFromRawLexer(RawTok); + } +} + +static void findMatchingTextualOccurrences( + const SourceManager &SM, const LangOptions &LangOpts, + ArrayRef<IndexedSymbol> Symbols, + llvm::function_ref<void(SymbolOccurrence::OccurrenceKind, + ArrayRef<SourceLocation> Locations, + unsigned SymbolIndex)> + MatchHandler) { + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, LangOpts); + RawLex.SetCommentRetentionState(true); + + llvm::SmallVector<TextualMatchOccurrence, 4> CommentMatches; + llvm::SmallVector<SelectorParser, 2> SelectorParsers; + for (const auto &Symbol : llvm::enumerate(Symbols)) { + if (Symbol.value().IsObjCSelector) + SelectorParsers.push_back( + SelectorParser(Symbol.value().Name, Symbol.index())); + } + + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + if (RawTok.is(tok::comment)) { + SourceRange Range(RawTok.getLocation(), RawTok.getEndLoc()); + StringRef Comment = Lexer::getSourceText( + CharSourceRange::getCharRange(Range), SM, LangOpts); + collectTextualMatchesInComment(Symbols, Range.getBegin(), Comment, + CommentMatches); + if (!CommentMatches.empty()) { + findTextualMatchesInComment(SM, LangOpts, Symbols, CommentMatches, + Range, MatchHandler); + CommentMatches.clear(); + } + } else if (!SelectorParsers.empty()) { + for (auto &Parser : SelectorParsers) { + if (Parser.handleToken(RawTok)) + MatchHandler(SymbolOccurrence::MatchingSelector, + Parser.SelectorLocations, Parser.SymbolIndex); + } + } + RawLex.LexFromRawLexer(RawTok); + } +} + +static void findInclusionDirectiveOccurrence( + const IndexedOccurrence &Occurrence, const IndexedSymbol &Symbol, + unsigned SymbolIndex, SourceManager &SM, const LangOptions &LangOpts, + IndexedFileOccurrenceConsumer &Consumer) { + if (!Occurrence.Line || !Occurrence.Column) + return; // Ignore any invalid indexed locations. + + SourceLocation Loc = SM.translateLineCol(SM.getMainFileID(), Occurrence.Line, + Occurrence.Column); + if (Loc.isInvalid()) + return; + unsigned Offset = SM.getDecomposedLoc(Loc).second; + const llvm::MemoryBuffer *File = SM.getBuffer(SM.getMainFileID()); + + InclusionLexer RawLex(Loc, LangOpts, File->getBufferStart() + Offset, + File->getBufferEnd()); + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + if (RawTok.isNot(tok::hash)) + return; + // include/import + RawLex.LexFromRawLexer(RawTok); + if (RawTok.isNot(tok::raw_identifier)) + return; + // string literal/angled literal. + RawLex.setParsingPreprocessorDirective(true); + RawLex.LexIncludeFilename(RawTok); + if (RawTok.isNot(tok::string_literal) && + RawTok.isNot(tok::angle_string_literal)) + return; + StringRef Filename = llvm::sys::path::filename( + StringRef(RawTok.getLiteralData(), RawTok.getLength()) + .drop_front() + .drop_back()); + size_t NameOffset = Filename.rfind_lower(Symbol.Name[0]); + if (NameOffset == StringRef::npos) + return; + SymbolOccurrence Result( + SymbolOccurrence::MatchingFilename, + /*IsMacroExpansion=*/false, SymbolIndex, + RawTok.getLocation().getLocWithOffset( + NameOffset + (Filename.data() - RawTok.getLiteralData()))); + Consumer.handleOccurrence(Result, SM, LangOpts); +} + +void IndexedFileOccurrenceProducer::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + PP.EnterMainSourceFile(); + + SourceManager &SM = getCompilerInstance().getSourceManager(); + const LangOptions &LangOpts = getCompilerInstance().getLangOpts(); + if (IsMultiPiece) { + findObjCMultiPieceSelectorOccurrences(getCompilerInstance(), Symbols, + Consumer); + } else { + for (const auto &Symbol : llvm::enumerate(Symbols)) { + for (const IndexedOccurrence &Occurrence : + Symbol.value().IndexedOccurrences) { + if (Occurrence.Kind == IndexedOccurrence::InclusionDirective) { + findInclusionDirectiveOccurrence(Occurrence, Symbol.value(), + Symbol.index(), SM, LangOpts, + Consumer); + continue; + } + SourceLocation BeginLoc; + MatchKind Match = + checkOccurrence(Occurrence, Symbol.value(), SM, LangOpts, BeginLoc); + if (Match == MatchKind::None) + continue; + + SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/Match == + MatchKind::MacroExpansion, + Symbol.index(), BeginLoc); + Consumer.handleOccurrence(Result, SM, LangOpts); + } + } + } + + if (Options && Options->get(option::AvoidTextualMatches())) + return; + findMatchingTextualOccurrences( + SM, LangOpts, Symbols, + [&](SymbolOccurrence::OccurrenceKind Kind, + ArrayRef<SourceLocation> Locations, unsigned SymbolIndex) { + SymbolOccurrence Result(Kind, /*IsMacroExpansion=*/false, SymbolIndex, + Locations); + Consumer.handleOccurrence(Result, SM, LangOpts); + }); +} + +namespace { + +/// Maps from source locations to the indexed occurrences. +typedef llvm::DenseMap<unsigned, std::pair<IndexedOccurrence, unsigned>> + SourceLocationsToIndexedOccurrences; + +enum class ObjCSymbolSelectorKind { MessageSend, MethodDecl }; + +} // end anonymous namespace + +static bool +findObjCSymbolSelectorPieces(ArrayRef<Token> Tokens, const SymbolName &Name, + SmallVectorImpl<SourceLocation> &Pieces, + ObjCSymbolSelectorKind Kind) { + assert(!Tokens.empty() && "no tokens"); + assert(Tokens[0].getRawIdentifier() == Name[0]); + assert(Name.size() > 1); + assert(Pieces.empty()); + + Pieces.push_back(Tokens[0].getLocation()); + + // We have to track square brackets, parens and braces as we want to skip the + // tokens inside them. This ensures that we don't use identical selector + // pieces in inner message sends, blocks, lambdas and @selector expressions. + unsigned SquareCount = 0; + unsigned ParenCount = 0; + unsigned BraceCount = 0; + + // Start looking for the next selector piece. + unsigned Last = Tokens.size() - 1; + // Skip the ':' or any other token after the first selector piece token. + for (unsigned Index = 2; Index < Last; ++Index) { + const auto &Tok = Tokens[Index]; + + bool NoScoping = SquareCount == 0 && BraceCount == 0 && ParenCount == 0; + if (NoScoping && Tok.is(tok::raw_identifier) && + Tokens[Index + 1].is(tok::colon) && + Tok.getRawIdentifier() == Name[Pieces.size()]) { + Pieces.push_back(Tok.getLocation()); + // All the selector pieces have been found. + if (Pieces.size() == Name.size()) + return true; + } else if (Tok.is(tok::r_square)) { + // Stop scanning at the end of the message send. + // Also account for spurious ']' in blocks or lambdas. + if (Kind == ObjCSymbolSelectorKind::MessageSend && !SquareCount && + !BraceCount) + break; + if (SquareCount) + --SquareCount; + } else if (Tok.is(tok::l_square)) + ++SquareCount; + else if (Tok.is(tok::l_paren)) + ++ParenCount; + else if (Tok.is(tok::r_paren)) { + if (!ParenCount) + break; + --ParenCount; + } else if (Tok.is(tok::l_brace)) { + // Stop scanning at the start of the of the method's body. + // Also account for any spurious blocks inside argument parameter types + // or parameter attributes. + if (Kind == ObjCSymbolSelectorKind::MethodDecl && !BraceCount && + !ParenCount) + break; + ++BraceCount; + } else if (Tok.is(tok::r_brace)) { + if (!BraceCount) + break; + --BraceCount; + } + // Stop scanning at the end of the method's declaration. + if (Kind == ObjCSymbolSelectorKind::MethodDecl && NoScoping && + (Tok.is(tok::semi) || Tok.is(tok::minus) || Tok.is(tok::plus))) + break; + } + return false; +} + +// Scan the file and find multi-piece selector occurrences in a token stream. +static void +findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, + ArrayRef<IndexedSymbol> Symbols, + IndexedFileOccurrenceConsumer &Consumer) { + for (const auto &Symbol : Symbols) { + (void)Symbol; + assert(Symbol.Name.size() > 1 && "Not a multi-piece symbol!"); + } + + SourceManager &SM = CI.getSourceManager(); + const LangOptions &LangOpts = CI.getLangOpts(); + // Create a mapping from source locations to the indexed occurrences. + SourceLocationsToIndexedOccurrences MappedIndexedOccurrences; + for (const auto &Symbol : llvm::enumerate(Symbols)) { + for (const IndexedOccurrence &Occurrence : + Symbol.value().IndexedOccurrences) { + // Selectors and names in #includes shouldn't really mix. + if (Occurrence.Kind == IndexedOccurrence::InclusionDirective) + continue; + SourceLocation Loc; + MatchKind Match = + checkOccurrence(Occurrence, Symbol.value(), SM, LangOpts, Loc); + if (Match == MatchKind::None) + continue; + if (Match == MatchKind::MacroExpansion) { + SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/true, Symbol.index(), Loc); + Consumer.handleOccurrence(Result, SM, LangOpts); + continue; + } + MappedIndexedOccurrences.try_emplace(Loc.getRawEncoding(), Occurrence, + Symbol.index()); + } + } + + // Lex the file and look for tokens. + // Start lexing the specified input file. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, LangOpts); + + std::vector<Token> Tokens; + bool SaveTokens = false; + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + // Start saving tokens only when we've got a match + if (!SaveTokens) { + if (MappedIndexedOccurrences.find( + RawTok.getLocation().getRawEncoding()) != + MappedIndexedOccurrences.end()) + SaveTokens = true; + } + if (SaveTokens) + Tokens.push_back(RawTok); + RawLex.LexFromRawLexer(RawTok); + } + + for (const auto &I : llvm::enumerate(Tokens)) { + const auto &Tok = I.value(); + auto It = MappedIndexedOccurrences.find(Tok.getLocation().getRawEncoding()); + if (It == MappedIndexedOccurrences.end()) + continue; + if (Tok.getKind() != tok::raw_identifier) + continue; + const IndexedOccurrence &Occurrence = It->second.first; + unsigned SymbolIndex = It->second.second; + + // Scan the source for the remaining selector pieces. + SmallVector<SourceLocation, 4> SelectorPieces; + ObjCSymbolSelectorKind Kind = + Occurrence.Kind == IndexedOccurrence::IndexedObjCMessageSend + ? ObjCSymbolSelectorKind::MessageSend + : ObjCSymbolSelectorKind::MethodDecl; + if (findObjCSymbolSelectorPieces( + llvm::makeArrayRef(Tokens).drop_front(I.index()), + Symbols[SymbolIndex].Name, SelectorPieces, Kind)) { + SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/false, SymbolIndex, + std::move(SelectorPieces)); + Consumer.handleOccurrence(Result, SM, LangOpts); + } + } +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RenamedSymbol.cpp b/clang/lib/Tooling/Refactor/RenamedSymbol.cpp new file mode 100644 index 0000000000000..16ec01f7f04cc --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenamedSymbol.cpp @@ -0,0 +1,41 @@ +//===--- RenamedSymbol.cpp - ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RenamedSymbol.h" +#include "clang/AST/DeclObjC.h" +#include <algorithm> + +using namespace clang; + +namespace clang { +namespace tooling { +namespace rename { + +Symbol::Symbol(const NamedDecl *FoundDecl, unsigned SymbolIndex, + const LangOptions &LangOpts) + : Name(FoundDecl->getNameAsString(), LangOpts), SymbolIndex(SymbolIndex), + FoundDecl(FoundDecl) { + if (const auto *MD = dyn_cast<ObjCMethodDecl>(FoundDecl)) + ObjCSelector = MD->getSelector(); +} + +bool operator<(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS) { + assert(!LHS.Locations.empty() && !RHS.Locations.empty()); + return LHS.Locations[0] < RHS.Locations[0]; +} + +bool operator==(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS) { + return LHS.Kind == RHS.Kind && LHS.SymbolIndex == RHS.SymbolIndex && + std::equal(LHS.Locations.begin(), LHS.Locations.end(), + RHS.Locations.begin()); +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RenamingOperation.cpp b/clang/lib/Tooling/Refactor/RenamingOperation.cpp new file mode 100644 index 0000000000000..80aa1bc6dd2c7 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenamingOperation.cpp @@ -0,0 +1,98 @@ +//===--- RenamingOperation.cpp - ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RenamingOperation.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" + +using namespace clang; + +/// \brief Lexes the given name string. +/// +/// \return False if the name was consumed fully, true otherwise. +static bool lexNameString(StringRef Name, Token &Result, + const LangOptions &LangOpts) { + Lexer Lex(SourceLocation(), LangOpts, Name.data(), Name.data(), + Name.data() + Name.size()); + return !Lex.LexFromRawLexer(Result); +} + +namespace clang { +namespace tooling { +namespace rename { + +bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector, + IdentifierTable &IDs, const LangOptions &LangOpts) { + Token Tok; + if (IsSymbolObjCSelector) { + // Check if the name is a valid selector. + for (const auto &Name : NewName.strings()) { + // Lex the name and verify that it was fully consumed. Then make sure that + // it's a valid identifier. + if (lexNameString(Name, Tok, LangOpts) || !Tok.isAnyIdentifier()) + return false; + } + return true; + } + + for (const auto &Name : NewName.strings()) { + // Lex the name and verify that it was fully consumed. Then make sure that + // it's a valid identifier that's also not a language keyword. + if (lexNameString(Name, Tok, LangOpts) || !Tok.isAnyIdentifier() || + !tok::isAnyIdentifier(IDs.get(Name).getTokenID())) + return false; + } + return true; +} + +bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation, + IdentifierTable &IDs, const LangOptions &LangOpts) { + assert(!Operation.symbols().empty()); + return isNewNameValid(NewName, + Operation.symbols().front().ObjCSelector.hasValue(), + IDs, LangOpts); +} + +void determineNewNames(SymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl<SymbolName> &NewNames, + const LangOptions &LangOpts) { + auto Symbols = Operation.symbols(); + assert(!Symbols.empty()); + NewNames.push_back(std::move(NewName)); + if (const auto *PropertyDecl = + dyn_cast<ObjCPropertyDecl>(Symbols.front().FoundDecl)) { + assert(NewNames.front().size() == 1 && + "Property's name should have one string only"); + StringRef PropertyName = NewNames.front()[0]; + Symbols = Symbols.drop_front(); + + auto AddName = [&](const NamedDecl *D, StringRef Name) { + assert(Symbols.front().FoundDecl == D && "decl is missing"); + NewNames.push_back(SymbolName(Name, LangOpts)); + Symbols = Symbols.drop_front(); + }; + + if (!PropertyDecl->hasExplicitGetterName()) { + if (const auto *Getter = PropertyDecl->getGetterMethodDecl()) + AddName(Getter, PropertyName); + } + if (!PropertyDecl->hasExplicitSetterName()) { + if (const auto *Setter = PropertyDecl->getSetterMethodDecl()) { + auto SetterName = SelectorTable::constructSetterName(PropertyName); + AddName(Setter, SetterName); + } + } + } +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp b/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp new file mode 100644 index 0000000000000..0a0c4cdc9d3b5 --- /dev/null +++ b/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp @@ -0,0 +1,260 @@ +//===--- SourceLocationUtilities.cpp - Source location helper functions ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SourceLocationUtilities.h" +#include "clang/AST/Stmt.h" +#include "clang/Lex/Lexer.h" +#include <limits> + +namespace clang { +namespace tooling { + +SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd, + const Stmt *Body, + const SourceManager &SM) { + SourceLocation BodyStart = SM.getSpellingLoc(Body->getLocStart()); + unsigned BodyLine = SM.getSpellingLineNumber(BodyStart); + unsigned HeaderLine = SM.getSpellingLineNumber(HeaderEnd); + + if (BodyLine > HeaderLine) { + // The Last location on the previous line if the body is not on the same + // line as the last known location. + SourceLocation LineLocThatPrecedesBody = + SM.translateLineCol(SM.getFileID(BodyStart), BodyLine - 1, + std::numeric_limits<unsigned>::max()); + if (LineLocThatPrecedesBody.isValid()) + return LineLocThatPrecedesBody; + } + // We want to include the location of the '{'. + return isa<CompoundStmt>(Body) ? BodyStart : BodyStart.getLocWithOffset(-1); +} + +SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart, + const Stmt *PreviousBody, + const SourceManager &SM) { + if (!isa<CompoundStmt>(PreviousBody)) + return HeaderStart; + SourceLocation BodyEnd = SM.getSpellingLoc(PreviousBody->getLocEnd()); + unsigned BodyLine = SM.getSpellingLineNumber(BodyEnd); + unsigned HeaderLine = SM.getSpellingLineNumber(HeaderStart); + if (BodyLine >= HeaderLine) + return BodyEnd; + return HeaderStart; +} + +bool isLocationInAnyRange(SourceLocation Location, ArrayRef<SourceRange> Ranges, + const SourceManager &SM) { + for (const SourceRange &Range : Ranges) { + if (!isPointWithin(Location, Range.getBegin(), Range.getEnd(), SM)) + continue; + return true; + } + return false; +} + +SourceLocation getPreciseTokenLocEnd(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); +} + +SourceLocation findClosingParenLocEnd(SourceLocation LastKnownLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::findLocationAfterToken( + LastKnownLoc, tok::r_paren, SM, LangOpts, + /*SkipTrailingWhitespaceAndNewLine=*/false); +} + +SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation NextLoc = + Lexer::findLocationAfterToken(Loc, Kind, SM, LangOpts, + /*SkipTrailingWhitespaceAndNewLine=*/false); + if (NextLoc.isInvalid()) + return SourceRange(); + return SourceRange( + Lexer::GetBeginningOfToken(NextLoc.getLocWithOffset(-1), SM, LangOpts), + NextLoc); +} + +SourceLocation findLastNonCompoundLocation(const Stmt *S) { + const auto *CS = dyn_cast<CompoundStmt>(S); + if (!CS) + return S->getLocEnd(); + return CS->body_back() ? CS->body_back()->getLocEnd() : SourceLocation(); +} + +bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, + const SourceManager &SM) { + return !Loc1.isMacroID() && !Loc2.isMacroID() && + SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); +} + +SourceLocation +getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(!SpellingLoc.isMacroID() && "Expecting a spelling location"); + SourceLocation NextTokenLoc = + Lexer::findNextTokenLocationAfterTokenAt(SpellingLoc, SM, LangOpts); + if (NextTokenLoc.isValid()) { + bool IsSameLine = areOnSameLine(SpellingLoc, NextTokenLoc, SM); + if (IsSameLine) { + // Could be a ';' on the same line, so try looking after the ';' + if (isSemicolonAtLocation(NextTokenLoc, SM, LangOpts)) + return getLastLineLocationUnlessItHasOtherTokens(NextTokenLoc, SM, + LangOpts); + } else { + SourceLocation LastLoc = SM.translateLineCol( + SM.getFileID(SpellingLoc), SM.getSpellingLineNumber(SpellingLoc), + std::numeric_limits<unsigned>::max()); + if (LastLoc.isValid()) + return LastLoc; + } + } + return getPreciseTokenLocEnd(SpellingLoc, SM, LangOpts); +} + +bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM, + LangOpts) == ";"; +} + +SourceRange trimSelectionRange(SourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts) { + bool IsInvalid = false; + StringRef Text = Lexer::getSourceText(CharSourceRange::getCharRange(Range), + SM, LangOpts, &IsInvalid); + if (IsInvalid || Text.empty()) + return Range; + assert(Range.getBegin().isFileID() && "Not a file range!"); + + std::string Source = Text.str(); + Lexer Lex(Range.getBegin(), LangOpts, Source.c_str(), Source.c_str(), + Source.c_str() + Source.size()); + // Get comment tokens as well. + Lex.SetCommentRetentionState(true); + SourceLocation StartLoc, EndLoc; + while (true) { + Token Tok; + Lex.LexFromRawLexer(Tok); + if (Tok.getKind() == tok::eof) + break; + if (StartLoc.isInvalid()) + StartLoc = Tok.getLocation(); + if (Tok.getKind() != tok::semi) + EndLoc = Tok.getEndLoc(); + } + return StartLoc.isValid() && EndLoc.isValid() ? SourceRange(StartLoc, EndLoc) + : SourceRange(); +} + +/// Tokenize the given file and check if it contains a comment that ends at the +/// given location. +static SourceLocation findCommentThatEndsAt(FileID FID, + SourceLocation StartOfFile, + const SourceManager &SM, + const LangOptions &LangOpts, + SourceLocation ExpectedEndLoc) { + // Try to load the file buffer. + bool InvalidTemp = false; + StringRef File = SM.getBufferData(FID, &InvalidTemp); + if (InvalidTemp) + return SourceLocation(); + + // Search for the comment that ends at the given location. + Lexer Lex(StartOfFile, LangOpts, File.begin(), File.begin(), File.end()); + Lex.SetCommentRetentionState(true); + Token Tok; + while (!Lex.LexFromRawLexer(Tok)) { + if (Tok.is(tok::comment) && Tok.getEndLoc() == ExpectedEndLoc) + return Tok.getLocation(); + } + // Find the token. + return SourceLocation(); +} + +SourceLocation getLocationOfPrecedingComment(SourceLocation Location, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation PrevResult = Location; + SourceLocation Result = Location; + if (Result.isMacroID()) + Result = SM.getExpansionLoc(Result); + FileID FID = SM.getFileID(Result); + SourceLocation StartOfFile = SM.getLocForStartOfFile(FID); + Token Tok; + Tok.setKind(tok::unknown); + SourceLocation TokenLoc = Result; + auto GetPreviousToken = [&]() -> bool { + TokenLoc = + Lexer::GetBeginningOfToken(TokenLoc.getLocWithOffset(-1), SM, LangOpts); + return !Lexer::getRawToken(TokenLoc, Tok, SM, LangOpts); + }; + // Look for a comment token. + while (TokenLoc != StartOfFile) { + bool LocHasToken = GetPreviousToken(); + if (LocHasToken && Tok.is(tok::slash)) { + // Check if this the end of a multiline '/*' comment before returning. + SourceLocation CommentLoc = findCommentThatEndsAt( + FID, StartOfFile, SM, LangOpts, Tok.getEndLoc()); + return CommentLoc.isInvalid() ? Result : CommentLoc; + } + if (LocHasToken && Tok.isNot(tok::comment)) + break; + if (!LocHasToken) + continue; + // We found a preceding comment. Check if there are other preceding + // comments. + PrevResult = Result; + Result = Tok.getLocation(); + while (TokenLoc != StartOfFile) { + bool LocHasToken = GetPreviousToken(); + if (LocHasToken && Tok.isNot(tok::comment)) { + // Reset the result to the previous location if this comment trails + // another token on the same line. + if (SM.getSpellingLineNumber(Tok.getEndLoc()) == + SM.getSpellingLineNumber(Result)) + Result = PrevResult; + break; + } + if (!LocHasToken) + continue; + // The location of this comment is accepted only when the next comment + // is located immediately after this comment. + if (SM.getSpellingLineNumber(Tok.getEndLoc()) != + SM.getSpellingLineNumber(Result) - 1) + break; + PrevResult = Result; + Result = Tok.getLocation(); + } + break; + } + return Result; +} + +SourceLocation getLocationOfPrecedingToken(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation Result = Loc; + if (Result.isMacroID()) + Result = SM.getExpansionLoc(Result); + FileID FID = SM.getFileID(Result); + SourceLocation StartOfFile = SM.getLocForStartOfFile(FID); + if (Loc == StartOfFile) + return SourceLocation(); + return Lexer::GetBeginningOfToken(Result.getLocWithOffset(-1), SM, LangOpts); +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SourceLocationUtilities.h b/clang/lib/Tooling/Refactor/SourceLocationUtilities.h new file mode 100644 index 0000000000000..ba7425bf88c65 --- /dev/null +++ b/clang/lib/Tooling/Refactor/SourceLocationUtilities.h @@ -0,0 +1,174 @@ +//===--- SourceLocationUtilities.h - Source location helper functions -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" + +namespace clang { + +class Stmt; +class LangOptions; + +namespace tooling { + +inline bool isPairOfFileLocations(SourceLocation Start, SourceLocation End) { + return Start.isValid() && Start.isFileID() && End.isValid() && End.isFileID(); +} + +/// Return true if the Point is within Start and End. +inline bool isPointWithin(SourceLocation Location, SourceLocation Start, + SourceLocation End, const SourceManager &SM) { + return Location == Start || Location == End || + (SM.isBeforeInTranslationUnit(Start, Location) && + SM.isBeforeInTranslationUnit(Location, End)); +} + +/// Return true if the two given ranges overlap with each other. +inline bool areRangesOverlapping(SourceRange R1, SourceRange R2, + const SourceManager &SM) { + return isPointWithin(R1.getBegin(), R2.getBegin(), R2.getEnd(), SM) || + isPointWithin(R2.getBegin(), R1.getBegin(), R1.getEnd(), SM); +} + +/// \brief Return the source location that can be considered the last location +/// of the source construct before its body. +/// +/// The returned location is determined using the following rules: +/// +/// 1) If the source construct has a compound body that starts on the same line, +/// then this function will return the location of the opening '{'. +/// +/// if (condition) { +/// ^ +/// +/// 2) If the source construct's body is not a compound statement that starts +/// on the same line, then this function will return the location just before +/// the starting location of the body. +/// +/// if (condition) foo() +/// ^ +/// +/// 3) Otherwise, this function will return the last location on the line prior +/// to the the line on which the body starts. +/// +/// if (condition) +/// ^ +/// foo() +/// +/// \param HeaderEnd The last known location of the pre-body portion of the +/// source construct. For example, for an if statement, HeaderEnd should +/// be the ending location of its conditional expression. +SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd, + const Stmt *Body, + const SourceManager &SM); + +/// \brief Return the source location that can be considered the first location +/// of the source construct prior to the previous portion of its body. +/// +/// The returned location is determined using the following rules: +/// +/// 1) If the source construct's body is a compound statement that ends +/// on the same line, then this function will return the location of the +/// closing '}'. +/// +/// } else if (condition) +/// ^ +/// +/// 2) Otherwise, this function will return the starting location of the source +/// construct. +/// +/// foo(); +/// else if (condition) +/// ^ +/// +/// } +/// else if (condition) +/// ^ +/// +/// \param HeaderStart The first known location of the post-body portion of the +/// source construct. For example, for an if statement, HeaderStart should +/// be the starting location of the if keyword. +SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart, + const Stmt *PreviousBody, + const SourceManager &SM); + +/// Return true if the given \p Location is within any range. +bool isLocationInAnyRange(SourceLocation Location, ArrayRef<SourceRange> Ranges, + const SourceManager &SM); + +/// Return the precise end location for the given token. +SourceLocation getPreciseTokenLocEnd(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// \brief Find the source location right after the location of the next ')'. +/// +/// If the token that's located after \p LastKnownLoc isn't ')', then this +/// function returns an invalid source location. +SourceLocation findClosingParenLocEnd(SourceLocation LastKnownLoc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the range of the next token if it has the given kind. +SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the end location of the body when \p S is a compound statement or an +/// invalid location when \p S is an empty compound statement. Otherwise, +/// return the end location of the given statement \p S. +SourceLocation findLastNonCompoundLocation(const Stmt *S); + +/// Return true if the two locations are on the same line and aren't +/// macro locations. +bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, + const SourceManager &SM); + +/// Return the last location of the line which contains the given spellling +/// location \p SpellingLoc unless that line has other tokens after the given +/// location. +SourceLocation +getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return true if the token at the given location is a semicolon. +bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM, + const LangOptions &LangOpts); + +/// Shrink the given range by ignoring leading whitespace and trailing +/// whitespace and semicolons. +/// +/// Returns an invalid source range if the source range consists of whitespace +/// or semicolons only. +SourceRange trimSelectionRange(SourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the source location of the conjoined comment(s) that precede the +/// given location \p Loc, or the same location if there's no comment before +/// \p Loc. +SourceLocation getLocationOfPrecedingComment(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the source location of the token that comes before the token at the +/// given location. +SourceLocation getLocationOfPrecedingToken(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H diff --git a/clang/lib/Tooling/Refactor/StmtUtils.cpp b/clang/lib/Tooling/Refactor/StmtUtils.cpp new file mode 100644 index 0000000000000..53d426b8b39c0 --- /dev/null +++ b/clang/lib/Tooling/Refactor/StmtUtils.cpp @@ -0,0 +1,54 @@ +//===--- StmtUtils.cpp - Statement helper functions -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StmtUtils.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Lex/Lexer.h" + +using namespace clang; + +SourceLocation +clang::tooling::getLexicalEndLocForDecl(const Decl *D, const SourceManager &SM, + const LangOptions &LangOpts) { + if (!isa<ObjCImplDecl>(D)) + return D->getSourceRange().getEnd(); + auto AtEnd = D->getSourceRange().getEnd(); + auto AdjustedEnd = + Lexer::findNextTokenLocationAfterTokenAt(AtEnd, SM, LangOpts); + return AdjustedEnd.isValid() ? AdjustedEnd : AtEnd; +} + +bool clang::tooling::isSemicolonRequiredAfter(const Stmt *S) { + if (isa<CompoundStmt>(S)) + return false; + if (const auto *If = dyn_cast<IfStmt>(S)) + return isSemicolonRequiredAfter(If->getElse() ? If->getElse() + : If->getThen()); + if (const auto *While = dyn_cast<WhileStmt>(S)) + return isSemicolonRequiredAfter(While->getBody()); + if (const auto *For = dyn_cast<ForStmt>(S)) + return isSemicolonRequiredAfter(For->getBody()); + if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S)) + return isSemicolonRequiredAfter(CXXFor->getBody()); + if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S)) + return isSemicolonRequiredAfter(ObjCFor->getBody()); + switch (S->getStmtClass()) { + case Stmt::SwitchStmtClass: + case Stmt::CXXTryStmtClass: + case Stmt::ObjCAtSynchronizedStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: + case Stmt::ObjCAtTryStmtClass: + return false; + default: + return true; + } +} diff --git a/clang/lib/Tooling/Refactor/StmtUtils.h b/clang/lib/Tooling/Refactor/StmtUtils.h new file mode 100644 index 0000000000000..8f458770ae23b --- /dev/null +++ b/clang/lib/Tooling/Refactor/StmtUtils.h @@ -0,0 +1,33 @@ +//===--- StmtUtils.h - Statement helper functions -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H + +#include "clang/Basic/SourceLocation.h" + +namespace clang { + +class Decl; +class LangOptions; +class Stmt; + +namespace tooling { + +SourceLocation getLexicalEndLocForDecl(const Decl *D, const SourceManager &SM, + const LangOptions &LangOpts); + +/// \brief Returns true if there should be a semicolon after the given +/// statement. +bool isSemicolonRequiredAfter(const Stmt *S); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H diff --git a/clang/lib/Tooling/Refactor/SymbolName.cpp b/clang/lib/Tooling/Refactor/SymbolName.cpp new file mode 100644 index 0000000000000..2d30d1834e479 --- /dev/null +++ b/clang/lib/Tooling/Refactor/SymbolName.cpp @@ -0,0 +1,58 @@ +//===--- SymbolName.cpp - Clang refactoring library -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/SymbolName.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace tooling { + +static void initNames(std::vector<std::string> &Strings, StringRef Name, + bool IsObjectiveCSelector) { + if (!IsObjectiveCSelector) { + Strings.push_back(Name.str()); + return; + } + // Decompose an Objective-C selector name into multiple strings. + do { + auto StringAndName = Name.split(':'); + Strings.push_back(StringAndName.first.str()); + Name = StringAndName.second; + } while (!Name.empty()); +} + +SymbolName::SymbolName(StringRef Name, const LangOptions &LangOpts) { + initNames(Strings, Name, LangOpts.ObjC1); +} + +SymbolName::SymbolName(StringRef Name, bool IsObjectiveCSelector) { + initNames(Strings, Name, IsObjectiveCSelector); +} + +SymbolName::SymbolName(ArrayRef<StringRef> Name) { + for (const auto &Piece : Name) + Strings.push_back(Piece.str()); +} + +void SymbolName::print(raw_ostream &OS) const { + for (size_t I = 0, E = Strings.size(); I != E; ++I) { + if (I != 0) + OS << ':'; + OS << Strings[I]; + } +} + +raw_ostream &operator<<(raw_ostream &OS, const SymbolName &N) { + N.print(OS); + return OS; +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp b/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp new file mode 100644 index 0000000000000..38f4cc8d401fb --- /dev/null +++ b/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp @@ -0,0 +1,412 @@ +//===--- SymbolOccurrenceFinder.cpp - Clang refactoring library -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Methods for finding all instances of a USR. Our strategy is very +/// simple; we just compare the USR at every relevant AST node with the one +/// provided. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/SymbolOccurrenceFinder.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DependentASTVisitor.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" + +using namespace llvm; + +namespace clang { +namespace tooling { +namespace rename { + +namespace { +// \brief This visitor recursively searches for all instances of a USR in a +// translation unit and stores them for later usage. +class SymbolOccurrenceFinderASTVisitor + : public DependentASTVisitor<SymbolOccurrenceFinderASTVisitor> { +public: + explicit SymbolOccurrenceFinderASTVisitor( + const SymbolOperation &Operation, const ASTContext &Context, + std::vector<SymbolOccurrence> &Occurrences) + : Operation(Operation), Context(Context), Occurrences(Occurrences) {} + + /// Returns a \c Symbol if the given declaration corresponds to the symbol + /// that we're looking for. + const Symbol *symbolForDecl(const Decl *D) const { + if (!D) + return nullptr; + std::string USR = getUSRForDecl(D); + return Operation.getSymbolForUSR(USR); + } + + void checkDecl(const Decl *D, SourceLocation Loc, + SymbolOccurrence::OccurrenceKind Kind = + SymbolOccurrence::MatchingSymbol) { + if (!D) + return; + std::string USR = getUSRForDecl(D); + if (const Symbol *S = Operation.getSymbolForUSR(USR)) + checkAndAddLocations(S->SymbolIndex, Loc, Kind); + } + + // Declaration visitors: + + bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { + for (const auto *Initializer : ConstructorDecl->inits()) { + // Ignore implicit initializers. + if (!Initializer->isWritten()) + continue; + if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) + checkDecl(FieldDecl, Initializer->getSourceLocation()); + } + return true; + } + + bool VisitNamedDecl(const NamedDecl *Decl) { + checkDecl(Decl, Decl->getLocation()); + return true; + } + + bool WalkUpFromTypedefNameDecl(const TypedefNameDecl *D) { + // Don't visit the NamedDecl for TypedefNameDecl. + return VisitTypedefNamedDecl(D); + } + + bool VisitTypedefNamedDecl(const TypedefNameDecl *D) { + if (D->isTransparentTag()) { + if (const auto *Underlying = D->getUnderlyingType()->getAsTagDecl()) { + checkDecl(Underlying, D->getLocation()); + return true; + } + } + return VisitNamedDecl(D); + } + + bool WalkUpFromUsingDecl(const UsingDecl *D) { + // Don't visit the NamedDecl for UsingDecl. + return VisitUsingDecl(D); + } + + bool VisitUsingDecl(const UsingDecl *D) { + for (const auto *Shadow : D->shadows()) { + const NamedDecl *UD = Shadow->getUnderlyingDecl(); + if (UD->isImplicit() || UD == D) + continue; + if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(UD)) { + UD = FTD->getTemplatedDecl(); + if (!UD) + continue; + } + checkDecl(UD, D->getLocation()); + } + return true; + } + + bool WalkUpFromUsingDirectiveDecl(const UsingDirectiveDecl *D) { + // Don't visit the NamedDecl for UsingDirectiveDecl. + return VisitUsingDirectiveDecl(D); + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + checkDecl(D->getNominatedNamespaceAsWritten(), D->getLocation()); + return true; + } + + bool WalkUpFromUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingValueDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool + WalkUpFromUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingTypenameDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool WalkUpFromObjCMethodDecl(const ObjCMethodDecl *Decl) { + // Don't visit the NamedDecl for Objective-C methods. + return VisitObjCMethodDecl(Decl); + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *Decl) { + const Symbol *S = symbolForDecl(Decl); + if (!S) + return true; + SmallVector<SourceLocation, 8> SelectorLocs; + Decl->getSelectorLocs(SelectorLocs); + checkAndAddLocations(S->SymbolIndex, SelectorLocs); + return true; + } + + bool handleObjCProtocolList(const ObjCProtocolList &Protocols) { + for (auto It : enumerate(Protocols)) + checkDecl(It.value(), Protocols.loc_begin()[It.index()]); + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return handleObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return handleObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { + checkDecl(Decl, Decl->getCategoryNameLoc()); + // The location of the class name is the location of the declaration. + checkDecl(Decl->getClassInterface(), Decl->getLocation()); + return handleObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) { + checkDecl(Decl, Decl->getCategoryNameLoc()); + // The location of the class name is the location of the declaration. + checkDecl(Decl->getClassInterface(), Decl->getLocation()); + return true; + } + + bool VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *Decl) { + checkDecl(Decl->getClassInterface(), Decl->getClassInterfaceLoc()); + return true; + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *Decl) { + if (Decl->hasExplicitGetterName()) + checkDecl(Decl->getGetterMethodDecl(), Decl->getGetterNameLoc()); + if (Decl->hasExplicitSetterName()) + checkDecl(Decl->getSetterMethodDecl(), Decl->getSetterNameLoc()); + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *Decl) { + checkDecl(Decl->getPropertyDecl(), Decl->getLocation()); + if (Decl->isIvarNameSpecified()) + checkDecl(Decl->getPropertyIvarDecl(), Decl->getPropertyIvarDeclLoc()); + return true; + } + + // Expression visitors: + + bool VisitDeclRefExpr(const DeclRefExpr *Expr) { + checkDecl(Expr->getFoundDecl(), Expr->getLocation()); + return true; + } + + bool VisitMemberExpr(const MemberExpr *Expr) { + checkDecl(Expr->getFoundDecl().getDecl(), Expr->getMemberLoc()); + return true; + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *Expr) { + const Symbol *S = symbolForDecl(Expr->getMethodDecl()); + if (!S) + return true; + SmallVector<SourceLocation, 8> SelectorLocs; + Expr->getSelectorLocs(SelectorLocs); + checkAndAddLocations(S->SymbolIndex, SelectorLocs); + return true; + } + + bool VisitObjCProtocolExpr(const ObjCProtocolExpr *Expr) { + checkDecl(Expr->getProtocol(), Expr->getProtocolIdLoc()); + return true; + } + + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Expr) { + checkDecl(Expr->getDecl(), Expr->getLocation()); + return true; + } + + bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Expr) { + if (Expr->isClassReceiver()) + checkDecl(Expr->getClassReceiver(), Expr->getReceiverLocation()); + if (Expr->isImplicitProperty()) { + // Class properties that are explicitly defined using @property + // declarations are represented implicitly as there is no ivar for class + // properties. + if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) { + if (Getter->isClassMethod()) + if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl()) { + checkDecl(PD, Expr->getLocation()); + return true; + } + } + + checkDecl(Expr->getImplicitPropertyGetter(), Expr->getLocation(), + SymbolOccurrence::MatchingImplicitProperty); + // Add a manual location for a setter since a token like 'property' won't + // match the the name of the renamed symbol like 'setProperty'. + if (const auto *S = symbolForDecl(Expr->getImplicitPropertySetter())) + addLocation(S->SymbolIndex, Expr->getLocation(), + SymbolOccurrence::MatchingImplicitProperty); + return true; + } + checkDecl(Expr->getExplicitProperty(), Expr->getLocation()); + return true; + } + + // Other visitors: + + bool VisitTypeLoc(const TypeLoc Loc) { + TypedefTypeLoc TTL = Loc.getAs<TypedefTypeLoc>(); + if (TTL) { + const auto *TND = TTL.getTypedefNameDecl(); + if (TND->isTransparentTag()) { + if (const auto *Underlying = TND->getUnderlyingType()->getAsTagDecl()) { + checkDecl(Underlying, TTL.getNameLoc()); + return true; + } + } + checkDecl(TND, TTL.getNameLoc()); + return true; + } + TypeSpecTypeLoc TSTL = Loc.getAs<TypeSpecTypeLoc>(); + if (TSTL) { + checkDecl(Loc.getType()->getAsTagDecl(), TSTL.getNameLoc()); + } + if (const auto *TemplateTypeParm = + dyn_cast<TemplateTypeParmType>(Loc.getType())) { + checkDecl(TemplateTypeParm->getDecl(), Loc.getBeginLoc()); + } + if (const auto *TemplateSpecType = + dyn_cast<TemplateSpecializationType>(Loc.getType())) { + checkDecl(TemplateSpecType->getTemplateName().getAsTemplateDecl(), + Loc.getBeginLoc()); + } + return true; + } + + bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc Loc) { + checkDecl(Loc.getIFaceDecl(), Loc.getNameLoc()); + return true; + } + + bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc Loc) { + for (unsigned I = 0, E = Loc.getNumProtocols(); I < E; ++I) + checkDecl(Loc.getProtocol(I), Loc.getProtocolLoc(I)); + return true; + } + + bool VisitDependentSymbolReference(const NamedDecl *Symbol, + SourceLocation SymbolNameLoc) { + checkDecl(Symbol, SymbolNameLoc); + return true; + } + + // Non-visitors: + + // Namespace traversal: + void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { + while (NameLoc) { + checkDecl(NameLoc.getNestedNameSpecifier()->getAsNamespace(), + NameLoc.getLocalBeginLoc()); + NameLoc = NameLoc.getPrefix(); + } + } + +private: + size_t getOffsetForString(SourceLocation Loc, StringRef PrevNameString) { + const SourceLocation BeginLoc = Loc; + const SourceLocation EndLoc = Lexer::getLocForEndOfToken( + BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); + StringRef TokenName = + Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), + Context.getSourceManager(), Context.getLangOpts()); + return TokenName.find(PrevNameString); + } + + void checkAndAddLocations(unsigned SymbolIndex, + ArrayRef<SourceLocation> Locations, + SymbolOccurrence::OccurrenceKind Kind = + SymbolOccurrence::MatchingSymbol) { + if (Locations.size() != Operation.symbols()[SymbolIndex].Name.size()) + return; + + SmallVector<SourceLocation, 4> StringLocations; + for (size_t I = 0, E = Locations.size(); I != E; ++I) { + SourceLocation Loc = Locations[I]; + bool IsMacroExpansion = Loc.isMacroID(); + if (IsMacroExpansion) { + const SourceManager &SM = Context.getSourceManager(); + if (SM.isMacroArgExpansion(Loc)) { + Loc = SM.getSpellingLoc(Loc); + IsMacroExpansion = false; + } else + Loc = SM.getExpansionLoc(Loc); + } + if (IsMacroExpansion) { + Occurrences.push_back(SymbolOccurrence(Kind, /*IsMacroExpansion=*/true, + SymbolIndex, Loc)); + return; + } + size_t Offset = + getOffsetForString(Loc, Operation.symbols()[SymbolIndex].Name[I]); + if (Offset == StringRef::npos) + return; + StringLocations.push_back(Loc.getLocWithOffset(Offset)); + } + + Occurrences.push_back(SymbolOccurrence(Kind, /*IsMacroExpansion=*/false, + SymbolIndex, StringLocations)); + } + + /// Adds a location without checking if the name is actually there. + void addLocation(unsigned SymbolIndex, SourceLocation Location, + SymbolOccurrence::OccurrenceKind Kind) { + if (1 != Operation.symbols()[SymbolIndex].Name.size()) + return; + bool IsMacroExpansion = Location.isMacroID(); + if (IsMacroExpansion) { + const SourceManager &SM = Context.getSourceManager(); + if (SM.isMacroArgExpansion(Location)) { + Location = SM.getSpellingLoc(Location); + IsMacroExpansion = false; + } else + Location = SM.getExpansionLoc(Location); + } + Occurrences.push_back( + SymbolOccurrence(Kind, IsMacroExpansion, SymbolIndex, Location)); + } + + const SymbolOperation &Operation; + const ASTContext &Context; + std::vector<SymbolOccurrence> &Occurrences; +}; +} // namespace + +std::vector<SymbolOccurrence> +findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl) { + std::vector<SymbolOccurrence> Occurrences; + SymbolOccurrenceFinderASTVisitor Visitor(Operation, Decl->getASTContext(), + Occurrences); + Visitor.TraverseDecl(Decl); + NestedNameSpecifierLocFinder Finder(Decl->getASTContext()); + + for (const auto &Location : Finder.getNestedNameSpecifierLocations()) + Visitor.handleNestedNameSpecifierLoc(Location); + + return Occurrences; +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SymbolOperation.cpp b/clang/lib/Tooling/Refactor/SymbolOperation.cpp new file mode 100644 index 0000000000000..111b6c0762ba0 --- /dev/null +++ b/clang/lib/Tooling/Refactor/SymbolOperation.cpp @@ -0,0 +1,210 @@ +//===--- SymbolOperation.cpp - --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Tooling/Refactor/RefactoringActionFinder.h" + +using namespace clang; + +/// Return true if the given local record decl escapes the given enclosing +/// function or block \p Ctx. +static bool escapesEnclosingDecl(const DeclContext *Ctx, const RecordDecl *RD) { + QualType ReturnType; + bool DependentBlock = false; + if (const auto *FD = dyn_cast<FunctionDecl>(Ctx)) + ReturnType = FD->getReturnType(); + else if (const auto *BD = dyn_cast<BlockDecl>(Ctx)) { + ReturnType = BD->getSignatureAsWritten()->getType(); + // Blocks that don't have an explicitly specified type (represented with a + // dependent type) could potentially return the record, e.g. + // auto block = ^ { + // struct Foo { }; + // return Foo(); + // }; + if (const auto *FT = ReturnType->getAs<FunctionType>()) + ReturnType = FT->getReturnType(); + if (ReturnType->isDependentType()) + DependentBlock = true; + } else + return false; + + // The record can be returned from its enclosing function when the function's + // return type is auto. + // + // FIXME: Use a smarter heuristic that detects if the record type is + // actually returned from the function. Have to account for inner records, + // like in the example below: + // + // auto foo() { + // struct Foo { struct Bar { }; }; + // return Foo::Bar(); + // }; + // + // for types that depend on the record, like in the example below: + // + // auto foo() { + // template<typename T> struct C<T> { T x; }; + // struct Foo { struct Bar { }; }; + // return C<Bar>(); + // } + // + // and for things like typedefs and function types as well. + if (!DependentBlock && !ReturnType->getContainedAutoType()) + return false; + + // Even if the enclosing function returns the local record, this record is + // still local if the enclosing function is inside a function/method that + // doesn't return this record. + const auto *D = cast<Decl>(Ctx); + if (D->isLexicallyWithinFunctionOrMethod()) + return escapesEnclosingDecl(D->getParentFunctionOrMethod(), RD); + + return true; +} + +static bool escapesEnclosingDecl(const RecordDecl *RD, + const LangOptions &LangOpts) { + // We only care about things that escape in header files since things that + // escape in source files will be used only in the initial TU. + return LangOpts.IsHeaderFile && + escapesEnclosingDecl(RD->getParentFunctionOrMethod(), RD); +} + +/// Return true if the given declaration corresponds to a local symbol. +bool clang::tooling::isLocalSymbol(const NamedDecl *FoundDecl, + const LangOptions &LangOpts) { + // Template parameters aren't indexed, so use local rename. + if (isa<TemplateTypeParmDecl>(FoundDecl) || + isa<NonTypeTemplateParmDecl>(FoundDecl) || + isa<TemplateTemplateParmDecl>(FoundDecl)) + return true; + + if (const auto *VD = dyn_cast<VarDecl>(FoundDecl)) + return VD->isLocalVarDeclOrParm(); + + // Objective-C selector renames must be global. + if (isa<ObjCMethodDecl>(FoundDecl)) + return false; + + // Local declarations are defined in a function or a method, or are anonymous. + if (!FoundDecl->isLexicallyWithinFunctionOrMethod()) + return false; + + // A locally defined record is global when it is returned from the enclosing + // function because we can refer to its destructor externally. + if (const auto *RD = dyn_cast<CXXRecordDecl>(FoundDecl)) + return !escapesEnclosingDecl(RD, LangOpts); + + // A locally defined field is global when its record is returned from the + // enclosing function. + if (const auto *FD = dyn_cast<FieldDecl>(FoundDecl)) + return !escapesEnclosingDecl(FD->getParent(), LangOpts); + + if (const auto *MD = dyn_cast<CXXMethodDecl>(FoundDecl)) { + // A locally defined method is global when its record is returned from the + // enclosing function. + if (escapesEnclosingDecl(MD->getParent(), LangOpts)) + return false; + + // Method renames can be local only iff this method doesn't override + // a global method, for example: + // + // void func() { + // struct Foo: GlobalSuper { + // // When renaming foo we should also rename GlobalSuper's foo + // void foo() override; + // } + // } + // + // FIXME: We can try to be smarter about it and check if we override + // a local method, which would make this method local as well. + return !MD->isVirtual(); + } + + return true; +} + +static const NamedDecl * +findDeclThatRequiresImplementationTU(const NamedDecl *FoundDecl) { + // TODO: implement the rest. + if (const ObjCIvarDecl *IVarDecl = dyn_cast<ObjCIvarDecl>(FoundDecl)) { + // We need the implementation TU when the IVAR is declared in an @interface + // without an @implementation. + if (const auto *ID = + dyn_cast<ObjCInterfaceDecl>(IVarDecl->getDeclContext())) { + if (!ID->getImplementation()) + return IVarDecl; + } + } + return nullptr; +} + +namespace clang { +namespace tooling { + +SymbolOperation::SymbolOperation(const NamedDecl *FoundDecl, + ASTContext &Context) + : IsLocal(isLocalSymbol(FoundDecl, Context.getLangOpts())) { + // Take the category declaration if this is a category implementation. + if (const auto *CategoryImplDecl = + dyn_cast<ObjCCategoryImplDecl>(FoundDecl)) { + if (const auto *CategoryDecl = CategoryImplDecl->getCategoryDecl()) + FoundDecl = CategoryDecl; + } + // Use the property if this method is a getter/setter. + else if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(FoundDecl)) { + if (const auto *PropertyDecl = + MethodDecl->getCanonicalDecl()->findPropertyDecl()) { + // Don't use the property if the getter/setter method has an explicitly + // specified name. + if (MethodDecl->param_size() == 0 + ? !PropertyDecl->hasExplicitGetterName() + : !PropertyDecl->hasExplicitSetterName()) + FoundDecl = PropertyDecl; + } + } + + DeclThatRequiresImplementationTU = + findDeclThatRequiresImplementationTU(FoundDecl); + + // TODO: Split into initiation that works after implementation TU is loaded. + + // Find the set of symbols that this operation has to work on. + auto AddSymbol = [this, &Context](const NamedDecl *FoundDecl) { + unsigned Index = Symbols.size(); + Symbols.push_back(rename::Symbol(FoundDecl, Index, Context.getLangOpts())); + for (const auto &USR : findSymbolsUSRSet(FoundDecl, Context)) + USRToSymbol.insert(std::make_pair(USR.getKey(), Index)); + }; + AddSymbol(FoundDecl); + // Take getters, setters and ivars into account when dealing with + // Objective-C @property declarations. + if (const auto *PropertyDecl = dyn_cast<ObjCPropertyDecl>(FoundDecl)) { + // FIXME: findSymbolsUSRSet is called for every symbol we add, which is + // inefficient since we currently have to traverse the AST every time it is + // called. Fix this so that the AST isn't traversed more than once. + if (!PropertyDecl->hasExplicitGetterName()) { + if (const auto *Getter = PropertyDecl->getGetterMethodDecl()) + AddSymbol(Getter); + } + if (!PropertyDecl->hasExplicitSetterName()) { + if (const auto *Setter = PropertyDecl->getSetterMethodDecl()) + AddSymbol(Setter); + } + } +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SymbolUSRFinder.cpp b/clang/lib/Tooling/Refactor/SymbolUSRFinder.cpp new file mode 100644 index 0000000000000..267f90c7ed98a --- /dev/null +++ b/clang/lib/Tooling/Refactor/SymbolUSRFinder.cpp @@ -0,0 +1,206 @@ +//===--- SymbolUSRFinder.cpp - Clang refactoring library ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implements methods that find the set of USRs that correspond to +/// a symbol that's required for a refactoring operation. +/// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Tooling/Refactor/RefactoringActionFinder.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include "llvm/ADT/StringRef.h" + +#include <vector> + +using namespace clang; +using namespace clang::tooling::rename; + +namespace { + +/// \brief NamedDeclFindingConsumer delegates finding USRs of a found Decl to +/// \c AdditionalUSRFinder. \c AdditionalUSRFinder adds USRs of ctors and dtor +/// if the found declaration refers to a class and adds USRs of all overridden +/// methods if the declaration refers to a virtual C++ method or an ObjC method. +class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> { +public: + AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context) + : FoundDecl(FoundDecl), Context(Context) {} + + llvm::StringSet<> Find() { + llvm::StringSet<> USRSet; + + // Fill OverriddenMethods and PartialSpecs storages. + TraverseDecl(Context.getTranslationUnitDecl()); + if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) { + addUSRsOfOverridenFunctions(MethodDecl, USRSet); + // FIXME: Use a more efficient/optimal algorithm to find the related + // methods. + for (const auto &OverriddenMethod : OverriddenMethods) { + if (checkIfOverriddenFunctionAscends(OverriddenMethod, USRSet)) + USRSet.insert(getUSRForDecl(OverriddenMethod)); + } + } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) { + handleCXXRecordDecl(RecordDecl, USRSet); + } else if (const auto *TemplateDecl = + dyn_cast<ClassTemplateDecl>(FoundDecl)) { + handleClassTemplateDecl(TemplateDecl, USRSet); + } else if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(FoundDecl)) { + addUSRsOfOverriddenObjCMethods(MethodDecl, USRSet); + for (const auto &PotentialOverrider : PotentialObjCMethodOverridders) + if (checkIfPotentialObjCMethodOverriddes(PotentialOverrider, USRSet)) + USRSet.insert(getUSRForDecl(PotentialOverrider)); + } else { + USRSet.insert(getUSRForDecl(FoundDecl)); + } + return USRSet; + } + + bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) { + if (MethodDecl->isVirtual()) + OverriddenMethods.push_back(MethodDecl); + return true; + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *MethodDecl) { + if (const auto *FoundMethodDecl = dyn_cast<ObjCMethodDecl>(FoundDecl)) + if (DeclarationName::compare(MethodDecl->getDeclName(), + FoundMethodDecl->getDeclName()) == 0 && + MethodDecl->isOverriding()) + PotentialObjCMethodOverridders.push_back(MethodDecl); + return true; + } + + bool VisitClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *PartialSpec) { + if (!isa<ClassTemplateDecl>(FoundDecl) && !isa<CXXRecordDecl>(FoundDecl)) + return true; + PartialSpecs.push_back(PartialSpec); + return true; + } + +private: + void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl, + llvm::StringSet<> &USRSet) { + const auto *RD = RecordDecl->getDefinition(); + if (!RD) { + USRSet.insert(getUSRForDecl(RecordDecl)); + return; + } + if (const auto *ClassTemplateSpecDecl = + dyn_cast<ClassTemplateSpecializationDecl>(RD)) + handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate(), + USRSet); + addUSRsOfCtorDtors(RD, USRSet); + } + + void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl, + llvm::StringSet<> &USRSet) { + for (const auto *Specialization : TemplateDecl->specializations()) + addUSRsOfCtorDtors(Specialization, USRSet); + + for (const auto *PartialSpec : PartialSpecs) { + if (PartialSpec->getSpecializedTemplate() == TemplateDecl) + addUSRsOfCtorDtors(PartialSpec, USRSet); + } + addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl(), USRSet); + } + + void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl, + llvm::StringSet<> &USRSet) { + const CXXRecordDecl *RD = RecordDecl; + RecordDecl = RD->getDefinition(); + if (!RecordDecl) { + USRSet.insert(getUSRForDecl(RD)); + return; + } + + for (const auto *CtorDecl : RecordDecl->ctors()) { + auto USR = getUSRForDecl(CtorDecl); + if (!USR.empty()) + USRSet.insert(USR); + } + + auto USR = getUSRForDecl(RecordDecl->getDestructor()); + if (!USR.empty()) + USRSet.insert(USR); + USRSet.insert(getUSRForDecl(RecordDecl)); + } + + void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl, + llvm::StringSet<> &USRSet) { + USRSet.insert(getUSRForDecl(MethodDecl)); + // Recursively visit each OverridenMethod. + for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) + addUSRsOfOverridenFunctions(OverriddenMethod, USRSet); + } + + bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl, + const llvm::StringSet<> &USRSet) { + for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) { + if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) + return true; + return checkIfOverriddenFunctionAscends(OverriddenMethod, USRSet); + } + return false; + } + + /// \brief Recursively visit all the methods which the given method + /// declaration overrides and adds them to the USR set. + void addUSRsOfOverriddenObjCMethods(const ObjCMethodDecl *MethodDecl, + llvm::StringSet<> &USRSet) { + // Exit early if this method was already visited. + if (!USRSet.insert(getUSRForDecl(MethodDecl)).second) + return; + SmallVector<const ObjCMethodDecl *, 8> Overrides; + MethodDecl->getOverriddenMethods(Overrides); + for (const auto &OverriddenMethod : Overrides) + addUSRsOfOverriddenObjCMethods(OverriddenMethod, USRSet); + } + + /// \brief Returns true if the given Objective-C method overrides the + /// found Objective-C method declaration. + bool checkIfPotentialObjCMethodOverriddes(const ObjCMethodDecl *MethodDecl, + const llvm::StringSet<> &USRSet) { + SmallVector<const ObjCMethodDecl *, 8> Overrides; + MethodDecl->getOverriddenMethods(Overrides); + for (const auto &OverriddenMethod : Overrides) { + if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) + return true; + if (checkIfPotentialObjCMethodOverriddes(OverriddenMethod, USRSet)) + return true; + } + return false; + } + + const Decl *FoundDecl; + ASTContext &Context; + std::vector<const CXXMethodDecl *> OverriddenMethods; + std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs; + /// \brief An array of Objective-C methods that potentially override the + /// found Objective-C method declaration \p FoundDecl. + std::vector<const ObjCMethodDecl *> PotentialObjCMethodOverridders; +}; +} // end anonymous namespace + +namespace clang { +namespace tooling { + +llvm::StringSet<> findSymbolsUSRSet(const NamedDecl *FoundDecl, + ASTContext &Context) { + return AdditionalUSRFinder(FoundDecl, Context).Find(); +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/TypeUtils.cpp b/clang/lib/Tooling/Refactor/TypeUtils.cpp new file mode 100644 index 0000000000000..3a36aec1d5ba4 --- /dev/null +++ b/clang/lib/Tooling/Refactor/TypeUtils.cpp @@ -0,0 +1,193 @@ +//===--- TypeUtils.cpp - Type helper functions ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TypeUtils.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NSAPI.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; + +namespace { + +/// Returns false if a BOOL expression is found. +class BOOLUseFinder : public RecursiveASTVisitor<BOOLUseFinder> { +public: + NSAPI API; + + BOOLUseFinder(const ASTContext &Context) + : API(const_cast<ASTContext &>(Context)) {} + + bool VisitStmt(const Stmt *S) { + if (const auto *E = dyn_cast<Expr>(S)) + return !API.isObjCBOOLType(E->getType()); + return true; + } + + static bool hasUseOfObjCBOOL(const ASTContext &Ctx, const Expr *E) { + return !BOOLUseFinder(Ctx).TraverseStmt(const_cast<Expr *>(E)); + } +}; + +} // end anonymous namespace + +static QualType preferredBoolType(const Decl *FunctionLikeParentDecl, + const Expr *E, QualType T, + const PrintingPolicy &Policy, + const ASTContext &Ctx) { + // We want to target expressions that return either 'int' or 'bool' + const auto *BTy = T->getAs<BuiltinType>(); + if (!BTy) + return T; + switch (BTy->getKind()) { + case BuiltinType::Int: + case BuiltinType::Bool: + // In Objective-C[++] we want to try to use 'BOOL' when the 'BOOL' typedef + // is defined. + if (Ctx.getLangOpts().ObjC1 && Ctx.getBOOLDecl()) { + if (Ctx.getLangOpts().CPlusPlus && FunctionLikeParentDecl) { + // When extracting expression from a standalone function in + // Objective-C++ we should use BOOL when expression uses BOOL, otherwise + // we should use bool. + if (isa<FunctionDecl>(FunctionLikeParentDecl)) { + if (BOOLUseFinder::hasUseOfObjCBOOL(Ctx, E)) + return Ctx.getBOOLType(); + return T; + } + } + return Ctx.getBOOLType(); + } + // In C mode we want to use 'bool' instead of 'int' when the 'bool' macro + // is defined. + if (!Ctx.getLangOpts().CPlusPlus && Policy.Bool) + return Ctx.BoolTy; + break; + default: + break; + } + return T; +} + +static bool isInStdNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext(); + const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); + if (!ND) + return false; + + while (const DeclContext *Parent = ND->getParent()) { + if (!isa<NamespaceDecl>(Parent)) + break; + ND = cast<NamespaceDecl>(Parent); + } + + return ND->isStdNamespace(); +} + +static QualType desugarStdTypedef(QualType T) { + const auto *TT = T->getAs<TypedefType>(); + if (!TT) + return QualType(); + const TypedefNameDecl *TND = TT->getDecl(); + if (!isInStdNamespace(TND)) + return QualType(); + return TT->desugar(); +} + +// Desugars a typedef of a typedef that are both defined in STL. +// +// This is used to find the right type for a c_str() call on a std::string +// object: we want to return const char *, not const value_type *. +static QualType desugarStdType(QualType T) { + QualType DesugaredType = T; + if (const auto *PT = T->getAs<PointerType>()) + DesugaredType = PT->getPointeeType(); + DesugaredType = desugarStdTypedef(DesugaredType); + if (DesugaredType.isNull()) + return T; + if (const auto *ET = DesugaredType->getAs<ElaboratedType>()) + DesugaredType = ET->desugar(); + DesugaredType = desugarStdTypedef(DesugaredType); + if (DesugaredType.isNull()) + return T; + return T.getCanonicalType(); +} + +// Given an operator call like std::string() + "", we would like to ensure +// that we return std::string instead of std::basic_string. +static QualType canonicalizeStdOperatorReturnType(const Expr *E, QualType T) { + const auto *OCE = dyn_cast<CXXOperatorCallExpr>(E->IgnoreParenImpCasts()); + if (!OCE) + return T; + if (OCE->getNumArgs() < 2 || !isInStdNamespace(OCE->getCalleeDecl())) + return T; + QualType CanonicalReturn = T.getCanonicalType(); + if (const auto *RD = CanonicalReturn->getAsCXXRecordDecl()) { + if (!isInStdNamespace(RD)) + return T; + } else + return T; + for (unsigned I = 0, E = OCE->getNumArgs(); I < E; ++I) { + const Expr *Arg = OCE->getArgs()[I]; + QualType T = Arg->getType(); + if (const auto *ET = dyn_cast<ElaboratedType>(T)) + T = ET->desugar(); + if (desugarStdTypedef(T).isNull()) + continue; + QualType CanonicalArg = Arg->getType().getCanonicalType(); + CanonicalArg.removeLocalFastQualifiers(); + if (CanonicalArg == CanonicalReturn) { + QualType Result = Arg->getType(); + Result.removeLocalFastQualifiers(); + return Result; + } + } + return T; +} + +namespace clang { +namespace tooling { + +/// Tthe return type of the extracted function should match user's intent, +/// e.g. we want to use bool type whenever possible. +QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl, + const Expr *E, QualType T, + const PrintingPolicy &Policy, + const ASTContext &Ctx) { + // Get the correct property type. + if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { + if (PRE->isMessagingGetter()) { + if (PRE->isExplicitProperty()) + return PRE->getExplicitProperty()->getType(); + if (const ObjCMethodDecl *M = PRE->getImplicitPropertyGetter()) + return M->getReturnType(); + } + } + + // Perform STL-specific type corrections. + if (Ctx.getLangOpts().CPlusPlus) { + T = desugarStdType(T); + T = canonicalizeStdOperatorReturnType(E, T); + } + + // The bool type adjustment is required only in C or Objective-C[++]. + if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().ObjC1) + return T; + E = E->IgnoreParenImpCasts(); + if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { + if (BinOp->isLogicalOp() || BinOp->isComparisonOp()) + return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx); + } else if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { + if (UnOp->getOpcode() == UO_LNot) + return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx); + } + return T; +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/TypeUtils.h b/clang/lib/Tooling/Refactor/TypeUtils.h new file mode 100644 index 0000000000000..8d575e8bb9245 --- /dev/null +++ b/clang/lib/Tooling/Refactor/TypeUtils.h @@ -0,0 +1,35 @@ +//===--- TypeUtils.h - Type helper functions ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H + +#include "clang/AST/Type.h" + +namespace clang { + +class Decl; + +namespace tooling { + +/// \brief Find the most lexically appropriate type that can be used to describe +/// the return type of the given expression \p E. +/// +/// When extracting code, we want to produce a function that returns a type +/// that matches the user's intent. This function can be used to find such a +/// type. +QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl, + const Expr *E, QualType T, + const PrintingPolicy &Policy, + const ASTContext &Ctx); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H diff --git a/clang/lib/Tooling/Refactor/USRFinder.cpp b/clang/lib/Tooling/Refactor/USRFinder.cpp new file mode 100644 index 0000000000000..d4e78bd28a6b3 --- /dev/null +++ b/clang/lib/Tooling/Refactor/USRFinder.cpp @@ -0,0 +1,698 @@ +//===--- USRFinder.cpp - Clang refactoring library ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file Implements a recursive AST visitor that finds the USR of a symbol at a +/// point. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/USRFinder.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DependentASTVisitor.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/Core/RefactoringDiagnostic.h" +#include "llvm/ADT/SmallVector.h" +#include <functional> + +using namespace llvm; + +namespace clang { +namespace tooling { +namespace rename { + +typedef std::function<bool(const NamedDecl *, SourceLocation, SourceLocation)> + OccurrenceCheckerType; + +// NamedDeclFindingASTVisitor recursively visits each AST node to find the +// symbol underneath the cursor. +// FIXME: move to seperate .h/.cc file if this gets too large. +namespace { +class NamedDeclFindingASTVisitor + : public DependentASTVisitor<NamedDeclFindingASTVisitor> { +public: + // \brief Finds the NamedDecl at a point in the source. + // \param Point the location in the source to search for the NamedDecl. + explicit NamedDeclFindingASTVisitor( + const OccurrenceCheckerType &OccurrenceChecker, const ASTContext &Context) + : Result(nullptr), OccurrenceChecker(OccurrenceChecker), + Context(Context) {} + + // Declaration visitors: + + // \brief Checks if the point falls within the NameDecl. This covers every + // declaration of a named entity that we may come across. Usually, just + // checking if the point lies within the length of the name of the declaration + // and the start location is sufficient. + bool VisitNamedDecl(const NamedDecl *Decl) { + return dyn_cast<CXXConversionDecl>(Decl) + ? true + : checkOccurrence(Decl, Decl->getLocation(), + Decl->getNameAsString().length()); + } + + bool WalkUpFromTypedefNameDecl(const TypedefNameDecl *D) { + // Don't visit the NamedDecl for TypedefNameDecl. + return VisitTypedefNamedDecl(D); + } + + bool VisitTypedefNamedDecl(const TypedefNameDecl *D) { + if (D->isTransparentTag()) { + if (const auto *Underlying = D->getUnderlyingType()->getAsTagDecl()) + return checkOccurrence(Underlying, D->getLocation(), + D->getNameAsString().size()); + } + return VisitNamedDecl(D); + } + + bool WalkUpFromUsingDecl(const UsingDecl *D) { + // Don't visit the NamedDecl for UsingDecl. + return VisitUsingDecl(D); + } + + bool VisitUsingDecl(const UsingDecl *D) { + for (const auto *Shadow : D->shadows()) { + // Currently we always find the first declaration, but is this the right + // behaviour? + const NamedDecl *UD = Shadow->getUnderlyingDecl(); + if (UD->isImplicit() || UD == D) + continue; + if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(UD)) { + UD = FTD->getTemplatedDecl(); + if (!UD) + continue; + } + if (!checkOccurrence(UD, D->getLocation())) + return false; + } + return true; + } + + bool WalkUpFromUsingDirectiveDecl(const UsingDirectiveDecl *D) { + // Don't visit the NamedDecl for UsingDirectiveDecl. + return VisitUsingDirectiveDecl(D); + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + return checkOccurrence(D->getNominatedNamespaceAsWritten(), + D->getLocation()); + } + + bool WalkUpFromUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingValueDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool + WalkUpFromUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingTypenameDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool WalkUpFromObjCMethodDecl(const ObjCMethodDecl *Decl) { + // Don't visit the NamedDecl for Objective-C methods. + return VisitObjCMethodDecl(Decl); + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *Decl) { + // Check all of the selector source ranges. + for (unsigned I = 0, E = Decl->getNumSelectorLocs(); I != E; ++I) { + if (!checkOccurrence(Decl, Decl->getSelectorLoc(I), + Decl->getSelector().getNameForSlot(I).size())) + return false; + } + return true; + } + + bool VisitObjCProtocolList(const ObjCProtocolList &Protocols) { + for (unsigned I = 0, E = Protocols.size(); I != E; ++I) { + if (!checkOccurrence(Protocols[I], Protocols.loc_begin()[I])) + return false; + } + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return VisitObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return VisitObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool WalkUpFromObjCCategoryDecl(const ObjCCategoryDecl *Decl) { + // Don't visit the NamedDecl for Objective-C categories because the location + // of the name refers to the interface declaration. + return VisitObjCCategoryDecl(Decl); + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { + if (!checkOccurrence(Decl, Decl->getCategoryNameLoc())) + return false; + if (const auto *Class = Decl->getClassInterface()) { + // The location of the class name is the location of the declaration. + if (!checkOccurrence(Class, Decl->getLocation())) + return false; + } + return VisitObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool WalkUpFromObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) { + // Don't visit the NamedDecl for Objective-C categories because the location + // of the name refers to the interface declaration. + return VisitObjCCategoryImplDecl(Decl); + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) { + if (!checkOccurrence(Decl, Decl->getCategoryNameLoc())) + return false; + if (const auto *Class = Decl->getClassInterface()) { + // The location of the class name is the location of the declaration. + if (!checkOccurrence(Class, Decl->getLocation())) + return false; + } + return true; + } + + bool VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *Decl) { + return checkOccurrence(Decl->getClassInterface(), + Decl->getClassInterfaceLoc()); + } + + bool WalkUpFromObjCIvarDecl(ObjCIvarDecl *Decl) { + // Don't visit the NamedDecl for automatically synthesized ivars as the + // implicit ivars have the same location as the property declarations, and + // we want to find the property declarations. + if (Decl->getSynthesize()) + return true; + return RecursiveASTVisitor::WalkUpFromObjCIvarDecl(Decl); + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *Decl) { + if (Decl->hasExplicitGetterName()) { + if (const auto *Getter = Decl->getGetterMethodDecl()) + if (!checkOccurrence(Getter, Decl->getGetterNameLoc(), + Decl->getGetterName().getNameForSlot(0).size())) + return false; + } + if (Decl->hasExplicitSetterName()) { + if (const auto *Setter = Decl->getSetterMethodDecl()) + return checkOccurrence(Setter, Decl->getSetterNameLoc(), + Decl->getSetterName().getNameForSlot(0).size()); + } + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *Decl) { + if (!checkOccurrence(Decl->getPropertyDecl(), Decl->getLocation())) + return false; + if (Decl->isIvarNameSpecified()) + return checkOccurrence(Decl->getPropertyIvarDecl(), + Decl->getPropertyIvarDeclLoc()); + return true; + } + + // Expression visitors: + + bool VisitDeclRefExpr(const DeclRefExpr *Expr) { + const NamedDecl *Decl = Expr->getFoundDecl(); + return checkOccurrence(Decl, Expr->getLocation(), + Decl->getNameAsString().length()); + } + + bool VisitMemberExpr(const MemberExpr *Expr) { + const NamedDecl *Decl = Expr->getFoundDecl().getDecl(); + return checkOccurrence(Decl, Expr->getMemberLoc(), + Decl->getNameAsString().length()); + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *Expr) { + const ObjCMethodDecl *Decl = Expr->getMethodDecl(); + if (Decl == nullptr) + return true; + + // Check all of the selector source ranges. + for (unsigned I = 0, E = Expr->getNumSelectorLocs(); I != E; ++I) { + if (!checkOccurrence(Decl, Expr->getSelectorLoc(I), + Decl->getSelector().getNameForSlot(I).size())) + return false; + } + return true; + } + + bool VisitObjCProtocolExpr(const ObjCProtocolExpr *Expr) { + return checkOccurrence(Expr->getProtocol(), Expr->getProtocolIdLoc()); + } + + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Expr) { + return checkOccurrence(Expr->getDecl(), Expr->getLocation()); + } + + bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Expr) { + if (Expr->isClassReceiver()) + checkOccurrence(Expr->getClassReceiver(), Expr->getReceiverLocation()); + if (Expr->isImplicitProperty()) { + // Class properties that are explicitly defined using @property + // declarations are represented implicitly as there is no ivar for class + // properties. + if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) { + if (Getter->isClassMethod()) { + if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl()) + return checkOccurrence(PD, Expr->getLocation()); + } + } + + if (Expr->isMessagingGetter()) { + if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) + return checkOccurrence(Getter, Expr->getLocation()); + } else if (const ObjCMethodDecl *Setter = + Expr->getImplicitPropertySetter()) { + return checkOccurrence(Setter, Expr->getLocation()); + } + + return true; + } + return checkOccurrence(Expr->getExplicitProperty(), Expr->getLocation()); + } + + // Other visitors: + + bool VisitTypeLoc(const TypeLoc Loc) { + const SourceLocation TypeBeginLoc = Loc.getBeginLoc(); + const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken( + TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); + if (const auto *TemplateTypeParm = + dyn_cast<TemplateTypeParmType>(Loc.getType())) + return checkOccurrence(TemplateTypeParm->getDecl(), TypeBeginLoc, + TypeEndLoc); + if (const auto *TemplateSpecType = + dyn_cast<TemplateSpecializationType>(Loc.getType())) { + return checkOccurrence( + TemplateSpecType->getTemplateName().getAsTemplateDecl(), TypeBeginLoc, + TypeEndLoc); + } + TypedefTypeLoc TTL = Loc.getAs<TypedefTypeLoc>(); + if (TTL) { + const auto *TND = TTL.getTypedefNameDecl(); + if (TND->isTransparentTag()) { + if (const auto *Underlying = TND->getUnderlyingType()->getAsTagDecl()) + return checkOccurrence(Underlying, TTL.getNameLoc()); + } + return checkOccurrence(TND, TTL.getNameLoc()); + } + TypeSpecTypeLoc TSTL = Loc.getAs<TypeSpecTypeLoc>(); + if (TSTL) { + return checkOccurrence(Loc.getType()->getAsTagDecl(), TSTL.getNameLoc()); + } + return true; + } + + bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc Loc) { + return checkOccurrence(Loc.getIFaceDecl(), Loc.getNameLoc()); + } + + bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc Loc) { + for (unsigned I = 0, E = Loc.getNumProtocols(); I < E; ++I) { + if (!checkOccurrence(Loc.getProtocol(I), Loc.getProtocolLoc(I))) + return false; + } + return true; + } + + bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { + for (const auto *Initializer : ConstructorDecl->inits()) { + // Ignore implicit initializers. + if (!Initializer->isWritten()) + continue; + if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) { + const SourceLocation InitBeginLoc = Initializer->getSourceLocation(), + InitEndLoc = Lexer::getLocForEndOfToken( + InitBeginLoc, 0, Context.getSourceManager(), + Context.getLangOpts()); + if (!checkOccurrence(FieldDecl, InitBeginLoc, InitEndLoc)) + return false; + } + } + return true; + } + + bool VisitDependentSymbolReference(const NamedDecl *Symbol, + SourceLocation SymbolNameLoc) { + return checkOccurrence(Symbol, SymbolNameLoc); + } + + // Other: + + const NamedDecl *getNamedDecl() { return Result; } + + bool isDone() const { return Result; } + + // \brief Determines if a namespace qualifier contains the point. + // \returns false on success and sets Result. + void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { + while (NameLoc) { + const NamespaceDecl *Decl = + NameLoc.getNestedNameSpecifier()->getAsNamespace(); + checkOccurrence(Decl, NameLoc.getLocalBeginLoc(), + NameLoc.getLocalEndLoc()); + NameLoc = NameLoc.getPrefix(); + } + } + +private: + /// \brief Sets Result to \p Decl if the occurrence checker returns true. + /// + /// \returns false on success. + bool checkRange(const NamedDecl *Decl, SourceLocation Start, + SourceLocation End) { + assert(!Start.isMacroID() && !End.isMacroID() && "Macro location?"); + if (!Decl) + return true; + if (isa<ImplicitParamDecl>(Decl)) + return true; + if (const auto *FD = dyn_cast<FunctionDecl>(Decl)) { + // Don't match operators. + if (FD->isOverloadedOperator()) + return true; + } + if (!OccurrenceChecker(Decl, Start, End)) + return true; + Result = Decl; + return false; + } + + /// Checks if the given declaration is valid, and if it is, sets Result to + /// \p Decl if the occurrence checker returns true. + /// + /// \returns false if the point of interest is inside the range that + /// corresponds the occurrence of this declaration. + bool checkOccurrence(const NamedDecl *Decl, SourceLocation Loc) { + if (!Decl) + return true; + return checkOccurrence(Decl, Loc, Decl->getNameAsString().size()); + } + + /// \brief Sets Result to \p Decl if the occurrence checker returns true. + /// + /// \returns false on success. + bool checkOccurrence(const NamedDecl *Decl, SourceLocation Loc, + unsigned Length) { + if (Loc.isMacroID()) { + const SourceManager &SM = Context.getSourceManager(); + if (SM.isMacroArgExpansion(Loc)) + Loc = SM.getSpellingLoc(Loc); + else + return true; + } + + return Length == 0 || + checkRange(Decl, Loc, Loc.getLocWithOffset(Length - 1)); + } + + bool checkOccurrence(const NamedDecl *ND, SourceLocation Start, + SourceLocation End) { + const SourceManager &SM = Context.getSourceManager(); + if (Start.isMacroID()) { + if (SM.isMacroArgExpansion(Start)) + Start = SM.getSpellingLoc(Start); + else + return true; + } + if (End.isMacroID()) { + if (SM.isMacroArgExpansion(End)) + End = SM.getSpellingLoc(End); + else + return true; + } + return checkRange(ND, Start, End); + } + + const NamedDecl *Result; + const OccurrenceCheckerType &OccurrenceChecker; + const ASTContext &Context; +}; + +} // namespace + +static const ExternalSourceSymbolAttr *getExternalSymAttr(const Decl *D) { + if (const auto *A = D->getAttr<ExternalSourceSymbolAttr>()) + return A; + if (const auto *DCD = dyn_cast<Decl>(D->getDeclContext())) { + if (const auto *A = DCD->getAttr<ExternalSourceSymbolAttr>()) + return A; + } + return nullptr; +} + +static bool overridesSystemMethod(const ObjCMethodDecl *MD, + const SourceManager &SM) { + SmallVector<const ObjCMethodDecl *, 8> Overrides; + MD->getOverriddenMethods(Overrides); + for (const auto *Override : Overrides) { + SourceLocation Loc = Override->getLocStart(); + if (Loc.isValid()) { + if (SM.getFileCharacteristic(Loc) != SrcMgr::C_User) + return true; + } + if (overridesSystemMethod(Override, SM)) + return true; + } + return false; +} + +// TODO: Share with the indexer? +static bool isTemplateImplicitInstantiation(const Decl *D) { + TemplateSpecializationKind TKind = TSK_Undeclared; + if (const ClassTemplateSpecializationDecl *SD = + dyn_cast<ClassTemplateSpecializationDecl>(D)) { + TKind = SD->getSpecializationKind(); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + TKind = FD->getTemplateSpecializationKind(); + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + TKind = VD->getTemplateSpecializationKind(); + } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) { + if (RD->getInstantiatedFromMemberClass()) + TKind = RD->getTemplateSpecializationKind(); + } else if (const auto *ED = dyn_cast<EnumDecl>(D)) { + if (ED->getInstantiatedFromMemberEnum()) + TKind = ED->getTemplateSpecializationKind(); + } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D) || + isa<EnumConstantDecl>(D)) { + if (const auto *Parent = dyn_cast<Decl>(D->getDeclContext())) + return isTemplateImplicitInstantiation(Parent); + } + switch (TKind) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + return false; + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + return true; + } + llvm_unreachable("invalid TemplateSpecializationKind"); +} + +static const CXXRecordDecl * +getDeclContextForTemplateInstationPattern(const Decl *D) { + if (const auto *CTSD = + dyn_cast<ClassTemplateSpecializationDecl>(D->getDeclContext())) + return CTSD->getTemplateInstantiationPattern(); + else if (const auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext())) + return RD->getInstantiatedFromMemberClass(); + return nullptr; +} + +static const NamedDecl * +adjustTemplateImplicitInstantiation(const NamedDecl *D) { + if (const ClassTemplateSpecializationDecl *SD = + dyn_cast<ClassTemplateSpecializationDecl>(D)) { + return SD->getTemplateInstantiationPattern(); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + return FD->getTemplateInstantiationPattern(); + } else if (auto *VD = dyn_cast<VarDecl>(D)) { + return VD->getTemplateInstantiationPattern(); + } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) { + return RD->getInstantiatedFromMemberClass(); + } else if (const auto *ED = dyn_cast<EnumDecl>(D)) { + return ED->getInstantiatedFromMemberEnum(); + } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) { + const auto *ND = cast<NamedDecl>(D); + if (const CXXRecordDecl *Pattern = + getDeclContextForTemplateInstationPattern(ND)) { + for (const NamedDecl *BaseND : Pattern->lookup(ND->getDeclName())) { + if (BaseND->isImplicit()) + continue; + if (BaseND->getKind() == ND->getKind()) + return BaseND; + } + } + } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) { + if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) { + if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) { + for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName())) + return BaseECD; + } + } + } + return D; +} + +const NamedDecl *getNamedDeclAt(const ASTContext &Context, + SourceLocation Point) { + if (Point.isMacroID()) + Point = Context.getSourceManager().getSpellingLoc(Point); + + OccurrenceCheckerType PointChecker = [Point, &Context]( + const NamedDecl *Decl, SourceLocation Start, SourceLocation End) -> bool { + return Start.isValid() && Start.isFileID() && End.isValid() && + End.isFileID() && + isPointWithin(Point, Start, End, Context.getSourceManager()); + }; + NamedDeclFindingASTVisitor Visitor(PointChecker, Context); + + // We only want to search the decls that exist in the same file as the point. + StringRef SearchFile = Context.getSourceManager().getFilename(Point); + for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { + const SourceLocation FileLoc = CurrDecl->getLocStart(); + StringRef FileName = Context.getSourceManager().getFilename(FileLoc); + // FIME: Skip system headers. + // FIXME: Add test. + if (FileName == SearchFile) + Visitor.TraverseDecl(CurrDecl); + if (Visitor.isDone()) + break; + } + + if (!Visitor.isDone()) { + NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context)); + for (const auto &Location : Finder.getNestedNameSpecifierLocations()) { + Visitor.handleNestedNameSpecifierLoc(Location); + if (Visitor.isDone()) + break; + } + } + + const auto Diag = [&](unsigned DiagID) -> DiagnosticBuilder { + return Context.getDiagnostics().Report(Point, DiagID); + }; + const auto *ND = Visitor.getNamedDecl(); + if (!ND) + return nullptr; + + // Canonicalize the found declaration. + // + // If FoundDecl is a constructor or destructor, we want to instead take + // the Decl of the corresponding class. + if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(ND)) + ND = CtorDecl->getParent(); + else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(ND)) + ND = DtorDecl->getParent(); + + if (isTemplateImplicitInstantiation(ND)) + ND = adjustTemplateImplicitInstantiation(ND); + + // Builtins can't be renamed. + if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { + if (FD->getBuiltinID()) { + Diag(diag::err_rename_builtin_function) << ND->getDeclName(); + return nullptr; + } + } + // Declarations with invalid locations are probably implicit. + if (ND->getLocStart().isInvalid()) + return nullptr; + // Declarations in system headers can't be renamed. + auto CheckSystemLoc = [&](SourceLocation Loc) -> bool { + if (Context.getSourceManager().getFileCharacteristic(Loc) != + SrcMgr::C_User) { + Diag(diag::err_rename_sys_header) << ND->getDeclName(); + return true; + } + return false; + }; + if (CheckSystemLoc(ND->getLocStart())) + return nullptr; + if (const auto *TD = dyn_cast<TypedefNameDecl>(ND)) { + if (const TypedefNameDecl *CTD = TD->getCanonicalDecl()) { + if (CheckSystemLoc(CTD->getLocStart())) + return nullptr; + } + } else if (const auto *TD = dyn_cast<TagDecl>(ND)) { + if (const TagDecl *CTD = TD->getCanonicalDecl()) { + if (CheckSystemLoc(CTD->getLocStart())) + return nullptr; + } + } else if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { + if (const FunctionDecl *CFD = FD->getCanonicalDecl()) { + if (CheckSystemLoc(CFD->getLocStart())) + return nullptr; + } + } else if (const auto *VD = dyn_cast<VarDecl>(ND)) { + if (const VarDecl *CVD = VD->getCanonicalDecl()) { + if (CheckSystemLoc(CVD->getLocStart())) + return nullptr; + } + } + // Declarations from other languages can't be renamed. + if (const ExternalSourceSymbolAttr *ESSA = getExternalSymAttr(ND)) { + Diag(diag::err_rename_external_source_symbol) << ND->getDeclName() + << ESSA->getLanguage(); + return nullptr; + } + // Methods that override methods from system headers can't be renamed. + if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) { + if (overridesSystemMethod(MD, Context.getSourceManager())) { + Diag(diag::err_method_rename_override_sys_framework) << ND->getDeclName(); + return nullptr; + } + } + return ND; +} + +const NamedDecl *getNamedDeclWithUSR(const ASTContext &Context, StringRef USR) { + // TODO: Remove in favour of the new converter. + OccurrenceCheckerType USRChecker = + [USR](const NamedDecl *Decl, SourceLocation Start, SourceLocation End) { + return USR == getUSRForDecl(Decl); + }; + NamedDeclFindingASTVisitor Visitor(USRChecker, Context); + + for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { + Visitor.TraverseDecl(CurrDecl); + if (Visitor.isDone()) + break; + } + + // Don't need to visit nested name specifiers as they refer to previously + // declared declarations that we've already seen. + return Visitor.getNamedDecl(); +} + +std::string getUSRForDecl(const Decl *Decl) { + llvm::SmallVector<char, 128> Buff; + + // FIXME: Add test for the nullptr case. + if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff)) + return ""; + + return std::string(Buff.data(), Buff.size()); +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt index b2f9b4f4c0cdd..2520bb9598a82 100644 --- a/clang/lib/Tooling/Refactoring/CMakeLists.txt +++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt @@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_library(clangToolingRefactor +add_clang_library(clangToolingRefactoring AtomicChange.cpp LINK_LIBS diff --git a/clang/test/Analysis/misc-ps.m b/clang/test/Analysis/misc-ps.m index 9a75cfd87b623..c610a7d922c42 100644 --- a/clang/test/Analysis/misc-ps.m +++ b/clang/test/Analysis/misc-ps.m @@ -1086,7 +1086,7 @@ void test_enum_cases(enum Cases C) { } void test_enum_cases_positive(enum Cases C) { - switch (C) { // expected-warning{{enumeration value 'C4' not handled in switch}} + switch (C) { // expected-warning{{enumeration value 'C4' not handled in switch}} expected-note {{add missing switch cases}} case C1: case C2: case C3: diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index 23d23bcddcc19..50e33449ed0e2 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -46,9 +46,10 @@ list(APPEND CLANG_TEST_DEPS c-index-test diagtool clang-tblgen clang-offload-bundler + clang-refactor-test clang-import-test ) - + if(CLANG_ENABLE_STATIC_ANALYZER) list(APPEND CLANG_TEST_DEPS clang-check diff --git a/clang/test/FixIt/fixit-fill-in-missing-switch-cases.cpp b/clang/test/FixIt/fixit-fill-in-missing-switch-cases.cpp new file mode 100644 index 0000000000000..20b948f46195e --- /dev/null +++ b/clang/test/FixIt/fixit-fill-in-missing-switch-cases.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s + +enum Color { + Black, + Blue, + White, + Gold +}; + +void fillInCases(Color c) { + switch (c) { + case Black: + break; + } +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" + switch (c) { + } +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" +} + +enum class NoDenseMap: long long { + Baddie = 0x7fffffffffffffffLL, + BigBaddie = -0x7fffffffffffffffLL-1 +}; + +void fillInAllCases(NoDenseMap v) { + switch (v) { + } +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case NoDenseMap::Baddie:\n<#code#>\nbreak;\ncase NoDenseMap::BigBaddie:\n<#code#>\nbreak;\n" +} + diff --git a/clang/test/FixIt/fixit-fill-in-protocol-requirements.m b/clang/test/FixIt/fixit-fill-in-protocol-requirements.m new file mode 100644 index 0000000000000..c86b8c28672c8 --- /dev/null +++ b/clang/test/FixIt/fixit-fill-in-protocol-requirements.m @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -verify -Wno-objc-root-class -serialize-diagnostic-file /dev/null %s +// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -serialize-diagnostic-file /dev/null %s 2>&1 | FileCheck %s + +@protocol P1 + +- (void)p2Method; + +@end + +@protocol P2 <P1> + +- (void)p1Method; + +@end + +@interface I <P2> + +@end + +@implementation I // expected-warning {{warning: class 'I' does not conform to protocols 'P2' and 'P1'}} expected-note {{add stubs for missing protocol requirements}} + +@end +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"- (void)p2Method { \n <#code#>\n}\n\n- (void)p1Method { \n <#code#>\n}\n\n" + +@protocol P3 + ++ (void)p3ClassMethod; + +@end + +@interface I (Category) <P3> // expected-warning {{warning: category 'Category' does not conform to protocols 'P3'}} expected-note {{add stubs for missing protocol requirements}} + +@end + +@implementation I (Category) + +@end +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"+ (void)p3ClassMethod { \n <#code#>\n}\n\n" + +@protocol P4 + +- (void)anotherMethod; + +@end + +@interface ThreeProtocols <P2, P1, P3> // expected-warning {{class 'ThreeProtocols' does not conform to protocols 'P2', 'P1' and 'P3'}} expected-note {{add stubs for missing protocol requirements}} +@end +@implementation ThreeProtocols +@end + +@interface FourProtocols <P2, P3, P4> // expected-warning {{class 'ThreeProtocols' does not conform to protocols 'P2', 'P1', 'P3', ...}} expected-note {{add stubs for missing protocol requirements}} +@end +@implementation FourProtocols +@end diff --git a/clang/test/Misc/ast-dump-decl.m b/clang/test/Misc/ast-dump-decl.m index 4cfb8aa0c41d4..bc5a0588cabf8 100644 --- a/clang/test/Misc/ast-dump-decl.m +++ b/clang/test/Misc/ast-dump-decl.m @@ -143,3 +143,6 @@ void f() { __typeof__(B.foo) Test; } // CHECK: VarDecl{{.*}}Test 'typeof (B.foo)':'int' + +@compatibility_alias TestCompatibilityAlias A; +// CHECK: ObjCCompatibleAliasDecl{{.*}}col:22 TestCompatibilityAlias col:45 diff --git a/clang/test/Parser/switch-recovery.cpp b/clang/test/Parser/switch-recovery.cpp index 4b06d55ba5944..019682f626a6a 100644 --- a/clang/test/Parser/switch-recovery.cpp +++ b/clang/test/Parser/switch-recovery.cpp @@ -21,7 +21,7 @@ struct B { void test2() { enum X { Xa, Xb } x; - switch (x) { // expected-warning {{enumeration value 'Xb' not handled in switch}} + switch (x) { // expected-warning {{enumeration value 'Xb' not handled in switch}} expected-note {{add missing switch cases}} case Xa; // expected-error {{expected ':' after 'case'}} break; } diff --git a/clang/test/Refactor/Extract/captured-variable-block-types.m b/clang/test/Refactor/Extract/captured-variable-block-types.m new file mode 100644 index 0000000000000..ef20181844bbf --- /dev/null +++ b/clang/test/Refactor/Extract/captured-variable-block-types.m @@ -0,0 +1,30 @@ + +int capturedBlock(void (^block)(int x, int (^)())) { + return capturedBlock(block); +} + +// CHECK1: extracted(void (^block)(int, int (^)())) + +typedef void (^BlockTypedef)(); + +int capturedBlockTypedef(BlockTypedef fp) { + return capturedBlockTypedef(fp); +} +// CHECK1: extracted(BlockTypedef fp) + +// RUN: clang-refactor-test perform -action extract -selected=%s:3:10-3:29 -selected=%s:11:10-11:34 %s -fblocks | FileCheck --check-prefix=CHECK1 %s + +@interface I +@end + +@implementation I + +- (void)method { + void (^block)(int x, int (^)()); + block(2, ^ (void) { return 0; }); +} +// CHECK2: - (void)extracted:(void (^)(int, int (^)()))block { + +@end + +// RUN: clang-refactor-test perform -action extract-method -selected=%s:24:3-24:35 %s -fblocks | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Extract/captured-variable-function-types.cpp b/clang/test/Refactor/Extract/captured-variable-function-types.cpp new file mode 100644 index 0000000000000..cf6ccee1a4673 --- /dev/null +++ b/clang/test/Refactor/Extract/captured-variable-function-types.cpp @@ -0,0 +1,17 @@ + +int capturedFunc(void (*fp)(int x)) { + auto fp2 = fp; + capturedFunc(fp); + return capturedFunc(fp2); +} + +// CHECK1: extracted(void (*fp)(int)) + +typedef void (*FuncTypedef)(); + +int capturedFuncTypedef(FuncTypedef fp) { + return capturedFuncTypedef(fp); +} +// CHECK1: extracted(FuncTypedef fp) + +// RUN: clang-refactor-test perform -action extract -selected=%s:4:3-4:19 -selected=%s:13:10-13:33 %s | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/Extract/captured-variable-lambda-type.cpp b/clang/test/Refactor/Extract/captured-variable-lambda-type.cpp new file mode 100644 index 0000000000000..6042830639cd6 --- /dev/null +++ b/clang/test/Refactor/Extract/captured-variable-lambda-type.cpp @@ -0,0 +1,18 @@ + +int capturedLambda(int x) { + auto Lambda = [] () { }; + Lambda(); + auto Lambda2 = [] (int x, int y) -> int { return x + y * 2; }; + int y = Lambda2(x, 1); + auto Lambda3 = [&] (int y) { + x = y + 2; + }; + Lambda3(3); + return Lambda2(x, 1); +} + +// CHECK1: extracted(const std::function<void ()> &Lambda) +// CHECK1: extracted(const std::function<auto (int, int) -> int> &Lambda2, int x) +// CHECK1: extracted(const std::function<auto (int, int) -> int> &Lambda2, const std::function<void (int)> &Lambda3, int x) + +// RUN: clang-refactor-test perform -action extract -selected=%s:4:3-4:11 -selected=%s:6:3-6:24 -selected=%s:10:3-11:23 %s -std=c++11 | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/Extract/captured-variable-typedef.cpp b/clang/test/Refactor/Extract/captured-variable-typedef.cpp new file mode 100644 index 0000000000000..f6991ead44cef --- /dev/null +++ b/clang/test/Refactor/Extract/captured-variable-typedef.cpp @@ -0,0 +1,15 @@ +typedef signed int NSInteger; + +int capturedTypedef(NSInteger x) { + return capturedTypedef(x); +} +// CHECK1: "static int extracted(NSInteger x) {\nreturn capturedTypedef(x);\n}\n\n" [[@LINE-3]] +// RUN: clang-refactor-test perform -action extract -selected=%s:4:10-4:28 %s -std=c++14 | FileCheck --check-prefix=CHECK1 %s + +using NSUInteger = unsigned int; + +int capturedUsing(NSUInteger x) { + return capturedUsing(x); +} +// CHECK2: "static int extracted(NSUInteger x) {\nreturn capturedUsing(x);\n}\n\n" [[@LINE-3]] +// RUN: clang-refactor-test perform -action extract -selected=%s:12:10-12:26 %s -std=c++14 | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Extract/captured-variable-types.cpp b/clang/test/Refactor/Extract/captured-variable-types.cpp new file mode 100644 index 0000000000000..1a83884300c47 --- /dev/null +++ b/clang/test/Refactor/Extract/captured-variable-types.cpp @@ -0,0 +1,223 @@ + +typedef struct { + int width, height; +} Rectangle; + +int basicTypes(int i, float f, char c, const int *ip, float *fp, + const Rectangle *structPointer) { + return basicTypes(i, f, c, ip, fp, structPointer); +} +// CHECK1: "static int extracted(char c, float f, float *fp, int i, const int *ip, const Rectangle *structPointer) {\nreturn basicTypes(i, f, c, ip, fp, structPointer);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK1-NEXT: "extracted(c, f, fp, i, ip, structPointer)" [[@LINE-3]]:10 -> [[@LINE-3]]:52 + +// RUN: clang-refactor-test perform -action extract -selected=%s:8:10-8:52 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:8:10-8:52 %s -x c | FileCheck --check-prefix=CHECK1 %s + +#ifndef __cplusplus +#define bool _Bool +#define true 1 +#define false 0 +#endif + +int boolType(bool b) { + bool b2 = true; + return boolType(b && b2 && true && false); +} +// CHECK2: "static int extracted(bool b, bool b2) {\nreturn boolType(b && b2 && true && false);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK2-NEXT: "extracted(b, b2)" [[@LINE-3]]:10 -> [[@LINE-3]]:44 + +// RUN: clang-refactor-test perform -action extract -selected=%s:24:10-24:44 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:24:10-24:44 %s -x c | FileCheck --check-prefix=CHECK2 %s + +int global = 0; + +void dontCaptureGlobalVariable() { + static int staticInFunction = 0; + int i = global + staticInFunction; +} +// CHECK3: "static int extracted(int staticInFunction) {\nreturn global + staticInFunction;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK3-NEXT: "extracted(staticInFunction)" [[@LINE-3]]:11 -> [[@LINE-3]]:36 + +// RUN: clang-refactor-test perform -action extract -selected=%s:36:11-36:36 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:36:11-36:36 %s -x c | FileCheck --check-prefix=CHECK3 %s + +#ifdef __cplusplus + +int referenceType(const Rectangle &r, int &i) { + return referenceType(r, i); +} +// CHECK4: "static int extracted(int &i, const Rectangle &r) {\nreturn referenceType(r, i);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK4-NEXT: "extracted(i, r)" [[@LINE-3]]:10 -> [[@LINE-3]]:29 + +#endif + +// RUN: clang-refactor-test perform -action extract -selected=%s:47:10-47:29 %s | FileCheck --check-prefix=CHECK4 %s + +typedef union { + int x; + Rectangle r; +} Union; + +int aggregateTypeNotMutated(Rectangle r, Union u) { + Rectangle r2 = r; + Union u2 = u; + aggregateTypeNotMutated(r, u); + return aggregateTypeNotMutated(r2, u2); +} +// CHECK5-CPP: "static int extracted(const Rectangle &r, const Union &u) {\nreturn aggregateTypeNotMutated(r, u);\n}\n\n" [[@LINE-6]]:1 -> [[@LINE-6]]:1 +// CHECK5-CPP-NEXT: "extracted(r, u)" [[@LINE-4]]:3 -> [[@LINE-4]]:32 +// CHECK5-C: "static int extracted(const Rectangle *r, const Union *u) {\nreturn aggregateTypeNotMutated(*r, *u);\n}\n\n" [[@LINE-8]]:1 -> [[@LINE-8]]:1 +// CHECK5-C-NEXT: "extracted(&r, &u)" [[@LINE-6]]:3 -> [[@LINE-6]]:32 +// CHECK5-CPP: "static int extracted(const Rectangle &r2, const Union &u2) {\nreturn aggregateTypeNotMutated(r2, u2);\n}\n\n" [[@LINE-10]]:1 -> [[@LINE-10]]:1 +// CHECK5-CPP-NEXT: "extracted(r2, u2)" [[@LINE-7]]:10 -> [[@LINE-7]]:41 +// CHECK5-C: "static int extracted(const Rectangle *r2, const Union *u2) {\nreturn aggregateTypeNotMutated(*r2, *u2);\n}\n\n" [[@LINE-12]]:1 -> [[@LINE-12]]:1 +// CHECK5-C-NEXT: "extracted(&r2, &u2)" [[@LINE-9]]:10 -> [[@LINE-9]]:41 + +// RUN: clang-refactor-test perform -action extract -selected=%s:64:3-64:32 -selected=%s:65:10-65:41 %s | FileCheck --check-prefix=CHECK5-CPP %s +// RUN: clang-refactor-test perform -action extract -selected=%s:64:3-64:32 -selected=%s:65:10-65:41 %s -x c | FileCheck --check-prefix=CHECK5-C %s +; +int arrayType(const int x[]) { + int v[2] = {1, 2}; + arrayType(x); + return arrayType(v); +} +// CHECK6: "static int extracted(const int *x) {\nreturn arrayType(x);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1 +// CHECK6-NEXT: "extracted(x)" [[@LINE-4]]:3 -> [[@LINE-4]]:15 +// CHECK6: "static int extracted(int *v) {\nreturn arrayType(v);\n}\n\n" [[@LINE-7]]:1 -> [[@LINE-7]]:1 +// CHECK6-NEXT: "extracted(v)" [[@LINE-5]]:10 -> [[@LINE-5]]:22 + +// RUN: clang-refactor-test perform -action extract -selected=%s:81:3-81:15 -selected=%s:82:10-82:22 %s | FileCheck --check-prefix=CHECK6 %s + +typedef enum { + EnumA, EnumB +} Enum; + +int enumType(Enum e) { + return enumType(e); +} +// CHECK7: "static int extracted(Enum e) {\nreturn enumType(e);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK7-NEXT: "extracted(e)" [[@LINE-3]]:10 -> [[@LINE-3]]:21 + +// RUN: clang-refactor-test perform -action extract -selected=%s:96:10-96:21 %s | FileCheck --check-prefix=CHECK7 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:96:10-96:21 %s -x c | FileCheck --check-prefix=CHECK7 %s +; +int qualifierReduction(const int i, const int * const p, int *volatile pv, int *__restrict__ rv, + const Enum e) { + return qualifierReduction(i, p, pv, rv, e); +} +// CHECK8: "static int extracted(Enum e, int i, const int *p, int *volatile pv, int *__restrict rv) {\nreturn qualifierReduction(i, p, pv, rv, e);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK8-NEXT: "extracted(e, i, p, pv, rv)" [[@LINE-3]]:10 -> [[@LINE-3]]:45 + +// RUN: clang-refactor-test perform -action extract -selected=%s:106:10-106:45 %s | FileCheck --check-prefix=CHECK8 %s + +#ifdef __cplusplus + +int autoTypeHandling(int x, Rectangle &ref) { + auto i = x; + auto &r = ref; + return autoTypeHandling(i, r); +} +// CHECK9: "static int extracted(int i, Rectangle &r) {\nreturn autoTypeHandling(i, r);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1 +// CHECK9-NEXT: "extracted(i, r)" [[@LINE-3]]:10 -> [[@LINE-3]]:32 + +#endif + +// RUN: clang-refactor-test perform -action extract -selected=%s:118:10-118:32 %s | FileCheck --check-prefix=CHECK9 %s + +Rectangle globalRect; +int capturedVariableMutation(int x, int y, int z, const int *ip, Rectangle r) { + return x = 0, y += 2, z &= 3, ip = 0, r = globalRect, capturedVariableMutation(x, 1, 2, ip, r); +} +// CHECK10-CPP: "static int extracted(const int *&ip, Rectangle &r, int &x, int &y, int &z) {\nreturn x = 0, y += 2, z &= 3, ip = 0, r = globalRect, capturedVariableMutation(x, 1, 2, ip, r);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK10-CPP-NEXT: "extracted(ip, r, x, y, z)" [[@LINE-3]]:10 -> [[@LINE-3]]:97 + +// CHECK10-C: "static int extracted(const int **ip, Rectangle *r, int *x, int *y, int *z) {\nreturn *x = 0, *y += 2, *z &= 3, *ip = 0, *r = globalRect, capturedVariableMutation(*x, 1, 2, *ip, *r);\n}\n\n" [[@LINE-6]]:1 -> [[@LINE-6]]:1 +// CHECK10-C-NEXT: "extracted(&ip, &r, &x, &y, &z)" [[@LINE-6]]:10 -> [[@LINE-6]]:97 + +#ifdef __cplusplus + +int capturedMutatedRef(Rectangle &r) { + return r = globalRect, capturedMutatedRef(r); +} +// CHECK10-CPP: "static int extracted(Rectangle &r) {\nreturn r = globalRect, capturedMutatedRef(r);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK10-CPP-NEXT: "extracted(r)" [[@LINE-3]]:10 -> [[@LINE-3]]:47 + +#endif + +int capturedArrayMutation(int x[]) { + return (x) = 0, capturedArrayMutation(x); +} +// CHECK10-CPP: "static int extracted(int *&x) {\nreturn (x) = 0, capturedArrayMutation(x);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK10-CPP-NEXT: "extracted(x)" [[@LINE-3]]:10 -> [[@LINE-3]]:43 +// CHECK10-C: "static int extracted(int **x) {\nreturn (*x) = 0, capturedArrayMutation(*x);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1 +// CHECK10-C-NEXT: "extracted(&x)" [[@LINE-5]]:10 -> [[@LINE-5]]:43 +; +void mutationOutsideOfExtractedCode(int x) { + x = 0; + x = 1, mutationOutsideOfExtractedCode(x + 1); + x = 2; + return 0; +} +// CHECK10-CPP: "static void extracted(int x) {\nmutationOutsideOfExtractedCode(x + 1);\n}\n\n" [[@LINE-6]]:1 -> [[@LINE-6]]:1 +// CHECK10-CPP-NEXT: "extracted(x)" [[@LINE-5]]:10 -> [[@LINE-5]]:47 + +// RUN: clang-refactor-test perform -action extract -selected=%s:129:10-129:97 -selected=%s:140:10-140:47 -selected=%s:148:10-148:43 -selected=%s:157:10-157:47 %s | FileCheck --check-prefix=CHECK10-CPP %s +// RUN: clang-refactor-test perform -action extract -selected=%s:129:10-129:97 -selected=%s:148:10-148:43 %s -x c | FileCheck --check-prefix=CHECK10-C %s +; +void extractStatementRangeRewritePointerUse(int x) { + extractStatementRangeRewritePointerUse(x); + x = 1; + extractStatementRangeRewritePointerUse(x); + extractStatementRangeRewritePointerUse(x); +} +// CHECK11: "static void extracted(int *x) {\n*x = 1;\n extractStatementRangeRewritePointerUse(*x);\n}\n\n" [[@LINE-6]]:1 +// CHECK11-NEXT: "extracted(&x)" [[@LINE-5]]:3 -> [[@LINE-4]]:44 + +// RUN: clang-refactor-test perform -action extract -selected=%s:169:3-170:10 %s -x c | FileCheck --check-prefix=CHECK11 %s + +void mutationAfterUse(int x) { + int y = x; + (void)y; + y = 2; +} +// CHECK12: "static void extracted(int &y) {\n(void)y;\n y = 2;\n}\n\n" +// RUN: clang-refactor-test perform -action extract -selected=%s:180:3-181:4 %s | FileCheck --check-prefix=CHECK12 %s + +enum TagEnum { + TagEnumA, TagEnumB +}; + +int tagEnumType(enum TagEnum e) { + return tagEnumType(e); +} +// CHECK13: (enum TagEnum e) +// RUN: clang-refactor-test perform -action extract -selected=%s:191:10-191:24 %s | FileCheck --check-prefix=CHECK13 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:191:10-191:24 %s -x c | FileCheck --check-prefix=CHECK13 %s + +struct TagStruct { + int x; +}; + +int tagStructType(struct TagStruct *s) { + return tagStructType(s); +} +// CHECK14: (struct TagStruct *s) +// RUN: clang-refactor-test perform -action extract -selected=%s:202:10-202:26 %s | FileCheck --check-prefix=CHECK14 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:202:10-202:26 %s -x c | FileCheck --check-prefix=CHECK14 %s + +namespace { +struct Foo { int x; }; +} + +void anonymousNamespaceTypes() { + Foo x; +// anonymous-ns-type1-begin: +1:1 + x.x = 0; +// anonymous-ns-type1-end: +0:1 +// CHECK-ANON: "static void extracted(Foo &x) {\nx.x = 0;\n}\n\n" +// anonymous-ns-type2-begin: +1:7 + x = Foo { 0 }; +// anonymous-ns-type2-end: -1:16 +// CHECK-ANON: "static Foo extracted() {\nreturn Foo { 0 };\n}\n\n" +} +// RUN: clang-refactor-test perform -action extract -selected=anonymous-ns-type1 -selected=anonymous-ns-type2 %s -std=c++11 | FileCheck --check-prefix=CHECK-ANON %s diff --git a/clang/test/Refactor/Extract/captured-variable-types.m b/clang/test/Refactor/Extract/captured-variable-types.m new file mode 100644 index 0000000000000..d764217144914 --- /dev/null +++ b/clang/test/Refactor/Extract/captured-variable-types.m @@ -0,0 +1,56 @@ + +@interface Interface + +@end + +@protocol Protocol + +@end + +int basicObjCTypes(Interface *ip, id p, id<Protocol> pp, Interface<Protocol> *ipp) { + return basicObjCTypes(ip, p, pp, ipp); +} +// CHECK1: "static int extracted(Interface *ip, Interface<Protocol> *ipp, id p, id<Protocol> pp) {\nreturn basicObjCTypes(ip, p, pp, ipp);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK1-NEXT: "extracted(ip, ipp, p, pp)" [[@LINE-3]]:10 -> [[@LINE-3]]:40 + +// RUN: clang-refactor-test perform -action extract -selected=%s:11:10-11:40 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:11:10-11:40 %s -x objective-c++ | FileCheck --check-prefix=CHECK1 %s + +typedef signed char BOOL; +#define YES __objc_yes +#define NO __objc_no + +int boolType(BOOL b) { + BOOL b2 = YES; + return boolType(b && b2 && YES && NO); +} +// CHECK2: "static int extracted(BOOL b, BOOL b2) {\nreturn boolType(b && b2 && YES && NO);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK2-NEXT: "extracted(b, b2)" [[@LINE-3]]:10 -> [[@LINE-3]]:40 + +// RUN: clang-refactor-test perform -action extract -selected=%s:25:10-25:40 %s | FileCheck --check-prefix=CHECK2 %s +; +int mutationOfObjCPointer(Interface *ip) { + return ip = 0, mutationOfObjCPointer(ip); +} +// CHECK3-C: "static int extracted(Interface **ip) {\nreturn *ip = 0, mutationOfObjCPointer(*ip);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK3-C-NEXT: "extracted(&ip)" [[@LINE-3]]:10 -> [[@LINE-3]]:43 +// CHECK3-CPP: "static int extracted(Interface *&ip) {\nreturn ip = 0, mutationOfObjCPointer(ip);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1 +// CHECK3-CPP-NEXT: "extracted(ip)" [[@LINE-5]]:10 -> [[@LINE-5]]:43 + +// RUN: clang-refactor-test perform -action extract -selected=%s:33:10-33:43 %s | FileCheck --check-prefix=CHECK3-C %s +// RUN: clang-refactor-test perform -action extract -selected=%s:33:10-33:43 %s -x objective-c++ | FileCheck --check-prefix=CHECK3-CPP %s + +void silenceStrongInARC() { + Interface *pointer; +// silence-strong-in-arc-begin: +1:1 + mutationOfObjCPointer(pointer); +// silence-strong-in-arc-end: +0:1 +// silence-strong2-in-arc-begin: +1:1 + pointer = 0; +// silence-strong2-in-arc-end: +0:1 + (void)pointer; +} +// CHECK-ARC: "static int extracted(Interface *pointer) {\nreturn mutationOfObjCPointer(pointer);\n}\n\n" +// CHECK-ARC: "static void extracted(Interface **pointer) {\n*pointer = 0;\n}\n\n" + +// RUN: clang-refactor-test perform -action extract -selected=silence-strong-in-arc -selected=silence-strong2-in-arc %s -fobjc-arc | FileCheck --check-prefix=CHECK-ARC %s diff --git a/clang/test/Refactor/Extract/disallowed-expressions.cpp b/clang/test/Refactor/Extract/disallowed-expressions.cpp new file mode 100644 index 0000000000000..522cb598b9e08 --- /dev/null +++ b/clang/test/Refactor/Extract/disallowed-expressions.cpp @@ -0,0 +1,50 @@ +void disallowExtractOfSimpleExpressions(int param) { + int var = param; + short var2 = (short)(var); + int x = 0; + const char *s = "Test"; + char c = 'C'; + double y = ((2.1)); + const char *f = __func__; +} + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:2:13-2:18 -selected=%s:2:14-2:15 -selected=%s:2:16-2:18 %s 2>&1 | FileCheck %s +// RUN: not clang-refactor-test initiate -action extract -selected=%s:3:16-3:28 -selected=%s:4:11-4:12 -selected=%s:5:19-5:25 %s 2>&1 | FileCheck %s +// RUN: not clang-refactor-test initiate -action extract -selected=%s:6:12-6:15 -selected=%s:7:14-7:21 -selected=%s:8:19-8:27 %s 2>&1 | FileCheck %s + +// CHECK: Failed to initiate the refactoring action (the selected expression is too simple)! + +void allowOperationsOnSimpleExpression(int x, int y) { + int z = x + y; + int zz = 0 + 1; + allowOperationsOnSimpleExpression(1, y); +} + +// RUN: clang-refactor-test initiate -action extract -selected=%s:18:11-18:16 %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: Initiated the 'extract' action at 18:11 -> 18:16 + +// RUN: clang-refactor-test initiate -action extract -selected=%s:19:12-19:17 %s | FileCheck --check-prefix=CHECK2 %s +// CHECK2: Initiated the 'extract' action at 19:12 -> 19:17 + +// RUN: clang-refactor-test initiate -action extract -selected=%s:20:37-20:41 %s | FileCheck --check-prefix=CHECK3 %s +// CHECK3: Initiated the 'extract' action at 20:3 -> 20:42 + +void defaultParameter(int x = 0 + 1) { + +} + +struct Struct { + int y = 22 + 21; + + Struct(int x) : y(x + 1) { } +}; + +int initializerExpression = 1 + 2; + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:32:31-32:36 -selected=%s:37:11-37:18 -selected=%s:39:21-39:26 -selected=%s:42:29-42:34 %s 2>&1 | FileCheck --check-prefix=NOT-IN-FUNC %s +// NOT-IN-FUNC: Failed to initiate the refactoring action (the selected expression is not in a function)! + +void disallowWholeFunctionBody() { + int x = 0; +} +// RUN: not clang-refactor-test initiate -action extract -selected=%s:47:34-49:2 %s 2>&1 | FileCheck --check-prefix=NOT-IN-FUNC %s diff --git a/clang/test/Refactor/Extract/extract-address-of-captured-variable.cpp b/clang/test/Refactor/Extract/extract-address-of-captured-variable.cpp new file mode 100644 index 0000000000000..c2d4a79e232d7 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-address-of-captured-variable.cpp @@ -0,0 +1,194 @@ + +void takesVoidPtr(void *x) { } +void takesPtr(int *x) { } +void takesPtrPtr(int **x) { } + +void addressOfVariableImpliesPossibleMutation(int x, int *ip) { + takesPtr(&x); +// CHECK1-C: (int *x) {\ntakesPtr(x);\n} +// CHECK1-CPP: (int &x) {\ntakesPtr(&x);\n} + takesVoidPtr(&x); +// CHECK1-C: (int *x) {\ntakesVoidPtr(x);\n} +// CHECK1-CPP: (int &x) {\ntakesVoidPtr(&x);\n} + &x; +// CHECK1-C: (int *x) {\nreturn x;\n} +// CHECK1-CPP: (int &x) {\nreturn &x;\n} + *(&x) = 0; +// CHECK1-C: extracted(int *x) {\n*(x) = 0;\n} +// CHECK1-CPP: extracted(int &x) {\n*(&x) = 0;\n} + takesPtrPtr(&ip); +// CHECK1-C: (int **ip) {\ntakesPtrPtr(ip);\n} +// CHECK1-CPP: (int *&ip) {\ntakesPtrPtr(&ip);\n} + takesPtr(ip); +// CHECK1-C: (int *ip) {\ntakesPtr(ip);\n} +// CHECK1-CPP: (int *ip) {\ntakesPtr(ip);\n} + takesVoidPtr(ip); +// CHECK1-C: (int *ip) {\ntakesVoidPtr(ip);\n} +// CHECK1-CPP: (int *ip) {\ntakesVoidPtr(ip);\n} + takesPtr(&((x))); +// CHECK1-C: (int *x) {\ntakesPtr(((x)));\n} +// CHECK1-CPP: (int &x) {\ntakesPtr(&((x)));\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:7:3-7:15 -selected=%s:10:3-10:19 -selected=%s:13:3-13:5 -selected=%s:16:3-16:12 -selected=%s:19:3-19:19 -selected=%s:22:3-22:15 -selected=%s:25:3-25:19 -selected=%s:28:3-28:19 %s -x c | FileCheck --check-prefix=CHECK1-C %s +// RUN: clang-refactor-test perform -action extract -selected=%s:7:3-7:15 -selected=%s:10:3-10:19 -selected=%s:13:3-13:5 -selected=%s:16:3-16:12 -selected=%s:19:3-19:19 -selected=%s:22:3-22:15 -selected=%s:25:3-25:19 -selected=%s:28:3-28:19 %s | FileCheck --check-prefix=CHECK1-CPP %s + +typedef struct { + int width, height; +} Rectangle; + +void takesStructPtr(Rectangle *sp) { } + +#ifdef __cplusplus + +void addressOfRef(int &x, int *&ip, Rectangle &r) { + takesPtr(&x); +// CHECK2: (int &x) {\ntakesPtr(&x);\n} + takesPtrPtr(&ip); +// CHECK2: (int *&ip) {\ntakesPtrPtr(&ip);\n} + takesStructPtr(&r); +// CHECK2: (Rectangle &r) {\ntakesStructPtr(&r);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:45:3-45:15 -selected=%s:47:3-47:19 -selected=%s:49:3-49:21 %s | FileCheck --check-prefix=CHECK2 %s + +#endif + +void addressOfArray(int x[]) { + takesPtrPtr(&x); +// CHECK3-C: (int **x) {\ntakesPtrPtr(x);\n} +// CHECK3-CPP: (int *&x) {\ntakesPtrPtr(&x);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:58:3-58:18 %s -x c | FileCheck --check-prefix=CHECK3-C %s +// RUN: clang-refactor-test perform -action extract -selected=%s:58:3-58:18 %s | FileCheck --check-prefix=CHECK3-CPP %s + +typedef struct { + Rectangle r; +} RectangleInStruct; + +void addressOfMember(Rectangle r, RectangleInStruct rs) { + takesPtr(&r.width); +// CHECK4-C: (Rectangle *r) {\ntakesPtr(&r->width);\n} +// CHECK4-CPP: (Rectangle &r) {\ntakesPtr(&r.width);\n} + takesPtr(&(rs).r.width); +// CHECK4-C: (RectangleInStruct *rs) {\ntakesPtr(&(rs)->r.width);\n} +// CHECK4-CPP: (RectangleInStruct &rs) {\ntakesPtr(&(rs).r.width);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:71:3-71:21 -selected=%s:74:3-74:26 %s -x c | FileCheck --check-prefix=CHECK4-C %s +// RUN: clang-refactor-test perform -action extract -selected=%s:71:3-71:21 -selected=%s:74:3-74:26 %s | FileCheck --check-prefix=CHECK4-CPP %s + +void takesConstPtr(const int *x) { } + +#ifdef __cplusplus + +void addressOfMember(const Rectangle &r) { + takesConstPtr(&r.width); +// CHECK5: (const Rectangle &r) {\ntakesConstPtr(&r.width);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:87:3-87:26 %s | FileCheck --check-prefix=CHECK5 %s + +class PrivateInstanceVariables { + int x; + Rectangle r; + + void method() { + takesPtr(&x); +// CHECK6: extracted(int &x) {\ntakesPtr(&x);\n} + takesStructPtr(&(r)); +// CHECK6: extracted(Rectangle &r) {\ntakesStructPtr(&(r));\n} + takesPtr(&((r).width)); +// CHECK6: extracted(Rectangle &r) {\ntakesPtr(&((r).width));\n} + } +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:98:5-98:17 -selected=%s:100:5-100:25 -selected=%s:102:5-102:27 %s | FileCheck --check-prefix=CHECK6 %s + +#endif + +void takesCVoidPtr(const void *x) { } +void takesCPtr(const int *x) { } +void takesCPtrPtr(int * const *x) { } +void takesBothPtrs(const int *x, int *y) {} +void addressForConstUseShouldPassAsConst(int x, int *ip) { + takesCPtr(&x); +// CHECK7-C: (const int *x) {\ntakesCPtr(x);\n} +// CHECK7-CPP: (const int &x) {\ntakesCPtr(&x);\n} + takesCVoidPtr((&(x))); +// CHECK7-C: (const int *x) {\ntakesCVoidPtr(((x)));\n} +// CHECK7-CPP: (const int &x) {\ntakesCVoidPtr((&(x)));\n} + takesCPtrPtr(&ip); +// CHECK7-C: (int *const *ip) {\ntakesCPtrPtr(ip);\n} +// CHECK7-CPP: (int *const &ip) {\ntakesCPtrPtr(&ip);\n} + takesCPtr(ip); +// CHECK7-C: (int *ip) {\ntakesCPtr(ip);\n} +// CHECK7-CPP: (int *ip) {\ntakesCPtr(ip);\n} + takesBothPtrs(&x, &x); +// CHECK7-C: (int *x) {\ntakesBothPtrs(x, x);\n} +// CHECK7-CPP: (int &x) {\ntakesBothPtrs(&x, &x);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:116:3-116:16 -selected=%s:119:3-119:22 -selected=%s:122:3-122:20 -selected=%s:125:3-125:16 -selected=%s:128:3-128:24 %s -x c | FileCheck --check-prefix=CHECK7-C %s +// RUN: clang-refactor-test perform -action extract -selected=%s:116:3-116:16 -selected=%s:119:3-119:22 -selected=%s:122:3-122:20 -selected=%s:125:3-125:16 -selected=%s:128:3-128:24 %s | FileCheck --check-prefix=CHECK7-CPP %s + +void addressOfConstUseAndMutation(int x) { + x = 0; + takesCPtr(&x); + x = 1; +} +// CHECK8: extracted(int &x) {\nx = 0;\n takesCPtr(&x);\n} +// CHECK8: extracted(int &x) {\ntakesCPtr(&x);\n x = 1;\n} + +// RUN: clang-refactor-test perform -action extract -selected=%s:137:3-138:16 -selected=%s:138:3-139:8 %s | FileCheck --check-prefix=CHECK8 %s + +void takesCStructPtr(const Rectangle *r) { } + +void constAddressOfMember(Rectangle r, RectangleInStruct rs) { + takesCStructPtr(&r); +// CHECK9-C: extracted(const Rectangle *r) {\ntakesCStructPtr(r);\n} +// CHECK9-CPP: extracted(const Rectangle &r) {\ntakesCStructPtr(&r);\n} + takesCPtr(&r.width); +// CHECK9-C: (const Rectangle *r) {\ntakesCPtr(&r->width);\n} +// CHECK9-CPP: (const Rectangle &r) {\ntakesCPtr(&r.width);\n} + takesCPtr((&(rs).r.height)); +// CHECK9-C: (const RectangleInStruct *rs) {\ntakesCPtr((&(rs)->r.height));\n} +// CHECK9-CPP: (const RectangleInStruct &rs) {\ntakesCPtr((&(rs).r.height));\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:149:3-149:22 -selected=%s:152:3-152:22 -selected=%s:155:3-155:30 %s -x c | FileCheck --check-prefix=CHECK9-C %s +// RUN: clang-refactor-test perform -action extract -selected=%s:149:3-149:22 -selected=%s:152:3-152:22 -selected=%s:155:3-155:30 %s | FileCheck --check-prefix=CHECK9-CPP %s + +#ifdef __cplusplus + +// RUN: clang-refactor-test perform -action extract -selected=%s:87:3-87:26 %s | FileCheck --check-prefix=CHECK5 %s + +class PrivateInstanceVariablesConstAddress { + int x; + Rectangle r; + + void method() { + takesCPtr(&x); +// CHECK10: extracted(const int &x) {\ntakesCPtr(&x);\n} + takesCStructPtr(&r); +// CHECK10: extracted(const Rectangle &r) {\ntakesCStructPtr(&r);\n} + } +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:172:5-172:18 -selected=%s:174:5-174:24 %s | FileCheck --check-prefix=CHECK10 %s + +#endif + +void rewriteToPtrWithDerefParensForArrow(Rectangle *r) { + int y = r->width; + r = 0; +// CHECK11: (Rectangle **r) {\nint y = (*r)->width;\n *r = 0;\n}\n\n" +} +// RUN: clang-refactor-test perform -action extract -selected=%s:184:3-185:8 %s -x c | FileCheck --check-prefix=CHECK11 %s + +void constAddressWithConditionalOperator(int x, int y) { + takesCPtr(&(x == 0 ? x : y)); +// CHECK12: (const int &x, const int &y) {\ntakesCPtr(&(x == 0 ? x : y));\n} +} +// RUN: clang-refactor-test perform -action extract -selected=%s:191:3-191:31 %s | FileCheck --check-prefix=CHECK12 %s diff --git a/clang/test/Refactor/Extract/extract-address-of-captured-variable.mm b/clang/test/Refactor/Extract/extract-address-of-captured-variable.mm new file mode 100644 index 0000000000000..9d6661cca8551 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-address-of-captured-variable.mm @@ -0,0 +1,44 @@ + +void takesVoidPtr(void *x) { } +void takesPtr(int *x) { } +void takesPtrPtr(int **x) { } + +#ifdef USECONST +#define CONST const +#else +#define CONST +#endif + +typedef struct { + int width, height; +} Rectangle; + +@interface I + +- (int)takesPtr:(CONST int *)x; ++ (int)takesPtr:(CONST int *)x; +- (int)takesVoidPtr:(CONST void *)x; +- (int)takesStructPtr:(CONST Rectangle *)r; + +@end + +void methodTakesPtr(I *i, int x, Rectangle r) { + [i takesPtr: &x]; +// CHECK1: extracted(I *i, int &x) {\nreturn [i takesPtr: &x];\n} +// CHECK2: extracted(I *i, const int &x) {\nreturn [i takesPtr: &x];\n} + [I takesPtr: (&x)]; +// CHECK1: extracted(int &x) {\nreturn [I takesPtr: (&x)];\n} +// CHECK2: extracted(const int &x) {\nreturn [I takesPtr: (&x)];\n} + [i takesVoidPtr: (&(x))]; +// CHECK1: extracted(I *i, int &x) {\nreturn [i takesVoidPtr: (&(x))];\n} +// CHECK2: extracted(I *i, const int &x) {\nreturn [i takesVoidPtr: (&(x))];\n} + [i takesStructPtr: &r]; +// CHECK1: extracted(I *i, Rectangle &r) {\nreturn [i takesStructPtr: &r];\n} +// CHECK2: extracted(I *i, const Rectangle &r) {\nreturn [i takesStructPtr: &r];\n} + [I takesPtr: &(r).width]; +// CHECK1: extracted(Rectangle &r) {\nreturn [I takesPtr: &(r).width];\n} +// CHECK2: extracted(const Rectangle &r) {\nreturn [I takesPtr: &(r).width];\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:26:3-26:19 -selected=%s:29:3-29:21 -selected=%s:32:3-32:27 -selected=%s:35:3-35:25 -selected=%s:38:3-38:27 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:26:3-26:19 -selected=%s:29:3-29:21 -selected=%s:32:3-32:27 -selected=%s:35:3-35:25 -selected=%s:38:3-38:27 %s -DUSECONST | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Extract/extract-before-comments.cpp b/clang/test/Refactor/Extract/extract-before-comments.cpp new file mode 100644 index 0000000000000..f20e0ab059ac4 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-before-comments.cpp @@ -0,0 +1,64 @@ + +// comment 1 +void extractBeforeComment1(int x) { + int y = x * x; +} +// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-4]]:1 + +/* comment 2 */ + +void extractBeforeComment2(int x) { + int y = x * x; +} +// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-5]]:1 + +/// comment 1 +/// +/// line 2 +void extractBeforeDocComment1(int x) { + int y = x * x; +} +// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-6]]:1 + +/** + * @brief extractBeforeDocComment2 + * @param x + */ +void extractBeforeDocComment2(int x) { + int y = x * x; +} +// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-7]]:1 + +// RUN: clang-refactor-test perform -action extract -selected=%s:4:11-4:16 -selected=%s:11:11-11:16 -selected=%s:19:11-19:16 -selected=%s:28:11-28:16 %s | FileCheck --check-prefix=CHECK1 %s + +/** + * @brief The AClass class + */ +class AClass { + + /// doc comment + int method(int x) { + return x * x; + } +// CHECK2: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-9]]:1 +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:41:12-41:17 %s | FileCheck --check-prefix=CHECK2 %s + +namespace { + +} // end anonymous namespace + +void afterBraceAfterComment() { // CHECK3: "static void extracted() {\nint x = 0;\n}\n\n" [[@LINE]]:1 +// after-brace-begin: +1:1 + int x = 0; +// after-brace-end: +0:1 +} // another trailing +// This is valid CHECK3: "static void extracted() {\nint x = 0;\n}\n\n" [[@LINE]]:1 +void inbetweenerTwoComments() { +// inbetween-begin: +1:1 + int x = 0; +// inbetween-end: +0:1 +} + +// RUN: clang-refactor-test perform -action extract -selected=after-brace -selected=inbetween %s | FileCheck --check-prefix=CHECK3 %s diff --git a/clang/test/Refactor/Extract/extract-capture-instance-variable.cpp b/clang/test/Refactor/Extract/extract-capture-instance-variable.cpp new file mode 100644 index 0000000000000..b98bc0a6a8afa --- /dev/null +++ b/clang/test/Refactor/Extract/extract-capture-instance-variable.cpp @@ -0,0 +1,73 @@ + +typedef struct { + int width, height; +} Rectangle; + +class PrivateInstanceVariables { + int x; + Rectangle r; + + int method() { + int y = x; + return r.width + r.height * x + y; + } +// CHECK1: (int x) {\nint y = x;\nreturn y;\n} +// CHECK1-NEXT: extracted(x) +// CHECK1: extracted(const Rectangle &r, int x) {\nint y = x;\n return r.width + r.height * x + y;\n} +// CHECK1-NEXT: extracted(r, x) +// CHECK1: extracted(const Rectangle &r, int x, int y) {\nreturn r.width + r.height * x + y;\n} +// CHECK1-NEXT: extracted(r, x, y) +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:11:5-11:14 -selected=%s:11:5-12:38 -selected=%s:12:12-12:38 %s | FileCheck --check-prefix=CHECK1 %s + +class PrivateInstanceVariablesExplicitThis { + int x; + + + int method(int y) { + y = this->x; + { + int x = (this)->x; + } + y = x; + x = 0; + this->x = 0; + y = (y == 0 ? this : (PrivateInstanceVariablesExplicitThis *)0) -> x; + } +// CHECK2: (const PrivateInstanceVariablesExplicitThis &object, int &y) {\ny = object.x;\n {\n int x = (object).x;\n }\n y = object.x;\n} +// CHECK2-NEXT: extracted(*this, y) +// CHECK2: extracted(const PrivateInstanceVariablesExplicitThis &object) {\nint x = (object).x;\n} +// CHECK2-NEXT: extracted(*this) +// CHECK2: (PrivateInstanceVariablesExplicitThis &object, int &y) {\ny = object.x;\n {\n int x = (object).x;\n }\n y = object.x;\n object.x = 0;\n} +// CHECK2: (PrivateInstanceVariablesExplicitThis &object) {\nobject.x = 0;\n} +// CHECK2: (PrivateInstanceVariablesExplicitThis &object, int &y) {\ny = (y == 0 ? &object : (PrivateInstanceVariablesExplicitThis *)0) -> x;\n} + +// RUN: clang-refactor-test perform -action extract -selected=%s:29:5-33:10 -selected=%s:31:7-31:24 -selected=%s:29:5-34:10 -selected=%s:35:5-35:16 -selected=%s:36:5-36:73 %s | FileCheck --check-prefix=CHECK2 %s +}; + +class PublicInstanceVariables { int private_; +public: + int x; + Rectangle r; + + void method() { + int y; y = x; + x = 0; + int z = r.width + r.height * x + y; + this->x = (this)->r.height + (y == 0 ? this : (PublicInstanceVariables *)0) -> x; + x = private_; + } +// CHECK3: (const PublicInstanceVariables &object, int &y) {\ny = object.x;\n} +// CHECK3: (PublicInstanceVariables &object, int &y) {\ny = object.x;\n object.x = 0;\n} +// CHECK3: (PublicInstanceVariables &object) {\nobject.x = 0;\n} +// CHECK3: (const PublicInstanceVariables &object, int y) {\nint z = object.r.width + object.r.height * object.x + y;\n} +// CHECK3: (PublicInstanceVariables &object, int y) {\nobject.x = (object).r.height + (y == 0 ? &object : (PublicInstanceVariables *)0) -> x;\n} +// CHECK3: (PublicInstanceVariables &object, int private_) {\nobject.x = object.private_;\n} + void constMethod() const { + const_cast<PublicInstanceVariables *>(this)->x = 2; + } +// CHECK3: (const PublicInstanceVariables &object) {\nconst_cast<PublicInstanceVariables *>(&object)->x = 2;\n} +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:55:12-55:17 -selected=%s:55:12-56:10 -selected=%s:56:5-56:10 -selected=%s:57:5-57:39 -selected=%s:58:5-58:85 -selected=%s:59:5-59:17 -selected=%s:68:5-68:55 %s | FileCheck --check-prefix=CHECK3 %s diff --git a/clang/test/Refactor/Extract/extract-capture-self.m b/clang/test/Refactor/Extract/extract-capture-self.m new file mode 100644 index 0000000000000..4f8375a194d07 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-capture-self.m @@ -0,0 +1,56 @@ + +@interface AClass { + int ivar1; +} + +@property int prop; + +- (int)instanceMethod; ++ (int)classMethod; + +@end + +@implementation AClass { + int ivar2; +} + +- (int)instanceMethod { + ivar2 = 0; + ivar1 = 0; + self->ivar2 = 0; + self.prop = 0; + int x = self->ivar1; + int y = self.prop; + [self instanceMethod]; + [AClass classMethod]; + return 0; +} +// CHECK1: (AClass *object) {\nobject->ivar2 = 0;\n} +// CHECK1: (AClass *object) {\nobject->ivar2 = 0;\n} +// CHECK1: (AClass *object) {\nobject.prop = 0;\n}\n\n" +// CHECK1: (AClass *object) {\nint y = object.prop;\n} +// CHECK1: (AClass *object) {\nreturn [object instanceMethod];\n} +// CHECK1: (AClass *object) {\nobject->ivar2 = 0;\n object->ivar1 = 0;\n object->ivar2 = 0;\n object.prop = 0;\n int x = object->ivar1;\n int y = object.prop;\n [object instanceMethod];\n [AClass classMethod];\n}\n\n" +// CHECK1: () {\nreturn [AClass classMethod];\n}\n\n" + +// RUN: clang-refactor-test perform -action extract -selected=%s:18:3-18:12 -selected=%s:20:3-20:18 -selected=%s:21:3-21:16 -selected=%s:23:3-23:20 -selected=%s:24:3-24:24 -selected=%s:18:3-25:23 -selected=%s:25:3-25:23 %s | FileCheck --check-prefix=CHECK1 %s + ++ (int)classMethod { + int x = self.classMethod; + [self classMethod]; +} + +// CHECK2: () {\nint x = AClass.classMethod;\n} +// CHECK2: () {\nreturn [AClass classMethod];\n} + +// RUN: clang-refactor-test perform -action extract -selected=%s:39:3-39:27 -selected=%s:40:3-40:21 %s | FileCheck --check-prefix=CHECK2 %s + +- (void)rhsSelfCaptureAndRewrite:(AClass *)i { // CHECK3: "static void extracted(AClass *object, AClass *i) {\ni.prop= object.prop;\n}\n\n" +// rhs-prop-begin: +1:3 + i.prop= self.prop; +// rhs-prop-end: -1:21 +} + +// RUN: clang-refactor-test perform -action extract -selected=rhs-prop %s | FileCheck --check-prefix=CHECK3 %s + +@end diff --git a/clang/test/Refactor/Extract/extract-capture-static-var.cpp b/clang/test/Refactor/Extract/extract-capture-static-var.cpp new file mode 100644 index 0000000000000..097e176ba9bea --- /dev/null +++ b/clang/test/Refactor/Extract/extract-capture-static-var.cpp @@ -0,0 +1,12 @@ + + +void captureStaticVars() { + static int x; + int y = x; + x += 1; +// CHECK1: extracted(int x) {\nint y = x;\n}\n\n" +// CHECK1: extracted(int &x) {\nx += 1;\n} +// CHECK1: extracted() {\nstatic int x;\n int y = x;\n x += 1;\n}\n\n" +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:5:3-5:12 -selected=%s:6:3-6:9 -selected=%s:4:3-6:9 %s | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/Extract/extract-capture-super.m b/clang/test/Refactor/Extract/extract-capture-super.m new file mode 100644 index 0000000000000..0b18143ab3f80 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-capture-super.m @@ -0,0 +1,42 @@ +// RUN: clang-refactor-test perform -action extract -selected=super-msg -selected=super-prop -selected=super-self -selected=super-class-msg -selected=super-class-prop %s | FileCheck %s + +@interface BaseClass + +@property int prop; + +- (int)instanceMethod; ++ (void)classMethod; + +@property(class) int classProp; + +@end + +@interface SubClass: BaseClass + +@end + +@implementation SubClass + +- (void)method { + // super-msg-begin: +1:1 + [super instanceMethod]; // CHECK: extracted(BaseClass *superObject) {\n[superObject instanceMethod];\n} + // super-msg-end: +0:1 // CHECK: extracted(super.self) + // super-prop-begin: +1:11 + int x = super.prop; // CHECK: extracted(BaseClass *superObject) {\nreturn superObject.prop;\n} + // super-prop-end: -1:21 // CHECK: extracted(super.self) + // super-self-begin: +1:1 + int y = self.prop; // CHECK: extracted(SubClass *object, BaseClass *superObject) {\nint y = object.prop;\n int z = superObject.prop;\n} + int z = super.prop; // CHECK: extracted(self, super.self); + // super-self-end: +0:1 +} + ++ (void)classMethod { + // super-class-msg-begin: +1:1 + [super classMethod]; // CHECK: extracted() {\n[BaseClass classMethod];\n} + // super-class-msg-end: +0:1 // CHECK: extracted() + // super-class-prop-begin: +1:9 + (void)super.classProp; // CHECK: extracted() {\nreturn BaseClass.classProp;\n} + // super-class-prop-end: -1:24 // CHECK: extracted() +} + +@end diff --git a/clang/test/Refactor/Extract/extract-capture-this.cpp b/clang/test/Refactor/Extract/extract-capture-this.cpp new file mode 100644 index 0000000000000..cb15a5e3fdea0 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-capture-this.cpp @@ -0,0 +1,66 @@ + +class AClassWithMethods { + void method() { + } + void constMethod() const { + } + int operator << (int x) const { return x; } + int operator >> (int x) { return x; } + + void toBeExtracted() { + method(); + constMethod(); + *this << 2; + *(this) >> 2; + this->constMethod(); + (*(this)).constMethod(); + } +// CHECK1: (AClassWithMethods &object) {\nobject.method();\n} +// CHECK1: (const AClassWithMethods &object) {\nobject.constMethod();\n} +// CHECK1: (AClassWithMethods &object) {\nobject.method();\n object.constMethod();\n} +// CHECK1: (const AClassWithMethods &object) {\nreturn object << 2;\n} +// CHECK1: (AClassWithMethods &object) {\nreturn (object) >> 2;\n} +// CHECK1: (const AClassWithMethods &object) {\nobject.constMethod();\n} +// CHECK1: (const AClassWithMethods &object) {\n((object)).constMethod();\n} + + void toBeExtracted2(); +}; +// RUN: clang-refactor-test perform -action extract -selected=%s:11:5-11:13 -selected=%s:12:5-12:17 -selected=%s:11:5-12:17 -selected=%s:13:5-13:14 -selected=%s:14:5-14:16 -selected=%s:15:5-15:24 -selected=%s:16:5-16:28 %s | FileCheck --check-prefix=CHECK1 %s + +void takesRef(AClassWithMethods &object) {} +void takesConstRef(const AClassWithMethods &object) {} +void takesPtr(AClassWithMethods *object) {} +void takesConstPtr(const AClassWithMethods *object) {} + +void AClassWithMethods::toBeExtracted2() { + takesRef(*this); + takesConstRef((*(this))); + takesPtr(this); + takesConstPtr((this)); + takesConstPtr(false ? this : (AClassWithMethods*)0); +} +// CHECK2: (AClassWithMethods &object) {\ntakesRef(object);\n} +// CHECK2: (const AClassWithMethods &object) {\ntakesConstRef(((object)));\n} +// CHECK2: (AClassWithMethods &object) {\ntakesPtr(&object);\n} +// CHECK2: (const AClassWithMethods &object) {\ntakesConstPtr((&object));\n} +// CHECK2: (AClassWithMethods &object) {\ntakesConstPtr(false ? &object : (AClassWithMethods*)0);\n} + +// RUN: clang-refactor-test perform -action extract -selected=%s:36:3-36:18 -selected=%s:37:3-37:27 -selected=%s:38:3-38:17 -selected=%s:39:3-39:24 -selected=%s:40:3-40:54 %s | FileCheck --check-prefix=CHECK2 %s + +#ifdef USECONST +#define CONST const +#else +#define CONST +#endif + +class FallbackToMethodConstness { + int getter() const { return 0; } + int method(int x, FallbackToMethodConstness *other) CONST { + return (x == 0 ? this : other)->getter(); + } +// CHECK3: (FallbackToMethodConstness &object, FallbackToMethodConstness *other, int x) {\nreturn (x == 0 ? &object : other)->getter();\n} +// CHECK3-CONST: (const FallbackToMethodConstness &object, FallbackToMethodConstness *other, int x) {\nreturn (x == 0 ? &object : other)->getter();\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:59:5-59:45 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:59:5-59:45 %s -DUSECONST | FileCheck --check-prefix=CHECK3-CONST %s diff --git a/clang/test/Refactor/Extract/extract-capture-used-after-extraction.cpp b/clang/test/Refactor/Extract/extract-capture-used-after-extraction.cpp new file mode 100644 index 0000000000000..03fff5c67d94a --- /dev/null +++ b/clang/test/Refactor/Extract/extract-capture-used-after-extraction.cpp @@ -0,0 +1,214 @@ + +void takesInt(int x); + +typedef struct { int width; int height; } Rectangle; + +#ifdef USEINIT1 +#define INIT1 r.width = 0; { int r, y; } +#else +#define INIT1 { int r, y; } +#endif + +void extractCaptureUsedAfterSimple(int x) { + Rectangle r; + INIT1; + int y = x * x; +#ifdef USEAFTER1 + takesInt(y); +#endif +#ifdef USEAFTER2 + takesInt(r.height); +#endif +#ifdef USEAFTER3 + r.width = 0; +#endif +#ifdef USEAFTER4 + y += 1; +#endif +} +// CHECK1: "static void extracted(int x) {\nRectangle r;\n INIT1;\n int y = x * x;\n}\n\n" +// CHECK1-NEXT: "extracted(x);" +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s | FileCheck --check-prefix=CHECK1 %s + +// CHECK2: "static void extracted(Rectangle &r, int x, int &y) {\n\n INIT1;\n y = x * x;\n}\n\n" +// CHECK2-NEXT: "Rectangle r;\nint y;\nextracted(r, x, y);" +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER1 -DUSEAFTER2 | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER1 -DUSEAFTER2 -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK2 %s + +// CHECK3: "static void extracted(int x, int &y) {\n\n INIT1;\n y = x * x;\n}\n\n" +// CHECK3-NEXT: "Rectangle r;\nint y;\nextracted(x, y);" +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER1 -DUSEAFTER2 | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER1 -DUSEAFTER2 -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK3 %s + +// CHECK4: "static void extracted(int x, int &y) {\nRectangle r;\n INIT1;\n y = x * x;\n}\n\n" +// CHECK4-NEXT: "int y;\nextracted(x, y);" +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER1 | FileCheck --check-prefix=CHECK4 %s + +// CHECK5: "static void extracted(Rectangle &r, int x) {\n\n INIT1;\n int y = x * x;\n}\n\n" +// CHECK5-NEXT: "Rectangle r;\nextracted(r, x);" +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER3 | FileCheck --check-prefix=CHECK5 %s + +// CHECK6: "static void extracted(int x) {\n\n INIT1;\n int y = x * x;\n}\n\n" +// CHECK6-NEXT: "Rectangle r;\nextracted(x);" +// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER3 | FileCheck --check-prefix=CHECK6 %s + +void extractCaptureAfterUseMultipleDecls() { +#ifdef MULTIPLE_DECL1 + int x = 1, y = 2, z = 3; { int x, y, z; }; +#endif +#ifdef MULTIPLE_DECL2 + int x, y = 2, *z; { int y; } +#endif +#ifdef MULTIPLE_DECL3 + int x = 1, y, * z = 0, a, b = {0}; { int a; } +#endif +#ifdef USEX + x; +#endif +#ifdef USEY + y; +#endif +#ifdef USEZ + z; +#endif +#ifdef USEA + a; +#endif +#ifdef USEB + b; +#endif +} + +// CHECK7: "static void extracted() {\nint x = 1, y = 2, z = 3;\n}\n\n" +// CHECK7-NEXT; "extracted();" +// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:26 %s -DMULTIPLE_DECL1 | FileCheck --check-prefix=CHECK7 %s +// CHECK8: "static void extracted(int &x, int &y, int &z) {\nx = 1; y = 2; z = 3;\n}\n\n" +// CHECK8-NEXT: "int x;\nint y;\nint z;\nextracted(x, y, z);" +// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:26 %s -DMULTIPLE_DECL1 -DUSEX -DUSEY -DUSEZ | FileCheck --check-prefix=CHECK8 %s +// CHECK9: "static void extracted(int &x) {\nx = 1; int y = 2; int z = 3; { int x, y, z; }\n}\n\n" +// CHECK9-NEXT: "int x;\nextracted(x);" +// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:44 %s -DMULTIPLE_DECL1 -DUSEX | FileCheck --check-prefix=CHECK9 %s +// CHECK10: "static void extracted(int &y) {\nint x = 1; y = 2; int z = 3; { int x, y, z; }\n}\n\n" +// CHECK10-NEXT: "int y;\nextracted(y);" +// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:44 %s -DMULTIPLE_DECL1 -DUSEY | FileCheck --check-prefix=CHECK10 %s +// CHECK11: "static void extracted(int &z) {\nint x = 1; int y = 2; z = 3; { int x, y, z; }\n}\n\n" +// CHECK11-NEXT: "int z;\nextracted(z);" +// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:44 %s -DMULTIPLE_DECL1 -DUSEZ | FileCheck --check-prefix=CHECK11 %s + +// CHECK12: "static void extracted() {\nint x, y = 2, *z;\n}\n\n" +// CHECK12-NEXT: "extracted();" +// RUN: clang-refactor-test perform -action extract -selected=%s:62:3-62:19 %s -DMULTIPLE_DECL2 | FileCheck --check-prefix=CHECK12 %s +// CHECK13: "static void extracted(int &y) {\ny = 2;\n}\n\n" +// CHECK13-NEXT: "int x;\nint y;\nint * z;\nextracted(y);" +// RUN: clang-refactor-test perform -action extract -selected=%s:62:3-62:19 %s -DMULTIPLE_DECL2 -DUSEX -DUSEY -DUSEZ | FileCheck --check-prefix=CHECK13 %s +// CHECK14: "static void extracted(int &y) {\nint x; y = 2; int * z; { int y; }\n}\n\n" +// CHECK14-NEXT: "int y;\nextracted(y);" +// RUN: clang-refactor-test perform -action extract -selected=%s:62:3-62:31 %s -DMULTIPLE_DECL2 -DUSEY | FileCheck --check-prefix=CHECK14 %s + +// CHECK15: "static void extracted() {\nint x = 1, y, * z = 0, a, b = {0};\n}\n\n" +// CHECK15-NEXT: "extracted();" +// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:36 %s -DMULTIPLE_DECL3 | FileCheck --check-prefix=CHECK15 %s +// CHECK16: "static void extracted(int &x, int *&z) {\nx = 1; z = 0; int a; int b = {0};\n}\n\n" +// CHECK16-NEXT: "int x;\nint y;\nint * z;\nextracted(x, z);" +// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:36 %s -DMULTIPLE_DECL3 -DUSEX -DUSEY -DUSEZ | FileCheck --check-prefix=CHECK16 %s +// CHECK17: "static void extracted(int &b, int &x, int *&z) {\nx = 1; z = 0; b = {0};\n}\n\n" +// CHECK17-NEXT: "int x;\nint y;\nint * z;\nint a;\nint b;\nextracted(b, x, z);" +// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:36 %s -DMULTIPLE_DECL3 -DUSEX -DUSEY -DUSEZ -DUSEA -DUSEB | FileCheck --check-prefix=CHECK17 %s +// CHECK18: "static void extracted() {\nint x = 1; int y; int * z = 0; int b = {0}; { int a; }\n}\n\n" +// CHECK18-NEXT: "int a;\nextracted();" +// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:48 %s -DMULTIPLE_DECL3 -DUSEA | FileCheck --check-prefix=CHECK18 %s + +#define ONE 1 + +void preserveInitExpressionText() { + int a = ONE; + int x = ONE, y = ONE; + a, y; +} + +// CHECK19: "static void extracted(int &a, int &y) {\na = ONE;\n int x = ONE; y = ONE;\n}\n\n +// CHECK19-NEXT: "int a;\nint y;\nextracted(a, y);" +// RUN: clang-refactor-test perform -action extract -selected=%s:126:3-127:23 %s | FileCheck --check-prefix=CHECK19 %s + +class Construct { +public: + Construct(); + Construct(int x, int y); +}; + +void handleConstruct() { + Construct a(1, 2); + Construct b(3, 4), c(5, 6); + a, c; +} +// CHECK20: "static void extracted(Construct &a, Construct &c) {\na = Construct(1, 2);\n Construct b(3, 4); c = Construct(5, 6);\n}\n\n" +// CHECK20-NEXT: "Construct a;\nConstruct c;\nextracted(a, c);" +// RUN: clang-refactor-test perform -action extract -selected=%s:142:3-143:29 %s | FileCheck --check-prefix=CHECK20 %s + +struct Construct2 { + int x, y; +}; + +void handleConstruct2() { + Construct2 a = {1, 2}; + Construct2 b = {3, 4}, c = {5, 6}; + a, c; +} +// CHECK21: "static void extracted(Construct2 &a, Construct2 &c) {\na = {1, 2};\n Construct2 b = {3, 4}; c = {5, 6};\n}\n\n" +// CHECK21-NEXT: "Construct2 a;\nConstruct2 c;\nextracted(a, c);" +// RUN: clang-refactor-test perform -action extract -selected=%s:155:3-156:29 %s -std=c++11 | FileCheck --check-prefix=CHECK21 %s + +class Construct3 { +public: + Construct3(); + Construct3(int x); +}; + +void handleConstruct3() { + Construct3 a = 1; + Construct3 b = 2, c = 3 + 3; + a, c; +} +// CHECK22: "static void extracted(Construct3 &a, Construct3 &c) {\na = 1;\n Construct3 b = 2; c = 3 + 3;\n}\n\n" +// CHECK22-NEXT: "Construct3 a;\nConstruct3 c;\nextracted(a, c);" +// RUN: clang-refactor-test perform -action extract -selected=%s:170:3-171:26 %s -std=c++11 | FileCheck --check-prefix=CHECK22 %s + +void handleConstruct2InitList() { + Construct2 a { 5, 6 }; + Construct2 b { 1, 2 }, c { 3, 4 }; + a, c; +} +// CHECK23: "static void extracted(Construct2 &a, Construct2 &c) {\na = { 5, 6 };\n Construct2 b = { 1, 2 }; c = { 3, 4 };\n}\n\n" +// CHECK23-NEXT: "Construct2 a;\nConstruct2 c;\nextracted(a, c);" +// RUN: clang-refactor-test perform -action extract -selected=%s:179:3-180:36 %s -std=c++11 | FileCheck --check-prefix=CHECK23 %s + +void onlyOneUsedAfterExtractionIsReturned() { + const int x = 0; + x; + Construct a(1, 2); + a; +} +// CHECK24: "static int extracted() {\nconst int x = 0;\nreturn x;\n}\n\n" +// CHECK24-NEXT: "const int x = extracted();" +// CHECK24: "static Construct extracted() {\nConstruct a(1, 2);\nreturn a;\n}\n\n" +// CHECK24-NEXT: "Construct a = extracted();" + +int avoidReturningWhenReturnUsed() { + int x = 0; + if (x != 0) { return 22; } + x; +} +// CHECK24: "static int extracted(int &x) {\nx = 0;\n if (x != 0) { return 22; }\n}\n\n" +// CHECK24-NEXT: "int x;\nextracted(x);" + +void returnOnlyWhenReturnReturnsNothing() { + int x = 0; + if (x != 0) { return; } + x; +} +// CHECK24: "static int extracted() {\nint x = 0;\n if (x != 0) { return x; }\nreturn x;\n}\n\n" +// CHECK24-NEXT: "int x = extracted();" + +// RUN: clang-refactor-test perform -action extract -selected=%s:188:3-188:18 -selected=%s:190:3-190:20 -selected=%s:199:3-200:29 -selected=%s:207:3-208:26 %s -std=c++11 | FileCheck --check-prefix=CHECK24 %s diff --git a/clang/test/Refactor/Extract/extract-capture-used-after-extraction.m b/clang/test/Refactor/Extract/extract-capture-used-after-extraction.m new file mode 100644 index 0000000000000..c115babbf8622 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-capture-used-after-extraction.m @@ -0,0 +1,25 @@ +void takesInt(int); +void passByPointer() { + int x = 1; + int y = 0, z = 2; + takesInt(x); + z = 2; + x, z; +} +// CHECK1: "static void extracted(int *x, int *z) {\n*x = 1;\n int y = 0; *z = 2;\n takesInt(*x);\n *z = 2;\n}\n\n" +// CHECK1-NEXT: "int x;\nint z;\nextracted(&x, &z)" +// RUN: clang-refactor-test perform -action extract -selected=%s:3:3-6:8 %s | FileCheck --check-prefix=CHECK1 %s + +typedef struct { int width; int height; } Rectangle; + +void handleStructInit() { + Rectangle a = { 5, 6 }; + Rectangle b = { 1, 2 }, c = { 3, 4 }; + takesInt(a.width); + c.height = 10; + a, c; +} +// The produced code is invalid but we can let the user deal with it: +// CHECK2: "static void extracted(Rectangle *a, Rectangle *c) {\n*a = { 5, 6 };\n Rectangle b = { 1, 2 }; *c = { 3, 4 };\n takesInt(a->width);\n c->height = 10;\n}\n\n" 15:1 -> 15:1 +// CHECK2: "Rectangle a;\nRectangle c;\nextracted(&a, &c)" +// RUN: clang-refactor-test perform -action extract -selected=%s:16:3-19:16 %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Extract/extract-expression.cpp b/clang/test/Refactor/Extract/extract-expression.cpp new file mode 100644 index 0000000000000..56b26df8487b6 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-expression.cpp @@ -0,0 +1,60 @@ + +struct Rectangle { int width, height; }; + +int sumArea(Rectangle *rs, int count) { + int sum = 0; + for (int i = 0; i < count; ++i) { + Rectangle r = rs[i]; + sum += r.width * r.height; + } + return sum; +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:8:12-8:30 %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: "static int extracted(const Rectangle &r) {\nreturn r.width * r.height;\n}\n\n" 4:1 -> 4:1 +// CHECK1-NEXT: "extracted(r)" 8:12 -> 8:30 +; +void extractFullExpressionIfPartiallySelected(const Rectangle &r) { + int area = r.width * r.height; +} +// CHECK2: "static int extracted(const Rectangle &r) {\nreturn r.width * r.height;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK2-NEXT: "extracted(r)" [[@LINE-3]]:14 -> [[@LINE-3]]:32 + +// RUN: clang-refactor-test perform -action extract -selected=%s:18:20-18:25 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:18:15-18:32 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:18:14-18:30 %s | FileCheck --check-prefix=CHECK2 %s +; +int extractFullMultipleCandidates(const Rectangle &r1) { + int y = r1.width - r1.width * r1.height; +} +// CHECK3-1: "static int extracted(const Rectangle &r1) {\nreturn - r1.width * r1.height;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK3-1-NEXT: "+ extracted(r1)" [[@LINE-3]]:20 -> [[@LINE-3]]:42 +// CHECK3-2: "static int extracted(const Rectangle &r1) {\nreturn r1.width - r1.width * r1.height;\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1 +// CHECK3-2-NEXT: "extracted(r1)" [[@LINE-5]]:11 -> [[@LINE-5]]:42 + +// RUN: clang-refactor-test perform -action extract -selected=%s:28:20-28:42 %s | FileCheck --check-prefix=CHECK3-1 %s +// RUN: clang-refactor-test perform -action extract -candidate 0 -selected=%s:28:20-28:42 %s | FileCheck --check-prefix=CHECK3-1 %s +// RUN: clang-refactor-test perform -action extract -candidate 1 -selected=%s:28:20-28:42 %s | FileCheck --check-prefix=CHECK3-2 %s + +// RUN: not clang-refactor-test perform -action extract -candidate 2 -selected=%s:28:20-28:42 %s 2>&1 | FileCheck --check-prefix=CHECK-CAND-FAIL %s +// CHECK-CAND-FAIL: failed to select the refactoring candidate +; +int extractFullMultipleCandidatesCaptureJustExtractedVariables( + const Rectangle &r1, const Rectangle &r2) { + return r1.width - r2.width * r2.height; +} +// CHECK4: "static int extracted(const Rectangle &r2) {\nreturn - r2.width * r2.height;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK4-NEXT: "+ extracted(r2)" [[@LINE-3]]:19 -> [[@LINE-3]]:41 + +// RUN: clang-refactor-test perform -action extract -candidate 0 -selected=%s:44:19-44:41 %s | FileCheck --check-prefix=CHECK4 %s + +// Even when the expression result is unused statement, we still want to extract +// it as an expression. +; +void extractStatementExpression(const Rectangle &r) { + r.width * r.height; +} +// CHECK5: "static int extracted(const Rectangle &r) {\nreturn r.width * r.height;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK5-NEXT: "extracted(r)" [[@LINE-3]]:3 -> [[@LINE-3]]:21 + +// RUN: clang-refactor-test perform -action extract -selected=%s:55:3-55:21 %s | FileCheck --check-prefix=CHECK5 %s diff --git a/clang/test/Refactor/Extract/extract-from-class.cpp b/clang/test/Refactor/Extract/extract-from-class.cpp new file mode 100644 index 0000000000000..29d9a0041050b --- /dev/null +++ b/clang/test/Refactor/Extract/extract-from-class.cpp @@ -0,0 +1,45 @@ + +#ifdef MULTIPLE +class OuterClass { +#endif + +class AClass { + + int method(int x) { + return x + x * 2; + } +// CHECK1: "static int extracted(int x) {\nreturn x + x * 2;\n}\n\n" [[@LINE-5]]:1 +// CHECK2: "static int extracted(int x) {\nreturn x + x * 2;\n}\n\n" [[@LINE-9]]:1 +; + AClass(int x) { + int y = x * 1; + } +// CHECK1: "static int extracted(int x) {\nreturn x * 1;\n}\n\n" [[@LINE-11]]:1 +// CHECK2: "static int extracted(int x) {\nreturn x * 1;\n}\n\n" [[@LINE-15]]:1 + ~AClass() { + int x = 0 + 4; + } +// CHECK1: "static int extracted() {\nreturn 0 + 4;\n}\n\n" [[@LINE-16]]:1 +// CHECK2: "static int extracted() {\nreturn 0 + 4;\n}\n\n" [[@LINE-20]]:1 +; + int operator +(int x) { + return x + x * 3; + } +// CHECK1: "static int extracted(int x) {\nreturn x + x * 3;\n}\n\n" [[@LINE-22]]:1 +// CHECK2: "static int extracted(int x) {\nreturn x + x * 3;\n}\n\n" [[@LINE-26]]:1 +; + void otherMethod(int x); +}; + +#ifdef MULTIPLE +} +#endif + +// RUN: clang-refactor-test perform -action extract -selected=%s:9:12-9:21 -selected=%s:15:13-15:18 -selected=%s:20:13-20:18 -selected=%s:26:12-26:21 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:9:12-9:21 -selected=%s:15:13-15:18 -selected=%s:20:13-20:18 -selected=%s:26:12-26:21 %s -DMULTIPLE | FileCheck --check-prefix=CHECK2 %s +; +void AClass::otherMethod(int x) { + int y = x * x + 10; +} +// CHECK3: "static int extracted(int x) {\nreturn x * x + 10;\n}\n\n" [[@LINE-3]]:1 +// RUN: clang-refactor-test perform -action extract -selected=%s:42:11-42:21 %s | FileCheck --check-prefix=CHECK3 %s diff --git a/clang/test/Refactor/Extract/extract-header-inline.h b/clang/test/Refactor/Extract/extract-header-inline.h new file mode 100644 index 0000000000000..a323a1164a027 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-header-inline.h @@ -0,0 +1,7 @@ +// RUN: clang-refactor-test perform -action extract -selected=extract %s | FileCheck %s +; +void extractInline(int x) { // CHECK: "inline int extracted(int x) {\nreturn x + 1;\n}\n\n" [[@LINE]]:1 +// extract-begin: +1:11 + int y = x + 1; +// extract-end: -1:16 +} diff --git a/clang/test/Refactor/Extract/extract-initiate.cpp b/clang/test/Refactor/Extract/extract-initiate.cpp new file mode 100644 index 0000000000000..3ddbc9fc63dd2 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-initiate.cpp @@ -0,0 +1,150 @@ +struct Rectangle { int width, height; }; + +int sumArea(Rectangle *rs, int count) { + int sum = 0; + for (int i = 0; i < count; ++i) { + Rectangle r = rs[i]; + sum += r.width * r.height; + } + return sum; +} + +// RUN: clang-refactor-test list-actions -at=%s:7:30 -selected=%s:7:12-7:30 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Extract Function + +// Ensure the an entire expression can be extracted: + +// RUN: clang-refactor-test initiate -action extract -selected=%s:7:12-7:30 %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: Initiated the 'extract' action at 7:12 -> 7:30 + +// Ensure that an expression can be extracted even when it's not fully selected: + +// RUN: clang-refactor-test initiate -action extract -selected=%s:7:13-7:30 -selected=%s:7:18-7:30 -selected=%s:7:20-7:30 -selected=%s:7:19-7:30 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action extract -selected=%s:7:12-7:29 -selected=%s:7:12-7:23 -selected=%s:7:12-7:21 -selected=%s:7:12-7:22 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action extract -selected=%s:7:19-7:22 -selected=%s:7:20-7:21 -selected=%s:7:15-7:25 %s | FileCheck --check-prefix=CHECK1 %s + +// Ensure that the action isn't allowed be when no expression is selected: + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:1:1-1:5 -selected=%s:2:1-2:1 -selected=%s:3:1-3:38 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// RUN: not clang-refactor-test initiate -action extract -at=%s:1:1 -in=%s:3:1-38 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO: Failed to initiate the refactoring action +// CHECK-NO-NOT: Initiated the 'extract' action + +int multipleCandidates(Rectangle &r1, Rectangle &r2) { + int x = r1.width + r1.height; // CHECK2-SINGLE: Initiated the 'extract' action at [[@LINE]]:11 -> [[@LINE]]:31 + int y = r1.width - r2.width * r2.height; +} + +// RUN: clang-refactor-test initiate -action extract -selected=%s:34:20-34:31 -selected=%s:34:19-34:23 %s | FileCheck --check-prefix=CHECK2 %s +// CHECK2: Initiated the 'extract' action with multiple candidates: +// CHECK2-NEXT: + r1.height +// CHECK2-NEXT: r1.width + r1.height +// RUN: clang-refactor-test initiate -action extract -selected=%s:34:20-34:21 %s | FileCheck --check-prefix=CHECK2-SINGLE %s + +// RUN: clang-refactor-test initiate -action extract -selected=%s:35:19-35:42 %s | FileCheck --check-prefix=CHECK3 %s +// CHECK3: Initiated the 'extract' action with multiple candidates: +// CHECK3-NEXT: - r2.width * r2.height +// CHECK3-NEXT: r1.width - r2.width * r2.height + +void trimWhitespaceAndSemiColons(const Rectangle &r) { + int x = r.width + r.width * r.height; ; + //CHECK4: Initiated the 'extract' action at [[@LINE-1]]:26 -> [[@LINE-1]]:44 + //CHECK5: Initiated the 'extract' action at [[@LINE-2]]:12 -> [[@LINE-2]]:44 +} + +// RUN: clang-refactor-test initiate -action extract -selected=%s:50:23-50:46 -selected=%s:50:23-50:45 %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action extract -selected=%s:50:10-51:3 %s | FileCheck --check-prefix=CHECK5 %s + +void disallowBlankStatements() { + // comment + ; + +} + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:59:1-59:3 -selected=%s:60:1-62:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void allowCommentSelection(int y) { + char c = "//fake comment" [y] ; +} + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:67:13-67:16 %s 2>&1 | FileCheck --check-prefix=CHECK-SIMPLE %s +// CHECK-SIMPLE: Failed to initiate the refactoring action (the selected expression is too simple) + +void extractStatements(const Rectangle &r) { + (void)r; + int area = r.width * r.height; + int perimeter = r.width + r.height; + int sum = area + perimeter; + // CHECK6: Initiated the 'extract' action at [[@LINE-3]]:3 -> [[@LINE-2]]:38 + // CHECK7: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-2]]:30 +} + +// RUN: clang-refactor-test initiate -action extract -selected=%s:75:1-76:38 -selected=%s:74:11-77:3 -selected=%s:75:14-76:26 %s | FileCheck --check-prefix=CHECK6 %s + +// RUN: clang-refactor-test initiate -action extract -selected=%s:74:1-77:30 -selected=%s:73:45-78:1 %s | FileCheck --check-prefix=CHECK7 %s + +void extractStatementsCompoundChild(const Rectangle &r) { + int x = 0; + { + int area = r.width * r.height; + } + int y = 0; + int z = 1; + // CHECK8: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-2]]:13 +} + +// RUN: clang-refactor-test initiate -action extract -selected=%s:89:5-91:12 %s | FileCheck --check-prefix=CHECK8 %s + +void extractStatementsTrimComments(const Rectangle &r) { + int x = 0; + + // comment + int area = r.width * r.height; + + // another comment + int y = 0; + + // trailing comment +} +// CHECK9: Initiated the 'extract' action at [[@LINE-7]]:3 -> [[@LINE-7]]:33 +// CHECK10: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-5]]:13 + +// RUN: clang-refactor-test initiate -action extract -selected=%s:100:1-102:32 -selected=%s:101:6-104:21 -selected=%s:100:1-105:3 %s | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test initiate -action extract -selected=%s:103:1-105:12 -selected=%s:104:6-107:22 -selected=%s:103:1-108:1 %s | FileCheck --check-prefix=CHECK10 %s + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:101:1-101:13 -selected=%s:106:1-107:22 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void disallowEmptyCompoundStatement() { + // comment +} + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:118:1-118:13 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void trimLeadingCompoundStatementComment() { + // comment + + int x = 0; +} + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:124:1-124:13 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void extractEntireCompoundStatement() { + { + int x = 0; + int y = 0; + } + // CHECK11: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-1]]:4 + // CHECK12: Initiated the 'extract' action at [[@LINE-4]]:5 -> [[@LINE-3]]:15 + // CHECK13: Initiated the 'extract' action at [[@LINE-5]]:5 -> [[@LINE-4]]:15 +} + +// RUN: clang-refactor-test initiate -action extract -selected=%s:132:3-135:4 %s | FileCheck --check-prefix=CHECK11 %s +// RUN: clang-refactor-test initiate -action extract -selected=%s:132:3-135:3 %s | FileCheck --check-prefix=CHECK12 %s +// RUN: clang-refactor-test initiate -action extract -selected=%s:132:4-135:4 %s | FileCheck --check-prefix=CHECK13 %s + +void disallowExtractionWhenSelectionRangeIsOutsideFunction() { + int x = 0; + int x = 1; +} + +// RUN: not clang-refactor-test initiate -action extract -selected=%s:143:1-147:12 -selected=%s:146:3-150:3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/Extract/extract-initiate.m b/clang/test/Refactor/Extract/extract-initiate.m new file mode 100644 index 0000000000000..4af03c6dd62d5 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-initiate.m @@ -0,0 +1,14 @@ + +@interface I +@end + +@implementation I + +- (int)computeAreaSquaredGivenWidth:(int)width height:(int)height { + int area = width * height; + return area * area; +} +//CHECK1: Initiated the 'extract' action at [[@LINE-3]]:14 -> [[@LINE-3]]:28 +// RUN: clang-refactor-test initiate -action extract -selected=%s:8:14-8:28 %s | FileCheck --check-prefix=CHECK1 %s + +@end diff --git a/clang/test/Refactor/Extract/extract-macros.cpp b/clang/test/Refactor/Extract/extract-macros.cpp new file mode 100644 index 0000000000000..ecac1cee60541 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-macros.cpp @@ -0,0 +1,39 @@ +#define ONE 1 + +void extractExprMacros(int x) { + bool b = x == ONE; +// CHECK1: {\nreturn x == ONE;\n} + int y = ONE + x; +// CHECK1: {\nreturn ONE + x;\n} + int z = ONE - ONE * ONE; +// CHECK1: {\nreturn ONE - ONE * ONE;\n} +// CHECK1: {\nreturn ONE * ONE;\n} +// CHECK1: {\nreturn - ONE * ONE;\n} +} +// RUN: clang-refactor-test perform -action extract -selected=%s:4:12-4:20 -selected=%s:6:11-6:19 -selected=%s:8:12-8:26 -selected=%s:8:17-8:26 -selected=%s:8:15-8:26 %s | FileCheck --check-prefix=CHECK1 %s + +#define MACRO2(x, y) x, y + +int function(int x); + +// MACRO-ARG3: "static int extracted(int x, int &y, int z) {\nreturn y = MACRO2(x + 2, function(z));\n}\n\n" [[@LINE+4]]:1 +// MACRO-ARG2: "static int extracted(int z) {\nreturn function(z);\n}\n\n" [[@LINE+3]]:1 +// MACRO-ARG1: "static int extracted(int x) {\nreturn x + 2;\n}\n\n" [[@LINE+2]]:1 +; +void extractFromMacroArgument(int x, int y, int z) { + + // macro-arg-expr4-begin: +4:7 + // macro-arg-expr3-begin: +3:14 + // macro-arg-expr2-begin: +2:21 + // macro-arg-expr1-begin: +1:14 + y = MACRO2(x + 2, function(z)); // comment4 + // macro-arg-expr1-end: -1:19 // MACRO-ARG1: "extracted(x)" [[@LINE-1]]:14 -> [[@LINE-1]]:19 + // macro-arg-expr2-end: -2:32 // MACRO-ARG2: "extracted(z)" [[@LINE-2]]:21 -> [[@LINE-2]]:32 + // macro-arg-expr3-end: -3:32 // MACRO-ARG3: "extracted(x, y, z)" [[@LINE-3]]:3 -> [[@LINE-3]]:33 + // macro-arg-expr4-end: -4:19 +} + +// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr1 %s | FileCheck --check-prefix=MACRO-ARG1 %s +// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr2 %s | FileCheck --check-prefix=MACRO-ARG2 %s +// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr3 %s | FileCheck --check-prefix=MACRO-ARG3 %s +// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr4 %s | FileCheck --check-prefix=MACRO-ARG3 %s diff --git a/clang/test/Refactor/Extract/extract-method.cpp b/clang/test/Refactor/Extract/extract-method.cpp new file mode 100644 index 0000000000000..84beaa87d6b82 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-method.cpp @@ -0,0 +1,153 @@ + +void function(int x) { + int y = x * x; +} + +class InitiateMethodExtraction { +public: + + InitiateMethodExtraction() { + int x = constMethod(1); + } +// CHECK1: "void extracted() {\nint x = constMethod(1);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +// CHECK1-NEXT: "extracted();" [[@LINE-3]]:5 -> [[@LINE-3]]:28 +// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(1);\n}\n\n" +// CHECK2-NEXT: "extracted(*this);" +; + ~InitiateMethodExtraction() { + int x = constMethod(2); + } +// CHECK1: "void extracted() {\nint x = constMethod(2);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +// CHECK1-NEXT: "extracted();" [[@LINE-3]]:5 -> [[@LINE-3]]:28 +// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(2);\n}\n\n" +// CHECK2-NEXT: "extracted(*this);" +; + void method() { + int x = constMethod(3); + } +// CHECK1: "void extracted() {\nint x = constMethod(3);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +// CHECK1-NEXT: "extracted();" [[@LINE-3]]:5 -> [[@LINE-3]]:28 +// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(3);\n}\n\n" +// CHECK2-NEXT: "extracted(*this);" +; + int constMethod(int x) const { + return x + x * 2; + } +// CHECK1: "int extracted(int x) const {\nreturn x + x * 2;\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +// CHECK1-NEXT: "extracted(x);" [[@LINE-3]]:5 -> [[@LINE-3]]:22 +// CHECK2: "static int extracted(int x) {\nreturn x + x * 2;\n}\n\n" +// CHECK2-NEXT: "extracted(x);" +; + int operator << (int x) { + return constMethod(x); + } +// CHECK1: "int extracted(int x) {\nreturn constMethod(x);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +// CHECK1-NEXT: "extracted(x)" [[@LINE-3]]:12 -> [[@LINE-3]]:26 +// CHECK2: "static int extracted(const InitiateMethodExtraction &object, int x) {\nreturn object.constMethod(x);\n}\n\n" +// CHECK2-NEXT: "extracted(*this, x)" +; + static void staticMethod(int x) { + int y = x * x; + } +// CHECK1: "static void extracted(int x) {\nint y = x * x;\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +// CHECK1-NEXT: "extracted(x);" [[@LINE-3]]:5 -> [[@LINE-3]]:19 +// CHECK2: "static void extracted(int x) {\nint y = x * x;\n}\n\n" +// CHECK2-NEXT: "extracted(x);" +; + void otherMethod(); +}; + +void InitiateMethodExtraction::otherMethod() { + int x = constMethod(4); +} +// CHECK1: "void extracted();\n\n" [[@LINE-6]]:3 -> [[@LINE-6]]:3 +// CHECK1-NEXT: "void InitiateMethodExtraction::extracted() {\nint x = constMethod(4);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK1-NEXT: "extracted();" [[@LINE-4]]:3 -> [[@LINE-4]]:26 +// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(4);\n}\n\n" +// CHECK2-NEXT: "extracted(*this);" + +// RUN: clang-refactor-test list-actions -at=%s:10:5 -selected=%s:10:5-10:27 %s | FileCheck --check-prefix=CHECK-METHOD %s + +// CHECK-METHOD: Extract Function{{$}} +// CHECK-METHOD-NEXT: Extract Method{{$}} + +// RUN: clang-refactor-test list-actions -at=%s:3:3 -selected=%s:3:3-3:16 %s | FileCheck --check-prefix=CHECK-FUNC %s + +// CHECK-FUNC: Extract Function{{$}} +// CHECK-FUNC-NOT: Extract Method + +// RUN: clang-refactor-test perform -action extract-method -selected=%s:10:5-10:27 -selected=%s:18:5-18:27 -selected=%s:26:5-26:27 -selected=%s:34:5-34:21 -selected=%s:42:12-42:26 -selected=%s:50:5-50:18 -selected=%s:61:3-61:25 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:10:5-10:27 -selected=%s:18:5-18:27 -selected=%s:26:5-26:27 -selected=%s:34:5-34:21 -selected=%s:42:12-42:26 -selected=%s:50:5-50:18 -selected=%s:61:3-61:25 %s | FileCheck --check-prefix=CHECK2 %s + + + + + + + +namespace ns { + struct Outer { + struct Inner { + Inner(int x); + + // comment + void method2(int x) const; // this stays! + int field; + }; + }; +} +ns::Outer::Inner::Inner(int x) { + int y = x + x; +} +// CHECK3: "void extracted(int x);\n\n" [[@LINE-11]]:7 -> [[@LINE-11]]:7 +// CHECK3: "void ns::Outer::Inner::extracted(int x) {\nint y = x + x;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK3: "extracted(x);" [[@LINE-4]]:3 -> [[@LINE-4]]:17 + +namespace ns { + + // comment + void Outer::Inner::method2(int x) const { + if (x != 0) + int z = x * x; + else { + (void)x; + } + } +// CHECK3: "void extracted(int x) const;\n\n" [[@LINE-23]]:7 -> [[@LINE-23]]:7 +// CHECK3: "void Outer::Inner::extracted(int x) const {\nif (x != 0)\n int z = x * x;\n else {\n (void)x;\n }\n}\n\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3 +// CHECK3: "extracted(x);" [[@LINE-8]]:5 -> [[@LINE-4]]:6 +; + void Outer::Inner::methodNotDeclared(int x) { + int z = x * x; + } +} +// CHECK3: "\n\nvoid extracted(int x);\n" [[@LINE-30]]:48 -> [[@LINE-30]]:48 +// CHECK3: "void Outer::Inner::extracted(int x) {\nint z = x * x;\n}\n\n" [[@LINE-5]]:3 -> [[@LINE-5]]:3 +// CHECK3: "extracted(x);" [[@LINE-5]]:5 -> [[@LINE-5]]:19 + +struct Empty { + int field; +}; + +void Empty::method() { + field = 22; +} +// CHECK3: "void extracted();\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1 +// CHECK3: "void Empty::extracted() {\nfield = 22;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK3: "extracted()" [[@LINE-4]]:3 -> [[@LINE-4]]:13 + +// RUN: clang-refactor-test perform -action extract-method -selected=%s:100:3-100:16 -selected=%s:110:5-114:4 -selected=%s:121:5-121:18 -selected=%s:133:3-133:13 %s | FileCheck --check-prefix=CHECK3 %s + +template<typename T1, typename T2, int X> +struct TemplateExtraction { + void method(); // CHECK4: "void extracted();\n\n" [[@LINE]]:3 -> [[@LINE]]:3 +}; + +template<typename T1, typename T2, int x> // CHECK4: "template <typename T1, typename T2, int x> \nvoid TemplateExtraction<T1, T2, x>::extracted() {\nint y = x;\n}\n\n" [[@LINE]]:1 -> [[@LINE]]:1 +void TemplateExtraction<T1, T2, x>::method() { +// template-method-begin: +1:1 + int y = x; +// template-method-end: +0:1 +} + +// RUN: clang-refactor-test perform -action extract-method -selected=template-method %s | FileCheck --check-prefix=CHECK4 %s diff --git a/clang/test/Refactor/Extract/extract-method.m b/clang/test/Refactor/Extract/extract-method.m new file mode 100644 index 0000000000000..6c136b0770dbf --- /dev/null +++ b/clang/test/Refactor/Extract/extract-method.m @@ -0,0 +1,57 @@ + +void function(int x) { + int y = x * x; +} + +@interface MethodExtraction + +- (int)method; +- (void)classMethod; + +@end + +@implementation MethodExtraction + +- (void)aMethod:(int)x withY:(int)y { + int a = x + y; +} +// CHECK1: "- (void)extracted:(int)x y:(int)y {\nint a = x + y;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK1-NEXT: "[self extracted:x y:y];" [[@LINE-3]]:3 -> [[@LINE-3]]:17 +// CHECK2: "static void extracted(int x, int y) {\nint a = x + y;\n}\n\n" +// CHECK2-NEXT: "extracted(x, y);" +; ++ (void)classMethod { + int x = 1; + int y = function(x); +} +// CHECK1: "+ (void)extracted {\nint x = 1;\n int y = function(x);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 +// CHECK1-NEXT: "[self extracted];" [[@LINE-4]]:3 -> [[@LINE-3]]:23 +// CHECK2: "static void extracted() {\nint x = 1;\n int y = function(x);\n}\n\n" +// CHECK2-NEXT: "extracted();" + +@end + +@implementation MethodExtraction (Category) + +- (void)catMethod { + int x = [self method]; +} + ++ (void)catClassMethod { + int x = function(42); +} + +@end + +// RUN: clang-refactor-test list-actions -at=%s:16:3 -selected=%s:16:3-16:16 %s | FileCheck --check-prefix=CHECK-METHOD %s + +// CHECK-METHOD: Extract Function{{$}} +// CHECK-METHOD-NEXT: Extract Method{{$}} + +// RUN: clang-refactor-test list-actions -at=%s:3:3 -selected=%s:3:3-3:16 %s | FileCheck --check-prefix=CHECK-FUNC %s + +// CHECK-FUNC: Extract Function{{$}} +// CHECK-FUNC-NOT: Extract Method + +// RUN: clang-refactor-test perform -action extract-method -selected=%s:16:3-16:16 -selected=%s:24:3-25:22 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:16:3-16:16 -selected=%s:24:3-25:22 %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Extract/extract-mutation-of-captured-variable.cpp b/clang/test/Refactor/Extract/extract-mutation-of-captured-variable.cpp new file mode 100644 index 0000000000000..1b9e2eeba78b8 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-mutation-of-captured-variable.cpp @@ -0,0 +1,130 @@ + +typedef struct { + int width, height; +} Rectangle; + +#ifdef ADD +#define MUT(x, y) x += y +#endif + +#ifdef MUL +#define MUT(x, y) x *= y +#endif + +#ifdef BIT +#define MUT(x, y) x |= y +#endif + +#ifdef SHIFT +#define MUT(x, y) x >>= y +#endif + +#ifdef INC1 +#define MUT(x, y) ++x +#endif + +#ifdef INC2 +#define MUT(x, y) x++ +#endif + +#ifdef DEC1 +#define MUT(x, y) --x +#endif + +#ifdef DEC2 +#define MUT(x, y) x-- +#endif + +#ifndef MUT +#define MUT(x, y) x = y +#endif + +#ifdef FIELD +class MutatePrivateInstanceVariables { + int x; + int y; + Rectangle r; + +#endif + +void mutateVariableOrField +#ifndef FIELD + (int x, int y, Rectangle r) +#else + () +#endif +{ + (MUT(x, 1)); +// CHECK1: (int &x) {\nreturn (MUT(x, 1));\n} + + (MUT((x), 1)); +// CHECK1: (int &x) {\nreturn (MUT((x), 1));\n} + + (MUT(r.width, 1)); +// CHECK1: (Rectangle &r) {\nreturn (MUT(r.width, 1));\n} + + (MUT((x, r.height), 1)); +// CHECK1: (Rectangle &r, int x) {\nreturn (MUT((x, r.height), 1));\n} + + (MUT((x == 0 ? x : y), 1)); +// CHECK1: (int &x, int &y) {\nreturn (MUT((x == 0 ? x : y), 1));\n} + + Rectangle a, b; + (x == 0 ? (r) : b) = a; +// CHECK2: (const Rectangle &a, Rectangle &b, Rectangle &r, int x) {\nreturn (x == 0 ? (r) : b) = a;\n} + +} + +#ifdef FIELD +}; +#endif + +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DFIELD | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test perform -action extract -selected=%s:73:3-73:25 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:73:3-73:25 %s -DFIELD | FileCheck --check-prefix=CHECK2 %s + +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DADD | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DMUL -DFIELD | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DBIT | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DSHIFT -DFIELD | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DINC1 | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DINC2 -DFIELD | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DDEC1 | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DDEC2 -DFIELD | FileCheck --check-prefix=CHECK1 %s + +void dontMutateVariable(int *array, int x) { + array[x] = 0; +// CHECK3: (int *array, int x) {\narray[x] = 0;\n} + *array = 0; +// CHECK3: (int *array) {\n*array = 0;\n} + array = 0; +// CHECK3: extracted(int *&array) {\narray = 0;\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:101:3-101:15 -selected=%s:103:3-103:13 -selected=%s:105:3-105:12 %s | FileCheck --check-prefix=CHECK3 %s + +#ifdef __cplusplus + +int &returnsRef(int x) { + static int result = 0; + return result; +} + +void dontMutateCallArguments(int x) { + returnsRef(x) = 0; +// CHECK4: extracted(int x) {\nreturnsRef(x) = 0;\n} +} + +void mutateRefVar(int &x) { + x = 0; +// CHECK4: extracted(int &x) {\nx = 0;\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:119:3-119:20 -selected=%s:124:3-124:8 %s | FileCheck --check-prefix=CHECK4 %s + +#endif diff --git a/clang/test/Refactor/Extract/extract-objc-property.m b/clang/test/Refactor/Extract/extract-objc-property.m new file mode 100644 index 0000000000000..94cb4ef70991a --- /dev/null +++ b/clang/test/Refactor/Extract/extract-objc-property.m @@ -0,0 +1,47 @@ +@interface HasProperty + +@property (strong) HasProperty *item; + +- (HasProperty *)implicitProp; + +- (void)setImplicitSetter:(HasProperty *)value; + +@end + +@implementation HasProperty + +- (void)test { +// property-name-begin: +2:8 +// property-begin: +1:3 + self.item; +// property-end: -1:12 +// property-name-end: -2:12 +// CHECK: "static HasProperty * extracted(HasProperty *object) {\nreturn object.item;\n}\n\n" + +// implicit-name-begin: +2:8 +// implicit-begin: +1:3 + self.implicitProp; +// implicit-end: -1:20 +// implicit-name-end: -2:20 +// CHECK: "static HasProperty * extracted(HasProperty *object) {\nreturn object.implicitProp;\n}\n\n" +} + +// RUN: clang-refactor-test perform -action extract -selected=property -selected=implicit %s -fobjc-arc | FileCheck %s +// RUN: clang-refactor-test perform -action extract -selected=property-name -selected=implicit-name %s -fobjc-arc | FileCheck %s + +- (void)prohibitSetterExtraction { +// setter-pref-begin: +2:8 +// setter-begin: +1:3 + self.item = 0; +// setter-end: -1:12 +// setter-pref-end: -2:12 +// implicit-setter-pref-begin: +2:8 +// implicit-setter-begin: +1:3 + self.implicitSetter = 0; +// implicit-setter-end: -1:22 +// implicit-setter-pref-end: -2:22 +} +// CHECK-SETTER: Failed to initiate the refactoring action (property setter can't be extracted)! +// RUN: not clang-refactor-test initiate -action extract -selected=setter -selected=setter-pref -selected=implicit-setter -selected=implicit-setter-pref %s -fobjc-arc 2>&1 | FileCheck --check-prefix=CHECK-SETTER %s + +@end diff --git a/clang/test/Refactor/Extract/extract-reference-of-captured-variable.cpp b/clang/test/Refactor/Extract/extract-reference-of-captured-variable.cpp new file mode 100644 index 0000000000000..1c7116965d6d3 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-reference-of-captured-variable.cpp @@ -0,0 +1,287 @@ + +void takesPtr(int *x) { } + +typedef struct { + int width, height; +} Rectangle; + +void takesStructPtr(Rectangle *sp) { } + +void variableTakesRef(int x, Rectangle r) { + int &y = x; + takesPtr(&y); +// CHECK1: (int &x) {\nint &y = x;\n takesPtr(&y);\n} + Rectangle p = r; + Rectangle &rp = p; + takesStructPtr(&rp); +// CHECK1: (const Rectangle &r) {\nRectangle p = r;\n Rectangle &rp = p;\n takesStructPtr(&rp);\n} +// CHECK1: (Rectangle &p) {\nRectangle &rp = p;\n takesStructPtr(&rp);\n} + int &member = ((r).width); + int z = member; +// CHECK1: (Rectangle &r) {\nint &member = ((r).width);\n int z = member;\n} + +// Even though y takes a reference to x, we still want to pass it by value here. + int a = x; +// CHECK1: (int x) {\nint a = x;\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:11:3-12:15 -selected=%s:14:3-16:22 -selected=%s:15:3-16:22 -selected=%s:19:3-20:17 -selected=%s:24:3-24:12 %s | FileCheck --check-prefix=CHECK1 %s + +class PrivateInstanceVariables { + int x; + Rectangle r; + + void method() { + int &y = x; +// CHECK2: extracted(int &x) {\nint &y = x;\n} + Rectangle &rr = r; +// CHECK2: extracted(Rectangle &r) {\nRectangle &rr = r;\n} + int &z = ((r).width); +// CHECK2: extracted(Rectangle &r) {\nint &z = ((r).width);\n} + } +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:35:5-35:15 -selected=%s:37:5-37:22 -selected=%s:39:5-39:25 %s | FileCheck --check-prefix=CHECK2 %s + +#ifdef USECONST +#define CONST const +#else +#define CONST +#endif + +void takesRef(CONST int &x) { } + +void takesStructRef(CONST Rectangle &r) { } + +void takesValue(int x) { } + +struct ConsTakesRef { + ConsTakesRef(CONST int &x) { } + + void takesRef(CONST int &x) const { } + void takesValue(int x) const { } +}; + +int operator << (CONST Rectangle &r, CONST int &x) { return 0; } + +void callTakesRef(int x, Rectangle r) { + takesRef(x); +// CHECK3: extracted(int &x) {\ntakesRef(x);\n} +// CHECK4: extracted(int x) {\ntakesRef(x);\n} + takesValue(x); +// CHECK3: extracted(int x) {\ntakesValue(x);\n} +// CHECK4: extracted(int x) {\ntakesValue(x);\n} + auto k = ConsTakesRef(x); auto y = ConsTakesRef(x); +// CHECK3: extracted(int &x) {\nauto k = ConsTakesRef(x);\n} +// CHECK4: extracted(int x) {\nauto k = ConsTakesRef(x);\n} + y.takesRef((x)); +// CHECK3: extracted(int &x, const ConsTakesRef &y) {\ny.takesRef((x));\n} +// CHECK4: extracted(int x, const ConsTakesRef &y) {\ny.takesRef((x));\n} + y.takesValue(x); +// CHECK3: extracted(int x, const ConsTakesRef &y) {\ny.takesValue(x);\n} +// CHECK4: extracted(int x, const ConsTakesRef &y) {\ny.takesValue(x);\n} + takesStructRef((r)); +// CHECK3: extracted(Rectangle &r) {\ntakesStructRef((r));\n} +// CHECK4: extracted(const Rectangle &r) {\ntakesStructRef((r));\n} + takesRef((r).height); +// CHECK3: extracted(Rectangle &r) {\ntakesRef((r).height);\n} +// CHECK4: extracted(const Rectangle &r) {\ntakesRef((r).height);\n} + y.takesRef(r.width); +// CHECK3: extracted(Rectangle &r, const ConsTakesRef &y) {\ny.takesRef(r.width);\n} +// CHECK4: extracted(const Rectangle &r, const ConsTakesRef &y) {\ny.takesRef(r.width);\n} + takesValue(r.width); +// CHECK3: extracted(const Rectangle &r) {\ntakesValue(r.width);\n} +// CHECK4: extracted(const Rectangle &r) {\ntakesValue(r.width);\n} + r << x; +// CHECK3: extracted(Rectangle &r, int &x) {\nreturn r << x;\n} +// CHECK4: extracted(const Rectangle &r, int x) {\nreturn r << x;\n} + + int &r1 = x; + takesRef(x); +// CHECK3: extracted(int &x) {\nint &r1 = x;\n takesRef(x);\n} +// CHECK4: extracted(int &x) {\nint &r1 = x;\n takesRef(x);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:68:3-68:14 -selected=%s:71:3-71:16 -selected=%s:74:3-74:27 -selected=%s:77:3-77:18 -selected=%s:80:3-80:18 -selected=%s:83:3-83:22 -selected=%s:86:3-86:23 -selected=%s:89:3-89:22 -selected=%s:92:3-92:22 -selected=%s:95:3-95:9 -selected=%s:99:3-100:14 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:68:3-68:14 -selected=%s:71:3-71:16 -selected=%s:74:3-74:27 -selected=%s:77:3-77:18 -selected=%s:80:3-80:18 -selected=%s:83:3-83:22 -selected=%s:86:3-86:23 -selected=%s:89:3-89:22 -selected=%s:92:3-92:22 -selected=%s:95:3-95:9 -selected=%s:99:3-100:14 %s -DUSECONST | FileCheck --check-prefix=CHECK4 %s + +void takesConstRef(const int &x) { } + +void callTakeRefAndConstRef(int x) { + takesRef(x); + takesConstRef(x); +// CHECK5: extracted(int &x) {\ntakesRef(x);\n takesConstRef(x);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:111:3-112:19 %s | FileCheck --check-prefix=CHECK5 %s + +class PrivateInstanceVariablesCallRefs { + int x; + Rectangle r; + + void callsTakeRef() { + takesRef(x); +// CHECK6: extracted(int &x) {\ntakesRef(x);\n} +// CHECK7: extracted(int x) {\ntakesRef(x);\n} + takesStructRef(r); +// CHECK6: extracted(Rectangle &r) {\ntakesStructRef(r);\n} +// CHECK7: extracted(const Rectangle &r) {\ntakesStructRef(r);\n} + takesRef(r.width); +// CHECK6: extracted(Rectangle &r) {\ntakesRef(r.width);\n} +// CHECK7: extracted(const Rectangle &r) {\ntakesRef(r.width);\n} + } +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:123:5-123:16 -selected=%s:126:5-126:22 -selected=%s:129:5-129:22 %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:123:5-123:16 -selected=%s:126:5-126:22 -selected=%s:129:5-129:22 %s -DUSECONST | FileCheck --check-prefix=CHECK7 %s + +void variableTakesConstRef(int x, Rectangle r) { + const int &y = x; +// CHECK8: extracted(int x) {\nconst int &y = x;\n} + const Rectangle &p = r; +// CHECK8: extracted(const Rectangle &r) {\nconst Rectangle &p = r;\n} + const int &z = r.width; +// CHECK8: extracted(const Rectangle &r) {\nconst int &z = r.width;\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:139:3-139:19 -selected=%s:141:3-141:25 -selected=%s:143:3-143:25 %s | FileCheck --check-prefix=CHECK8 %s + +class ClassWithMethod { +public: + int method() CONST { return 0; } + int operator + (int x) CONST { return x; } +}; + +void nonConstMethodCallImpliesNonConstReceiver(ClassWithMethod x) { + x.method(); +// CHECK10: extracted(ClassWithMethod &x) {\nreturn x.method();\n} +// CHECK11: extracted(const ClassWithMethod &x) {\nreturn x.method();\n} + x.operator +(2); +// CHECK10: extracted(ClassWithMethod &x) {\nreturn x.operator +(2);\n} +// CHECK11: extracted(const ClassWithMethod &x) {\nreturn x.operator +(2);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:156:3-156:14 -selected=%s:159:3-159:18 %s | FileCheck --check-prefix=CHECK10 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:156:3-156:14 -selected=%s:159:3-159:18 %s -DUSECONST | FileCheck --check-prefix=CHECK11 %s + +void ignoreMethodCallsOnPointer(ClassWithMethod *x) { + x->method(); +// CHECK12: extracted(ClassWithMethod *x) {\nreturn x->method();\n} + x->operator +(2); +// CHECK12: extracted(ClassWithMethod *x) {\nreturn x->operator +(2);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:168:3-168:13 -selected=%s:170:3-170:19 %s | FileCheck --check-prefix=CHECK12 %s + +void takesRValueRef(int &&x) { } +void takesRValueStructRef(Rectangle &&r) { } + +void callTakesRValueRef(int x) { + takesRValueRef(static_cast<int&&>(x)); +// CHECK13: extracted(int &x) {\ntakesRValueRef(static_cast<int&&>(x));\n} + Rectangle r; + takesRValueStructRef((static_cast<Rectangle&&>(r))); +// CHECK13: extracted(Rectangle &r) {\ntakesRValueStructRef((static_cast<Rectangle&&>(r)));\n} + int &&y = static_cast<int&&>(r.height); +// CHECK13: extracted(Rectangle &r) {\nint &&y = static_cast<int&&>(r.height);\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:180:3-180:20 -selected=%s:183:3-183:54 -selected=%s:185:3-185:41 %s | FileCheck --check-prefix=CHECK13 %s + +void referencesInConditionalOperator(int x, int y) { + takesRef(x == 0 ? x : y); +// CHECK14: (int &x, int &y) {\ntakesRef(x == 0 ? x : y);\n} +// CHECK15: (int x, int y) {\ntakesRef(x == 0 ? x : y);\n} + Rectangle a, b; + takesStructRef(y == 0 ? (a) : b); +// CHECK14: (Rectangle &a, Rectangle &b, int y) {\ntakesStructRef(y == 0 ? (a) : b);\n} +// CHECK15: (const Rectangle &a, const Rectangle &b, int y) {\ntakesStructRef(y == 0 ? (a) : b);\n} + takesRef(x == 0 ? (a).width : (y == 0 ? y : b.height)); +// CHECK14: (Rectangle &a, Rectangle &b, int x, int &y) {\ntakesRef(x == 0 ? (a).width : (y == 0 ? y : b.height));\n} +// CHECK15: (const Rectangle &a, const Rectangle &b, int x, int y) {\ntakesRef(x == 0 ? (a).width : (y == 0 ? y : b.height));\n} + takesRef((x == 0 ? a : (b)).width); +// CHECK14: (Rectangle &a, Rectangle &b, int x) {\ntakesRef((x == 0 ? a : (b)).width);\n} +// CHECK15: (const Rectangle &a, const Rectangle &b, int x) {\ntakesRef((x == 0 ? a : (b)).width);\n} + takesRef(x == 0 ? y : y); +// CHECK14: (int x, int &y) {\ntakesRef(x == 0 ? y : y);\n} +// CHECK15: (int x, int y) {\ntakesRef(x == 0 ? y : y);\n} + ClassWithMethod caller1, caller2; + (x == 0 ? caller1 : caller2).method(); +// CHECK14: (ClassWithMethod &caller1, ClassWithMethod &caller2, int x) {\nreturn (x == 0 ? caller1 : caller2).method();\n} +// CHECK15: (const ClassWithMethod &caller1, const ClassWithMethod &caller2, int x) {\nreturn (x == 0 ? caller1 : caller2).method();\n} +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:192:3-192:27 -selected=%s:196:3-196:35 -selected=%s:199:3-199:57 -selected=%s:202:3-202:37 -selected=%s:205:3-205:27 -selected=%s:209:3-209:40 %s | FileCheck --check-prefix=CHECK14 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:192:3-192:27 -selected=%s:196:3-196:35 -selected=%s:199:3-199:57 -selected=%s:202:3-202:37 -selected=%s:205:3-205:27 -selected=%s:209:3-209:40 %s -DUSECONST | FileCheck --check-prefix=CHECK15 %s + +class PrivateInstanceVariablesConditionalOperatorRefs { + int x; + Rectangle r; + + void callsTakeRef(int y) { + takesRef(y == 0 ? x : r.width); + } +}; +// CHECK16: (Rectangle &r, int &x, int y) {\ntakesRef(y == 0 ? x : r.width);\n} +// RUN: clang-refactor-test perform -action extract -selected=%s:222:5-222:35 %s | FileCheck --check-prefix=CHECK16 %s + +class ReferencesInCommaOperator { + int x; + + void callsTakeRef(int y, Rectangle r) { + takesRef((x, y)); +// CHECK17: (int x, int &y) {\ntakesRef((x, y));\n} + takesRef((y, x)); +// CHECK17: (int &x, int y) {\ntakesRef((y, x));\n} + takesStructRef((takesValue(x), r)); +// CHECK17: (Rectangle &r, int x) {\ntakesStructRef((takesValue(x), r));\n} + } +}; + +// RUN: clang-refactor-test perform -action extract -selected=%s:232:5-232:21 -selected=%s:234:5-234:21 -selected=%s:236:5-236:39 %s | FileCheck --check-prefix=CHECK17 %s + +struct StaticMember { + static int staticMember; +}; + +void memberMustBeNonStaticField(StaticMember s) { + takesRef(s.staticMember); +// CHECK18: (const StaticMember &s) {\ntakesRef(s.staticMember);\n} +} +// RUN: clang-refactor-test perform -action extract -selected=%s:248:3-248:27 %s | FileCheck --check-prefix=CHECK18 %s + +class ClassWithMethod2 { +public: + ClassWithMethod member; +}; + +class ClassWithMethod; + +void nonConstMethodCallImpliesNonConstReceiver2(ClassWithMethod2 x) { + x.member.method(); +// CHECK19: (ClassWithMethod2 &x) {\nreturn x.member.method();\n} +// CHECK20: (const ClassWithMethod2 &x) {\nreturn x.member.method();\n} +} +// RUN: clang-refactor-test perform -action extract -selected=%s:261:3-261:20 %s | FileCheck --check-prefix=CHECK19 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:261:3-261:20 %s -DUSECONST | FileCheck --check-prefix=CHECK20 %s + +class PrivateInstaceVariablesCallRefsBase { + int x; +}; + +class PrivateInstanceVariablesCallRefs2: public PrivateInstaceVariablesCallRefsBase { + int y; + Rectangle r; + + void callsTakeRef() { + takesRef(this->x); + takesRef((this)->y); + takesRef(static_cast<PrivateInstaceVariablesCallRefsBase *>(this)->x); + takesRef((0, ((const_cast<PrivateInstanceVariablesCallRefs2 *>(this)->r.width)))); + } +// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef(object.x);\n} +// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef((object).y);\n} +// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef(static_cast<PrivateInstaceVariablesCallRefsBase *>(&object)->x);\n} +// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef((0, ((const_cast<PrivateInstanceVariablesCallRefs2 *>(&object)->r.width))));\n} +}; +// RUN: clang-refactor-test perform -action extract -selected=%s:277:5-277:22 -selected=%s:278:5-278:24 -selected=%s:279:5-279:74 -selected=%s:280:5-280:86 %s | FileCheck --check-prefix=CHECK21 %s diff --git a/clang/test/Refactor/Extract/extract-reference-of-captured-variable.mm b/clang/test/Refactor/Extract/extract-reference-of-captured-variable.mm new file mode 100644 index 0000000000000..9d0f9c2ba3ea9 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-reference-of-captured-variable.mm @@ -0,0 +1,50 @@ + +#ifdef USECONST +#define CONST const +#else +#define CONST +#endif + +typedef struct { + int width, height; +} Rectangle; + +@interface I + +- (int)takesRef:(CONST int &)x; ++ (int)takesRef:(CONST int &)x; +- (int)takesVal:(int)x; +- (int)takesStructRef:(CONST Rectangle &)r; + +@end + +void methodTakesRef(I *i, int x, Rectangle r) { + [i takesRef: x]; +// CHECK1: extracted(I *i, int &x) {\nreturn [i takesRef: x];\n} +// CHECK2: extracted(I *i, int x) {\nreturn [i takesRef: x];\n} + [I takesRef: x]; +// CHECK1: extracted(int &x) {\nreturn [I takesRef: x];\n} +// CHECK2: extracted(int x) {\nreturn [I takesRef: x];\n} + [i takesVal: x]; +// CHECK1: extracted(I *i, int x) {\nreturn [i takesVal: x];\n} +// CHECK2: extracted(I *i, int x) {\nreturn [i takesVal: x];\n} + [i takesStructRef: r]; +// CHECK1: extracted(I *i, Rectangle &r) {\nreturn [i takesStructRef: r];\n} +// CHECK2: extracted(I *i, const Rectangle &r) {\nreturn [i takesStructRef: r];\n} + [I takesRef: (r).width]; +// CHECK1: extracted(Rectangle &r) {\nreturn [I takesRef: (r).width];\n} +// CHECK2: extracted(const Rectangle &r) {\nreturn [I takesRef: (r).width];\n} +} + +class PrivateInstanceVariablesMethodCallRefs { + int x; + + void methodTakesRef(I *j) { + [j takesRef: x]; +// CHECK1: extracted(I *j, int &x) {\nreturn [j takesRef: x];\n} +// CHECK2: extracted(I *j, int x) {\nreturn [j takesRef: x];\n} + } +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:22:3-22:18 -selected=%s:25:3-25:18 -selected=%s:28:3-28:18 -selected=%s:31:3-31:24 -selected=%s:34:3-34:26 -selected=%s:43:5-43:20 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:22:3-22:18 -selected=%s:25:3-25:18 -selected=%s:28:3-28:18 -selected=%s:31:3-31:24 -selected=%s:34:3-34:26 -selected=%s:43:5-43:20 %s -DUSECONST | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Extract/extract-statement-macros.cpp b/clang/test/Refactor/Extract/extract-statement-macros.cpp new file mode 100644 index 0000000000000..b412a34b199b5 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-statement-macros.cpp @@ -0,0 +1,47 @@ +#define INT int +#define MACRO INT y = x * x + +void extractStatementsTrimComments(int x) { + INT y = 0; + + // comment + MACRO; + + int z = 0; +} +// CHECK1: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-2]]:13 +// CHECK2: Initiated the 'extract' action at [[@LINE-8]]:3 -> [[@LINE-5]]:9 + +// RUN: clang-refactor-test initiate -action extract -selected=%s:6:1-10:12 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action extract -selected=%s:5:3-9:1 %s | FileCheck --check-prefix=CHECK2 %s + +#define BLOCK __attribute__((__blocks__(byref))) +void macroAtDeclStmt() { + // attr-begin: +1:38 + BLOCK const char *Message = "HELLO"; + int X = 123; + // attr-end: -1:13 +} +// CHECK3: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-3]]:15 +// RUN: clang-refactor-test initiate -action extract -selected=attr %s -fblocks | FileCheck --check-prefix=CHECK3 %s + +#define MUT(x) (--(x)) + +void macroExtractionEndsInMacroArgument(int x, int y) { // CHECK4: "static void extracted(int &x, int &y) {\ny = MUT(x);\n}\n\n" [[@LINE]]:1 +// CHECK5: "static int extracted(int &x) {\nreturn MUT(x);\n}\n\n" [[@LINE-1]]:1 + + // macro-arg-expr-begin: +3:7 + // macro-arg-end1-begin: +2:1 + // macro-arg-end2-begin: +1:1 + y = MUT(x); // comment + // macro-arg-end1-end: -1:25 + // macro-arg-end2-end: -2:14 + // macro-arg-expr-end: -3:13 + + // CHECK4: "extracted(x, y)" [[@LINE-5]]:3 -> [[@LINE-5]]:13 + // CHECK5: "extracted(x)" [[@LINE-6]]:7 -> [[@LINE-6]]:13 +} + +// RUN: clang-refactor-test perform -action extract -selected=macro-arg-end1 %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test perform -action extract -selected=macro-arg-end2 %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr %s | FileCheck --check-prefix=CHECK5 %s diff --git a/clang/test/Refactor/Extract/extract-statements.cpp b/clang/test/Refactor/Extract/extract-statements.cpp new file mode 100644 index 0000000000000..4861d1100af1c --- /dev/null +++ b/clang/test/Refactor/Extract/extract-statements.cpp @@ -0,0 +1,178 @@ + +struct Rectangle { int width, height; }; + +void extractStatement(const Rectangle &r) { + int area = r.width * r.height; +// CHECK1: "static void extracted(const Rectangle &r) {\nint area = r.width * r.height;\n}\n\n" +// CHECK1-NEXT: "extracted(r);" [[@LINE-2]]:3 -> [[@LINE-2]]:33 + if (r.width) { + int x = r.height; + } +// CHECK1: "static void extracted(const Rectangle &r) {\nif (r.width) {\n int x = r.height;\n }\n}\n\n" +// CHECK1-NEXT: "extracted(r);" [[@LINE-4]]:3 -> [[@LINE-2]]:4 + if (r.width) { + int x = r.height; + } ; // This semicolon shouldn't be extracted. +// CHECK1: "static void extracted(const Rectangle &r) {\nif (r.width) {\n int x = r.height;\n }\n}\n\n" +// CHECK1-NEXT: "extracted(r);" [[@LINE-4]]:3 -> [[@LINE-2]]:4 + do { + } while (true) ; +// CHECK1: "static void extracted() {\ndo {\n } while (true) ;\n}\n\n" +// CHECK1-NEXT: "extracted();" [[@LINE-3]]:3 -> [[@LINE-2]]:19 + do { + } while (true) /*we still want to take this semicolon*/ ; +// CHECK1: "static void extracted() {\ndo {\n } while (true) /*we still want to take this semicolon*/ ;\n}\n\n" +// CHECK1-NEXT: "extracted();" [[@LINE-3]]:3 -> [[@LINE-2]]:60 +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:5:3-5:32 -selected=%s:8:3-10:4 -selected=%s:13:3-15:4 -selected=%s:18:3-19:17 -selected=%s:22:3-23:17 %s | FileCheck --check-prefix=CHECK1 %s +; +void extractCantFindSemicolon() { + do { + } while (true) + // Add a semicolon in both the extracted and original function as we don't + // want to extract the semicolon below: + ; +// CHECK2: "static void extracted() {\ndo {\n } while (true);\n}\n\n" +// CHECK2-NEXT: "extracted();" [[@LINE-6]]:3 -> [[@LINE-5]]:17 +} +// RUN: clang-refactor-test perform -action extract -selected=%s:31:3-32:17 %s | FileCheck --check-prefix=CHECK2 %s + +void extractedStmtNoNeedForSemicolon() { + { + int x = 0; + } +// CHECK3: "static void extracted() {\n{\n int x = 0;\n }\n}\n\n" + switch (2) { + case 1: + break; + case 2: + break; + } +// CHECK3: "static void extracted() {\nswitch (2) {\n case 1:\n break;\n case 2:\n break;\n }\n}\n\n" + while (true) { + int x = 0; + } +// CHECK3: "static void extracted() {\nwhile (true) {\n int x = 0;\n }\n}\n\n" + for (int i = 0; i < 10; ++i) { + } +// CHECK3: "static void extracted() {\nfor (int i = 0; i < 10; ++i) {\n }\n}\n\n" + struct XS { + int *begin() { return 0; } + int *end() { return 0; } + }; + XS xs; + for (int i : xs) { + } +// CHECK3: "static void extracted(const XS &xs) {\nfor (int i : xs) {\n }\n}\n\n" + try { int x = 0; } + catch (const int &i) { + int y = i; + } +// CHECK3: "static void extracted() {\ntry { int x = 0; }\n catch (const int &i) {\n int y = i;\n }\n}\n\n" +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:42:3-44:4 -selected=%s:46:3-51:4 -selected=%s:53:3-55:4 -selected=%s:57:3-58:4 -selected=%s:65:3-66:4 -selected=%s:68:3-71:4 %s -std=c++11 | FileCheck --check-prefix=CHECK3 %s +; +void extractStatementRange(int x) { + x = 2; + int y = 0; + extractStatementRange(x); + if (x == 2) { + int z = 0; + } + x = 2; + +// CHECK4: "static void extracted(int x) {\nextractStatementRange(x);\n if (x == 2) {\n int z = 0;\n }\n}\n\n" [[@LINE-9]]:1 +// CHECK4-NEXT: "extracted(x);" [[@LINE-7]]:3 -> [[@LINE-4]]:4 + +// CHECK4: "static void extracted(int x) {\nint y = 0;\n extractStatementRange(x);\n}\n\n" [[@LINE-12]]:1 +// CHECK4-NEXT: "extracted(x)" [[@LINE-11]]:3 -> [[@LINE-10]]:27 + +// CHECK4: "static void extracted(int &x) {\nx = 2;\n int y = 0;\n extractStatementRange(x);\n}\n\n" [[@LINE-15]]:1 +// CHECK4-NEXT: "extracted(x)" [[@LINE-15]]:3 -> [[@LINE-13]]:27 + +// CHECK4: "static void extracted(int &x) {\nx = 2;\n int y = 0;\n}\n\n" [[@LINE-18]]:1 +// CHECK4-NEXT: "extracted(x);" [[@LINE-18]]:3 -> [[@LINE-17]]:13 +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:80:3-83:4 -selected=%s:79:3-80:6 -selected=%s:78:3-80:6 -selected=%s:78:3-79:6 %s | FileCheck --check-prefix=CHECK4 %s + +void extractedVariableUsedAndDefinedInExtractedCode(int x) { + int y = x; + if (y == 1) { + int z = 0; + } +// CHECK5: "static void extracted(int x) {\nint y = x;\n if (y == 1) {\n int z = 0;\n }\n}\n\n" +// CHECK5-NEXT: "extracted(x);" +// CHECK5: "static void extracted(int y) {\nif (y == 1) {\n int z = 0;\n }\n}\n\n" +// CHECK5-NEXT: "extracted(y);" +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:102:2-105:4 -selected=%s:103:2-105:4 %s | FileCheck --check-prefix=CHECK5 %s + +void extractAssignmentAsStatementOrExpr(int x) { + x = 2; +// CHECK6: "static void extracted(int &x) {\nx = 2;\n}\n\n" + x = x = 3; +// CHECK6: "static int extracted(int &x) {\nreturn x = 3;\n}\n\n" + (void)(x = 4); +// CHECK6: "static int extracted(int &x) {\nreturn x = 4;\n}\n\n" + if (x = 5) { + } +// CHECK6: "static int extracted(int &x) {\nreturn x = 5;\n}\n\n" + if (true) + x = 6; +// CHECK6: "static void extracted(int &x) {\nx = 6;\n}\n\n" + bool b = 2; + if (b = false) { + } +// CHECK6: "static bool extracted(bool &b) {\nreturn b = false;\n}\n\n" +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:115:3-115:8 -selected=%s:117:7-117:12 -selected=%s:119:10-119:15 -selected=%s:121:7-121:12 -selected=%s:125:5-125:10 -selected=%s:128:7-128:16 %s | FileCheck --check-prefix=CHECK6 %s + +void extractCompoundAssignmentAsStatementOrExpr(int x) { + x += 2; +// CHECK7: "static void extracted(int &x) {\nx += 2;\n}\n\n" + x = x += 3; +// CHECK7: "static int extracted(int &x) {\nreturn x += 3;\n}\n\n" + if (x *= 4) { + } +// CHECK7: "static int extracted(int &x) {\nreturn x *= 4;\n}\n\n" +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:136:3-136:9 -selected=%s:138:7-138:13 -selected=%s:140:7-140:13 %s | FileCheck --check-prefix=CHECK7 %s + +int inferReturnTypeFromReturnStatement(int x) { + if (x == 0) { + return x; + } + if (x == 1) { + return x + 1; + } + return x + 2; +} +// CHECK8: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n return x + 2;\n}\n\n" +// CHECK8: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n}\n\n" + +// RUN: clang-refactor-test perform -action extract -selected=%s:151:3-154:15 -selected=%s:151:3-153:4 %s | FileCheck --check-prefix=CHECK8 %s + +void careForNonCompoundSemicolons() { +// if-open-begin:+1:1 + if (true) + careForNonCompoundSemicolons(); +// if-open-end: -1:35 +// CHECK9: "static void extracted() {\nif (true)\n careForNonCompoundSemicolons();\n}\n\n" +// CHECK9: "extracted();" [[@LINE-4]]:3 -> [[@LINE-3]]:36 + +// for-open-begin:+1:1 + for (int i = 0; i < 10; ++i) + while (i != 0) + ; +// for-open-end: +0:1 +// CHECK9:"static void extracted() {\nfor (int i = 0; i < 10; ++i)\n while (i != 0)\n ;\n}\n\n" [[@LINE-15]]:1 +// CHECK9: "extracted();" [[@LINE-5]]:3 -> [[@LINE-3]]:8 +} + +// RUN: clang-refactor-test perform -action extract -selected=if-open -selected=for-open %s | FileCheck --check-prefix=CHECK9 %s diff --git a/clang/test/Refactor/Extract/extract-statements.m b/clang/test/Refactor/Extract/extract-statements.m new file mode 100644 index 0000000000000..db86ab84023af --- /dev/null +++ b/clang/test/Refactor/Extract/extract-statements.m @@ -0,0 +1,57 @@ +@interface NSArray ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; +@end + +void extractedStmtNoNeedForSemicolon(NSArray *array) { + for (id i in array) { + int x = 0; + } +// CHECK1: "static void extracted(NSArray *array) {\nfor (id i in array) {\n int x = 0;\n }\n}\n\n" + id lock; + @synchronized(lock) { + int x = 0; + } +// CHECK1: "static void extracted(id lock) {\n@synchronized(lock) {\n int x = 0;\n }\n}\n\n" + @autoreleasepool { + int x = 0; + } +// CHECK1: "static void extracted() {\n@autoreleasepool {\n int x = 0;\n }\n}\n\n" + @try { + int x = 0; + } @finally { + } +// CHECK1: "static void extracted() {\n@try {\n int x = 0;\n } @finally {\n }\n}\n\n" +} + +// RUN: clang-refactor-test perform -action extract -selected=%s:6:3-8:4 -selected=%s:11:3-13:4 -selected=%s:15:3-17:4 -selected=%s:19:3-22:4 %s | FileCheck --check-prefix=CHECK1 %s + +@interface I + +@end + +@implementation I + +- (int)inferReturnTypeFromReturnStatement:(int)x { + if (x == 0) { + return x; + } + if (x == 1) { + return x + 1; + } + return x + 2; +} +// CHECK2: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n return x + 2;\n}\n\n" +// CHECK2: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n}\n\n" + +// RUN: clang-refactor-test perform -action extract -selected=%s:38:3-41:15 -selected=%s:38:3-40:4 %s | FileCheck --check-prefix=CHECK2 %s + +@end + +void partiallySelectedWithImpCastCrash(I *object) { +// partially-selected-begin: +1:3 + object; +// partially-selected-end: +1:11 +// comment +// CHECK3: "static void extracted(I *object) {\nobject;\n}\n\n" +// RUN: clang-refactor-test perform -action extract -selected=partially-selected %s | FileCheck --check-prefix=CHECK3 %s +} diff --git a/clang/test/Refactor/Extract/extract-whole-source-construct.cpp b/clang/test/Refactor/Extract/extract-whole-source-construct.cpp new file mode 100644 index 0000000000000..93df9e080fba2 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-whole-source-construct.cpp @@ -0,0 +1,120 @@ +void extractEntireIfWhenSelectedBody(int x) { + if (x == 1) + { + int z = x + 2; + } + else if (x == 3) + { + int y = x + 1; + } + // CHECK1: Initiated the 'extract' action at [[@LINE-8]]:3 -> [[@LINE-1]]:4 +} + +// RUN: clang-refactor-test initiate -action extract -selected=%s:3:3-5:4 -selected=%s:7:3-9:4 -selected=%s:4:5-8:19 -selected=%s:6:12-9:4 %s | FileCheck --check-prefix=CHECK1 %s + +void extractEntireSourceConstructWhenSelectedBody(int x) { + switch (x) + { + case 0: + break; + case 1: + break; + } +// CHECK2: Initiated the 'extract' action at [[@LINE-7]]:3 -> [[@LINE-1]]:4 +// RUN: clang-refactor-test initiate -action extract -selected=%s:17:4-22:4 %s | FileCheck --check-prefix=CHECK2 %s + + for (int i = 0; i < x; ++i) + { + break; + } +// CHECK3: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-1]]:4 +// RUN: clang-refactor-test initiate -action extract -selected=%s:27:4-29:4 %s | FileCheck --check-prefix=CHECK3 %s + + while (x < 0) + { + break; + } +// CHECK4: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-1]]:4 +// RUN: clang-refactor-test initiate -action extract -selected=%s:34:4-36:4 %s | FileCheck --check-prefix=CHECK4 %s + + do + { + break; + } + while (x != 0); +// CHECK5: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-1]]:17 +// RUN: clang-refactor-test initiate -action extract -selected=%s:41:4-43:4 %s | FileCheck --check-prefix=CHECK5 %s +} + +void extractJustTheCompoundStatement() { + { + { + int x = 0; + } + } +// CHECK6: Initiated the 'extract' action at [[@LINE-4]]:5 -> [[@LINE-2]]:6 +// RUN: clang-refactor-test initiate -action extract -selected=%s:51:5-53:6 %s | FileCheck --check-prefix=CHECK6 %s +} + +void extractSwitch(int x) { + switch (x) { + extractSwitch(x - 1); + + case 0: + extractSwitch(x + 1); + break; + + // comment + case 1: + extractSwitch(x + 2); + break; + + default: + extractSwitch(x + 2); + break; + + } +// CHECK7: Initiated the 'extract' action at [[@LINE-17]]:3 -> [[@LINE-1]]:4 +// RUN: clang-refactor-test initiate -action extract -selected=%s:61:3-64:27 -selected=%s:63:5-63:11 -selected=%s:68:5-69:27 -selected=%s:72:5-72:13 %s | FileCheck --check-prefix=CHECK7 %s +} + +class AClass { + void method(); + + void extractWholeCallWhenJustMethodSelected() { + method(); + } +}; +// CHECK8: Initiated the 'extract' action at [[@LINE-3]]:5 -> [[@LINE-3]]:13 +// RUN: clang-refactor-test initiate -action extract -selected=%s:85:5-85:6 -selected=%s:85:5-85:11 %s | FileCheck --check-prefix=CHECK8 %s + +void extractWholeCallWhenJustMethodSelected() { + AClass a; + a.method(); +} +// CHECK9: Initiated the 'extract' action at [[@LINE-2]]:3 -> [[@LINE-2]]:13 +// RUN: clang-refactor-test initiate -action extract -selected=%s:93:3-93:7 -selected=%s:93:5-93:11 %s | FileCheck --check-prefix=CHECK9 %s +; +void avoidExtractingTooMuch(bool boolean) { // CHECK10: void extracted() {\nint x = 2;\n // avoid-{{.*}}-end:+1:15\n int y = x;\n}\n\n" [[@LINE]]:1 + if (boolean) { + // avoid-too-much-begin:+1:1 // CHECK10: "extracted();" [[@LINE+1]]:5 -> [[@LINE+3]]:15 + int x = 2; + // avoid-too-much-end:+1:15 + int y = x; + } else { + int z = 3; + } + + // switch-casesel-begin: +4:3 // switch-casesel-end: +4:4 // CHECK10: void extracted(bool boolean) {\nswitch ((int)boolean) {\n case 0:\n avoidExtractingTooMuch(boolean);\n avoidExtractingTooMuch(boolean);\n break;\n }\n} + // switch-case0-begin: +4:5 // switch-case0-end: +4:36 // CHECK10: void extracted(bool boolean) {\navoidExtractingTooMuch(boolean);\n} + // switch-case1-begin: +3:5 // switch-case1-end: +4:36 // CHECK10: void extracted(bool boolean) {\navoidExtractingTooMuch(boolean);\n avoidExtractingTooMuch(boolean);\n}\n\n" + switch ((int)boolean) { + case 0: + avoidExtractingTooMuch(boolean); + avoidExtractingTooMuch(boolean); + break; + } + // CHECK10: "extracted(boolean)" [[@LINE-4]]:5 -> [[@LINE-3]]:36 +} + +// RUN: clang-refactor-test perform -action extract -selected=avoid-too-much -selected=switch-casesel -selected=switch-case0 -selected=switch-case1 %s | FileCheck --check-prefix=CHECK10 %s diff --git a/clang/test/Refactor/Extract/extracted-declaration-name.mm b/clang/test/Refactor/Extract/extracted-declaration-name.mm new file mode 100644 index 0000000000000..f9d72282ffb86 --- /dev/null +++ b/clang/test/Refactor/Extract/extracted-declaration-name.mm @@ -0,0 +1,50 @@ + +int compute(int n, int x, int y) { + int sum = 0; + for (int i = 0; i < n; ++i) { +// extract-func-begin: +1:12 + sum += (x - i) * (y + i); +// extract-func-end: -1:29 + } + return sum; +} +// RUN: clang-refactor-test perform -action extract -emit-associated -selected=extract-func %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: "static int extracted(int i, int x, int y) {\nreturn (x - i) * (y + i);\n}\n\n" [[@LINE-10]]:1 -> [[@LINE-10]]:1 [Symbol extracted-decl 0 1:12 -> 1:21] +// CHECK1-NEXT: "extracted(i, x, y)" [[@LINE-7]]:12 -> [[@LINE-7]]:29 [Symbol extracted-decl-ref 0 1:1 -> 1:10] + +struct Struct { + int func(int y) { return y; } + + int compute(int x, int y); +}; + +int Struct::compute(int x, int y) { +// extract-member-func-begin: +1:10 + return x * func(y + x) + y; +// extract-member-func-end: -1:25 +} +// RUN: clang-refactor-test perform -action extract-method -emit-associated -selected=extract-member-func %s | FileCheck --check-prefix=CHECK2 %s +// CHECK2: "int extracted(int x, int y);\n\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3 [Symbol extracted-decl 0 1:5 -> 1:14] +// CHECK2-NEXT: "int Struct::extracted(int x, int y) {\nreturn x * func(y + x);\n}\n\n" [[@LINE-7]]:1 -> [[@LINE-7]]:1 [Symbol extracted-decl 0 1:13 -> 1:22] +// CHECK2-NEXT: "extracted(x, y)" [[@LINE-6]]:10 -> [[@LINE-6]]:25 [Symbol extracted-decl-ref 0 1:1 -> 1:10] + +@interface I + +@property int p; + +- (void)foo:(int)x with:(int)y; + +@end + +@implementation I + +- (void)foo:(int)x with:(int)y { +// extract-selector-begin: +1:1 + int m = compute(10, self.p + y, x); +// extract-selector-end: +0:1 +} + +@end +// RUN: clang-refactor-test perform -action extract-method -emit-associated -selected=extract-selector %s | FileCheck --check-prefix=CHECK3 %s +// CHECK3: "- (void)extracted:(int)x y:(int)y {\nint m = compute(10, self.p + y, x);\n}\n\n" [[@LINE-8]]:1 -> [[@LINE-8]]:1 [Symbol extracted-decl 0 1:9 -> 1:18 1:26 -> 1:27] +// CHECK3-NEXT: "[self extracted:x y:y];" [[@LINE-7]]:3 -> [[@LINE-7]]:38 [Symbol extracted-decl-ref 0 1:7 -> 1:16 1:19 -> 1:20] diff --git a/clang/test/Refactor/Extract/return-c-bool.c b/clang/test/Refactor/Extract/return-c-bool.c new file mode 100644 index 0000000000000..0cb5a64673ca5 --- /dev/null +++ b/clang/test/Refactor/Extract/return-c-bool.c @@ -0,0 +1,28 @@ +#ifndef __cplusplus +#define bool _Bool +#define true 1 +#define false 0 +#endif +typedef struct { + bool b; +} HasBool; + +bool boolType(bool b, HasBool *s) { + bool x = b && true; + bool y = boolType(b, s); + bool z = s->b; + bool a = !b; + return (b || true); +} +// RUN: clang-refactor-test perform -action extract -selected=%s:11:12-11:21 -selected=%s:12:12-12:26 --selected=%s:13:12-13:16 --selected=%s:14:12-14:14 --selected=%s:15:10-15:21 %s | FileCheck %s +// RUN: clang-refactor-test perform -action extract -selected=%s:11:12-11:21 -selected=%s:12:12-12:26 --selected=%s:13:12-13:16 --selected=%s:14:12-14:14 --selected=%s:15:10-15:21 %s -x c++ | FileCheck %s +// CHECK: "static bool extracted +; +int boolCompareOps(int x, int y) { + bool a = x == y; + bool b = x >= y; + bool c = ((x < y)); + return 0; +} +// RUN: clang-refactor-test perform -action extract -selected=%s:22:12-22:18 -selected=%s:23:12-23:18 --selected=%s:24:12-24:21 %s | FileCheck %s +// RUN: clang-refactor-test perform -action extract -selected=%s:22:12-22:18 -selected=%s:23:12-23:18 --selected=%s:24:12-24:21 %s -x c++ | FileCheck %s diff --git a/clang/test/Refactor/Extract/return-correct-stl-type.cpp b/clang/test/Refactor/Extract/return-correct-stl-type.cpp new file mode 100644 index 0000000000000..f7b9444997106 --- /dev/null +++ b/clang/test/Refactor/Extract/return-correct-stl-type.cpp @@ -0,0 +1,51 @@ +namespace std { + +struct Traits { + typedef char char_type; +}; + +template<typename TraitsType> +struct BasicString { + typedef typename TraitsType::char_type value_type; + value_type value() const; + const value_type *data() const; +}; + +template<typename TraitsType> +BasicString<TraitsType> +operator+(const BasicString<TraitsType> &lhs, + const BasicString<TraitsType> &rhs); +template<typename TraitsType> +BasicString<TraitsType> +operator+(const BasicString<TraitsType> &lhs, + const char *rhs); + +template<typename TraitsType> +struct BasicString; +typedef BasicString<Traits> String; + +} // end namespace std + +void returnCharTypeNotUselessValueType() { +// CHECK1: "static char extracted(const std::String &x) {\nreturn x.value();\n}\n\n" [[@LINE-1]]:1 +// CHECK1: "static const char * extracted(const std::String &x) {\nreturn x.data();\n}\n\n" [[@LINE-2]]:1 + std::String x; +// return-char-begin: +1:9 + (void)x.value(); +// return-char-end: +0:1 +// return-data-begin: +1:9 + (void)x.data(); +// return-data-end: +0:1 +} // RUN: clang-refactor-test perform -action extract -selected=return-char -selected=return-data %s | FileCheck --check-prefix=CHECK1 %s + +void operatorTypeInferral() { +// CHECK2: "static std::String extracted(const std::String &x) {\nreturn x + "hello";\n}\n\n" [[@LINE-1]]:1 +// CHECK2: "static std::String extracted(const std::String &x) {\nreturn x + x;\n}\n\n" [[@LINE-2]]:1 + std::String x; +// infer-string1-begin: +1:10 + (void)(x + "hello"); +// infer-string1-end: -1:21 +// infer-string2-begin: +1:10 + (void)(x + x); +// infer-string2-end: -1:15 +} // RUN: clang-refactor-test perform -action extract -selected=infer-string1 -selected=infer-string2 %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Extract/return-objc-bool.m b/clang/test/Refactor/Extract/return-objc-bool.m new file mode 100644 index 0000000000000..109ae5be4dcbc --- /dev/null +++ b/clang/test/Refactor/Extract/return-objc-bool.m @@ -0,0 +1,89 @@ +#ifdef SIGNED +typedef signed char BOOL; +#else + +#ifndef __cplusplus +#define bool _Bool +#endif + +typedef bool BOOL; + +#endif + +#define YES __objc_yes +#define NO __objc_no + +typedef struct { + BOOL b; +} HasBool; + +// Always prefer to use BOOL in the Objective-C methods. + +@interface I +@end + +@implementation I + +- (BOOL)boolType:(BOOL)b with:(HasBool*)s { + BOOL x = b && YES; + BOOL y = [self boolType: b with: s]; + BOOL z = s->b; + BOOL a = !b; + return (b == NO); +} +// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s | FileCheck --check-prefix=CHECKBOOL %s +// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s +// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s +// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s -x objective-c++ -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s + +// CHECKBOOL: "static BOOL extracted + +#ifdef __cplusplus + +// Prefer BOOL even in Objective-C++ methods. + +- (BOOL)chooseBOOLEvenInCPlusPlus:(bool)b and:(bool)c { + bool x = b && c; + bool n = !b; +} + +#endif +// RUN: clang-refactor-test perform -action extract -selected=%s:46:12-46:18 -selected=%s:47:12-47:14 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s +// RUN: clang-refactor-test perform -action extract -selected=%s:46:12-46:18 -selected=%s:47:12-47:14 %s -x objective-c++ -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s + +@end + +#ifdef __cplusplus + +// In Objective-C++ functions/methods we want to pick the type based on the expression. + +BOOL boolObjCFunction(BOOL b, BOOL c) { + BOOL x = b && c; + BOOL y = boolObjCFunction(b, c); + return b; +} +// RUN: clang-refactor-test perform -action extract -selected=%s:61:12-61:18 -selected=%s:62:12-62:34 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s +// RUN: clang-refactor-test perform -action extract -selected=%s:61:12-61:18 -selected=%s:62:12-62:34 %s -x objective-c++ -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s + +bool boolCPlusPlusFunction(bool b, bool c) { + bool x = b && c; + bool y = boolCPlusPlusFunction(b, c); + return b; +} +// CHECKNORMAL: "static bool extracted +// RUN: clang-refactor-test perform -action extract -selected=%s:69:12-69:18 -selected=%s:70:12-70:39 %s -x objective-c++ | FileCheck --check-prefix=CHECKNORMAL %s + +class AClass { + AClass(BOOL b, BOOL c, bool d, bool e) { + BOOL x = b && c; + bool y = d && e; + } + void method(BOOL b, BOOL c, bool d, bool e) { + BOOL x = b || c; + bool y = d || e; + } +}; +// RUN: clang-refactor-test perform -action extract -selected=%s:78:14-78:20 -selected=%s:82:14-84:20 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s +// RUN: clang-refactor-test perform -action extract -selected=%s:79:14-79:20 -selected=%s:83:14-83:20 %s -x objective-c++ | FileCheck --check-prefix=CHECKNORMAL %s + +#endif diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.cpp b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.cpp new file mode 100644 index 0000000000000..ba97e23f8716f --- /dev/null +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.cpp @@ -0,0 +1,63 @@ + +struct AClass { + int method(); + int method2(); +}; +struct AWrapperClass { + AClass &object(int x); +}; + +void duplicatesWithParens(AWrapperClass &wrapper) { + wrapper.object(0).method(); +// CHECK1: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-1]]:3 + ((wrapper).object((0))).method(); +// CHECK2: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-1]]:4 +} + +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:11:3-20 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:13:4-25 %s | FileCheck --check-prefix=CHECK2 %s + + +void noDuplicatesWithParens(AWrapperClass &wrapper) { + wrapper.object(- 1).method(); +#ifndef DUPLICATE + wrapper.object((- 1)).method(); +#else + (wrapper).object(- 1).method(); +#endif +// CHECK3: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-6]]:3 + + wrapper.object(1 + 2).method(); +#ifndef DUPLICATE + wrapper.object((1 + 2)).method(); +#else + ((wrapper)).object(1 + 2).method(); +#endif +// CHECK4: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-6]]:3 + + wrapper.object(true ? 0 : 1).method(); +#ifndef DUPLICATE + wrapper.object((true ? 0 : 1)).method(); +#else + ((wrapper)).object(true ? (0) : (1)).method(); +#endif +// CHECK5: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-6]]:3 +} + +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:22:3-22 %s -DDUPLICATE | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:30:3-24 %s -DDUPLICATE | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:38:3-31 %s -DDUPLICATE | FileCheck --check-prefix=CHECK5 %s + +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:22:1-32 -in=%s:30:1-34 -in=%s:38:1-41 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO: Failed to initiate the refactoring action! + +void noDuplicatesWhenSemanticsChange(AWrapperClass &wrapper) { + wrapper.object(0).method(); + if (true) { + AWrapperClass wrapperBase; + AWrapperClass &wrapper = wrapperBase; + wrapper.object(0).method(); + } +} + +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:55:1-30 in=%s:59:1-32 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.m b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.m new file mode 100644 index 0000000000000..bafc192cbacb5 --- /dev/null +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.m @@ -0,0 +1,32 @@ + +@interface Object + +- (int)instanceMethod; + +@end + +@interface Wrapper + +- (Object *)returnsObject:(int)arg; + +- (Object *)classMethodReturnsObject; ++ (Object *)classMethodReturnsObject; + +@end + +void differentWrapperVariables(Wrapper *wrapper) { + [[wrapper returnsObject: 42] instanceMethod]; + Wrapper *copyWrapper = wrapper; + if (wrapper) { + Wrapper *wrapper = copyWrapper; + [[wrapper returnsObject: 42] prop]; + } + [[Wrapper classMethodReturnsObject] instanceMethod]; + if (wrapper) { + __auto_type Wrapper = wrapper; + [[Wrapper classMethodReturnsObject] instanceMethod]; + } +} + +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:18:1-48 -in=%s:24:1-55 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO: Failed to initiate the refactoring action! diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.cpp b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.cpp new file mode 100644 index 0000000000000..de8b1d80ac886 --- /dev/null +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.cpp @@ -0,0 +1,109 @@ + +struct AClass { + int method(); + int method2(); +}; +struct AWrapperClass { + AClass &object(); +}; + +void takesClass(AWrapperClass &wrapper) { + wrapper.object().method(); + wrapper.object().method(); + wrapper.object().method2(); +} +// CHECK1: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3 +// CHECK2: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3 +// CHECK3: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3 + +// Suggest extracting 'wrapper.object()' +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:11:3-19 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:12:3-19 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:13:3-19 %s | FileCheck --check-prefix=CHECK3 %s + +// CHECK-NO: Failed to initiate the refactoring action! + +// Avoid suggesting extraction of 'wrapper.object().method2()' +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:13:20-30 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test list-actions -at=%s:11:11 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Extract Repeated Expression + +AClass &returnsReference(int x); +AClass &returnsAndTakesFunctionPointer(AClass& (*)(int)); + +void checkReferenceCall() { + returnsReference(0).method(); + returnsReference(0).method2(); + returnsAndTakesFunctionPointer(returnsReference).method(); + returnsAndTakesFunctionPointer(returnsReference).method2(); +} +// CHECK4: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-5]]:3 +// CHECK5: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3 +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:36:3-22 %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:38:3-51 %s | FileCheck --check-prefix=CHECK5 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:36:23-32 -in=%s:37:23-32 -in=%s:38:52-61 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +struct WithFields { + int x, y; +}; +struct WithFieldsOperators { + WithFields *operator ->(); + WithFields &operator ()(); + + const WithFields &operator [](int x) const; + WithFields &operator [](int x); +}; + +void checkOperatorCalls(WithFieldsOperators &op, int id) { + op[id].x; + op[id].y; +// CHECK6: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3 + op().x; + op().x; +// CHECK7: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3 + op->x; + op->x; +} +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:59:3-9 %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:62:3-7 %s | FileCheck --check-prefix=CHECK7 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:59:10-12 -in=%s:62:8-10 -in=%s:65:3-9 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +struct AWrapperClass2 { + AClass *object() const; +}; + +void checkPointerType(AWrapperClass *object, AWrapperClass2 *object2) { + object->object().method(); + if (object) { + object->object().method2(); + } +// CHECK8: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3 + object2->object()->method(); + int m = object2->object()->method2(); +// CHECK9: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3 +} +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:77:3-19 %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:82:3-20 %s | FileCheck --check-prefix=CHECK9 %s + +struct ConstVsNonConst { + int field; + void constMethod() const; + void method(); +}; + +struct ConstVsNonConstWrapper { + const ConstVsNonConst &object() const; + ConstVsNonConst &object(); +}; + +void checkFoo(ConstVsNonConstWrapper &object) { + object.object().constMethod(); + object.object().method(); +} +// CHECK10: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-3]]:3 +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -at=%s:101:3 %s | FileCheck --check-prefix=CHECK10 %s + +// Check that the action can be initiate using a selection: +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -selected=%s:11:3-11:19 -selected=%s:11:15-11:19 -selected=%s:11:3-11:17 -selected=%s:11:15-11:18 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -selected=%s:11:3-11:22 -selected=%s:11:1-13:30 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m new file mode 100644 index 0000000000000..1cd28efa13c56 --- /dev/null +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m @@ -0,0 +1,94 @@ + +@interface Base + +@end + +@interface Object: Base { + int ivar; +} + +- (int)instanceMethod; + +@property int prop; +@property void (^block)(); + +@end + +@interface Wrapper + +- (Object *)returnsObject:(int)arg; + ++ (Object *)classMethodReturnsObject; + +@property(class) Object *classObject; + +@property Object *object; + +@end + +void test(Wrapper *wrapper) { + [[wrapper returnsObject: 42] instanceMethod]; + [[wrapper returnsObject: 42] prop]; +// CHECK1: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4 +// CHECK2: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4 + + wrapper.object.prop; + [wrapper.object instanceMethod]; +// CHECK3: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3 +// CHECK4: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4 + + [[wrapper object] block]; + [[wrapper object] instanceMethod]; +// CHECK5: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4 +// CHECK6: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4 + + [[Wrapper classMethodReturnsObject] instanceMethod]; + [[Wrapper classMethodReturnsObject] prop]; +// CHECK7: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4 +// CHECK8: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4 + + Wrapper.classObject.prop; + if (1) + [Wrapper.classObject instanceMethod]; +// CHECK9: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-3]]:3 +// CHECK10: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:6 +} + +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:30:4-31 %s -fblocks | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:31:4-31 %s -fblocks | FileCheck --check-prefix=CHECK2 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:30:1-3 -in=%s:30:32-48 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:35:3-17 %s -fblocks | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:36:4-18 %s -fblocks | FileCheck --check-prefix=CHECK4 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:35:18-23 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:40:4-20 %s -fblocks | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:41:4-20 %s -fblocks | FileCheck --check-prefix=CHECK6 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:40:21-28 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:45:4-38 %s -fblocks | FileCheck --check-prefix=CHECK7 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:46:4-38 %s -fblocks | FileCheck --check-prefix=CHECK8 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:45:39-55 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:50:3-22 %s -fblocks | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:52:6-25 %s -fblocks | FileCheck --check-prefix=CHECK10 %s +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:50:23-28 -in=%s:51:1-9 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// CHECK-NO: Failed to initiate the refactoring action! + +void testInvalidMethod(Wrapper *ref) { + if (2) + [[ref classObject] instanceMethod]; + [ref classObject].block(); +} +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:81:6-23 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +@interface ImplicitPropertyWithoutGetter +- (void) setValue: (int) value; +@end +void implicitPropertyWithoutGetter(ImplicitPropertyWithoutGetter *x) { + x.value = 0; + x.value = 1; +} + +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -at=%s:90:3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp new file mode 100644 index 0000000000000..4d8c40099801c --- /dev/null +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp @@ -0,0 +1,100 @@ + +struct AClass { + void constMethod() const; + void method(); +}; + +struct AWrapper { + const AClass &object(int x) const; + AClass &object(int x); +}; + +void takesClass(AWrapper &ref) { + int x = 0; + ref.object(x).constMethod(); + int y = 0; + ref.object(x).method(); +} +// CHECK1: "AClass &object = ref.object(x);\nobject" [[@LINE-4]]:3 -> [[@LINE-4]]:16 + +// CHECK1-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:16 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:14:3 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:16:3 %s | FileCheck --check-prefix=CHECK1 %s + +void variableNameSuggested(AWrapper &object) { +#ifdef IN_COMPOUND + { +#endif + object.object(21).constMethod(); + object.object(21).method(); +#ifdef IN_COMPOUND + } +#endif +} +// CHECK2: "AClass &object = object.object(21);\nobject" [[@LINE-6]]:3 -> [[@LINE-6]]:20 + +// CHECK2-NEXT: "object" [[@LINE-7]]:3 -> [[@LINE-7]]:20 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:29:3 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:29:3 %s -D IN_COMPOUND | FileCheck --check-prefix=CHECK2 %s + +void takesClass2(AWrapper &ref) { + int x = 0; + if (x) + ref.object(x).constMethod(); + ref.object(x).method(); +} +// CHECK3: "AClass &object = ref.object(x);\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3 +// CHECK3-NEXT: "object" [[@LINE-4]]:5 -> [[@LINE-4]]:18 +// CHECK3-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:16 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:45:5 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:46:3 %s | FileCheck --check-prefix=CHECK3 %s + +void takesClass4(AWrapper &ref) { + int x = 0; + if (x) { + ref.object(x).constMethod(); + ref.object(x).method(); + } +} +// CHECK4: "AClass &object = ref.object(x);\nobject" [[@LINE-4]]:5 -> [[@LINE-4]]:18 + +// CHECK4-NEXT: "object" [[@LINE-5]]:5 -> [[@LINE-5]]:18 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:58:5 %s | FileCheck --check-prefix=CHECK4 %s + +void insertIntoCommonCompound1(AWrapper &ref) { +#ifdef EMBED + int x = 0; + while (true) { +#endif + int x = 0; + if (x) { + if (true) { + int y = x; + ref.object(x).constMethod(); + } + } else { + ref.object(x).method(); + } +// CHECK5: "AClass &object = ref.object(x);\n" [[@LINE-8]]:3 -> [[@LINE-8]]:3 +// CHECK5-NEXT: "object" [[@LINE-6]]:7 -> [[@LINE-6]]:20 +// CHECK5-NEXT: "object" [[@LINE-4]]:5 -> [[@LINE-4]]:18 +#ifdef EMBED + } +#endif +} +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:77:7 %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:80:5 %s -DEMBED | FileCheck --check-prefix=CHECK5 %s + +void checkFirstStmtInCompoundPlacement(AWrapper &ref) { + while (true) { + ref.object(20); + ref.object(20).method(); +// CHECK6: "AClass &object = ref.object(20);\nobject" [[@LINE-2]]:5 -> [[@LINE-2]]:19 + } +} + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:94:5 %s | FileCheck --check-prefix=CHECK6 %s diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m new file mode 100644 index 0000000000000..723438d59345b --- /dev/null +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m @@ -0,0 +1,80 @@ + +@interface Object { + int ivar; +} + +- (int)instanceMethod; + +@property int prop; +@property void (^block)(); + +@end + +@interface Wrapper + +- (Object *)returnsObject:(int)arg; + ++ (Object *)classMethodReturnsObject; + +@property(class) Object *classObject; + +@property Object *object; + +@end + +void takesClass(Wrapper *ref) { + int x = 0; + [[ref returnsObject: x] instanceMethod]; + int y = x; + [ref returnsObject: x].prop; +} +// CHECK1: "Object *duplicate = [ref returnsObject:x];\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3 +// CHECK1-NEXT: "duplicate" [[@LINE-5]]:4 -> [[@LINE-5]]:26 +// CHECK1-NEXT: "duplicate" [[@LINE-4]]:3 -> [[@LINE-4]]:25 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:27:4 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:29:3 %s | FileCheck --check-prefix=CHECK1 %s + +void takesClass2(Wrapper *ref) { + if (2) + [[ref object] instanceMethod]; + [ref object].block(); +} +// CHECK2: "Object *object = [ref object];\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3 +// CHECK2-NEXT: "object" [[@LINE-4]]:6 -> [[@LINE-4]]:18 +// CHECK2-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:15 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:40:6 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:41:3 %s | FileCheck --check-prefix=CHECK2 %s + +void takesClass3(Wrapper *ref) { + if (ref) { + [ref.object instanceMethod]; + ref.object.block(); + } +} +// CHECK3: "Object *object = ref.object;\n" [[@LINE-4]]:5 -> [[@LINE-4]]:5 +// CHECK3-NEXT: "object" [[@LINE-5]]:6 -> [[@LINE-5]]:16 +// CHECK3-NEXT: "object" [[@LINE-5]]:5 -> [[@LINE-5]]:15 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:52:6 %s | FileCheck --check-prefix=CHECK3 %s + +void worksOnClass() { + [Wrapper classMethodReturnsObject]->ivar = 0; + [[Wrapper classMethodReturnsObject] instanceMethod]; +} +// CHECK4: "Object *classMethodReturnsObject = [Wrapper classMethodReturnsObject];\nclassMethodReturnsObject" [[@LINE-3]]:3 -> [[@LINE-3]]:37 + +// CHECK4-NEXT: "classMethodReturnsObject" [[@LINE-4]]:4 -> [[@LINE-4]]:38 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:63:4 %s | FileCheck --check-prefix=CHECK4 %s + +void worksOnClassProperty() { + Wrapper.classObject->ivar = 0; + Wrapper.classObject.prop = 2; +} +// CHECK5: "Object *classObject = Wrapper.classObject;\nclassObject" [[@LINE-3]]:3 -> [[@LINE-3]]:22 + +// CHECK5-NEXT: "classObject" [[@LINE-4]]:3 -> [[@LINE-4]]:22 + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:73:3 %s | FileCheck --check-prefix=CHECK5 %s diff --git a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-forward-decl.c b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-forward-decl.c new file mode 100644 index 0000000000000..dd63951819cc1 --- /dev/null +++ b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-forward-decl.c @@ -0,0 +1,26 @@ +#ifndef AFTER +enum ForwardEnumDecl; +#endif + +enum ForwardEnumDecl { + A, B +}; + +#ifdef AFTER +enum ForwardEnumDecl; +#endif + +void dontInitiateOnIncompleteEnum(enum ForwardEnumDecl e) { + switch (e) { + } +// CHECK: "case A:\n<#code#>\nbreak;\ncase B:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 + + switch (e) { + case A: + break; + } +// CHECK: "case B:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:14:3 -at=%s:18:3 %s | FileCheck %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:14:3 -at=%s:18:3 %s -D AFTER | FileCheck %s diff --git a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-initiate.cpp b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-initiate.cpp new file mode 100644 index 0000000000000..3d8c1a777f84a --- /dev/null +++ b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-initiate.cpp @@ -0,0 +1,150 @@ +enum Color { + Black, + Blue, + White, + Gold +}; + +void initiate(Color c, int i) { + switch (c) { + case Black: + break; + } +// CHECK1: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-4]]:3 + + switch (c) { + } +// CHECK2: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-2]]:3 +} + +// RUN: clang-refactor-test list-actions -at=%s:9:3 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Add Missing Switch Cases + +// Ensure the the action can be initiated around a switch: + +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:9:3-15 -in=%s:10:1-14 -in=%s:11:1-11 -in=%s:12:1-3 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:15:3-15 -in=%s:16:1-4 %s | FileCheck --check-prefix=CHECK2 %s + +// Ensure that the action can't be initiated in other places: + +// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:8:1-32 -in=%s:9:1-2 -in=%s:13:1-77 -in=%s:15:1-2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO: Failed to initiate the refactoring action + +void dontInitiate(Color c, int i) { + switch (c) { + case Black: + break; + case Blue: + break; + case White: + break; + case Gold: + break; + } + + switch (i) { + case 0: + break; + } + + switch ((int)c) { + case 0: + break; + } +} + +// Ensure that the action can't be initiated on switches that have all cases or +// that don't work with an enum. + +// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:34:3-15 %s 2>&1 | FileCheck --check-prefix=CHECK-ALL-COVERED %s +// CHECK-ALL-COVERED: Failed to initiate the refactoring action (All enum cases are already covered)! +// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:45:3-15 -in=%s:50:3-20 %s 2>&1 | FileCheck --check-prefix=CHECK-NOT-ENUM %s +// CHECK-NOT-ENUM: Failed to initiate the refactoring action (The switch doesn't operate on an enum)! + +void initiateWithDefault(Color c, int i) { + switch (c) { + case Black: + break; + default: + break; + } +// CHECK3: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-6]]:3 + + switch (c) { + default: + break; + } +// CHECK4: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-4]]:3 +} + +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:65:3 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:73:3 %s | FileCheck --check-prefix=CHECK4 %s + +enum class Shape { + Rectangle, + Circle, + Octagon +}; + +typedef enum { + Anon1, + Anon2 +} AnonymousEnum; + +void initiateEnumClass(Shape shape, AnonymousEnum anon) { + switch (shape) { + } +// CHECK5: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-2]]:3 + switch (anon) { + } +// CHECK6: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-2]]:3 +} + +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:95:3 %s -std=c++11 | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:98:3 %s | FileCheck --check-prefix=CHECK6 %s + +// Ensure that the operation can be initiated from a selection: + +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:9:3-12:4 -selected=%s:9:15-12:3 -selected=%s:9:15-12:3 -selected=%s:10:3-11:10 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:15:3-16:4 %s | FileCheck --check-prefix=CHECK2 %s + +void dontInitiateSelectedBody(Shape shape) { + switch (shape) { + case Shape::Rectangle: { + break; + } + case Shape::Circle: + break; + } +} + +// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:114:5-114:11 -selected=%s:117:5-117:10 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +enum IncompleteEnum : int; +enum class IncompleteClassEnum : short; +enum class IncompleteClassEnum2; +void dontInitiateOnIncompleteEnum(IncompleteEnum e1, IncompleteClassEnum e2, IncompleteClassEnum2 e3) { + switch (e1) { + } + switch (e1) { + case 0: + break; + } + switch (e2) { + } + switch (e2) { + case (IncompleteClassEnum)0: + break; + } + switch (e3) { + } +} + +// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:127:3 -at=%s:129:3 -at=%s:133:3 -at=%s:135:3 -at=%s:139:3 %s -std=c++11 2>&1 | FileCheck --check-prefix=CHECK-NOT-COMPLETE %s +// CHECK-NOT-COMPLETE: Failed to initiate the refactoring action (The enum type is incomplete)! + +void initiateWhenSelectionIsPartial() { + int partiallySelected = 0; +} +int global = 0; +// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:147:1-150:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-neatly-ordered.cpp b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-neatly-ordered.cpp new file mode 100644 index 0000000000000..97118ec2e9e69 --- /dev/null +++ b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-neatly-ordered.cpp @@ -0,0 +1,157 @@ + +enum Color { + Black, + Blue, + White, + Gold +}; + +void placeBeforeDefault(Color c) { + switch (c) { + case Black: + break; + case Blue: + break; + default: + break; + } +// CHECK1: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 + + switch (c) { + default: + break; + } +// CHECK1: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:10:3 -at=%s:20:3 %s | FileCheck --check-prefix=CHECK1 %s + +void dontPlaceBeforeDefault(Color c) { + switch (c) { + default: + break; + case Black: + break; + case Blue: + break; + } +// CHECK2: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + + switch (c) { + case Black: + break; + default: + break; + case Blue: + break; + } +// CHECK2: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:30:3 -at=%s:40:3 %s | FileCheck --check-prefix=CHECK2 %s + +void insertAtProperPlaces(Color c) { + switch (c) { + case Black: + break; + case White: + break; +#ifdef USEDEFAULT + default: + break; +#endif + } +// CHECK3: "case Blue:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3 +// CHECK3-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3 +// CHECK4: "case Blue:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3 +// CHECK4-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3 + + switch (c) { + case White: + break; +#ifdef USEDEFAULT + default: + break; +#endif + } +// CHECK3: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3 +// CHECK3-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3 +// CHECK4: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3 +// CHECK4-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3 + + switch (c) { + case Gold: + break; +#ifdef USEDEFAULT + default: + break; +#endif + } +// CHECK3: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3 +// CHECK4: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\n" [[@LINE-8]]:3 -> [[@LINE-8]]:3 + + switch (c) { + case Blue: + break; +#ifdef USEDEFAULT + default: + break; +#endif + } +// CHECK3: "case Black:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3 +// CHECK3-NEXT: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3 +// CHECK4: "case Black:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3 +// CHECK4-NEXT: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3 + + switch (c) { + case White: + break; + case Gold: + break; +#ifdef USEDEFAULT + default: + break; +#endif + } +// CHECK3: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3 +// CHECK4: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-10]]:3 -> [[@LINE-10]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:54:3 -at=%s:69:3 -at=%s:82:3 -at=%s:93:3 -at=%s:106:3 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:54:3 -at=%s:69:3 -at=%s:82:3 -at=%s:93:3 -at=%s:106:3 %s -D USEDEFAULT | FileCheck --check-prefix=CHECK4 %s + +void insertAtEndIfOrderingIsUncertain(Color c) { + switch (c) { + case Gold: + break; + case White: + break; + } +// CHECK5: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + + switch (c) { + case Blue: + break; + case Black: + break; + } +// CHECK5: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + + switch (c) { + case White: + break; + case Black: + break; + } +// CHECK5: "case Blue:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + + switch (c) { + case Gold: + break; + case Blue: + break; + } +// CHECK5: "case Black:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:124:3 -at=%s:132:3 -at=%s:140:3 -at=%s:148:3 %s | FileCheck --check-prefix=CHECK5 %s diff --git a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-opaque-decl.cpp b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-opaque-decl.cpp new file mode 100644 index 0000000000000..c1a4040c43b4a --- /dev/null +++ b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-opaque-decl.cpp @@ -0,0 +1,33 @@ +enum IncompleteEnum : int; + +enum IncompleteEnum : int { + A, B +}; + +enum class IncompleteClassEnum : short; + +enum class IncompleteClassEnum : short { + B, C +}; + +enum class IncompleteClassEnum2; + +enum class IncompleteClassEnum2 { + D, E +}; + +void dontInitiateOnIncompleteEnum(IncompleteEnum e1, IncompleteClassEnum e2, IncompleteClassEnum2 e3) { + switch (e1) { + } +// CHECK: "case A:\n<#code#>\nbreak;\ncase B:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 + + switch (e2) { + } +// CHECK: "case IncompleteClassEnum::B:\n<#code#>\nbreak;\ncase IncompleteClassEnum::C:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 + + switch (e3) { + } +// CHECK: "case IncompleteClassEnum2::D:\n<#code#>\nbreak;\ncase IncompleteClassEnum2::E:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:20:3 -at=%s:24:3 -at=%s:28:3 %s -std=c++11 | FileCheck %s diff --git a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp new file mode 100644 index 0000000000000..76ab1c2e0a547 --- /dev/null +++ b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp @@ -0,0 +1,80 @@ +#ifdef NESTEDANON +namespace { +#endif +#ifdef NESTED1 +namespace foo { +struct Struct { +#endif + +#ifdef ENUMCLASS +enum class Color { +#else +enum Color { +#endif + Black, + Blue, + White, + Gold +}; + +#ifdef NESTED1 +#define PREFIX foo::Struct:: +#else +#define PREFIX +#endif + +#ifdef ENUMCLASS +#define CASE(x) PREFIX Color::x +#else +#define CASE(x) PREFIX x +#endif + +#ifdef NESTED1 +} +#ifndef NESTED1NS +} +#endif +#endif +#ifdef NESTEDANON +} +#endif + +void perform1(PREFIX Color c) { + switch (c) { + case CASE(Black): + break; + } +// CHECK1: "case Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 +// CHECK2: "case Color::Blue:\n<#code#>\nbreak;\ncase Color::White:\n<#code#>\nbreak;\ncase Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3 +// CHECK3: "case foo::Struct::Blue:\n<#code#>\nbreak;\ncase foo::Struct::White:\n<#code#>\nbreak;\ncase foo::Struct::Gold:\n<#code#>\nbreak;\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3 +// CHECK4: "case foo::Struct::Color::Blue:\n<#code#>\nbreak;\ncase foo::Struct::Color::White:\n<#code#>\nbreak;\ncase foo::Struct::Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3 +// CHECK5: "case Struct::Blue:\n<#code#>\nbreak;\ncase Struct::White:\n<#code#>\nbreak;\ncase Struct::Gold:\n<#code#>\nbreak;\n" [[@LINE-5]]:3 -> [[@LINE-5]]:3 +// CHECK6: "case Struct::Color::Blue:\n<#code#>\nbreak;\ncase Struct::Color::White:\n<#code#>\nbreak;\ncase Struct::Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-6]]:3 -> [[@LINE-6]]:3 + + switch (c) { + case CASE(Black): + break; + case (Color)1: // Blue + break; + } +// CHECK1: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 +// CHECK2: "case Color::White:\n<#code#>\nbreak;\ncase Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3 + + switch (c) { + } +// CHECK1: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 +// CHECK2: "case Color::Black:\n<#code#>\nbreak;\ncase Color::Blue:\n<#code#>\nbreak;\ncase Color::White:\n<#code#>\nbreak;\ncase Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3 +} + +#ifdef NESTED1NS +} +#endif + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D ENUMCLASS | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 -D ENUMCLASS | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 -D NESTED1NS | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 -D NESTED1NS -D ENUMCLASS | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D NESTEDANON | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D NESTEDANON -D ENUMCLASS | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-wrap-in-compound.cpp b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-wrap-in-compound.cpp new file mode 100644 index 0000000000000..3e1f9fe2ebd23 --- /dev/null +++ b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-wrap-in-compound.cpp @@ -0,0 +1,63 @@ + +enum Color { + Black, + Blue, + White, + Gold +}; + +// Wrap the inserted case bodies in '{' '}' when the majority of others are +// wrapped as well. +void wrapInBraces(Color c) { + switch (c) { + case Black: { + int x = 0; + break; + } + } +// CHECK1: "case Blue: {\n<#code#>\nbreak;\n}\ncase White: {\n<#code#>\nbreak;\n}\ncase Gold: {\n<#code#>\nbreak;\n}\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + + switch (c) { + case Black: { + int x = 0; + break; + } + case Blue: { + int y = 0; + break; + } + } +// CHECK1: "case White: {\n<#code#>\nbreak;\n}\ncase Gold: {\n<#code#>\nbreak;\n}\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:12:3 -at=%s:20:3 %s | FileCheck --check-prefix=CHECK1 %s + +void dontWrapInBraces(Color c) { + switch (c) { + case Black: { + int x = 0; + break; + } + case Blue: + break; + } +// CHECK2: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + + switch (c) { + case Black: { + int x = 0; + break; + } + case Blue: + break; + case White: + break; + } +// CHECK2: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + + switch (c) { + } +// CHECK2: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:36:3 -at=%s:46:3 -at=%s:58:3 %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/FillInEnumSwitchCases/initiate-on-enum-constant.c b/clang/test/Refactor/FillInEnumSwitchCases/initiate-on-enum-constant.c new file mode 100644 index 0000000000000..891b4bfe154d2 --- /dev/null +++ b/clang/test/Refactor/FillInEnumSwitchCases/initiate-on-enum-constant.c @@ -0,0 +1,12 @@ +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=enum-c %s | FileCheck %s + +enum Enum { + Enum_a, + Enum_b, +}; +void testEnumConstantInCWithIntType() { +// enum-c: +1:1 +switch (Enum_a) { +case Enum_a: break; +} // CHECK: "case Enum_b:\n<#code#>\nbreak;\n" [[@LINE]]:1 -> [[@LINE]]:1 +} diff --git a/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-initiate.cpp b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-initiate.cpp new file mode 100644 index 0000000000000..7990cc5c55b22 --- /dev/null +++ b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-initiate.cpp @@ -0,0 +1,68 @@ +struct AbstractClass { + virtual void method() = 0; + virtual void otherMethod() { } +}; + +struct Base { + virtual void nonAbstractClassMethod() { } +}; + +struct Target : Base, AbstractClass { + int field = 0; + + union SubRecord { + }; + + void outerMethod() const; + + void innerMethod() { + int x = 0; + } +}; +// CHECK1: Initiated the 'fill-in-missing-abstract-methods' action at [[@LINE-12]]:1 + +// RUN: clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:10:1-end -in=%s:11:1-end -in=%s:12:1-end -in=%s:13:1-2 -in=%s:15:1-end -in=%s:16:1-2 -in=%s:17:1-end -in=%s:18:1-2 -in=%s:21:1-2 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:13:3-end -in=%s:14:1-2 -in=%s:16:3-27 -in=%s:18:3-end -in=%s:19:1-end -in=%s:20:1-3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// CHECK-NO: Failed to initiate the refactoring action + +// RUN: clang-refactor-test list-actions -at=%s:10:1 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Add Missing Abstract Class Overrides + + +void Target::outerMethod() const { +} + +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:33:1-end -in=%s:34:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +struct FinalTarget final : AbstractClass { +}; +// CHECK2: Initiated the 'fill-in-missing-abstract-methods' action at [[@LINE-2]]:1 + +// RUN: clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:38:1-end -in=%s:39:1-2 %s | FileCheck --check-prefix=CHECK2 %s + +union Union { }; +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:44:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +class NoAbstractParents : Base { }; +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:47:1-35 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-ABSTRACT-BASE %s +// CHECK-NO-ABSTRACT-BASE: Failed to initiate the refactoring action (The class has no abstract bases) + +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:1:1-end -in=%s:6:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO-ABSTRACT-BASE %s + +// Check selection: + +// RUN: clang-refactor-test initiate -action fill-in-missing-abstract-methods -selected=%s:10:1-21:2 -selected=%s:12:1-16:10 -selected=%s:11:1-20:2 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -selected=%s:13:3-14:4 -selected=%s:16:3-16:27 -selected=%s:18:3-20:4 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +struct HasAllMethods: AbstractClass { + virtual void method() override { } +}; +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -at=%s:58:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-MISSING-METHODS %s +// CHECK-NO-MISSING-METHODS: Failed to initiate the refactoring action (The class has no missing abstract class methods) + +// Shouldn't crash: +// forward-decl: +1:1 +struct ForwardDecl; + +// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -at=forward-decl %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-no-attributes.cpp b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-no-attributes.cpp new file mode 100644 index 0000000000000..e9f4404cd59bb --- /dev/null +++ b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-no-attributes.cpp @@ -0,0 +1,10 @@ + +struct AbstractClass { + __attribute__((annotate("test"))) + virtual void pureMethod() = 0; +}; + +struct Target : AbstractClass { +}; +// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:7:1 %s | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp new file mode 100644 index 0000000000000..cc6e8e59933f6 --- /dev/null +++ b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp @@ -0,0 +1,101 @@ +template <typename T> +struct Generic { T x; }; + +struct AbstractClass { + virtual void pureMethod() = 0; + virtual void aPureMethod(int (*fptr)(), Generic<int> y) = 0; + virtual int anotherPureMethod(const int &x) const = 0; + virtual int operator + (int) const = 0; + virtual void otherMethod() { } +}; + +struct Base { + virtual void nonAbstractClassMethod() { } +}; + +struct Target : Base, AbstractClass { +}; +// CHECK1: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint anotherPureMethod(const int &x) const override;\n\nint operator+(int) const override;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 + +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:16:1 %s | FileCheck --check-prefix=CHECK1 %s + +struct SubTarget : AbstractClass { + int anotherPureMethod(const int &) const { return 0; } +#ifdef HAS_OP + int operator + (int) const override { return 2; } +#endif +}; + +struct Target2 : SubTarget, Base { +}; +// CHECK2: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint operator+(int) const override;\n\n" [[@LINE-1]]:1 +// CHECK3: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\n" [[@LINE-2]]:1 + +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:29:1 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:29:1 %s -DHAS_OP | FileCheck --check-prefix=CHECK3 %s + +struct Abstract2 { + virtual void firstMethod(int x, int y) = 0; + virtual void secondMethod(int, int) { } + virtual void thirdMethod(int a) = 0; + virtual void fourthMethod() = 0; +}; + +struct FillInGoodLocations : Base, Abstract2 { + + void secondMethod(int, int) override; // comment + + void unrelatedMethod(); + +}; +// CHECK4: "\n\nvoid firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n\nvoid fourthMethod() override;\n" [[@LINE-5]]:51 -> [[@LINE-5]]:51 +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:44:1 %s | FileCheck --check-prefix=CHECK4 %s + +struct FillInGoodLocations2 : FillInGoodLocations, AbstractClass { + + void fourthMethod() override; + + // comment + void unrelatedMethod(); + + int operator + (int) const override; +}; +// CHECK5: "\n\nvoid firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n" [[@LINE-7]]:32 -> [[@LINE-7]]:32 +// CHECK5-NEXT: "\n\nvoid pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint anotherPureMethod(const int &x) const override;\n" [[@LINE-3]]:39 -> [[@LINE-3]]:39 +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:54:1 %s | FileCheck --check-prefix=CHECK5 %s + +struct FillInGoodLocations3 : Base, AbstractClass, Abstract2 { + + // comment + void unrelatedMethod(); + + void thirdMethod(int a) override; + + void firstMethod(int x, int y) override; + +}; +// CHECK6: "\n\nvoid fourthMethod() override;\n" [[@LINE-3]]:43 -> [[@LINE-3]]:43 +// CHECK6-NEXT: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint anotherPureMethod(const int &x) const override;\n\nint operator+(int) const override;\n\n" [[@LINE-2]]:1 -> [[@LINE-2]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:67:1 %s | FileCheck --check-prefix=CHECK6 %s + +struct FIllInGoodLocationsWithMacros : Abstract2 { +#define METHOD(decl) void decl override; + + METHOD(thirdMethod(int a)) + METHOD(firstMethod(int x, int y)) void foo(); +}; +// CHECK7: "\n\nvoid fourthMethod() override;\n" [[@LINE-2]]:36 -> [[@LINE-2]]:36 +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:81:1 %s | FileCheck --check-prefix=CHECK7 %s + +template<typename T> +class GenericType : Abstract2 { + +}; +// CHECK8: "void firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n\nvoid fourthMethod() override;\n\n" [[@LINE-1]]:1 + +struct GenericSubType : GenericType<int> { + +}; +// CHECK8: "void firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n\nvoid fourthMethod() override;\n\n" [[@LINE-1]]:1 + +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:91:1 -at=%s:96:1 %s | FileCheck --check-prefix=CHECK8 %s diff --git a/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-with-bodies.cpp b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-with-bodies.cpp new file mode 100644 index 0000000000000..2318a56073122 --- /dev/null +++ b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-with-bodies.cpp @@ -0,0 +1,50 @@ + +struct AbstractClass { + virtual void pureMethod() = 0; +}; + +#ifdef HAS_BODY + #define BODY { } +#else + #define BODY ; +#endif + +struct Target1 : AbstractClass { + void method1() BODY +}; +// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1 +// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1 + +struct Target2 : AbstractClass { + void method1() BODY + void method2() BODY +}; +// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1 +// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1 + +struct Target2_1 : AbstractClass { + void method1() BODY + void method2(); +}; +// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1 +// CHECK2: "void pureMethod() override;\n\n" [[@LINE-2]]:1 + +struct Target3 : AbstractClass { + void method1() BODY + void method2() BODY + void method3() BODY +}; +// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1 +// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1 + +struct Target4 : AbstractClass { + void method1() BODY + void method2() BODY + void method3() BODY + void method4() BODY +}; +// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1 +// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1 + +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:12:1 -at=%s:18:1 -at=%s:25:1 -at=%s:32:1 -at=%s:40:1 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:12:1 -at=%s:18:1 -at=%s:25:1 -at=%s:32:1 -at=%s:40:1 %s -DHAS_BODY | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-class-extension.m b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-class-extension.m new file mode 100644 index 0000000000000..ae785bcdf66cc --- /dev/null +++ b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-class-extension.m @@ -0,0 +1,51 @@ +@protocol Proto + +@required +-(void)method:(int)x; + +@end + +@protocol Proto2 + +@required +- (void)method2:(int)y; + +@end + +@interface Base +@end + +// Initiate the action from extension if the @implementation is in the same TU. +@interface WithExtension: Base<Proto> +@end +@interface WithExtension() +@end +@interface WithExtension() <Proto2> +@end +@implementation WithExtension +// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:21:1-27 -in=%s:22:1-5 -in=%s:23:1-36 %s | FileCheck --check-prefix=CHECK1 %s + +@interface WithoutImplementation: Base<Proto> +@end +@interface WithoutImplementation() +@end +@interface WithoutImplementation() <Proto2> +@end +// CHECK-NO-IMPL: Failed to initiate the refactoring action (Class extension without suitable @implementation)! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:32:1 -at=%s:34:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPL %s + +// Initiate from the implementation even when the class has no protocols, but +// its extension does. + +@interface NoProtocols: Base +@end +@interface NoProtocols() <Proto2> +@end +@implementation NoProtocols +@end +// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-2]]:1 +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:44:1 -at=%s:46:1 %s | FileCheck --check-prefix=CHECK2 %s +// CHECK-NO-EXT-FROM-INTERFACE: Failed to initiate the refactoring action! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:42:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-EXT-FROM-INTERFACE %s diff --git a/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-required-only.m b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-required-only.m new file mode 100644 index 0000000000000..249e273356c27 --- /dev/null +++ b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-required-only.m @@ -0,0 +1,146 @@ +@protocol Proto + +#ifndef NO_REQUIRED +@required +-(void)ofCourseItisRequired:(int)x; +#endif + +#ifndef NO_NOTHING +- (void)nothingSpecified:(int)y; +#endif + +#ifndef NO_OPTIONAL +@optional; +- (void)justOptional; +#endif + +@end + +@protocol Proto2 + +#ifndef NO_NOTHING +// Effectively a @required. +- (void)nothingSpecified2:(int)y; +#endif + +#ifndef NO_REQUIRED +@required +-(void)ofCourseItisRequired2:(int)x; +#endif + +#ifndef NO_OPTIONAL +@optional; +- (void)justOptional2; +#endif + +@end + +@interface Base +@end + +// Initiate in the @interface when the interface has missing @required +// declarations. +@interface I1 : Base<Proto> +// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +#ifdef DEF_REQUIRED +-(void)ofCourseItisRequired:(int)x; +#endif +#ifdef DEF_NOTHING +- (void)nothingSpecified:(int)y; +#endif +#ifdef DEF_OPTIONAL +- (void)justOptional; +#endif +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK1 %s +// CHECK-NO: Failed to initiate the refactoring action (All of the @required methods are there)! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_REQUIRED | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_NOTHING | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK1 %s + +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_REQUIRED -DDEF_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +@interface I1(Category) <Proto2> +// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +#ifdef DEF_REQUIRED +-(void)ofCourseItisRequired2:(int)x; +#endif +#ifdef DEF_NOTHING +- (void)nothingSpecified2:(int)y; +#endif +#ifdef DEF_OPTIONAL +- (void)justOptional2; +#endif +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK2 %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_REQUIRED | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_NOTHING | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK2 %s + +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_REQUIRED -DDEF_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// Initiate in the @implementatino when the implementation has missing @required +// methods. +@implementation I1 +// CHECK3: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +#ifdef IMPL_REQUIRED +-(void)ofCourseItisRequired:(int)x { } +#endif +#ifdef IMPL_NOTHING +- (void)nothingSpecified:(int)y { } +#endif +#ifdef IMPL_OPTIONAL +- (void)justOptional { } +#endif +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK3 %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_REQUIRED | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_NOTHING | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_OPTIONAL | FileCheck --check-prefix=CHECK3 %s + +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_REQUIRED -DIMPL_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_REQUIRED -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_NOTHING -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_OPTIONAL -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK3 %s + +@implementation I1 (Category) +// CHECK4: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +#ifdef IMPL_REQUIRED +-(void)ofCourseItisRequired2:(int)x { } +#endif +#ifdef IMPL_NOTHING +- (void)nothingSpecified2:(int)y { } +#endif +#ifdef IMPL_OPTIONAL +- (void)justOptional2 { } +#endif +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK4 %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_REQUIRED | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_NOTHING | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_OPTIONAL | FileCheck --check-prefix=CHECK4 %s + +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_REQUIRED -DIMPL_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_REQUIRED -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_NOTHING -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_OPTIONAL -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK4 %s diff --git a/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-when-protocoled.m b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-when-protocoled.m new file mode 100644 index 0000000000000..0957f8bd61675 --- /dev/null +++ b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-when-protocoled.m @@ -0,0 +1,97 @@ +@protocol Proto + +@required +-(void)method:(int)x; + +@required +- (void)method2:(int)y; + +@end + +@interface Base +@end + +// Initiate when @implementation's interface has a suitable protocol. +@interface I1 : Base<Proto> +@end +// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-2]]:1 +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:15:1 %s | FileCheck --check-prefix=CHECK1 %s + +@implementation I1 + +@end +// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-3]]:1 +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:20:1 %s | FileCheck --check-prefix=CHECK2 %s + +@interface I2 : I1 + +@end +// CHECK3: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-3]]:1 +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:26:1 %s | FileCheck --check-prefix=CHECK3 %s + +@implementation I2 +@end +// CHECK4: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-2]]:1 +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:32:1 %s | FileCheck --check-prefix=CHECK4 %s + +// Shouldn't initiate when the @interface is a forward declaration. +@class ForwardDecl; +// CHECK-FORWARD: Failed to initiate the refactoring action! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:38:1-19 %s 2>&1 | FileCheck --check-prefix=CHECK-FORWARD %s + +// Shouldn't initiate when the @interface has no protocols: + +@interface I3 : Base +@end +@implementation I3 +@end + +@implementation I4 +@end + +// CHECK-CLASS-NO-PROTO: Failed to initiate the refactoring action! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:11:1-16 -in=%s:12:1-5 -at=%s:44:1 -at=%s:46:1 -at=%s:49:1 %s 2>&1 | FileCheck --check-prefix=CHECK-CLASS-NO-PROTO %s + +@protocol Proto2 + +@required +-(int)method3; + +@end + +// Initiate when the category has a suitable protocol: +@interface I3 (Category) <Proto2> +// CHECK5: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s + +@implementation I3 (Category) +// CHECK6: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s + +@interface I1 (Category) <Proto2> +// CHECK7: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s + +@implementation I1 (Category) +// CHECK8: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1 +@end +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s + +// Shouldn't initiate when the category has no protocols (even when the class has them): +@interface I1 (Category2) +@end + +@implementation I1 (Category2) +@end + +@interface I3 (Category2) +@end + +@implementation I3 (Category2) +@end + +// CHECK-CAT-NO-PROTO: Failed to initiate the refactoring action! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:84:1 -at=%s:87:1 -at=%s:90:1 -at=%s:93:1 %s 2>&1 | FileCheck --check-prefix=CHECK-CAT-NO-PROTO %s diff --git a/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate.m b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate.m new file mode 100644 index 0000000000000..066d737b74f92 --- /dev/null +++ b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate.m @@ -0,0 +1,100 @@ +@protocol Proto + +@required +-(void)method:(int)x; + +@end + +@interface Base +@end + +@interface I : Base<Proto> +@property int p1; +@property int p2; +@end + +// Initiate the action within the @implementation +@implementation I + +@dynamic p1; +@synthesize p2 = _p2; + +- (void)anotherMethod { + int x = 0; +} + +void function(int x) { + int y = x; +} + +@end + +// RUN: clang-refactor-test list-actions -at=%s:18:1 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Add Missing Protocol Requirements + +// Ensure the the action can be initiated in the @implementation / @interface: + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:11:1-27 -in=%s:12:1-18 -in=%s:13:1-18 -in=%s:14:1-5 %s | FileCheck --check-prefix=CHECKI1 %s +// CHECKI1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-27]]:1 +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:17:1-18 -at=%s:18:1 -in=%s:19:1-13 -in=%s:20:1-22 -at=%s:21:1 -at=%s:25:1 -at=%s:29:1 -in=%s:30:1-5 %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-23]]:1 + +// Ensure that the action can't be initiated in other places: + +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:1:1-10 -in=%s:4:1-21 -in=%s:6:1-5 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO: Failed to initiate the refactoring action + +// Ensure that the action can't be initiated in methods/functions in @implementation: + +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:22:1-24 -in=%s:23:1-13 -in=%s:24:1-2 -in=%s:26:1-23 -in=%s:27:1-13 -in=%s:28:1-2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +@protocol P2 + +-(void)method2:(int)y; + +@end + +@interface I (Category) <P2> + +@end + +@implementation I (Category) + +- (void)anotherMethod2:(int)x { + int x = 0; +} + +void aFunction(int x) { + int y = x; +} + +@end + +// Ensure the the action can be initiated in the category @implementation: + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:57:1-29 -at=%s:58:1 -in=%s:59:1-5 %s | FileCheck --check-prefix=CHECKI2 %s +// CHECKI2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-19]]:1 + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:61:1-29 -at=%s:62:1 -at=%s:66:1 -at=%s:70:1 -in=%s:71:1-5 %s | FileCheck --check-prefix=CHECK2 %s +// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-18]]:1 + +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:60:1 -at=%s:72:1 -at=%s:73:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:63:1-32 -in=%s:64:1-13 -in=%s:65:1-2 -in=%s:67:1-24 -in=%s:68:1-13 -in=%s:69:1-2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + + +// Check that initiation works with selection as well: + +// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -selected=%s:17:1-30:5 -selected=%s:18:1-29:1 -selected=%s:20:1-20:10 -selected=%s:17:1-23:3 -selected=%s:27:3-30:5 -selected=%s:23:3-27:3 %s | FileCheck --check-prefix=CHECK1 %s + +// Not when just one entire method is selected though! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -selected=%s:22:1-24:2 -selected=%s:26:1-28:2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// And not when the container is just partially selected! +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -selected=%s:15:1-30:1 -selected=%s:17:1-40:1 -selected=%s:15:1-40:1 -selected=%s:1:1-90:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +@class ForwardClass; + +// forward-class: +1:1 +@implementation ForwardClass (ForwardClassCategory) +@end +// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=forward-class %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-no-attributes.m b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-no-attributes.m new file mode 100644 index 0000000000000..d2f37118be6f5 --- /dev/null +++ b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-no-attributes.m @@ -0,0 +1,28 @@ +@class AClass; + +@protocol Protocol + +- (void)methodAttribute __attribute__((availability(macos, introduced=10.10))); + +- (void)parameterTypeAttribute:(AClass * _Nullable)c; + +- (void)parameterTypeAttribute2:(AClass * _Nonnull)c; + +- (void)parameterAttribute:(int)p __attribute__((annotate("test"))); + +@end + +@interface Base +@end + +@interface I1 : Base<Protocol> + +@end +// CHECK1: "- (void)methodAttribute;\n\n- (void)parameterAttribute:(int)p;\n\n- (void)parameterTypeAttribute2:(AClass * _Nonnull)c;\n\n- (void)parameterTypeAttribute:(AClass * _Nullable)c;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 + +@implementation I1 + +@end +// CHECK1: "- (void)methodAttribute { \n <#code#>\n}\n\n- (void)parameterAttribute:(int)p { \n <#code#>\n}\n\n- (void)parameterTypeAttribute2:(AClass * _Nonnull)c { \n <#code#>\n}\n\n- (void)parameterTypeAttribute:(AClass * _Nullable)c { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:18:1 -at=%s:23:1 %s | FileCheck --check-prefix=CHECK1 %s + diff --git a/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-perform.m b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-perform.m new file mode 100644 index 0000000000000..900df300fba6c --- /dev/null +++ b/clang/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-perform.m @@ -0,0 +1,196 @@ +@protocol Proto1 + +- (void)requiredInstanceMethod:(int)y; + ++ (void)aRequiredInstanceMethod:(int (*)(void))function with:(Proto *)p; + +@optional; +- (void)anOptionalMethod; + +@end + +@protocol Proto2 + +- (void)a; + ++ (void)b; + +@end + +@protocol Proto3 + +- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock; + +@end + +@interface Base +@end + +@interface I1 : Base<Proto3> + +@end +// CHECK1: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:29:1 %s | FileCheck --check-prefix=CHECK1 %s + +@implementation I1 + +@end +// CHECK2: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:35:1 %s | FileCheck --check-prefix=CHECK2 %s + +@interface I2 : I1<Proto1, Proto2> + +@end +// CHECK3: "+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p;\n\n- (void)requiredInstanceMethod:(int)y;\n\n- (void)a;\n\n+ (void)b;\n\n- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:41:1 %s | FileCheck --check-prefix=CHECK3 %s + +@implementation I2 + +@end +// CHECK4: "+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p { \n <#code#>\n}\n\n- (void)requiredInstanceMethod:(int)y { \n <#code#>\n}\n\n- (void)a { \n <#code#>\n}\n\n+ (void)b { \n <#code#>\n}\n\n- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:47:1 %s | FileCheck --check-prefix=CHECK4 %s + +@interface I1(Category) <Proto2> +@end +// CHECK5: "- (void)a;\n\n+ (void)b;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 + +@implementation I1(Category) +@end +// CHECK5-NEXT: "- (void)a { \n <#code#>\n}\n\n+ (void)b { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:53:1 -at=%s:57:1 %s | FileCheck --check-prefix=CHECK5 %s + +@interface I3 : I1<Proto1, Proto2> + +- (void)requiredInstanceMethod:(int)y; + ++ (void)b; + +#ifdef HAS_OTHER +- (void) otherProtocolMethod:(int (^)(id<Proto2>))takesBlock; +#endif + +@end +// CHECK6: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p;\n" [[@LINE-9]]:39 -> [[@LINE-9]]:39 +// CHECK6-NEXT: "\n\n- (void)a;\n" [[@LINE-8]]:11 -> [[@LINE-8]]:11 +// CHECK6-NEXT: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK7: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p;\n" [[@LINE-12]]:39 -> [[@LINE-12]]:39 +// CHECK7-NEXT: "\n\n- (void)a;\n" [[@LINE-11]]:11 -> [[@LINE-11]]:11 + +@implementation I3 + +- (void)requiredInstanceMethod:(int)y { +} + ++ (void)b { +} + +#ifdef HAS_OTHER +- (void) otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { } +#endif + +@end +// CHECK6: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p { \n <#code#>\n}\n" [[@LINE-10]]:2 -> [[@LINE-10]]:2 +// CHECK6-NEXT: "\n\n- (void)a { \n <#code#>\n}\n" [[@LINE-8]]:2 -> [[@LINE-8]]:2 +// CHECK6-NEXT: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { \n <#code#>\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK7: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p { \n <#code#>\n}\n" [[@LINE-13]]:2 -> [[@LINE-13]]:2 +// CHECK7-NEXT: "\n\n- (void)a { \n <#code#>\n}\n" [[@LINE-11]]:2 -> [[@LINE-11]]:2 + +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:62:1 -at=%s:79:1 %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:62:1 -at=%s:79:1 %s -DHAS_OTHER | FileCheck --check-prefix=CHECK7 %s + +@protocol ProtoWith3Methods + +- (void)a; +- (void)b; +- (void)c; + +@end + +@interface I4 : Base<ProtoWith3Methods> + +#ifndef USE_MACRO +- (void)b; + +- (void)a; // comment + +#else + +#define METHOD(name) -(void)name; + +METHOD(b) +METHOD(c) - (void)d; + +#endif + +@end +// CHECK8: "\n\n- (void)c;\n" [[@LINE-12]]:22 -> [[@LINE-12]]:22 +// CHECK9: "\n\n- (void)a;\n" [[@LINE-6]]:10 -> [[@LINE-6]]:10 + +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:109:1 %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:109:1 %s -D USE_MACRO | FileCheck --check-prefix=CHECK9 %s + +@protocol NSObject + +- (void)nsObjectMethod; + +@end + +@protocol SubProto + +- (void)sub1; +- (id<SubProto>)sub2; + +@end + +@protocol SubProto2 <NSObject> + +- (void)sub11; + +@end + +@protocol SuperProto <SubProto, NSObject, SubProto2> + +@optional +- (void)mySub; + +@end + +@interface HasSubProtocolMethods: Base <SuperProto, NSObject> + +@end +// CHECK10: "- (void)sub1;\n\n- (id<SubProto>)sub2;\n\n- (void)sub11;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:158:1 %s | FileCheck --check-prefix=CHECK10 %s + + + +@interface SuperClassWithSomeDecls : Base<SuperProto> + +- (void)sub1; + +@end + +@interface SubClassOfSuperClassWithSomeDecls : SuperClassWithSomeDecls + +#ifdef HAS_SUB1_OVERRIDE +- (void)sub1; +#endif +#ifdef HAS_SUB11 +- (void)sub11; +#endif + +@end +// CHECK11: "- (id<SubProto>)sub2;\n\n- (void)sub11;\n\n" [[@LINE-1]]:1 +// CHECK12: "\n\n- (id<SubProto>)sub2;\n" [[@LINE-8]]:14 +// CHECK12-NEXT: "- (void)sub11;\n\n" [[@LINE-3]]:1 +// CHECK13: "- (id<SubProto>)sub2;\n\n" [[@LINE-4]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:172:1 %s | FileCheck --check-prefix=CHECK11 %s +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:172:1 %s -DHAS_SUB1_OVERRIDE | FileCheck --check-prefix=CHECK12 %s +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:172:1 %s -DHAS_SUB11 | FileCheck --check-prefix=CHECK13 %s + +@implementation SubClassOfSuperClassWithSomeDecls + +@end +// CHECK14: "- (id<SubProto>)sub2 { \n <#code#>\n}\n\n- (void)sub11 { \n <#code#>\n}\n\n" [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:190:1 %s | FileCheck --check-prefix=CHECK14 %s +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:190:1 %s -DHAS_SUB1_OVERRIDE | FileCheck --check-prefix=CHECK14 %s +// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:190:1 %s -DHAS_SUB11 | FileCheck --check-prefix=CHECK14 %s diff --git a/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-initiate.cpp b/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-initiate.cpp new file mode 100644 index 0000000000000..e8d063168f394 --- /dev/null +++ b/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-initiate.cpp @@ -0,0 +1,566 @@ +void foo(); void foobar(); + +void simpleCompoundBodyIf(int x) { + foo(); + + if (x == 2) { + int y = x; + } else if (x == 3) { + foo(); + } else { + foobar(); + } + + foobar(); +} + +// RUN: clang-refactor-test list-actions -at=%s:6:3 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Convert to Switch + +// Ensure the the action can be initiated around the ifs: + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:6:3-15 -in=%s:8:3-22 -in=%s:10:3-10 %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: Initiated the 'if-switch-conversion' action at 6:3 + +// Ensure that the action can't be initiated when not around the ifs: + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:1:1-10 -in=%s:3:1-18 -in=%s:4:1-9 -in=%s:6:1-2 -in=%s:14:1-12 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO: Failed to initiate the refactoring action +// CHECK-NO-NOT: Initiated the 'if-switch-conversion' action + +// Ensure that the action can't be initiated inside the ifs: +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:7:1-15 -in=%s:9:1-11 -in=%s:11:1-14 -in=%s:12:1-4 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void nestedIf(int x) { + if (x == 2) { + if (x == 3) { + foo(); + } else { + foo(); + } + } else { + foobar(); + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:35:3-15 -in=%s:35:3-10 %s | FileCheck --check-prefix=CHECK2 %s +// CHECK2: Initiated the 'if-switch-conversion' action at 35:3 + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:36:5-17 -in=%s:38:5-12 %s | FileCheck --check-prefix=CHECK3 %s +// CHECK3: Initiated the 'if-switch-conversion' action at 36:5 + + + + + +void simpleFlatBodyIfs(int x) { + if (x == 2) + foo(); + else if (x == 3) + foo(); + + else if (x == 4) foobar(); + + else foo(); + + if (x == 2) foobar(); + else + //comment + foo(); +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:57:3-14 -in=%s:59:3-19 -in=%s:62:3-19 -in=%s:64:3-7 %s | FileCheck --check-prefix=CHECK4 %s +// CHECK4: Initiated the 'if-switch-conversion' action at 57:3 + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:57:1-2 -in=%s:58:1-11 -in=%s:59:1-2 -in=%s:60:1-11 -in=%s:61:1-1 -in=%s:62:1-2 -in=%s:62:20-29 -in=%s:63:1-1 -in=%s:64:1-2 -in=%s:64:8-14 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:66:3-15 -in=%s:67:3-7 -in=%s:68:1-14 %s | FileCheck --check-prefix=CHECK5 %s +// CHECK5: Initiated the 'if-switch-conversion' action at 66:3 + +void differentLineCompoundIf(int x) { + if (x == 2) + { + foo(); + } + + else if (x == 3) + + { + foo(); + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:81:3-14 -in=%s:86:3-19 -in=%s:87:1-1 %s | FileCheck --check-prefix=CHECK6 %s +// CHECK6: Initiated the 'if-switch-conversion' action at 81:3 + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:81:1-2 -in=%s:82:1-4 -in=%s:84:1-4 -in=%s:85:1-1 -in=%s:86:1-2 -in=%s:88:1-4 -in=%s:90:1-4 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void simpleEmptyIf(int x) { + if (x == 1) ; + else if (x == 2) ; +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:99:3-14 -in=%s:100:3-19 %s | FileCheck --check-prefix=CHECK7 %s +// CHECK7: Initiated the 'if-switch-conversion' action at 99:3 + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:99:15-16 -in=%s:100:20-21 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void sameLineIfElse(int x) { + if (x == 1) foo(); else foo(); + if (x == 2) { foo(); } else if (x == 3) { foo(); } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:109:3-14 -in=%s:109:22-26 %s | FileCheck --check-prefix=CHECK8 %s +// CHECK8: Initiated the 'if-switch-conversion' action at 109:3 + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:109:15-21 -in=%s:109:27-33 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:110:3-15 -in=%s:110:24-43 %s | FileCheck --check-prefix=CHECK9 %s +// CHECK9: Initiated the 'if-switch-conversion' action at 110:3 + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:110:16-23 -in=%s:110:44-53 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void noIfsWithoutElses(int x) { + if (x == 1) { + foo(); + } + if (x == 2) ; +} + +// Ifs without any elses shouldn't be allowed: +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:124:1-16 -in=%s:127:1-16 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void noFancyIfs(const int *p) { + if (const int *x = p) { + } + else if (const int *y = p) { + } + + if (const int *x = p; *x == 2) { + } else if (const int *y = p; *y == 3) { + } +} + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:134:1-26 -in=%s:136:1-31 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:139:1-35 -in=%s:140:1-42 %s -std=c++1z 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +void prohibitBreaksCasesDefaults(int x) { + while (x != 0) { + break; + // Allowed: + if (x == 1) foo(); + else foo(); + // Not allowed: + if (x == 2) break; + else foo(); + if (x == 2) { foo(); } + else { break; } + if (x == 2) { foo(); } + else if (x == 1) { if (x == 2) { break; } } + } + switch (x) { + case 1: + // Allowed: + if (x == 1) foo(); + else foo(); + // Not allowed: + if (x == 2) foo(); + else if (x == 3) { + case 2: + foo(); + } + if (x == 3) foo(); + else { + default: + foo(); + } + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:151:5 %s | FileCheck --check-prefix=CHECK10 %s +// CHECK10: Initiated the 'if-switch-conversion' action at 151:5 + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:154:5 -at=%s:156:5 -at=%s:158:5 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s +// CHECK-INVALID-STATEMENTS: Failed to initiate the refactoring action (if's body contains a 'break'/'default'/'case' statement) + +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:164:5 %s | FileCheck --check-prefix=CHECK11 %s +// CHECK11: Initiated the 'if-switch-conversion' action at 164:5 + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:167:5 -at=%s:172:5 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s + +#ifdef DISALLOWED + #define DISALLOW(x) x +#else + #define DISALLOW(x) +#endif + +void allowBreaksInNestedLoops(int x) { + DISALLOW(while (true)) { + // Allowed: + if (x == 1) { + foo(); + } else if (x == 2) { + while (x != 0) { + break; + } + DISALLOW(break;) + } + + if (x == 1) { + foo(); + } else { + for (int y = 0; y < x; ++y) { + break; + } + DISALLOW(break;) + } + + if (x == 1) { + do { + break; + } while (x < 10); + DISALLOW(break;) + } else { + foo(); + } + + if (x == 1) { + do { + // nested loop. + while (true) { + } + break; + } while (x < 10); + DISALLOW(break;) + } else { + foo(); + } + + } + + // Still care about cases and defaults in loops: + switch (x) { + case 0: + if (x == 1) { + while (true) { + case 1: + } + } else { + foo(); + } + break; + } + + switch (x) { + case 0: + if (x == 1) { + while (true) { + default: + } + } else { + foo(); + } + break; + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:200:3 %s | FileCheck --check-prefix=CHECK-YES %s +// CHECK-YES: Initiated the 'if-switch-conversion' action + +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:209:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:218:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:227:3 %s | FileCheck --check-prefix=CHECK-YES %s + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:200:3 -at=%s:209:3 -at=%s:218:3 -at=%s:227:3 -at=%s:244:5 -at=%s:256:5 %s 2>&1 -D DISALLOWED | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s + +void allowBreakDefaultCaseInNestedSwitches(int x) { + DISALLOW(switch (x)) { + // Allowed: + if (x == 1) { + foo(); + } else if (x == 2) { + switch (x) { + case 0: + foo(); + } + DISALLOW(case 0: ;) + } + + if (x == 1) { + foo(); + } else { + switch (x) { + default: + foo(); + } + DISALLOW(default: ;) + } + + if (x == 1) { + switch (x) { + break; + } + DISALLOW(break;) + } else { + foo(); + } + + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:279:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:289:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:299:3 %s | FileCheck --check-prefix=CHECK-YES %s + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:279:3 -at=%s:289:3 -at=%s:299:3 %s 2>&1 -D DISALLOWED | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s + + + + + + + + + + + + + + + + +bool isTrue(); + +void allowOnlyEqualsOp(int x) { + if (x != 1) { + } else { + } + + if (x == 1) { + } else if (x > 2) { + } + + if (x == 3) { + } else if (x) { + } + + if (isTrue()) { + } else { + } +} + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:335:3 -at=%s:339:3 -at=%s:343:3 -at=%s:347:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s +// CHECK-INVALID-COND: Failed to initiate the refactoring action (unsupported conditional expression)! + +void allowEqualsOpInParens(int x) { + if ((x == 1)) { + } else { + } + + if (x == 1) { + } else if (((x == 2))) { + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:356:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:360:3 %s | FileCheck --check-prefix=CHECK-YES %s + +enum Switchable { + A, B +}; + +struct Struct { +}; + +bool operator == (const Struct &, int); + +void allowSwitchableTypes(int x, bool b, long l, char c, Switchable e, + float f, double d, Struct s, int *ip) { + // Allowed: + if (b == true) { + } else { + } + + if (1 == 1) { + } else { + } + + if (l == 4) { + } else { + } + + if (e == A) { + } else { + } + + if (x == A) { + } else { + } + + if (c == 'x') { + } else { + } + + // Disallowed: + if (f == 0) { + } else { + } + + if (d == 0) { + } else { + } + + if (x == 0) { + } else if (x == 0.0) { + } + + if (s == 0) { + } else { + } + + if (ip == 0) { + } else { + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:380:3 -at=%s:384:3 -at=%s:388:3 -at=%s:392:3 -at=%s:396:3 -at=%s:400:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:405:3 -at=%s:409:3 -at=%s:413:3 -at=%s:417:3 -at=%s:421:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s + +template<typename T> +void prohibitDependentOperators(T x) { + if (x == 0) { + } else { + } +} + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:431:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s + +int integerFunction(); + +void checkLHSSame(int x, int y) { + // Allowed: + if (integerFunction() == 1) { + } else if (integerFunction() == 2) { + } + + // Disallowed: + if (x == 1) { + } else if (y == 2) { + } + + if (x == 1) { + } else if (2 == 2) { + } + + if (integerFunction() == 1) { + } else if (x == 2) { + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:442:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:447:3 -at=%s:451:3 -at=%s:455:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s + +void checkRHSConstant(int x, int y, Switchable e) { + // Allowed: + if (x == (int)A) { + } else { + } + + if (e == (Switchable)1) { + } else { + } + + // Disallowed: + if (x == y) { + } else { + } + + if (x == 1) { + } else if (x == integerFunction()) { + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:465:3 -at=%s:469:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:474:3 -at=%s:478:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s + +void checkRHSUnique(int x, int y, Switchable e) { + // Disallowed: + if (x == 0) { + } else if (x == 0) { + } + + if (e == A) { + } else if (e == (Switchable)0) { + } +} + +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:474:3 -at=%s:478:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s + +void allowLHSParens(int x) { + if ((x) == 0) { + } else { + } +} + +void allowRHSParens(int x) { + if (x == (0)) { + } else { + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:500:3 -at=%s:506:3 %s | FileCheck --check-prefix=CHECK-YES %s + +void allowLogicalOr(int x, int y) { + // Allowed: + if (x == 0 || x == 1) { + } else { + } + + if (x == 0) { + } else if (x == 1 || x == 2) { + } + + if (x == (0) || (x == 1)) { + } else { + } + + if (x == 0) { + } else if ((x == 1 || x == 2)) { + } + + // Disallowed: + if (x == 0 && x == 1) { + } else { + } + + if (x == 0 | x == 1) { + } else { + } + + if (x == 0 || isTrue()) { + } else if (y == 2) { + } + + if (x == 0 || x == 1) { + } else if (y == 2) { + } + + if (x == 0) { + } else if (x == 1 || x == integerFunction()) { + } + + if (x == 1) { + } else if (x == 2 || x == 1) { + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:515:3 -at=%s:519:3 -at=%s:523:3 -at=%s:527:3 %s | FileCheck --check-prefix=CHECK-YES %s +// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:532:3 -at=%s:536:3 -at=%s:540:3 -at=%s:544:3 -at=%s:548:3 -at=%s:552:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s + +void parenImpCastsLHSEquivalence(int x) { + if ((x) == 1) { + } else if (x == 2) { + } +} + +// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:561:3 %s | FileCheck --check-prefix=CHECK-YES %s diff --git a/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp b/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp new file mode 100644 index 0000000000000..a16e165498696 --- /dev/null +++ b/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp @@ -0,0 +1,307 @@ +void foo(); + +void simpleCompoundBodyIf(int x) { + foo(); + + if (x == 2) { // CHECK1: "switch (" [[@LINE]]:3 -> [[@LINE]]:7 + (void)x; // CHECK1-NEXT: ") {\ncase " [[@LINE-1]]:8 -> [[@LINE-1]]:12 + // CHECK1-NEXT: ":" [[@LINE-2]]:13 -> [[@LINE-2]]:16 + } else { // CHECK1-NEXT: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:11 + foo(); + } + + if (((x) == 22 || x == 3) || x == 4) { // CHECK1: "switch (" [[@LINE]]:3 -> [[@LINE]]:9 + // CHECK1-NEXT: ") {\ncase " [[@LINE-1]]:10 -> [[@LINE-1]]:15 + // CHECK1-NEXT: ":\ncase " [[@LINE-2]]:17 -> [[@LINE-2]]:26 + // CHECK1-NEXT: ":\ncase " [[@LINE-3]]:27 -> [[@LINE-3]]:37 + // CHECK1-NEXT: ":" [[@LINE-4]]:38 -> [[@LINE-4]]:41 + } else { // CHECK1-NEXT: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:11 + } + + foo(); +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:6:3 -at=%s:13:3 %s | FileCheck --check-prefix=CHECK1 %s + +void colonInsertionCompoundBody(int x) { + if (x == 2) // CHECK2: ":" [[@LINE]]:13 -> [[@LINE+2]]:4 + + { + + } + else { + } +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:27:3 %s | FileCheck --check-prefix=CHECK2 %s + +void colonInsertionNonCompoundBody(int x) { + if (x == 2) foo(); // CHECK3: ":" [[@LINE]]:13 -> [[@LINE]]:14 + else { + } + + if (x == 2) // CHECK3: ":" [[@LINE]]:13 -> [[@LINE]]:14 + foo(); + else { + } + + if ((x == (2)) /*comment*/) // CHECK3: ":" [[@LINE]]:15 -> [[@LINE]]:30 + foo(); + else { + } + + if (x == 2 // CHECK3: ":" [[@LINE]]:13 -> [[@LINE+1]]:8 + ) + // comment + foo(); + else { + } +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:39:3 -at=%s:43:3 -at=%s:48:3 -at=%s:53:3 %s | FileCheck --check-prefix=CHECK3 %s + +void colonInsertionFailure(int x) { +#define EMPTY_MACRO + if (x == 1 EMPTY_MACRO ) foo(); + else { + } +} + +// RUN: not clang-refactor-test perform -action if-switch-conversion -at=%s:65:3 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR1 %s +// CHECK-ERR1: failed to perform the refactoring operation (couldn't find the location of ')') + +void elseNonCompoundBody(int x) { +#ifdef WITH_ELSEIF + if (x == 1) foo(); else +#endif + if (x == 2) + foo(); + else // CHECK4: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:7 + foo(); + +#ifdef WITH_ELSEIF + if (x == 1) foo(); else +#endif + if (x == 2) + foo(); + else foo(); // CHECK4: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:7 + +#ifdef WITH_ELSEIF + if (x == 1) foo(); else +#endif + if (x == 2) foo(); /*comment*/ else foo(); // CHECK4: "\nbreak;\ndefault:" [[@LINE]]:34 -> [[@LINE]]:38 + +#ifdef WITH_ELSEIF + if (x == 1) foo(); else +#endif + if (x == 2) ; else ; // CHECK4: "\nbreak;\ndefault:" [[@LINE]]:17 -> [[@LINE]]:21 +} + +void elseCompoundBody(int x) { +#ifdef WITH_ELSEIF + if (x == 1) foo(); else +#endif + if (x == 2) { foo(); } else { foo(); } // CHECK4: "\nbreak;\ndefault:" [[@LINE]]:24 -> [[@LINE]]:32 + +#ifdef WITH_ELSEIF + if (x == 1) foo(); else +#endif + if (x == 2) { + // comment. + } + else // CHECK4: "break;\ndefault:" [[@LINE-1]]:3 -> [[@LINE+1]]:4 + { + foo(); + } +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:77:3 -at=%s:85:3 -at=%s:92:3 -at=%s:97:3 -at=%s:104:3 -at=%s:109:3 %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:77:3 -at=%s:85:3 -at=%s:92:3 -at=%s:97:3 -at=%s:104:3 -at=%s:109:3 %s -D WITH_ELSEIF | FileCheck --check-prefix=CHECK4 %s + +void elseIfCompoundBody(int x) { + if (x == 2) { + foo(); + } else if (x == 3) { // CHECK5: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:19 + foo(); // CHECK5-NEXT: ":" [[@LINE-1]]:20 -> [[@LINE-1]]:23 + } else if (x == 4 || x == 55) { // CHECK5-NEXT: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:19 + foo(); // CHECK5-NEXT: ":\ncase " [[@LINE-1]]:20 -> [[@LINE-1]]:29 + // CHECK5-NEXT: ":" [[@LINE-2]]:31 -> [[@LINE-2]]:34 + } + + if (x == 2) { foo(); } else if (x == 3) { foo(); } // CHECK5: "\nbreak;\ncase " [[@LINE]]:24 -> [[@LINE]]:40 + // CHECK5-NEXT: ":" [[@LINE-1]]:41 -> [[@LINE-1]]:44 + + if (x == 2) { + // comment. + } + else if (x == 3) // CHECK5: "break;\ncase " [[@LINE-1]]:3 -> [[@LINE]]:17 + { // CHECK5-NEXT: ":" [[@LINE-1]]:18 -> [[@LINE]]:4 + foo(); + } +} + +void elseIfNonCompoundBody(int x) { + if (x == 2) + foo(); + else if (x == 21) // CHECK5: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:17 + foo(); // CHECK5-NEXT: ":" [[@LINE-1]]:19 -> [[@LINE-1]]:20 + else if (x == 5) ;// CHECK5-NEXT: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:17 + // CHECK5-NEXT: ":" [[@LINE-1]]:18 -> [[@LINE-1]]:19 + + if (x == 2) foo(); /*comment*/ else if (x == 3) foo(); // CHECK5: "\nbreak;\ncase " [[@LINE]]:34 -> [[@LINE]]:48 + // CHECK5-NEXT: ":" [[@LINE-1]]:49 -> [[@LINE-1]]:50 + + if (x == 2) ; else if (x == 3) ; // CHECK5: "\nbreak;\ncase " [[@LINE]]:17 -> [[@LINE]]:31 +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:122:3 -at=%s:131:3 -at=%s:134:3 -at=%s:144:3 -at=%s:151:3 -at=%s:154:3 %s | FileCheck --check-prefix=CHECK5 %s + +void closingBraceInsertion(int x) { + if (x == 2) { + } else if (x == 3) { + } else { + foo(); + } // CHECK6: "break;\n" [[@LINE]] + + if (x == 3) { + } else if (x == 4) { + } // CHECK6: "break;\n" [[@LINE]] + + if (x == 2) + foo(); + else if (x == 3) + foo(); + else if (x == 4) // CHECK6: "\nbreak;\n}" [[@LINE+1]]:11 -> [[@LINE+1]]:11 + foo(); + + if (x == 2) + foo(); + else // CHECK6: "\nbreak;\n}" [[@LINE+1]]:11 -> [[@LINE+1]]:11 + foo(); + + if (x == 2) foo(); // CHECK6: "\nbreak;\n}" [[@LINE+1]]:35 -> [[@LINE+1]]:35 + else foo(); // preserve comments + + if (x == 2) foo(); + else if (x == 3) // CHECK6: "\nbreak;\n}" [[@LINE+1]]:12 -> [[@LINE+1]]:12 + foo() ; foo(); // no preserve + + if (x == 2) foo(); // CHECK6: "\nbreak;\n}" [[@LINE+1]]:11 -> [[@LINE+1]]:11 + else ; ; +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:160:3 -at=%s:166:3 -at=%s:170:3 -at=%s:177:3 -at=%s:182:3 -at=%s:185:3 -at=%s:189:3 %s | FileCheck --check-prefix=CHECK6 %s + +void needBreaks(int x) { + if (x == 2) { + return; + x = 3; + } else if (x == 3) { // CHECK7: "break;\ncase " [[@LINE]] + foo(); + return; + foo(); + } else { // CHECK7: "break;\ndefault:" [[@LINE]] + if (x == 1) { + return; + } + } // CHECK7: "break;\n" [[@LINE]]:3 + + if (x == 2) + if (x == 3) + return; + else ; else // CHECK7: "\nbreak;\ndefault:" [[@LINE]] + while (x < 2) + return; // CHECK7: "\nbreak;\n}" [[@LINE]]:52 +} + +void noNeedForBreaks(int x) { + if (x == 2) { + return; + } else if (x == 3) { // CHECK7: "case " [[@LINE]] + foo(); + return; + } else { // CHECK7: "default:" [[@LINE]] + if (x == 1) { + } + { + return; + } + } // CHECK7-NOT: "{{.*}}break{{.*}}" [[@LINE]] + + if (x == 2) return; else return; // CHECK7: "\ndefault:" [[@LINE]] + // CHECK7: "\n}" [[@LINE-1]] + + // Invalid returns should work as well. + if (x == 2) + return 1; + else // CHECK7: "default:" [[@LINE]] + return 2; // CHECK7: "\n}" [[@LINE]] +} + +int noNeedForBreaksInvalidRets(int x) { + if (x == 2) + return; // This omits the 'break'. + // But this doesn't (should it?). + else { // CHECK7: "default:" [[@LINE]] + return ""; + } // CHECK7: "break;\n" [[@LINE]] +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:196:3 -at=%s:209:3 -at=%s:218:3 -at=%s:231:3 -at=%s:235:3 -at=%s:242:3 %s | FileCheck --check-prefix=CHECK7 %s + +void needBraces(int x) { + if (x == 2) { // CHECK8: ": {" [[@LINE]]:13 -> [[@LINE]]:16 + int a = x; + } else if (x == 1) { // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3 + int a = 0, y = 1; // CHECK8-NEXT: ": {" [[@LINE-1]]:20 -> [[@LINE-1]]:23 + return; + } else if (x == 3 || x == 4) { // CHECK8-NEXT: "}\ncase " [[@LINE]]:3 + int m = 2; // CHECK8-NEXT: ":\ncase " [[@LINE-1]] + // CHECK8-NEXT: ": {" [[@LINE-2]]:30 -> [[@LINE-2]]:33 + } else if (x == 5) { // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3 + return; // CHECK8-NEXT: ":" [[@LINE-1]]:20 -> [[@LINE-1]]:23 + } else { // CHECK8-NEXT: "default: {" [[@LINE]]:3 -> [[@LINE]]:11 + foo(); + int k = x; + foo(); + } // CHECK8-NEXT: "break;\n}\n" [[@LINE]]:3 -> [[@LINE]]:3 + + if (x == 2) { // CHECK8: ": {" [[@LINE]] + int a = 2; + } else if (x == 3) { // CHECK8: "break;\n}\ncase " [[@LINE]] + int b = x; // CHECK8-NEXT: ": {" [[@LINE-1]] + } // CHECK8-NEXT: "break;\n}\n" [[@LINE]] + + if (x == 2) // CHECK8: ": {" [[@LINE]]:13 -> [[@LINE]]:14 + int a = x; + else if (x == 1 || x == 3) // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3 -> [[@LINE]]:17 + int b = 2; // CHECK8-NEXT: ":\ncase " [[@LINE-1]] + // CHECK8-NEXT: ": {" [[@LINE-2]]:28 -> [[@LINE-2]]:29 + else if (x == 4) // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3 -> [[@LINE]]:17 + foo(); // CHECK8-NEXT: ":" [[@LINE-1]] + else if (x == 5) // CHECK8-NEXT: "break;\ncase " [[@LINE]] + return; // CHECK8-NEXT: ":" [[@LINE-1]] + else // CHECK8-NEXT: "default: {" [[@LINE]]:3 -> [[@LINE]]:7 + int c = x; + // CHECK8-NEXT: "\nbreak;\n}\n}" [[@LINE-1]]:15 -> [[@LINE-1]]:15 + + if (x == 2) int a = 1; else int k = x; + // CHECK8: ": {" [[@LINE-1]]:13 -> [[@LINE-1]]:14 + // CHECK8-NEXT: "\nbreak;\n}\ndefault: {" [[@LINE-2]]:26 -> [[@LINE-2]]:30 + // CHECK8-NEXT: "\nbreak;\n}\n}" [[@LINE-3]]:41 -> [[@LINE-3]]:41 +} + +void noBracesNeeded(int x) { + if (x == 2) { // CHECK8: ":" [[@LINE]] + if (int *z = p) { + } + } else if (x == 3) { // CHECK8: "break;\ncase " [[@LINE]] + for (int z = 0; z < x ; ++z) ; // CHECK8: ":" [[@LINE-1]] + } else if (x == 4) { // CHECK8: "break;\ncase " [[@LINE]] + { // CHECK8: ":" [[@LINE-1]] + int a = 1; + } + } +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:253:3 -at=%s:269:3 -at=%s:275:3 -at=%s:288:3 -at=%s:295:3 %s | FileCheck --check-prefix=CHECK8 %s diff --git a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/class.cpp b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/class.cpp new file mode 100644 index 0000000000000..f6389e2f1ddb0 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/class.cpp @@ -0,0 +1,25 @@ + +struct Class { + int field; + + Class(); + + Class(int x) { } + + ~Class(); + + // commment + static void method(const int &value, int defaultParam = 20); + + virtual int voidMethod(int y) const; + void implementedMethod() const { + + } + + void outOfLineImpl(int x); + + void anotherImplementedMethod() { + + } +}; +// CHECK1: "{{.*}}class.cpp" "\n\nClass::Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n" [[@LINE-1]]:3 diff --git a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.cpp b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.cpp new file mode 100644 index 0000000000000..6c16bdf0716a9 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.cpp @@ -0,0 +1,69 @@ +#include "classInHeader.h" + +#ifndef NO_IMPL + +#define PREFIX + +#ifdef USE_NAMESPACE +#ifdef USE_NAMESPACE_PREFIX +#define PREFIX ns::ns2:: +#else +#ifdef USE_NAMESPACE_USING +using namespace ns::ns2; +#else +namespace ns { +namespace ns2 { +#define CLOSE_NAMESPACES +#endif +#endif +#endif + +void PREFIX ClassInHeader::implementedToo() { + +} + +void PREFIX ClassInHeader::implemented() { + +} +// CHECK1: "{{.*}}classInHeader.cpp" "\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE-1]]:2 +// CHECK1-NS-PREFIX: "{{.*}}classInHeader.cpp" "\n\nvoid ns::ns2::ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ns::ns2::ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE-2]]:2 + +#ifdef CLOSE_NAMESPACES +} +} +#endif + +#endif + +namespace other { +#ifndef USE_NAMESPACE_USING +using namespace ns::ns2; +#else +} + +void usingCanBeHidden() { +#ifndef USE_NAMESPACE_USING +using namespace ns::ns2; +#else +} + +#ifdef USE_NAMESPACE_USING +using namespace ns::ns2; +#else +// We still want to insert 'using namespace ns::ns2' if the outer is already +// used. +using namespace ns; +#endif + +using namespace other; + +namespace ns { +namespace ns2 { +// Prefer to insert the methods at the end using 'using' instead of into a +// namespace. +} +} + +// CHECK1-NO-IMPL-USING-NS-IN-RECORD: "{{.*}}classInHeader.cpp" "\nusing namespace ns::ns2;\n\nvoid OuterRecord::ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid OuterRecord::ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE+3]]:1 +// CHECK1-NO-IMPL-USING-NS: "{{.*}}classInHeader.cpp" "\nusing namespace ns::ns2;\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE+2]]:1 +// CHECK1-NO-IMPL: "{{.*}}classInHeader.cpp" "\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE+1]]:1 -> [[@LINE+1]]:1 diff --git a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.h b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.h new file mode 100644 index 0000000000000..89c01bd5b8cba --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.h @@ -0,0 +1,31 @@ + +#ifdef USE_NAMESPACE +namespace ns { +namespace ns2 { +#endif + +#ifdef USE_ENCLOSING_RECORD +struct OuterRecord { +#endif + +struct ClassInHeader { + void pleaseImplement(); + void implemented(); + void pleaseImplementThisAsWell(); + void implementedToo(); + void anotherMethod(); +}; + +#ifdef USE_ENCLOSING_RECORD +} +#endif + +void ClassInHeader::anotherMethod() { +} +// CHECK: "{{.*}}classInHeader.h" "\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE-1]]:2 -> [[@LINE-1]]:2 + +#ifdef USE_NAMESPACE +} +} +#endif + diff --git a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/empty.cpp b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/empty.cpp new file mode 100644 index 0000000000000..8d1c8b69c3fce --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/empty.cpp @@ -0,0 +1 @@ + diff --git a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m new file mode 100644 index 0000000000000..1b31110b051ee --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m @@ -0,0 +1,13 @@ +#include "objcHeader.h" + +@implementation MyClass + +#ifdef MIX_IMPL ++ (void)classMethod { } + +- (void)method:(int)x with:(int)y { } +#endif + +@end +// CHECK1: "{{.*}}objcClass.m" "- (void)method { \n <#code#>;\n}\n\n+ (void)classMethod { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n- (void)method:(int)x with:(int)y { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// CHECK2: "{{.*}}objcClass.m" "- (void)method { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n" [[@LINE-2]]:1 diff --git a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcHeader.h b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcHeader.h new file mode 100644 index 0000000000000..8d79abedf4276 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcHeader.h @@ -0,0 +1,13 @@ + +@interface MyClass + +- (void)method; + ++ (void)classMethod; + +- (void)implementedMethod; + +- (void)method:(int)x with:(int)y; + +@end + diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.cpp b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.cpp new file mode 100644 index 0000000000000..b94d92b676282 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.cpp @@ -0,0 +1,64 @@ + +struct Class { + int field; + + Class(); + + Class(int x) { } + + ~Class(); + + // commment + void method(); + + virtual voidMethod(int y) const; + void implementedMethod() const { + + } + + void outOfLineImpl(int x); + + void anotherImplementedMethod() { + + } +}; +// CHECK1: Initiated the 'implement-declared-methods' action at [[@LINE-20]]:3 +// CHECK2: Initiated the 'implement-declared-methods' action at [[@LINE-17]]:3 +// CHECK3: Initiated the 'implement-declared-methods' action at [[@LINE-15]]:3 +// CHECK4: Initiated the 'implement-declared-methods' action at [[@LINE-14]]:3 + +void function(); + +void function() { + +} + +void Class::outOfLineImpl(int x) { + +} + +// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:5:3-10 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:9:3-11 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:12:3-16 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:14:3-34 %s | FileCheck --check-prefix=CHECK4 %s + +// RUN: not clang-refactor-test initiate -action implement-declared-methods -in=%s:2:1-end -in=%s:3:1-end -in=%s:4:1-end -in=%s:5:1-2 -in=%s:7:1-end -in=%s:9:1-2 -in=%s:11:1-end -in=%s:15:1-end -in=%s:16:1-end -in=%s:17:1-end -in=%s:19:1-end %s -in=%s:30:1-end 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// CHECK-NO: Failed to initiate the refactoring action + +// RUN: clang-refactor-test list-actions -at=%s:5:3 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Generate Missing Function Definitions + +// Class, ~Class, method, voidMethod: +// CHECK5: Initiated the 'implement-declared-methods' action at [[@LINE-48]]:3 -> [[@LINE-39]]:34 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:2:1-24:3 -selected=%s:3:1-23:1 -selected=%s:4:1-23:3 -selected=%s:5:1-14:35 -selected=%s:5:9-14:4 %s | FileCheck --check-prefix=CHECK5 %s + +// ~Class, method +// CHECK6: Initiated the 'implement-declared-methods' action at [[@LINE-48]]:3 -> [[@LINE-45]]:16 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:9:1-12:16 -selected=%s:7:1-13:1 -selected=%s:7:17-12:4 %s | FileCheck --check-prefix=CHECK6 %s + +// voidMethod +// CHECK7: Initiated the 'implement-declared-methods' action at [[@LINE-47]]:3 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:14:3-14:10 -selected=%s:14:22-14:27 %s | FileCheck --check-prefix=CHECK7 %s + +// RUN: not clang-refactor-test initiate -action implement-declared-methods -selected=%s:2:1-30:10 -selected=%s:15:1-15:10 -selected=%s:16:1-16:3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.m b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.m new file mode 100644 index 0000000000000..626926bc7361c --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.m @@ -0,0 +1,106 @@ + +@protocol P + +- (void)method; + +@end + +@interface MyClass { + int ivar; +} + +@property int prop; + +- (void)method; + +// comment ++ (void)classMethod; + +- (void)implementedMethod; + +- (void)method:(int)x with:(int)y; + +@end + +@implementation MyClass + +- (void)implementedMethod { + +} + +@end + + +// CHECK1: Initiated the 'implement-declared-methods' action at [[@LINE-20]]:1 +// CHECK2: Initiated the 'implement-declared-methods' action at [[@LINE-18]]:1 +// CHECK3: Initiated the 'implement-declared-methods' action at [[@LINE-15]]:1 + +// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:14:1-14 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:17:1-20 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:21:1-34 %s | FileCheck --check-prefix=CHECK3 %s + +// RUN: not clang-refactor-test initiate -action implement-declared-methods -in=%s:4:1-end -in=%s:27:1-end -in=%s:8:1-end -in=%s:9:1-end -in=%s:12:1-end -in=%s:16:1-end -in=%s:19:1-end -in=%s:23:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// CHECK-NO: Failed to initiate the refactoring action + +// method, classMethod, method:with: : +// CHECK4: Initiated the 'implement-declared-methods' action at [[@LINE-33]]:1 -> [[@LINE-26]]:35 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:8:1-24:3 -selected=%s:9:1-23:3 -selected=%s:14:1-21:35 -selected=%s:14:14-21:2 %s | FileCheck --check-prefix=CHECK4 %s + +// classMethod, method:with: +// CHECK5: Initiated the 'implement-declared-methods' action at [[@LINE-34]]:1 -> [[@LINE-30]]:35 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:17:1-21:35 -selected=%s:16:1-22:1 -selected=%s:17:20-21:2 %s | FileCheck --check-prefix=CHECK5 %s + +// classMethod +// CHECK6: Initiated the 'implement-declared-methods' action at [[@LINE-38]]:1 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:17:1-17:10 -selected=%s:17:20-18:1 %s | FileCheck --check-prefix=CHECK6 %s + +// RUN: not clang-refactor-test initiate -action implement-declared-methods -selected=%s:2:1-30:10 -selected=%s:2:1-6:10 -selected=%s:6:1-25:2 -selected=%s:27:1-29:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// Methods declared in class extensions / categories should be supported: + +@interface I2 + +@end + +@interface I2 () + +- (void)method; ++ (void)classMethod; +- (void)implementedMethod; + +@end +// CHECK7: Initiated the 'implement-declared-methods' action at [[@LINE-5]]:1 +// RUN: clang-refactor-test initiate -action implement-declared-methods -at=%s:68:1 %s | FileCheck --check-prefix=CHECK7 %s +// RUN: not clang-refactor-test initiate -action implement-declared-methods -at=%s:70:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// CHECK8: Initiated the 'implement-declared-methods' action at [[@LINE-9]]:1 -> [[@LINE-8]]:21 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:68:1-71:1 %s | FileCheck --check-prefix=CHECK8 %s + +@implementation I2 + +- (void)implementedMethod { +} + +@end + +@interface I2 (Extension) + +- (void)methodExt; ++ (void)classMethodExt; +- (void)implementedMethodExt; + +@end +// CHECK9: Initiated the 'implement-declared-methods' action at [[@LINE-5]]:1 +// RUN: clang-refactor-test initiate -action implement-declared-methods -at=%s:89:1 %s | FileCheck --check-prefix=CHECK9 %s +// RUN: not clang-refactor-test initiate -action implement-declared-methods -at=%s:91:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// CHECK10: Initiated the 'implement-declared-methods' action at [[@LINE-9]]:1 -> [[@LINE-8]]:24 +// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:89:1-92:1 %s | FileCheck --check-prefix=CHECK10 %s + +@implementation I2 (Extension) + +- (void)implementedMethodExt { +} + +@end diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp new file mode 100644 index 0000000000000..82ea02187aeb6 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp @@ -0,0 +1,164 @@ + +struct Class { + int field; + + Class(); + + Class(int x) { } + + ~Class(); + + // commment middle-methods-begin: +1:1 + static void method(const int &value, int defaultParam = 20); + + virtual int voidMethod(int y) const; + void implementedMethod() const { // middle-methods-end: -1:40 + + } + + void outOfLineImpl(int x); + + void anotherImplementedMethod() { + + } +}; +// CHECK1: "{{.*}}implement-declared-methods.cpp" "\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n\nint Class::voidMethod(int y) const { \n <#code#>;\n}\n" [[@LINE+5]]:37 -> [[@LINE+5]]:37 +// CHECK2: "{{.*}}implement-declared-methods.cpp" "\n\nClass::Class() { \n <#code#>;\n}\n\nClass::~Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n\nint Class::voidMethod(int y) const { \n <#code#>;\n}\n" [[@LINE+4]]:37 +// CHECK3: "{{.*}}implement-declared-methods.cpp" "\n\nClass::~Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n" [[@LINE+3]]:37 +// CHECK4: "{{.*}}implement-declared-methods.cpp" "\n\nClass::Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n" [[@LINE+2]]:37 + +void Class::outOfLineImpl(int x) { } + +// query-all-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 0, 0, 0] }] }] +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=middle-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:8:1-12:10 -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK3 %s + +// Implement the constructor and method: +// query-mix-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 1, 0, 1] }] }] +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%s -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK4 %s + +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%S/Inputs/class.cpp -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK1 %S/Inputs/class.cpp + +// Empty continuation TU should produce an error: +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%S/Inputs/empty.cpp -query-results=query-mix-impl %s 2>&1 | FileCheck --check-prefix=CHECK-EMPTY-ERR %s +// CHECK-EMPTY-ERR: failed to perform the refactoring continuation (the target class is not defined in the continuation AST unit)! + +#ifdef USE_NAMESPACE +namespace ns { +namespace ns2 { +#endif + +#ifdef USE_ENCLOSING_RECORD +struct OuterRecord { +#endif + +struct ClassInHeader { +// class-in-header-begin: +1:1 + void pleaseImplement(); + void implemented(); + void pleaseImplementThisAsWell(); + void implementedToo(); +// class-in-header-end: +1:1 +}; + +#ifdef USE_ENCLOSING_RECORD +struct } +#endif + +#ifdef USE_NAMESPACE +} +} +#endif + +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK1 %S/Inputs/classInHeader.cpp +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DUSE_NAMESPACE | FileCheck --check-prefix=CHECK1 %S/Inputs/classInHeader.cpp +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DUSE_NAMESPACE -DUSE_NAMESPACE_USING | FileCheck --check-prefix=CHECK1 %S/Inputs/classInHeader.cpp +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DUSE_NAMESPACE -DUSE_NAMESPACE_PREFIX | FileCheck --check-prefix=CHECK1-NS-PREFIX %S/Inputs/classInHeader.cpp + +// Test when the implementation file has no out-of-line definitions. +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL | FileCheck --check-prefix=CHECK1-NO-IMPL %S/Inputs/classInHeader.cpp +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL -DUSE_NAMESPACE | FileCheck --check-prefix=CHECK1-NO-IMPL-USING-NS %S/Inputs/classInHeader.cpp +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL -DUSE_NAMESPACE -DUSE_ENCLOSING_RECORD | FileCheck --check-prefix=CHECK1-NO-IMPL-USING-NS-IN-RECORD %S/Inputs/classInHeader.cpp +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL -DUSE_NAMESPACE -DUSE_NAMESPACE_USING | FileCheck --check-prefix=CHECK1-NO-IMPL %S/Inputs/classInHeader.cpp + +// query-mix-impl-header: [ { name: ast.producer.query, filenameResult: "%S/classInHeader.h" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 1, 0, 1] }] }] +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl-header %s | FileCheck %S/Inputs/classInHeader.h + +// Ensure that the methods which are placed right after the record are placed +// after the outermost record: +namespace ns { + +struct AfterRecordOuterOuter { +struct AfterRecordOuter { + struct AfterRecordInner { +// after-record-inner-begin: +1:1 + void pleaseImplement(); +// after-record-inner-end: +0:1 + }; + + AfterRecordOuter(); +}; +// comment +}; +// CHECK-OUTERMOST: "{{.*}}implement-declared-methods.cpp" "\n\nvoid AfterRecordOuterOuter::AfterRecordOuter::AfterRecordInner::pleaseImplement() { \n <#code#>;\n}\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3 + +} +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=after-record-inner -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-OUTERMOST %s + +#ifdef INNER_TEMPLATE +template<typename T> +struct OuterTemplateRecord { +#else + template<typename U> +#endif + struct InnerTemplate { +// inner-template-begin: +0:1 + InnerTemplate(); + void function(); +// inner-template-end: +1:1 + }; +#ifdef INNER_TEMPLATE +}; +#endif + +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=inner-template %s 2>&1 | FileCheck --check-prefix=CHECK-TEMPLATE-NO %s +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=inner-template %s -DINNER_TEMPLATE 2>&1 | FileCheck --check-prefix=CHECK-TEMPLATE-NO %s + +// CHECK-TEMPLATE-NO: Failed to initiate the refactoring action (templates are unsupported)! + +template<int x, typename T> +class TemplateSpecialization { +}; + +template<> +class TemplateSpecialization<0, int> { +// template-specialization-begin: +0:1 + TemplateSpecialization(); + void function(); + void operator ()(int) const; + operator int() const; +// template-specialization-end: +0:1 +}; +// CHECK-SPECIALIZATION: "{{.*}}implement-declared-methods.cpp" "\n\nTemplateSpecialization<0, int>::TemplateSpecialization() { \n <#code#>;\n}\n\nvoid TemplateSpecialization<0, int>::function() { \n <#code#>;\n}\n\nvoid TemplateSpecialization<0, int>::operator()(int) const { \n <#code#>;\n}\n\nTemplateSpecialization<0, int>::operator int() const { \n <#code#>;\n}\n" [[@LINE-1]]:3 +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=template-specialization -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-SPECIALIZATION %s + +template<int x> +class TemplateSpecialization<x, int> { +// template-partial-specialization-begin: +0:1 + void function(); +// template-partial-specialization-end: +0:1 +}; + +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=template-partial-specialization %s -DINNER_TEMPLATE 2>&1 | FileCheck --check-prefix=CHECK-TEMPLATE-NO %s + +struct ProhibitTemplateFunctions { +// template-function-begin: +0:1 + void function(); + template<typename T> + void functionTemplate(const T &); + void anotherFunction(); +// template-function-end: +0:1 +}; +// CHECK-FUNCTION-TEMPLATE: "{{.*}}implement-declared-methods.cpp" "\n\nvoid ProhibitTemplateFunctions::function() { \n <#code#>;\n}\n\nvoid ProhibitTemplateFunctions::anotherFunction() { \n <#code#>;\n}\n" [[@LINE-1]]:3 +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=template-function -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-FUNCTION-TEMPLATE %s diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m new file mode 100644 index 0000000000000..3949e32650c48 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m @@ -0,0 +1,55 @@ + +@interface MyClass + +// all-methods-begin: +1:1 +- (void)method; + ++ (void)classMethod; + +- (void)implementedMethod; + +- (void)method:(int)x with:(int)y; +// all-methods-end: +0:1 + +@end + +#ifndef NO_IMPL +@implementation MyClass + +- (void)someOtherMethod { } + +@end +// CHECK1: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n+ (void)classMethod { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n- (void)method:(int)x with:(int)y { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 +// CHECK2: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n" [[@LINE-2]]:1 +#endif +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK2 %s + +// query-all-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 0, 0, 0] }] }] +// query-mix-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 1, 0, 1] }] }] + +// Empty continuation TU or TU without @implementation should produce an error: +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/empty.cpp -query-results=query-all-impl %s 2>&1 | FileCheck --check-prefix=CHECK-EMPTY-ERR %s +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s -DNO_IMPL 2>&1 | FileCheck --check-prefix=CHECK-EMPTY-ERR %s +// CHECK-EMPTY-ERR: failed to perform the refactoring continuation (the target @interface is not implemented in the continuation AST unit)! + +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/objcClass.m -query-results=query-all-impl %s -DNO_IMPL | FileCheck --check-prefix=CHECK1 %S/Inputs/objcClass.m +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/objcClass.m -query-results=query-mix-impl %s -DNO_IMPL -DMIX_IMPL | FileCheck --check-prefix=CHECK2 %S/Inputs/objcClass.m + +@interface MyClass (Category) + +// all-category-methods-begin: +1:1 +- (void)categoryMethod; ++ (MyClass *)classCategoryMethod; +// all-category-methods-end: +0:1 + +@end + +@implementation MyClass (Category) + +- (void)anotherMethod { +} + +@end +// CHECK3: "{{.*}}implement-declared-methods.m" "- (void)categoryMethod { \n <#code#>;\n}\n\n+ (MyClass *)classCategoryMethod { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-category-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK3 %s diff --git a/clang/test/Refactor/ImplementDeclaredMethods/local-record.cpp b/clang/test/Refactor/ImplementDeclaredMethods/local-record.cpp new file mode 100644 index 0000000000000..6fbc3e46808d9 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/local-record.cpp @@ -0,0 +1,30 @@ +void function() { +struct Class { + // one-method: +2:3 + // all-methods-begin: +1:1 + Class(); + + ~Class(); // comment + + int constOverride() const override; + + // comment + void method(const int &value, int defaultParam = 20) + ; + + void implementedMethod() const { + + } +}; +// all-methods-end: -1:1 +} +// CHECK1: " { \n <#code#>;\n}" [[@LINE-16]]:10 -> [[@LINE-16]]:11 +// RUN: clang-refactor-test perform -action implement-declared-methods -at=one-method %s | FileCheck --check-prefix=CHECK1 %s + +// CHECK2: " { \n <#code#>;\n}" [[@LINE-19]]:10 -> [[@LINE-19]]:11 +// CHECK2-NEXT: "" [[@LINE-18]]:11 -> [[@LINE-18]]:12 +// CHECK2-NEXT: " { \n <#code#>;\n}" [[@LINE-19]]:23 -> [[@LINE-19]]:23 +// CHECK2-NEXT: " { \n <#code#>;\n}" [[@LINE-18]]:37 -> [[@LINE-18]]:38 +// CHECK2-NEXT: " { \n <#code#>;\n}" [[@LINE-15]]:7 -> [[@LINE-15]]:8 +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods %s | FileCheck --check-prefix=CHECK2 %s + diff --git a/clang/test/Refactor/ImplementDeclaredMethods/prohibited-methods.cpp b/clang/test/Refactor/ImplementDeclaredMethods/prohibited-methods.cpp new file mode 100644 index 0000000000000..6f434d0d02210 --- /dev/null +++ b/clang/test/Refactor/ImplementDeclaredMethods/prohibited-methods.cpp @@ -0,0 +1,19 @@ +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods %s -std=c++11 | FileCheck %s +// default, deleted and pure methods should not be implemented! + +void function() { +struct Class { +// all-methods-begin: +1:1 + Class(); +// CHECK: " { \n <#code#>;\n}" [[@LINE-1]]:10 -> [[@LINE-1]]:11 + + Class(const Class &Other) = default; + Class(const Class &&Other) = delete; + + virtual void pureMethod(int x) = 0; + + virtual void method() const; +// CHECK-NEXT: " { \n <#code#>;\n}" [[@LINE-1]]:30 -> [[@LINE-1]]:31 +}; +// all-methods-end: -1:1 +} diff --git a/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-initiate.m b/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-initiate.m new file mode 100644 index 0000000000000..335608e68078b --- /dev/null +++ b/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-initiate.m @@ -0,0 +1,26 @@ +@class NSString; + +void initiate() { + NSString *string = @"hello"; + const char *cString = "world"; +} + +// RUN: clang-refactor-test list-actions -at=%s:4:25 %s | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Wrap in NSLocalizedString + +// Ensure the the action can be initiated in the string literal: + +// RUN: clang-refactor-test initiate -action localize-objc-string-literal -in=%s:4:22-30 %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: Initiated the 'localize-objc-string-literal' action at 4:22 + +// Ensure that the action can't be initiated in other places: + +// RUN: not clang-refactor-test initiate -action localize-objc-string-literal -in=%s:1:1-10 -in=%s:3:1-18 -in=%s:4:1-21 -in=%s:5:1-32 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO: Failed to initiate the refactoring action + +// Ensure that the action can be initiated using a selection, and only when that +// selection doesn't go out of the string. + +// RUN: clang-refactor-test initiate -action localize-objc-string-literal -selected=%s:4:22-4:30 -selected=%s:4:25-4:30 -selected=%s:4:22-4:27 -selected=%s:4:25-4:27 -selected=%s:4:24-4:29 -selected=%s:4:23-4:30 %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: not clang-refactor-test initiate -action localize-objc-string-literal -selected=%s:4:20-4:30 -selected=%s:4:1-4:25 -selected=%s:4:25-5:5 -selected=%s:3:17-6:2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m b/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m new file mode 100644 index 0000000000000..8c0de42faa39a --- /dev/null +++ b/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m @@ -0,0 +1,9 @@ +@class NSString; + +void perform() { + NSString *string = @"hello"; +} +// CHECK1: "NSLocalizedString(" [[@LINE-2]]:22 -> [[@LINE-2]]:22 +// CHECK1-NEXT: ", @"")" [[@LINE-3]]:30 -> [[@LINE-3]]:30 +// RUN: clang-refactor-test perform -action localize-objc-string-literal -at=%s:4:22 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action localize-objc-string-literal -selected=%s:4:23-4:30 %s | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/Rename/CanonicalizeInstantiatedDecls.cpp b/clang/test/Refactor/Rename/CanonicalizeInstantiatedDecls.cpp new file mode 100644 index 0000000000000..f520a2e004233 --- /dev/null +++ b/clang/test/Refactor/Rename/CanonicalizeInstantiatedDecls.cpp @@ -0,0 +1,86 @@ + +template<typename T> +class BaseTemplate { +public: + T baseTemplateFunction(); + + T baseTemplateField; + + struct NestedBaseType { }; +}; + +template<typename T, typename S> +class TemplateClass: public BaseTemplate<T> { +public: + T function() { return T(); } + + static void staticFunction() { } + + T field; + + struct NestedType { + T nestedField; + + class SubNestedType { + public: + SubNestedType(int); + }; + using TypeAlias = T; + + typedef int Typedef; + + enum Enum { + EnumCase + }; + }; +}; + +void canonicalizeInstaniationReferences(TemplateClass<int, float> &object) { + (void)object.function(); +// CHECK1: 'c:@ST>2#T#T@TemplateClass@F@function#' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK1 %s + (void)object.field; +// CHECK2: 'c:@ST>2#T#T@TemplateClass@FI@field' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK2 %s + (void)object.baseTemplateFunction(); +// CHECK3: 'c:@ST>1#T@BaseTemplate@F@baseTemplateFunction#' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK3 %s + (void)object.baseTemplateField; +// CHECK4: 'c:@ST>1#T@BaseTemplate@FI@baseTemplateField' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK4 %s + + TemplateClass<int, float>::staticFunction(); +// CHECK5: 'c:@ST>2#T#T@TemplateClass@F@staticFunction#S' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):30 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK5 %s + + TemplateClass<int, float>::NestedBaseType nestedBaseType; +// CHECK6: 'c:@ST>1#T@BaseTemplate@S@NestedBaseType' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):30 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK6 %s + TemplateClass<int, float>::NestedType nestedSubType; +// CHECK7: 'c:@ST>2#T#T@TemplateClass@S@NestedType' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):30 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK7 %s + (void)nestedSubType.nestedField; +// CHECK8: 'c:@ST>2#T#T@TemplateClass@S@NestedType@FI@nestedField' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):23 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK8 %s + + typedef TemplateClass<int, float> TT; + TT::NestedType::SubNestedType subNestedType(0); +// CHECK9: 'c:@ST>2#T#T@TemplateClass@S@NestedType' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):7 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK9 %s +// CHECK10: 'c:@ST>2#T#T@TemplateClass@S@NestedType@S@SubNestedType' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-4):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK10 %s + + TT::NestedType::TypeAlias nestedTypeAlias; +// CHECK11: 'c:@ST>2#T#T@TemplateClass@S@NestedType@TypeAlias' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK11 %s + TT::NestedType::Typedef nestedTypedef; +// CHECK12: 'c:{{.*}}CanonicalizeInstantiatedDecls.cpp@ST>2#T#T@TemplateClass@S@NestedType@T@Typedef' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK12 %s + + TT::NestedType::Enum nestedEnum; +// CHECK13: 'c:@ST>2#T#T@TemplateClass@S@NestedType@E@Enum' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK13 %s + (void)TT::NestedType::Enum::EnumCase; +// CHECK14: 'c:@ST>2#T#T@TemplateClass@S@NestedType@E@Enum@EnumCase' +// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):31 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK14 %s +} diff --git a/clang/test/Refactor/Rename/ClassAsTemplateArgument.cpp b/clang/test/Refactor/Rename/ClassAsTemplateArgument.cpp new file mode 100644 index 0000000000000..63107801a75e4 --- /dev/null +++ b/clang/test/Refactor/Rename/ClassAsTemplateArgument.cpp @@ -0,0 +1,20 @@ +class Foo /* Test 1 */ {}; // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 + +template <typename T> +void func() {} + +template <typename T> +class Baz {}; + +int main() { + func<Foo>(); /* Test 2 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11 + Baz<Foo> /* Test 3 */ obj; // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 + return 0; +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:1:7 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:10:8 -new-name=Bar %s | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:11:7 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/ClassSimpleRenaming.cpp b/clang/test/Refactor/Rename/ClassSimpleRenaming.cpp new file mode 100644 index 0000000000000..78d213c4bdf97 --- /dev/null +++ b/clang/test/Refactor/Rename/ClassSimpleRenaming.cpp @@ -0,0 +1,38 @@ +class Foo /* Test 1 */ { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +public: + void foo(int x); +}; + +void Foo::foo(int x) /* Test 2 */ {} // CHECK: rename [[@LINE]]:6 -> [[@LINE]]:9 + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:1:7 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:6:6 -new-name=Bar %s | FileCheck %s + +struct ForwardDeclaration; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:26 + +ForwardDeclaration *variable; // CHECK2: rename [[@LINE]]:1 -> [[@LINE]]:19 + +// RUN: clang-refactor-test rename-initiate -at=%s:13:8 -at=%s:15:1 -new-name=Bar %s | FileCheck --check-prefix=CHECK2 %s + +template<typename T> +struct SpecializationWithoutDefinition { }; // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:39 + +template<> +struct SpecializationWithoutDefinition<int> { }; // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:39 + +template<> +struct SpecializationWithoutDefinition<float>; // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:39 + +// RUN: clang-refactor-test rename-initiate -at=%s:20:8 -at=%s:23:8 -new-name=Bar %s | FileCheck --check-prefix=CHECK3 %s + +template<typename T> +struct Class { + void method(); +}; + +template<typename T> // CHECK4: [[@LINE]]:19 -> [[@LINE]]:20 +void Class<T>::method() { } // CHECK4: [[@LINE]]:12 -> [[@LINE]]:13 + +// RUN: clang-refactor-test rename-initiate -at=%s:35:19 -new-name=Bar %s | FileCheck --check-prefix=CHECK4 %s diff --git a/clang/test/Refactor/Rename/ComplexFunctionOverride.cpp b/clang/test/Refactor/Rename/ComplexFunctionOverride.cpp new file mode 100644 index 0000000000000..52f09e870d864 --- /dev/null +++ b/clang/test/Refactor/Rename/ComplexFunctionOverride.cpp @@ -0,0 +1,76 @@ +struct A { + virtual void foo() {} /* Test 1 */ // CHECK: rename [[@LINE]]:16 -> [[@LINE]]:19 +}; + +struct B : A { + void foo() override {} /* Test 2 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11 +}; + +struct C : B { + void foo() override {} /* Test 3 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11 +}; + +struct D : B { + void foo() override {} /* Test 4 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11 +}; + +struct E : D { + void foo() override {} /* Test 5 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11 +}; + +int main() { + A a; + a.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + B b; + b.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + C c; + c.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + D d; + d.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + E e; + e.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + return 0; +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:2:16 -new-name=bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:6:8 -new-name=bar %s | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:10:8 -new-name=bar %s | FileCheck %s +// Test 4. +// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=bar %s | FileCheck %s +// Test 5. +// RUN: clang-refactor-test rename-initiate -at=%s:18:8 -new-name=bar %s | FileCheck %s + +// Check virtual inheritance + +struct A2 { + virtual void foo() {} // CHECK-VIRT: rename [[@LINE]]:16 -> [[@LINE]]:19 +}; +struct B2 : virtual A2 { + void foo() { } // CHECK-VIRT: rename [[@LINE]]:8 -> [[@LINE]]:11 +}; +struct C2 : virtual A2 { + void foo() override { } // CHECK-VIRT: rename [[@LINE]]:8 -> [[@LINE]]:11 +}; +struct D2 : B2, C2 { + void foo() override { // CHECK-VIRT: rename [[@LINE]]:8 -> [[@LINE]]:11 + A2::foo(); // CHECK-VIRT: rename [[@LINE]]:9 -> [[@LINE]]:12 + } +}; + +int bar() { + A2 a; + a.foo(); // CHECK-VIRT: rename [[@LINE]]:5 -> [[@LINE]]:8 + D2 d; + d.foo(); // CHECK-VIRT: rename [[@LINE]]:5 -> [[@LINE]]:8 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:49:16 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s +// RUN: clang-refactor-test rename-initiate -at=%s:52:8 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s +// RUN: clang-refactor-test rename-initiate -at=%s:55:8 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s +// RUN: clang-refactor-test rename-initiate -at=%s:58:8 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s +// RUN: clang-refactor-test rename-initiate -at=%s:59:9 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s +// RUN: clang-refactor-test rename-initiate -at=%s:65:5 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s +// RUN: clang-refactor-test rename-initiate -at=%s:67:5 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s diff --git a/clang/test/Refactor/Rename/ComplicatedClassType.cpp b/clang/test/Refactor/Rename/ComplicatedClassType.cpp new file mode 100644 index 0000000000000..eb0fea88e081c --- /dev/null +++ b/clang/test/Refactor/Rename/ComplicatedClassType.cpp @@ -0,0 +1,62 @@ +// Forward declaration. +class Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 + +class Baz { + virtual int getValue() const = 0; +}; + +class Foo : public Baz { /* Test 2 */// CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +public: + Foo(int value = 0) : x(value) {} // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + + Foo &operator++(int) { // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + x++; + return *this; + } + + bool operator<(Foo const &rhs) { // CHECK: rename [[@LINE]]:18 -> [[@LINE]]:21 + return this->x < rhs.x; + } + + int getValue() const { + return 0; + } + +private: + int x; +}; + +int main() { + Foo *Pointer = 0; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + Foo Variable = Foo(10); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + // CHECK: rename [[@LINE-1]]:18 -> [[@LINE-1]]:21 + for (Foo it; it < Variable; it++) { // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11 + } + const Foo *C = new Foo(); // CHECK: rename [[@LINE]]:9 -> [[@LINE]]:12 + // CHECK: rename [[@LINE-1]]:22 -> [[@LINE-1]]:25 + const_cast<Foo *>(C)->getValue(); // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17 + Foo foo; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + const Baz &BazReference = foo; + const Baz *BazPointer = &foo; + dynamic_cast<const Foo &>(BazReference).getValue(); /* Test 3 */ // CHECK: rename [[@LINE]]:22 -> [[@LINE]]:25 + dynamic_cast<const Foo *>(BazPointer)->getValue(); /* Test 4 */ // CHECK: rename [[@LINE]]:22 -> [[@LINE]]:25 + reinterpret_cast<const Foo *>(BazPointer)->getValue(); /* Test 5 */ // CHECK: rename [[@LINE]]:26 -> [[@LINE]]:29 + static_cast<const Foo &>(BazReference).getValue(); /* Test 6 */ // CHECK: rename [[@LINE]]:21 -> [[@LINE]]:24 + static_cast<const Foo *>(BazPointer)->getValue(); /* Test 7 */ // CHECK: rename [[@LINE]]:21 -> [[@LINE]]:24 + return 0; +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=Bar %s -frtti | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:8:7 -new-name=Bar %s -frtti | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:41:22 -new-name=Bar %s -frtti | FileCheck %s +// Test 4. +// RUN: clang-refactor-test rename-initiate -at=%s:42:22 -new-name=Bar %s -frtti | FileCheck %s +// Test 5. +// RUN: clang-refactor-test rename-initiate -at=%s:43:26 -new-name=Bar %s -frtti | FileCheck %s +// Test 6. +// RUN: clang-refactor-test rename-initiate -at=%s:44:21 -new-name=Bar %s -frtti | FileCheck %s +// Test 7. +// RUN: clang-refactor-test rename-initiate -at=%s:45:21 -new-name=Bar %s -frtti | FileCheck %s diff --git a/clang/test/Refactor/Rename/Ctor.cpp b/clang/test/Refactor/Rename/Ctor.cpp new file mode 100644 index 0000000000000..73ac0206c3255 --- /dev/null +++ b/clang/test/Refactor/Rename/Ctor.cpp @@ -0,0 +1,29 @@ +class Foo { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +public: + Foo(); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + Foo(int x, int y); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + ~Foo(); // CHECK: rename [[@LINE]]:4 -> [[@LINE]]:7 +}; + +Foo::Foo() {} // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4 +// CHECK: rename [[@LINE-1]]:6 -> [[@LINE-1]]:9 + +Foo::Foo(int x, int y) { } // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4 +// CHECK: rename [[@LINE-1]]:6 -> [[@LINE-1]]:9 + +Foo::~Foo() {} // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4 +// CHECK: rename [[@LINE-1]]:7 -> [[@LINE-1]]:10 + +Foo f(const Foo &Other) { // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4 + // CHECK: rename [[@LINE-1]]:13 -> [[@LINE-1]]:16 + return Foo(Other); // CHECK: rename [[@LINE]]:10 -> [[@LINE]]:13 +} + +// Declarations. +// RUN: clang-refactor-test rename-initiate -at=%s:3:3 -at=%s:4:3 -at=%s:5:4 -new-name=Bar %s | FileCheck %s + +// Definitions. +// RUN: clang-refactor-test rename-initiate -at=%s:8:6 -at=%s:11:6 -at=%s:14:7 -new-name=Bar %s | FileCheck %s + +// Implicit copy constructor. +// RUN: clang-refactor-test rename-initiate -at=%s:19:10 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/CtorInitializer.cpp b/clang/test/Refactor/Rename/CtorInitializer.cpp new file mode 100644 index 0000000000000..fe5b6098ea55c --- /dev/null +++ b/clang/test/Refactor/Rename/CtorInitializer.cpp @@ -0,0 +1,14 @@ +class Baz {}; + +class Qux { + Baz Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +public: + Qux(); +}; + +Qux::Qux() : Foo() /* Test 2 */ {} // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17 + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:4:7 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:9:14 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/DeclRefExpr.cpp b/clang/test/Refactor/Rename/DeclRefExpr.cpp new file mode 100644 index 0000000000000..3808343538716 --- /dev/null +++ b/clang/test/Refactor/Rename/DeclRefExpr.cpp @@ -0,0 +1,21 @@ +class C { +public: + static int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17 +}; + +int foo(int x) { return 0; } +#define MACRO(a) foo(a) + +int main() { + C::Foo = 1; /* Test 2 */ // CHECK: rename [[@LINE]]:6 -> [[@LINE]]:9 + MACRO(C::Foo); // CHECK: rename [[@LINE]]:12 -> [[@LINE]]:15 + int y = C::Foo; /* Test 3 */ // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17 + return 0; +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:3:14 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:10:6 -new-name=Bar %s | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:12:14 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/DependentExpressions.cpp b/clang/test/Refactor/Rename/DependentExpressions.cpp new file mode 100644 index 0000000000000..0c7612cba9b80 --- /dev/null +++ b/clang/test/Refactor/Rename/DependentExpressions.cpp @@ -0,0 +1,72 @@ +int invalid; + +class Base { + void baseFunction(); // CHECK3: [[@LINE]]:8 -> [[@LINE]]:20 + + int baseField; + + static void staticBaseFunction(); // CHECK7: [[@LINE]]:15 -> [[@LINE]]:33 +}; + +template<typename T> +class BaseTemplate { +public: + T baseTemplateFunction(); + + T baseTemplateField; // CHECK4: [[@LINE]]:5 -> [[@LINE]]:22 + + static T baseTemplateVariable; // CHECK8: [[@LINE]]:12 -> [[@LINE]]:32 +}; + +template<typename T, typename S> +class TemplateClass: public Base , public BaseTemplate<T> { +public: + ~TemplateClass(); + + T function() { } // CHECK1: [[@LINE]]:5 -> [[@LINE]]:13 + + static void staticFunction() { } // CHECK5: [[@LINE]]:15 -> [[@LINE]]:29 + + T field; // CHECK2: [[@LINE]]:5 -> [[@LINE]]:10 + + static T variable; // CHECK6: [[@LINE]]:12 -> [[@LINE]]:20 + + struct Struct { }; // CHECK9: [[@LINE]]:10 -> [[@LINE]]:16 + + enum Enum { EnumValue }; // CHECK10: [[@LINE]]:8 -> [[@LINE]]:12 + + using TypeAlias = S; // CHECK11: [[@LINE]]:9 -> [[@LINE]]:18 + typedef T Typedef; + + void overload1(const T &); + void overload1(const S &); +}; + +template<typename T, typename S> +void renameSimpleDependentDeclarations(const TemplateClass<T, S> &object) { + + object.function(); // CHECK1: [[@LINE]]:10 -> [[@LINE]]:18 +// RUN: clang-refactor-test rename-initiate -at=%s:26:5 -at=%s:48:10 -new-name=x %s | FileCheck --check-prefix=CHECK1 %s + object.field; // CHECK2: [[@LINE]]:10 -> [[@LINE]]:15 +// RUN: clang-refactor-test rename-initiate -at=%s:30:5 -at=%s:50:10 -new-name=x %s | FileCheck --check-prefix=CHECK2 %s + object.baseFunction(); // CHECK3: [[@LINE]]:10 -> [[@LINE]]:22 +// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -at=%s:52:10 -new-name=x %s | FileCheck --check-prefix=CHECK3 %s + object.baseTemplateField; // CHECK4: [[@LINE]]:10 -> [[@LINE]]:27 +// RUN: clang-refactor-test rename-initiate -at=%s:16:5 -at=%s:54:10 -new-name=x %s | FileCheck --check-prefix=CHECK4 %s + + TemplateClass<T, S>::staticFunction(); // CHECK5: [[@LINE]]:24 -> [[@LINE]]:38 +// RUN: clang-refactor-test rename-initiate -at=%s:28:15 -at=%s:57:24 -new-name=x %s | FileCheck --check-prefix=CHECK5 %s + TemplateClass<T, S>::variable; // CHECK6: [[@LINE]]:24 -> [[@LINE]]:32 +// RUN: clang-refactor-test rename-initiate -at=%s:32:12 -at=%s:59:24 -new-name=x %s | FileCheck --check-prefix=CHECK6 %s + TemplateClass<T, S>::staticBaseFunction(); // CHECK7: [[@LINE]]:24 -> [[@LINE]]:42 +// RUN: clang-refactor-test rename-initiate -at=%s:8:15 -at=%s:61:24 -new-name=x %s | FileCheck --check-prefix=CHECK7 %s + TemplateClass<T, S>::baseTemplateVariable; // CHECK8: [[@LINE]]:24 -> [[@LINE]]:44 +// RUN: clang-refactor-test rename-initiate -at=%s:18:12 -at=%s:63:24 -new-name=x %s | FileCheck --check-prefix=CHECK8 %s + + typename TemplateClass<T, S>::Struct Val; // CHECK9: [[@LINE]]:33 -> [[@LINE]]:39 +// RUN: clang-refactor-test rename-initiate -at=%s:34:10 -at=%s:66:33 -new-name=x %s | FileCheck --check-prefix=CHECK9 %s + typename TemplateClass<T, S>::Enum EnumVal; // CHECK10: [[@LINE]]:33 -> [[@LINE]]:37 +// RUN: clang-refactor-test rename-initiate -at=%s:36:8 -at=%s:68:33 -new-name=x %s | FileCheck --check-prefix=CHECK10 %s + typename TemplateClass<T, S>::TypeAlias Val2; // CHECK11: [[@LINE]]:33 -> [[@LINE]]:42 +// RUN: clang-refactor-test rename-initiate -at=%s:38:9 -at=%s:70:33 -new-name=x %s | FileCheck --check-prefix=CHECK11 %s +} diff --git a/clang/test/Refactor/Rename/Field.cpp b/clang/test/Refactor/Rename/Field.cpp new file mode 100644 index 0000000000000..94b144add1650 --- /dev/null +++ b/clang/test/Refactor/Rename/Field.cpp @@ -0,0 +1,12 @@ +class Baz { + int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +public: + Baz(); +}; + +Baz::Baz() : Foo(0) /* Test 2 */ {} // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17 + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:7:14 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/FunctionMacro.cpp b/clang/test/Refactor/Rename/FunctionMacro.cpp new file mode 100644 index 0000000000000..946c4d11fa993 --- /dev/null +++ b/clang/test/Refactor/Rename/FunctionMacro.cpp @@ -0,0 +1,17 @@ +#define moo foo + +int foo() /* Test 1 */ { // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + return 42; +} + +void boo(int value) {} + +void qoo() { + foo(); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + boo(foo()); // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 + moo(); // CHECK: macro [[@LINE]]:3 -> [[@LINE]]:3 + boo(moo()); // CHECK: macro [[@LINE]]:7 -> [[@LINE]]:7 +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:3:5 -new-name=macro_function %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/FunctionOverride.cpp b/clang/test/Refactor/Rename/FunctionOverride.cpp new file mode 100644 index 0000000000000..9c6fc3431f2ee --- /dev/null +++ b/clang/test/Refactor/Rename/FunctionOverride.cpp @@ -0,0 +1,10 @@ +class A { virtual void foo(); /* Test 1 */ }; // CHECK: rename [[@LINE]]:24 -> [[@LINE]]:27 +class B : public A { void foo(); /* Test 2 */ }; // CHECK: rename [[@LINE]]:27 -> [[@LINE]]:30 +class C : public B { void foo(); /* Test 3 */ }; // CHECK: rename [[@LINE]]:27 -> [[@LINE]]:30 + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:1:24 -new-name=bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:2:27 -new-name=bar %s | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:3:27 -new-name=bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/IndexedObjCMessageSend.mm b/clang/test/Refactor/Rename/IndexedObjCMessageSend.mm new file mode 100644 index 0000000000000..1fd607098c239 --- /dev/null +++ b/clang/test/Refactor/Rename/IndexedObjCMessageSend.mm @@ -0,0 +1,882 @@ ++(BOOL) onEntity { + call() = [_undef_ivar name: @"string literal" method: @"string literal"]; + // CHECK1: [[@LINE-1]]:25 -> [[@LINE-1]]:29, [[@LINE-1]]:49 -> [[@LINE-1]]:55 +} +// RUN: clang-refactor-test rename-indexed-file -name=name:method -new-name=object:world -indexed-file=%s -indexed-at=2:25 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK1 %s + +-(const Object &) a_200 { + some_type_t world = @"string literal"; + [self withSomething: [] () { + some_type_t foo = [super struct: @"string literal" + world: [] () { + } name: [] { + + + } a_200: 12 a_200: globalArray[i]]; + // CHECK2: [[@LINE-6]]:29 -> [[@LINE-6]]:35, [[@LINE-5]]:2 -> [[@LINE-5]]:7, [[@LINE-4]]:5 -> [[@LINE-4]]:9, [[@LINE-1]]:4 -> [[@LINE-1]]:9, [[@LINE-1]]:14 -> [[@LINE-1]]:19 + + } onEntity: ']' perform: "]"]; + // CHECK3: [[@LINE-10]]:9 -> [[@LINE-10]]:22, [[@LINE-1]]:4 -> [[@LINE-1]]:12, [[@LINE-1]]:18 -> [[@LINE-1]]:25 +} +// RUN: clang-refactor-test rename-indexed-file -name=struct:world:name:a_200:a_200 -new-name=withSomething:foo:part:struct:method -indexed-file=%s -indexed-at=10:29 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:onEntity:perform -new-name=method:foo:name -indexed-file=%s -indexed-at=9:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK3 %s + +-(int) struct { + // comment +} + +-(int) bar { + return ']'; +} + +-(int) part { + call() = [self test: 12 method: globalArray[i] test: globalArray[i] world: ']']; + // CHECK4: [[@LINE-1]]:18 -> [[@LINE-1]]:22, [[@LINE-1]]:27 -> [[@LINE-1]]:33, [[@LINE-1]]:50 -> [[@LINE-1]]:54, [[@LINE-1]]:71 -> [[@LINE-1]]:76 + [_undef_ivar withSomething: @{ @1, @3 } onEntity: [] () { ([self foo: "]" piece: foo: (']')]); + // CHECK5: [[@LINE-1]]:71 -> [[@LINE-1]]:74, [[@LINE-1]]:80 -> [[@LINE-1]]:85, [[@LINE-1]]:88 -> [[@LINE-1]]:91 + }]; + // CHECK6: [[@LINE-3]]:16 -> [[@LINE-3]]:29, [[@LINE-3]]:43 -> [[@LINE-3]]:51 + int bar = [self name: ']' z_Z_42: [] () { [globalObject send: [self perform: globalArray[i] * "string" a_200: 12 class: 12 perform: ']' object: [] () { + }] other: 42]; + // CHECK7: [[@LINE-2]]:74 -> [[@LINE-2]]:81, [[@LINE-2]]:109 -> [[@LINE-2]]:114, [[@LINE-2]]:119 -> [[@LINE-2]]:124, [[@LINE-2]]:129 -> [[@LINE-2]]:136, [[@LINE-2]]:142 -> [[@LINE-2]]:148 + } bar: ^ { + [globalObject message] = ([self onEntity: globalArray[i] class: 12 foo: "string"]); + // CHECK8: [[@LINE-1]]:36 -> [[@LINE-1]]:44, [[@LINE-1]]:61 -> [[@LINE-1]]:66, [[@LINE-1]]:71 -> [[@LINE-1]]:74 + + } bar: ^ () { + some_type_t foo = [self perform: 12 bar: (^ () { + }) bar: ^ { + } perform: @"string literal" test: "]" + 12]; + // CHECK9: [[@LINE-3]]:28 -> [[@LINE-3]]:35, [[@LINE-3]]:40 -> [[@LINE-3]]:43, [[@LINE-2]]:6 -> [[@LINE-2]]:9, [[@LINE-1]]:5 -> [[@LINE-1]]:12, [[@LINE-1]]:32 -> [[@LINE-1]]:36 + + }]; + // CHECK10: [[@LINE-14]]:19 -> [[@LINE-14]]:23, [[@LINE-14]]:29 -> [[@LINE-14]]:35, [[@LINE-11]]:5 -> [[@LINE-11]]:8, [[@LINE-7]]:4 -> [[@LINE-7]]:7 + [self piece: @"string literal" method: globalArray[i] method: "string" class: globalArray[i]]; + // CHECK11: [[@LINE-1]]:9 -> [[@LINE-1]]:14, [[@LINE-1]]:34 -> [[@LINE-1]]:40, [[@LINE-1]]:57 -> [[@LINE-1]]:63, [[@LINE-1]]:74 -> [[@LINE-1]]:79 +} +// RUN: clang-refactor-test rename-indexed-file -name=test:method:test:world -new-name=a_200:struct:perform:piece -indexed-file=%s -indexed-at=33:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:piece:foo -new-name=world:onEntity:name -indexed-file=%s -indexed-at=35:71 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:onEntity -new-name=class:object -indexed-file=%s -indexed-at=35:16 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:a_200:class:perform:object -new-name=method:perform:class:foo:name -indexed-file=%s -indexed-at=39:74 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK7 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:class:foo -new-name=piece:onEntity:bar -indexed-file=%s -indexed-at=43:36 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:bar:bar:perform:test -new-name=foo:world:class:struct:z_Z_42 -indexed-file=%s -indexed-at=47:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:z_Z_42:bar:bar -new-name=world:piece:perform:test -indexed-file=%s -indexed-at=39:19 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK10 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:method:method:class -new-name=a_200:withSomething:onEntity:onEntity -indexed-file=%s -indexed-at=54:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK11 %s + ++(int) struct { + call() = [self name: ^ () { const Object & piece = 12; + } perform: @{ @1, @3 } a_200: ^ { + some_type_t foo = [self name: ']' name: ^ () { + } method: ']']; + // CHECK12: [[@LINE-2]]:28 -> [[@LINE-2]]:32, [[@LINE-2]]:38 -> [[@LINE-2]]:42, [[@LINE-1]]:5 -> [[@LINE-1]]:11 + + }]; + // CHECK13: [[@LINE-7]]:18 -> [[@LINE-7]]:22, [[@LINE-6]]:5 -> [[@LINE-6]]:12, [[@LINE-6]]:26 -> [[@LINE-6]]:31 +} +// RUN: clang-refactor-test rename-indexed-file -name=name:name:method -new-name=part:bar:usingThing -indexed-file=%s -indexed-at=69:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK12 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:perform:a_200 -new-name=a_200:piece:class -indexed-file=%s -indexed-at=67:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK13 %s + ++(BOOL) world { + int bar = ([_undef_ivar world: "string" struct: @"string literal" world: globalArray[i] + perform: ^ { // comment + } < "string" name: [] () { + int bar = [self a_200: "string" * 12 method: globalArray[i] usingThing: @"string literal" part: ']']; + // CHECK14: [[@LINE-1]]:20 -> [[@LINE-1]]:25, [[@LINE-1]]:41 -> [[@LINE-1]]:47, [[@LINE-1]]:64 -> [[@LINE-1]]:74, [[@LINE-1]]:94 -> [[@LINE-1]]:98 + + }]); + // CHECK15: [[@LINE-7]]:27 -> [[@LINE-7]]:32, [[@LINE-7]]:43 -> [[@LINE-7]]:49, [[@LINE-7]]:69 -> [[@LINE-7]]:74, [[@LINE-6]]:2 -> [[@LINE-6]]:9, [[@LINE-5]]:16 -> [[@LINE-5]]:20 + [self perform: [self object: globalArray[i] onEntity: /*]*/ [] () { + ; ;[self.undef_property test: [] () { + + + } onEntity: @"string literal" +]; + // CHECK16: [[@LINE-5]]:28 -> [[@LINE-5]]:32, [[@LINE-2]]:4 -> [[@LINE-2]]:12 + + } method: "]"] world: "]" withSomething: @"string literal"]; + // CHECK17: [[@LINE-9]]:24 -> [[@LINE-9]]:30, [[@LINE-9]]:47 -> [[@LINE-9]]:55, [[@LINE-1]]:4 -> [[@LINE-1]]:10 + // CHECK18: [[@LINE-10]]:9 -> [[@LINE-10]]:16, [[@LINE-2]]:17 -> [[@LINE-2]]:22, [[@LINE-2]]:28 -> [[@LINE-2]]:41 + [self usingThing: ^ { + ; + + } world: "string" test: "]"]; + // CHECK19: [[@LINE-4]]:9 -> [[@LINE-4]]:19, [[@LINE-1]]:4 -> [[@LINE-1]]:9, [[@LINE-1]]:20 -> [[@LINE-1]]:24 + call() = [self bar: [] { globalArray[12] = [self onEntity: ^ () { ] } foo: "string" piece: @{ @1, @3 } bar: ']']; + // CHECK20: [[@LINE-1]]:56 -> [[@LINE-1]]:64, [[@LINE-1]]:77 -> [[@LINE-1]]:80, [[@LINE-1]]:91 -> [[@LINE-1]]:96, [[@LINE-1]]:110 -> [[@LINE-1]]:113 + } +//comment + struct: [] { return ^ () { + }; + } piece: "string" onEntity: ^ () { ] }]; + // CHECK21: [[@LINE-7]]:18 -> [[@LINE-7]]:21, [[@LINE-3]]:2 -> [[@LINE-3]]:8, [[@LINE-1]]:5 -> [[@LINE-1]]:10, [[@LINE-1]]:21 -> [[@LINE-1]]:29 + some_type_t foo = [_undef_ivar world: 12 + bar: [] () { + [globalObject send: [super class: 12 world: [self perform: 12 perform: @{ @1, @3 } + object: "string"] class: "string" z_Z_42: @{ @1, @3 }] other: 42]; + // CHECK22: [[@LINE-2]]:54 -> [[@LINE-2]]:61, [[@LINE-2]]:66 -> [[@LINE-2]]:73, [[@LINE-1]]:2 -> [[@LINE-1]]:8 + // CHECK23: [[@LINE-3]]:31 -> [[@LINE-3]]:36, [[@LINE-3]]:41 -> [[@LINE-3]]:46, [[@LINE-2]]:20 -> [[@LINE-2]]:25, [[@LINE-2]]:36 -> [[@LINE-2]]:42 + + }]; + // CHECK24: [[@LINE-8]]:34 -> [[@LINE-8]]:39, [[@LINE-7]]:2 -> [[@LINE-7]]:5 +} +// RUN: clang-refactor-test rename-indexed-file -name=a_200:method:usingThing:part -new-name=foo:bar:bar:class -indexed-file=%s -indexed-at=83:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK14 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:struct:world:perform:name -new-name=perform:bar:object:foo:object -indexed-file=%s -indexed-at=80:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK15 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:onEntity -new-name=name:class -indexed-file=%s -indexed-at=89:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK16 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:onEntity:method -new-name=usingThing:a_200:onEntity -indexed-file=%s -indexed-at=88:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK17 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:world:withSomething -new-name=onEntity:method:part -indexed-file=%s -indexed-at=88:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK18 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:world:test -new-name=name:object:onEntity -indexed-file=%s -indexed-at=99:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK19 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:foo:piece:bar -new-name=test:foo:test:name -indexed-file=%s -indexed-at=104:56 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK20 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:struct:piece:onEntity -new-name=usingThing:method:part:piece -indexed-file=%s -indexed-at=104:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK21 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:perform:object -new-name=usingThing:withSomething:withSomething -indexed-file=%s -indexed-at=114:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK22 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:world:class:z_Z_42 -new-name=bar:piece:class:z_Z_42 -indexed-file=%s -indexed-at=114:31 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK23 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:bar -new-name=foo:bar -indexed-file=%s -indexed-at=112:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK24 %s + +-(void) test { + call() = [self name: globalArray[i] onEntity: "]" bar: ^ { return [self withSomething: [] () { + + + } name: ']' part: 12]; + // CHECK25: [[@LINE-4]]:79 -> [[@LINE-4]]:92, [[@LINE-1]]:4 -> [[@LINE-1]]:8, [[@LINE-1]]:14 -> [[@LINE-1]]:18 + } usingThing: "]" object: ]; + // CHECK26: [[@LINE-6]]:18 -> [[@LINE-6]]:22, [[@LINE-6]]:39 -> [[@LINE-6]]:47, [[@LINE-6]]:53 -> [[@LINE-6]]:56, [[@LINE-1]]:5 -> [[@LINE-1]]:15, [[@LINE-1]]:21 -> [[@LINE-1]]:27 + [globalObject message] = [self a_200: globalArray[i] struct: @{ @1, @3 } +]; + // CHECK27: [[@LINE-2]]:34 -> [[@LINE-2]]:39, [[@LINE-2]]:56 -> [[@LINE-2]]:62 +} +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:name:part -new-name=z_Z_42:object:test -indexed-file=%s -indexed-at=135:79 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK25 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:onEntity:bar:usingThing:object -new-name=foo:method:perform:onEntity:perform -indexed-file=%s -indexed-at=135:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK26 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:struct -new-name=bar:perform -indexed-file=%s -indexed-at=142:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK27 %s + +-(int) class { + return /*]*/ [] () { + call() = [self struct: (@"string literal") method: ^ () { + } foo: @"string literal" onEntity: [] { + + + } < globalArray[i] method: ']']; + // CHECK28: [[@LINE-5]]:19 -> [[@LINE-5]]:25, [[@LINE-5]]:47 -> [[@LINE-5]]:53, [[@LINE-4]]:5 -> [[@LINE-4]]:8, [[@LINE-4]]:28 -> [[@LINE-4]]:36, [[@LINE-1]]:21 -> [[@LINE-1]]:27 + + }; + [self name: [] () { if ([] { + + + }) { + BOOL class = @{ @1, @3 }; + + } + } perform: 12 onEntity: @"string literal"]; + // CHECK29: [[@LINE-8]]:9 -> [[@LINE-8]]:13, [[@LINE-1]]:5 -> [[@LINE-1]]:12, [[@LINE-1]]:17 -> [[@LINE-1]]:25 + return globalArray[i] * [self withSomething: [self part: 12 name: ^ { [globalObject send: [super withSomething: [self test: [] () { + + + } world: [_undef_ivar piece: 12 class: @"string literal" test: "string" bar: /*]*/ globalArray[i]] +] withSomething: 12 name: ']' test: "]" usingThing: @"string literal"] other: 42]; + // CHECK30: [[@LINE-2]]:24 -> [[@LINE-2]]:29, [[@LINE-2]]:34 -> [[@LINE-2]]:39, [[@LINE-2]]:59 -> [[@LINE-2]]:63, [[@LINE-2]]:74 -> [[@LINE-2]]:77 + // CHECK31: [[@LINE-6]]:125 -> [[@LINE-6]]:129, [[@LINE-3]]:4 -> [[@LINE-3]]:9 + // CHECK32: [[@LINE-7]]:104 -> [[@LINE-7]]:117, [[@LINE-3]]:3 -> [[@LINE-3]]:16, [[@LINE-3]]:21 -> [[@LINE-3]]:25, [[@LINE-3]]:31 -> [[@LINE-3]]:35, [[@LINE-3]]:41 -> [[@LINE-3]]:51 + } part: @{ @1, @3 }] withSomething: ']' foo: globalArray[i]]; + // CHECK33: [[@LINE-9]]:54 -> [[@LINE-9]]:58, [[@LINE-9]]:63 -> [[@LINE-9]]:67, [[@LINE-1]]:5 -> [[@LINE-1]]:9 + // CHECK34: [[@LINE-10]]:33 -> [[@LINE-10]]:46, [[@LINE-2]]:24 -> [[@LINE-2]]:37, [[@LINE-2]]:43 -> [[@LINE-2]]:46 +} +// RUN: clang-refactor-test rename-indexed-file -name=struct:method:foo:onEntity:method -new-name=object:piece:struct:foo:name -indexed-file=%s -indexed-at=152:19 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK28 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:perform:onEntity -new-name=z_Z_42:bar:z_Z_42 -indexed-file=%s -indexed-at=160:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK29 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:class:test:bar -new-name=world:bar:object:perform -indexed-file=%s -indexed-at=172:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK30 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:world -new-name=a_200:bar -indexed-file=%s -indexed-at=169:125 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK31 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:withSomething:name:test:usingThing -new-name=bar:class:class:perform:perform -indexed-file=%s -indexed-at=169:104 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK32 %s +// RUN: clang-refactor-test rename-indexed-file -name=part:name:part -new-name=class:perform:name -indexed-file=%s -indexed-at=169:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK33 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:withSomething:foo -new-name=test:object:withSomething -indexed-file=%s -indexed-at=169:33 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK34 %s + ++(Object *) usingThing { + return "string"; +} + ++(void) class { + if (@{ @1, @3 }) { + globalArray[12] = [self foo: [] { [globalObject message] = [self object: @{ @1, @3 } usingThing: globalArray[i] perform: "]"]; + // CHECK35: [[@LINE-1]]:76 -> [[@LINE-1]]:82, [[@LINE-1]]:96 -> [[@LINE-1]]:106, [[@LINE-1]]:123 -> [[@LINE-1]]:130 + } class: usingThing: "]" perform: [self.undef_property name: ^ () { ] } piece: 12 name: ^ () { + globalArray[12] = [_undef_ivar foo: ']' foo: [] { + } + bar: ^ { + } + [] { + + + } +]; + // CHECK36: [[@LINE-8]]:35 -> [[@LINE-8]]:38, [[@LINE-8]]:44 -> [[@LINE-8]]:47, [[@LINE-6]]:2 -> [[@LINE-6]]:5 + + }]]; + // CHECK37: [[@LINE-12]]:59 -> [[@LINE-12]]:63, [[@LINE-12]]:76 -> [[@LINE-12]]:81, [[@LINE-12]]:86 -> [[@LINE-12]]:90 + // CHECK38: [[@LINE-15]]:31 -> [[@LINE-15]]:34, [[@LINE-13]]:5 -> [[@LINE-13]]:10, [[@LINE-13]]:13 -> [[@LINE-13]]:23, [[@LINE-13]]:29 -> [[@LINE-13]]:36 + + } +} +// RUN: clang-refactor-test rename-indexed-file -name=object:usingThing:perform -new-name=test:object:z_Z_42 -indexed-file=%s -indexed-at=195:76 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK35 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:foo:bar -new-name=method:part:class -indexed-file=%s -indexed-at=198:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK36 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:piece:name -new-name=perform:onEntity:struct -indexed-file=%s -indexed-at=197:59 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK37 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:class:usingThing:perform -new-name=name:z_Z_42:method:test -indexed-file=%s -indexed-at=195:31 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK38 %s + +-(some_type_t) struct { + globalArray[12] = [super a_200: globalArray[i] + name: globalArray[i] world: [] { + [globalObject send: [self part: ']' z_Z_42: ^ { + } + "]" struct: "string" + bar: [] () { + + + }] other: 42]; + // CHECK39: [[@LINE-6]]:30 -> [[@LINE-6]]:34, [[@LINE-6]]:40 -> [[@LINE-6]]:46, [[@LINE-5]]:11 -> [[@LINE-5]]:17, [[@LINE-4]]:2 -> [[@LINE-4]]:5 + + } onEntity: ']' == globalArray[i]]; + // CHECK40: [[@LINE-11]]:28 -> [[@LINE-11]]:33, [[@LINE-10]]:2 -> [[@LINE-10]]:6, [[@LINE-10]]:23 -> [[@LINE-10]]:28, [[@LINE-1]]:4 -> [[@LINE-1]]:12 + [self a_200: [] () { BOOL class = 12; + } part: ']' test: ^ () { int z_Z_42 = [self struct: "]" perform: "]" method: globalArray[i]]; + // CHECK41: [[@LINE-1]]:50 -> [[@LINE-1]]:56, [[@LINE-1]]:62 -> [[@LINE-1]]:69, [[@LINE-1]]:75 -> [[@LINE-1]]:81 + }]; + // CHECK42: [[@LINE-4]]:9 -> [[@LINE-4]]:14, [[@LINE-3]]:5 -> [[@LINE-3]]:9, [[@LINE-3]]:15 -> [[@LINE-3]]:19 +} +// RUN: clang-refactor-test rename-indexed-file -name=part:z_Z_42:struct:bar -new-name=world:a_200:a_200:test -indexed-file=%s -indexed-at=222:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK39 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:name:world:onEntity -new-name=object:object:perform:z_Z_42 -indexed-file=%s -indexed-at=220:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK40 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:perform:method -new-name=perform:test:bar -indexed-file=%s -indexed-at=233:50 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK41 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:part:test -new-name=class:method:test -indexed-file=%s -indexed-at=232:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK42 %s + ++(some_type_t) piece { + call() = [self class: "string" part: 12]; + // CHECK43: [[@LINE-1]]:18 -> [[@LINE-1]]:23, [[@LINE-1]]:34 -> [[@LINE-1]]:38 + [self struct: globalArray[i] part: "]" part: [self method: @"string literal" == globalArray[i] + [self onEntity: ^ { [_undef_ivar piece: @{ @1, @3 } world: globalArray[i]]; + // CHECK44: [[@LINE-1]]:137 -> [[@LINE-1]]:142, [[@LINE-1]]:156 -> [[@LINE-1]]:161 + } object: (^ () { [self class: 12 object: [] { + + + } == ^ () { + + + }]; + // CHECK45: [[@LINE-7]]:30 -> [[@LINE-7]]:35, [[@LINE-7]]:40 -> [[@LINE-7]]:46 + }) piece: ']' world: 12 part: (12)] struct: /*]*/ globalArray[i] foo: [self object: ^ { // comment + // CHECK46: [[@LINE-11]]:106 -> [[@LINE-11]]:114, [[@LINE-9]]:5 -> [[@LINE-9]]:11, [[@LINE-1]]:6 -> [[@LINE-1]]:11, [[@LINE-1]]:17 -> [[@LINE-1]]:22, [[@LINE-1]]:27 -> [[@LINE-1]]:31 + } world: (^ { + int bar = [_undef_ivar test: globalArray[i] + "string" == ']' class: 12]; + // CHECK47: [[@LINE-1]]:27 -> [[@LINE-1]]:31, [[@LINE-1]]:66 -> [[@LINE-1]]:71 + + }) part: ']' +] perform: "]"] perform: @{ @1, @3 }]; + // CHECK48: [[@LINE-8]]:79 -> [[@LINE-8]]:85, [[@LINE-6]]:5 -> [[@LINE-6]]:10, [[@LINE-2]]:5 -> [[@LINE-2]]:9 + // CHECK49: [[@LINE-19]]:54 -> [[@LINE-19]]:60, [[@LINE-9]]:39 -> [[@LINE-9]]:45, [[@LINE-9]]:68 -> [[@LINE-9]]:71, [[@LINE-2]]:3 -> [[@LINE-2]]:10 + // CHECK50: [[@LINE-20]]:9 -> [[@LINE-20]]:15, [[@LINE-20]]:32 -> [[@LINE-20]]:36, [[@LINE-20]]:42 -> [[@LINE-20]]:46, [[@LINE-3]]:17 -> [[@LINE-3]]:24 + some_type_t foo = [self.undef_property class: "]" part: [] { + [self bar: [] () { + } z_Z_42: "]" name: globalArray[i]]; + // CHECK51: [[@LINE-2]]:10 -> [[@LINE-2]]:13, [[@LINE-1]]:5 -> [[@LINE-1]]:11, [[@LINE-1]]:17 -> [[@LINE-1]]:21 + + } + onEntity: ^ { return [super withSomething: "string" usingThing: @"string literal" test: @"string literal" withSomething: ']' world: [] () { + + + } +]; + // CHECK52: [[@LINE-5]]:34 -> [[@LINE-5]]:47, [[@LINE-5]]:58 -> [[@LINE-5]]:68, [[@LINE-5]]:88 -> [[@LINE-5]]:92, [[@LINE-5]]:112 -> [[@LINE-5]]:125, [[@LINE-5]]:131 -> [[@LINE-5]]:136 + } class: 12 struct: @"string literal" == globalArray[i]]; + // CHECK53: [[@LINE-13]]:42 -> [[@LINE-13]]:47, [[@LINE-13]]:53 -> [[@LINE-13]]:57, [[@LINE-7]]:2 -> [[@LINE-7]]:10, [[@LINE-1]]:5 -> [[@LINE-1]]:10, [[@LINE-1]]:15 -> [[@LINE-1]]:21 +} +// RUN: clang-refactor-test rename-indexed-file -name=class:part -new-name=a_200:class -indexed-file=%s -indexed-at=244:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK43 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:world -new-name=part:name -indexed-file=%s -indexed-at=246:137 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK44 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:object -new-name=onEntity:z_Z_42 -indexed-file=%s -indexed-at=248:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK45 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:object:piece:world:part -new-name=onEntity:piece:a_200:class:struct -indexed-file=%s -indexed-at=246:106 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK46 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:class -new-name=a_200:test -indexed-file=%s -indexed-at=259:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK47 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:world:part -new-name=class:z_Z_42:onEntity -indexed-file=%s -indexed-at=256:79 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK48 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:struct:foo:perform -new-name=onEntity:bar:part:test -indexed-file=%s -indexed-at=246:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK49 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:part:part:perform -new-name=piece:object:world:usingThing -indexed-file=%s -indexed-at=246:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK50 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:z_Z_42:name -new-name=bar:test:struct -indexed-file=%s -indexed-at=268:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK51 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:usingThing:test:withSomething:world -new-name=foo:test:usingThing:piece:piece -indexed-file=%s -indexed-at=273:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK52 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:part:onEntity:class:struct -new-name=z_Z_42:onEntity:onEntity:a_200:class -indexed-file=%s -indexed-at=267:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK53 %s + ++(void) z_Z_42 { + int a_200 = @{ @1, @3 }; + // comment + [globalObject send: ([self onEntity: [] () { [super onEntity: ^ { + } class: @"string literal" perform: @"string literal"] other: 42]; + // CHECK54: [[@LINE-2]]:58 -> [[@LINE-2]]:66, [[@LINE-1]]:5 -> [[@LINE-1]]:10, [[@LINE-1]]:30 -> [[@LINE-1]]:37 + } class: globalArray[i] a_200: ^ () { ] } +]); + // CHECK55: [[@LINE-5]]:30 -> [[@LINE-5]]:38, [[@LINE-2]]:5 -> [[@LINE-2]]:10, [[@LINE-2]]:27 -> [[@LINE-2]]:32 + globalArray[12] = [super method: @"string literal" test: ^ () { [self struct: "]" method: globalArray[i]]; + // CHECK56: [[@LINE-1]]:76 -> [[@LINE-1]]:82, [[@LINE-1]]:88 -> [[@LINE-1]]:94 + } name: globalArray[i]]; + // CHECK57: [[@LINE-3]]:28 -> [[@LINE-3]]:34, [[@LINE-3]]:54 -> [[@LINE-3]]:58, [[@LINE-1]]:5 -> [[@LINE-1]]:9 + BOOL struct = 12; +} +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:class:perform -new-name=bar:z_Z_42:onEntity -indexed-file=%s -indexed-at=297:58 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK54 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:class:a_200 -new-name=withSomething:piece:foo -indexed-file=%s -indexed-at=297:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK55 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:method -new-name=onEntity:struct -indexed-file=%s -indexed-at=303:76 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK56 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:test:name -new-name=name:z_Z_42:object -indexed-file=%s -indexed-at=303:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK57 %s + ++(int) onEntity { + call() = [self piece: [] { return "string"; + } foo: ']' bar: globalArray[i]]; + // CHECK58: [[@LINE-2]]:18 -> [[@LINE-2]]:23, [[@LINE-1]]:5 -> [[@LINE-1]]:8, [[@LINE-1]]:14 -> [[@LINE-1]]:17 + [globalObject send: [self usingThing: [] { + [_undef_ivar test: globalArray[i] part: 12 world: "string" onEntity: 12] other: 42]; + // CHECK59: [[@LINE-1]]:17 -> [[@LINE-1]]:21, [[@LINE-1]]:38 -> [[@LINE-1]]:42, [[@LINE-1]]:47 -> [[@LINE-1]]:52, [[@LINE-1]]:63 -> [[@LINE-1]]:71 + + } a_200: @"string literal" a_200: "string" object: ("string") +//comment +]; + // CHECK60: [[@LINE-7]]:29 -> [[@LINE-7]]:39, [[@LINE-3]]:4 -> [[@LINE-3]]:9, [[@LINE-3]]:29 -> [[@LINE-3]]:34, [[@LINE-3]]:45 -> [[@LINE-3]]:51 + [globalObject send: [super object: ']' name: ^ { + [globalObject send: [self.undef_property bar: == [self name: "string" bar: ']'] + part: @"string literal" z_Z_42: ']' part: ']' test: "string"] other: 42] other: 42]; + // CHECK61: [[@LINE-2]]:60 -> [[@LINE-2]]:64, [[@LINE-2]]:75 -> [[@LINE-2]]:78 + // CHECK62: [[@LINE-3]]:45 -> [[@LINE-3]]:48, [[@LINE-2]]:2 -> [[@LINE-2]]:6, [[@LINE-2]]:26 -> [[@LINE-2]]:32, [[@LINE-2]]:38 -> [[@LINE-2]]:42, [[@LINE-2]]:48 -> [[@LINE-2]]:52 + + } + object: globalArray[i] foo: (']')]; + // CHECK63: [[@LINE-8]]:30 -> [[@LINE-8]]:36, [[@LINE-8]]:42 -> [[@LINE-8]]:46, [[@LINE-1]]:2 -> [[@LINE-1]]:8, [[@LINE-1]]:25 -> [[@LINE-1]]:28 + [self.undef_property world: globalArray[i] onEntity: ']' object: @"string literal" == 12 struct: [] { + int bar = [self onEntity: "]" piece: [] () { + }]; + // CHECK64: [[@LINE-2]]:20 -> [[@LINE-2]]:28, [[@LINE-2]]:34 -> [[@LINE-2]]:39 + + } struct: @"string literal"]; + // CHECK65: [[@LINE-6]]:24 -> [[@LINE-6]]:29, [[@LINE-6]]:46 -> [[@LINE-6]]:54, [[@LINE-6]]:60 -> [[@LINE-6]]:66, [[@LINE-6]]:92 -> [[@LINE-6]]:98, [[@LINE-1]]:4 -> [[@LINE-1]]:10 +} +// RUN: clang-refactor-test rename-indexed-file -name=piece:foo:bar -new-name=class:onEntity:method -indexed-file=%s -indexed-at=315:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK58 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:part:world:onEntity -new-name=z_Z_42:bar:piece:perform -indexed-file=%s -indexed-at=319:17 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK59 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:a_200:a_200:object -new-name=a_200:object:method:perform -indexed-file=%s -indexed-at=318:29 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK60 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:bar -new-name=z_Z_42:z_Z_42 -indexed-file=%s -indexed-at=327:60 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK61 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:part:z_Z_42:part:test -new-name=name:a_200:bar:name:z_Z_42 -indexed-file=%s -indexed-at=327:45 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK62 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:name:object:foo -new-name=z_Z_42:object:usingThing:world -indexed-file=%s -indexed-at=326:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK63 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:piece -new-name=struct:piece -indexed-file=%s -indexed-at=336:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK64 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:onEntity:object:struct:struct -new-name=withSomething:test:foo:object:bar -indexed-file=%s -indexed-at=335:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK65 %s + ++(const Object &) object { + globalArray[12] = [self world: globalArray[i] object: ']' usingThing: usingThing: globalArray[i]]; + // CHECK66: [[@LINE-1]]:27 -> [[@LINE-1]]:32, [[@LINE-1]]:49 -> [[@LINE-1]]:55, [[@LINE-1]]:61 -> [[@LINE-1]]:71, [[@LINE-1]]:74 -> [[@LINE-1]]:84 + globalArray[12] = ([self.undef_property struct: ']' usingThing: "string"]); + // CHECK67: [[@LINE-1]]:43 -> [[@LINE-1]]:49, [[@LINE-1]]:55 -> [[@LINE-1]]:65 +} +// RUN: clang-refactor-test rename-indexed-file -name=world:object:usingThing:usingThing -new-name=a_200:struct:test:z_Z_42 -indexed-file=%s -indexed-at=353:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK66 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:usingThing -new-name=struct:a_200 -indexed-file=%s -indexed-at=355:43 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK67 %s + +-(void) part { + [self.undef_property withSomething: "string" method: [] { call() = [self.undef_property object: @"string literal" test: 12 + test: globalArray[i]]; + // CHECK68: [[@LINE-2]]:95 -> [[@LINE-2]]:101, [[@LINE-2]]:121 -> [[@LINE-2]]:125, [[@LINE-1]]:2 -> [[@LINE-1]]:6 + }]; + // CHECK69: [[@LINE-4]]:24 -> [[@LINE-4]]:37, [[@LINE-4]]:48 -> [[@LINE-4]]:54 +} +// RUN: clang-refactor-test rename-indexed-file -name=object:test:test -new-name=piece:bar:a_200 -indexed-file=%s -indexed-at=362:95 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK68 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:method -new-name=withSomething:test -indexed-file=%s -indexed-at=362:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK69 %s + ++(BOOL) method { + [self object: ^ () { ] } a_200: "]" foo: @{ @1, @3 } piece: [] { [self.undef_property a_200: 12 foo: 12 withSomething: globalArray[i] onEntity: ']' struct: 12]; + // CHECK70: [[@LINE-1]]:93 -> [[@LINE-1]]:98, [[@LINE-1]]:103 -> [[@LINE-1]]:106, [[@LINE-1]]:111 -> [[@LINE-1]]:124, [[@LINE-1]]:141 -> [[@LINE-1]]:149, [[@LINE-1]]:155 -> [[@LINE-1]]:161 + } +//comment +]; + // CHECK71: [[@LINE-5]]:9 -> [[@LINE-5]]:15, [[@LINE-5]]:28 -> [[@LINE-5]]:33, [[@LINE-5]]:39 -> [[@LINE-5]]:42, [[@LINE-5]]:56 -> [[@LINE-5]]:61 + [super method: 12 struct: ^ { /*comment*/[super foo: [self bar: @"string literal" method: "string" test: 12 test: "string" world: ']'] + onEntity: globalArray[i] method: ^ { + }]; + // CHECK72: [[@LINE-3]]:66 -> [[@LINE-3]]:69, [[@LINE-3]]:89 -> [[@LINE-3]]:95, [[@LINE-3]]:106 -> [[@LINE-3]]:110, [[@LINE-3]]:115 -> [[@LINE-3]]:119, [[@LINE-3]]:130 -> [[@LINE-3]]:135 + // CHECK73: [[@LINE-4]]:55 -> [[@LINE-4]]:58, [[@LINE-3]]:2 -> [[@LINE-3]]:10, [[@LINE-3]]:27 -> [[@LINE-3]]:33 + } a_200: "string"]; + // CHECK74: [[@LINE-6]]:10 -> [[@LINE-6]]:16, [[@LINE-6]]:21 -> [[@LINE-6]]:27, [[@LINE-1]]:5 -> [[@LINE-1]]:10 + if (12) { + [self world: "]" usingThing: @"string literal" + [] () { call() = [_undef_ivar part: "]" usingThing: 12 +]; + // CHECK75: [[@LINE-2]]:89 -> [[@LINE-2]]:93, [[@LINE-2]]:99 -> [[@LINE-2]]:109 + } foo: 12]; + // CHECK76: [[@LINE-4]]:13 -> [[@LINE-4]]:18, [[@LINE-4]]:24 -> [[@LINE-4]]:34, [[@LINE-1]]:5 -> [[@LINE-1]]:8 + + } + [super test: (12) foo: "string" name: [] { + return @{ @1, @3 }; + + } object: "]"]; + // CHECK77: [[@LINE-4]]:10 -> [[@LINE-4]]:14, [[@LINE-4]]:21 -> [[@LINE-4]]:24, [[@LINE-4]]:35 -> [[@LINE-4]]:39, [[@LINE-1]]:4 -> [[@LINE-1]]:10 + return ']'; +} +// RUN: clang-refactor-test rename-indexed-file -name=a_200:foo:withSomething:onEntity:struct -new-name=world:bar:class:perform:z_Z_42 -indexed-file=%s -indexed-at=372:93 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK70 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:a_200:foo:piece -new-name=part:withSomething:name:foo -indexed-file=%s -indexed-at=372:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK71 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:method:test:test:world -new-name=withSomething:perform:usingThing:usingThing:method -indexed-file=%s -indexed-at=378:66 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK72 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:onEntity:method -new-name=foo:name:a_200 -indexed-file=%s -indexed-at=378:55 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK73 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:struct:a_200 -new-name=method:z_Z_42:struct -indexed-file=%s -indexed-at=378:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK74 %s +// RUN: clang-refactor-test rename-indexed-file -name=part:usingThing -new-name=withSomething:name -indexed-file=%s -indexed-at=386:89 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK75 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:usingThing:foo -new-name=usingThing:method:class -indexed-file=%s -indexed-at=386:13 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK76 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:foo:name:object -new-name=z_Z_42:world:name:object -indexed-file=%s -indexed-at=393:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK77 %s + +-(BOOL) object { + [self part: @{ @1, @3 } foo: "]" method: ([self a_200: @"string literal" test: part: globalArray[i] object: "]"]) usingThing: @{ @1, @3 }]; + // CHECK78: [[@LINE-1]]:51 -> [[@LINE-1]]:56, [[@LINE-1]]:76 -> [[@LINE-1]]:80, [[@LINE-1]]:83 -> [[@LINE-1]]:87, [[@LINE-1]]:104 -> [[@LINE-1]]:110 + // CHECK79: [[@LINE-2]]:9 -> [[@LINE-2]]:13, [[@LINE-2]]:27 -> [[@LINE-2]]:30, [[@LINE-2]]:36 -> [[@LINE-2]]:42, [[@LINE-2]]:118 -> [[@LINE-2]]:128 + [self onEntity: [] () { int bar = [self test: "string"/*comment*/ bar: globalArray[i] method: [] () { + }]; + // CHECK80: [[@LINE-2]]:46 -> [[@LINE-2]]:50, [[@LINE-2]]:72 -> [[@LINE-2]]:75, [[@LINE-2]]:92 -> [[@LINE-2]]:98 + } part: @"string literal" perform: [self.undef_property onEntity: globalArray[i] test: [] { + if ("]") { + [super foo: "string" z_Z_42: 12]; + // CHECK81: [[@LINE-1]]:14 -> [[@LINE-1]]:17, [[@LINE-1]]:28 -> [[@LINE-1]]:34 + + } + + } method: @"string literal" usingThing: ^ () { + Object * method = globalArray[i]; + + }]]; + // CHECK82: [[@LINE-11]]:59 -> [[@LINE-11]]:67, [[@LINE-11]]:84 -> [[@LINE-11]]:88, [[@LINE-4]]:4 -> [[@LINE-4]]:10, [[@LINE-4]]:30 -> [[@LINE-4]]:40 + // CHECK83: [[@LINE-15]]:9 -> [[@LINE-15]]:17, [[@LINE-12]]:5 -> [[@LINE-12]]:9, [[@LINE-12]]:29 -> [[@LINE-12]]:36 + [self usingThing: ^ () { int bar = [self onEntity: [self.undef_property perform: "]" + struct: [self name: "string" withSomething: ^ () { + + + }]] foo: ']' object: [_undef_ivar world: [super withSomething: (@{ @1, @3 }) name: @"string literal" +] onEntity: [super perform: "string" class: @"string literal"]] usingThing: 12 == @"string literal" +]; + // CHECK84: [[@LINE-6]]:16 -> [[@LINE-6]]:20, [[@LINE-6]]:31 -> [[@LINE-6]]:44 + // CHECK85: [[@LINE-8]]:78 -> [[@LINE-8]]:85, [[@LINE-7]]:2 -> [[@LINE-7]]:8 + // CHECK86: [[@LINE-5]]:50 -> [[@LINE-5]]:63, [[@LINE-5]]:79 -> [[@LINE-5]]:83 + // CHECK87: [[@LINE-5]]:20 -> [[@LINE-5]]:27, [[@LINE-5]]:38 -> [[@LINE-5]]:43 + // CHECK88: [[@LINE-7]]:36 -> [[@LINE-7]]:41, [[@LINE-6]]:3 -> [[@LINE-6]]:11 + // CHECK89: [[@LINE-12]]:47 -> [[@LINE-12]]:55, [[@LINE-8]]:6 -> [[@LINE-8]]:9, [[@LINE-8]]:15 -> [[@LINE-8]]:21, [[@LINE-7]]:65 -> [[@LINE-7]]:75 + } withSomething: "string" world: "string"]; + // CHECK90: [[@LINE-14]]:9 -> [[@LINE-14]]:19, [[@LINE-1]]:5 -> [[@LINE-1]]:18, [[@LINE-1]]:29 -> [[@LINE-1]]:34 + globalArray[12] = [self.undef_property bar: "string" z_Z_42: ^ { + [self class: ']' struct: @{ @1, @3 } + withSomething: ']' class: (']') world: @{ @1, @3 }]; + // CHECK91: [[@LINE-2]]:10 -> [[@LINE-2]]:15, [[@LINE-2]]:21 -> [[@LINE-2]]:27, [[@LINE-1]]:2 -> [[@LINE-1]]:15, [[@LINE-1]]:21 -> [[@LINE-1]]:26, [[@LINE-1]]:34 -> [[@LINE-1]]:39 + + } + a_200: [] () { ; ;[_undef_ivar class: ']' struct: ^ { + } z_Z_42: [super withSomething: ^ { + + + } foo: globalArray[i] perform: (@"string literal") perform: 12]]; + // CHECK92: [[@LINE-4]]:20 -> [[@LINE-4]]:33, [[@LINE-1]]:4 -> [[@LINE-1]]:7, [[@LINE-1]]:24 -> [[@LINE-1]]:31, [[@LINE-1]]:53 -> [[@LINE-1]]:60 + // CHECK93: [[@LINE-6]]:36 -> [[@LINE-6]]:41, [[@LINE-6]]:47 -> [[@LINE-6]]:53, [[@LINE-5]]:5 -> [[@LINE-5]]:11 + } +]; + // CHECK94: [[@LINE-15]]:42 -> [[@LINE-15]]:45, [[@LINE-15]]:56 -> [[@LINE-15]]:62, [[@LINE-9]]:2 -> [[@LINE-9]]:7 +} +// RUN: clang-refactor-test rename-indexed-file -name=a_200:test:part:object -new-name=object:a_200:object:object -indexed-file=%s -indexed-at=410:51 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK78 %s +// RUN: clang-refactor-test rename-indexed-file -name=part:foo:method:usingThing -new-name=world:onEntity:foo:a_200 -indexed-file=%s -indexed-at=410:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK79 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:bar:method -new-name=object:method:withSomething -indexed-file=%s -indexed-at=413:46 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK80 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:z_Z_42 -new-name=foo:class -indexed-file=%s -indexed-at=418:14 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK81 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:test:method:usingThing -new-name=class:a_200:class:object -indexed-file=%s -indexed-at=416:59 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK82 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:part:perform -new-name=name:z_Z_42:class -indexed-file=%s -indexed-at=413:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK83 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:withSomething -new-name=withSomething:z_Z_42 -indexed-file=%s -indexed-at=430:16 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK84 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:struct -new-name=usingThing:struct -indexed-file=%s -indexed-at=429:78 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK85 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:name -new-name=name:foo -indexed-file=%s -indexed-at=433:50 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK86 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:class -new-name=test:z_Z_42 -indexed-file=%s -indexed-at=434:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK87 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:onEntity -new-name=foo:z_Z_42 -indexed-file=%s -indexed-at=433:36 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK88 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:foo:object:usingThing -new-name=test:part:struct:object -indexed-file=%s -indexed-at=429:47 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK89 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:withSomething:world -new-name=a_200:test:struct -indexed-file=%s -indexed-at=429:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK90 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:struct:withSomething:class:world -new-name=test:bar:foo:usingThing:usingThing -indexed-file=%s -indexed-at=445:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK91 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:foo:perform:perform -new-name=onEntity:struct:piece:withSomething -indexed-file=%s -indexed-at=451:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK92 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:struct:z_Z_42 -new-name=onEntity:method:a_200 -indexed-file=%s -indexed-at=450:36 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK93 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:z_Z_42:a_200 -new-name=withSomething:class:z_Z_42 -indexed-file=%s -indexed-at=444:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK94 %s + ++(BOOL) world { + ; ;[self struct: [] { call() = [self part: 12 class: ^ { + + + } test: ^ () { + + + } a_200: "string"]; + // CHECK95: [[@LINE-7]]:44 -> [[@LINE-7]]:48, [[@LINE-7]]:53 -> [[@LINE-7]]:58, [[@LINE-4]]:4 -> [[@LINE-4]]:8, [[@LINE-1]]:4 -> [[@LINE-1]]:9 + } piece: "string" z_Z_42: [] { [self onEntity: struct: ^ () { + } world: [_undef_ivar withSomething: globalArray[i] piece: ^ { + + + } method: + part: @"string literal" world: @"string literal" +]]; + // CHECK96: [[@LINE-6]]:25 -> [[@LINE-6]]:38, [[@LINE-6]]:55 -> [[@LINE-6]]:60, [[@LINE-3]]:4 -> [[@LINE-3]]:10, [[@LINE-2]]:2 -> [[@LINE-2]]:6, [[@LINE-2]]:26 -> [[@LINE-2]]:31 + // CHECK97: [[@LINE-8]]:44 -> [[@LINE-8]]:52, [[@LINE-8]]:55 -> [[@LINE-8]]:61, [[@LINE-7]]:5 -> [[@LINE-7]]:10 + }]; + // CHECK98: [[@LINE-18]]:12 -> [[@LINE-18]]:18, [[@LINE-10]]:5 -> [[@LINE-10]]:10, [[@LINE-10]]:21 -> [[@LINE-10]]:27 + if (globalArray[i]) { + ([_undef_ivar world: ']' class: usingThing: 12]); + // CHECK99: [[@LINE-1]]:21 -> [[@LINE-1]]:26, [[@LINE-1]]:32 -> [[@LINE-1]]:37, [[@LINE-1]]:40 -> [[@LINE-1]]:50 + + } + some_type_t foo = [self.undef_property object: ']' + test: ^ () { + globalArray[12] = [_undef_ivar z_Z_42: 12 withSomething: [self a_200: ']' perform: globalArray[i] z_Z_42: [] { + + + } perform: @"string literal" z_Z_42: globalArray[i]] + name: 12]; + // CHECK100: [[@LINE-5]]:67 -> [[@LINE-5]]:72, [[@LINE-5]]:78 -> [[@LINE-5]]:85, [[@LINE-5]]:102 -> [[@LINE-5]]:108, [[@LINE-2]]:4 -> [[@LINE-2]]:11, [[@LINE-2]]:31 -> [[@LINE-2]]:37 + // CHECK101: [[@LINE-6]]:35 -> [[@LINE-6]]:41, [[@LINE-6]]:46 -> [[@LINE-6]]:59, [[@LINE-2]]:2 -> [[@LINE-2]]:6 + + } bar: "]" +//comment + usingThing: [] { + return [self onEntity: 12 < 12 withSomething: [] { + + + }]; + // CHECK102: [[@LINE-4]]:17 -> [[@LINE-4]]:25, [[@LINE-4]]:35 -> [[@LINE-4]]:48 + + } withSomething: ^ { + [super class: ([self.undef_property bar: [self withSomething: [self piece: "string" < globalArray[i] onEntity: [self method: 12 + part: "string" usingThing: @{ @1, @3 }]] perform: globalArray[i] bar: [] { + } name: 12] withSomething: "]"]) world: [] () { + } usingThing: "]"]; + // CHECK103: [[@LINE-4]]:121 -> [[@LINE-4]]:127, [[@LINE-3]]:2 -> [[@LINE-3]]:6, [[@LINE-3]]:17 -> [[@LINE-3]]:27 + // CHECK104: [[@LINE-5]]:72 -> [[@LINE-5]]:77, [[@LINE-5]]:105 -> [[@LINE-5]]:113 + // CHECK105: [[@LINE-6]]:51 -> [[@LINE-6]]:64, [[@LINE-5]]:43 -> [[@LINE-5]]:50, [[@LINE-5]]:67 -> [[@LINE-5]]:70, [[@LINE-4]]:5 -> [[@LINE-4]]:9 + // CHECK106: [[@LINE-7]]:40 -> [[@LINE-7]]:43, [[@LINE-5]]:15 -> [[@LINE-5]]:28 + // CHECK107: [[@LINE-8]]:11 -> [[@LINE-8]]:16, [[@LINE-6]]:36 -> [[@LINE-6]]:41, [[@LINE-5]]:5 -> [[@LINE-5]]:15 + + }]; + // CHECK108: [[@LINE-31]]:42 -> [[@LINE-31]]:48, [[@LINE-30]]:2 -> [[@LINE-30]]:6, [[@LINE-21]]:4 -> [[@LINE-21]]:7, [[@LINE-19]]:2 -> [[@LINE-19]]:12, [[@LINE-12]]:4 -> [[@LINE-12]]:17 +} +// RUN: clang-refactor-test rename-indexed-file -name=part:class:test:a_200 -new-name=perform:name:test:struct -indexed-file=%s -indexed-at=480:44 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK95 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:piece:method:part:world -new-name=object:piece:world:piece:world -indexed-file=%s -indexed-at=489:25 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK96 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:struct:world -new-name=onEntity:world:world -indexed-file=%s -indexed-at=488:44 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK97 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:piece:z_Z_42 -new-name=part:object:onEntity -indexed-file=%s -indexed-at=480:12 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK98 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:class:usingThing -new-name=class:struct:method -indexed-file=%s -indexed-at=500:21 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK99 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:perform:z_Z_42:perform:z_Z_42 -new-name=piece:part:piece:class:onEntity -indexed-file=%s -indexed-at=506:67 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK100 %s +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:withSomething:name -new-name=usingThing:onEntity:a_200 -indexed-file=%s -indexed-at=506:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK101 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:withSomething -new-name=method:usingThing -indexed-file=%s -indexed-at=517:17 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK102 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:part:usingThing -new-name=object:piece:method -indexed-file=%s -indexed-at=524:121 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK103 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:onEntity -new-name=bar:struct -indexed-file=%s -indexed-at=524:72 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK104 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:perform:bar:name -new-name=usingThing:z_Z_42:class:part -indexed-file=%s -indexed-at=524:51 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK105 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:withSomething -new-name=world:perform -indexed-file=%s -indexed-at=524:40 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK106 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:world:usingThing -new-name=object:name:name -indexed-file=%s -indexed-at=524:11 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK107 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:test:bar:usingThing:withSomething -new-name=bar:piece:class:perform:class -indexed-file=%s -indexed-at=504:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK108 %s + ++(Object *) test { + [super foo: globalArray[i] a_200: globalArray[i] +//comment + foo: 12 + struct: ^ () { + [_undef_ivar class: globalArray[i] struct: 12 object: @"string literal" foo: "string"]; + // CHECK109: [[@LINE-1]]:17 -> [[@LINE-1]]:22, [[@LINE-1]]:39 -> [[@LINE-1]]:45, [[@LINE-1]]:50 -> [[@LINE-1]]:56, [[@LINE-1]]:76 -> [[@LINE-1]]:79 + + } * [super struct: [self world: ']' struct: [] { + [self method: [] { + + + } withSomething: "string" usingThing: [] () { + + + }]; + // CHECK110: [[@LINE-7]]:10 -> [[@LINE-7]]:16, [[@LINE-4]]:4 -> [[@LINE-4]]:17, [[@LINE-4]]:28 -> [[@LINE-4]]:38 + + } struct: ^ () { + return [] () { + }; + + } class: [_undef_ivar part: "string" name: [] { + int bar = [self withSomething: "string" bar: @"string literal" usingThing: @"string literal"]; + // CHECK111: [[@LINE-1]]:20 -> [[@LINE-1]]:33, [[@LINE-1]]:44 -> [[@LINE-1]]:47, [[@LINE-1]]:67 -> [[@LINE-1]]:77 + + }] class: "]"] < 12 foo: * "]"] + "string" + "]" == [] () { + some_type_t foo = [_undef_ivar part: 12 name: @"string literal" test: "]" bar: ^ { + }]; + // CHECK112: [[@LINE-7]]:24 -> [[@LINE-7]]:28, [[@LINE-7]]:39 -> [[@LINE-7]]:43 + // CHECK113: [[@LINE-22]]:27 -> [[@LINE-22]]:32, [[@LINE-22]]:38 -> [[@LINE-22]]:44, [[@LINE-12]]:4 -> [[@LINE-12]]:10, [[@LINE-8]]:4 -> [[@LINE-8]]:9, [[@LINE-4]]:5 -> [[@LINE-4]]:10 + // CHECK114: [[@LINE-23]]:13 -> [[@LINE-23]]:19, [[@LINE-5]]:22 -> [[@LINE-5]]:25 + // CHECK115: [[@LINE-5]]:35 -> [[@LINE-5]]:39, [[@LINE-5]]:44 -> [[@LINE-5]]:48, [[@LINE-5]]:68 -> [[@LINE-5]]:72, [[@LINE-5]]:78 -> [[@LINE-5]]:81 + + } < ^ { + int bar = [self struct: ^ () { + + + } a_200: @{ @1, @3 }]; + // CHECK116: [[@LINE-4]]:20 -> [[@LINE-4]]:26, [[@LINE-1]]:4 -> [[@LINE-1]]:9 + + } == [super withSomething: [] { return [_undef_ivar usingThing: [] () { + } + world: [] () { + + + } foo: 12 perform: globalArray[i] name: @"string literal"]; + // CHECK117: [[@LINE-6]]:58 -> [[@LINE-6]]:68, [[@LINE-4]]:2 -> [[@LINE-4]]:7, [[@LINE-1]]:4 -> [[@LINE-1]]:7, [[@LINE-1]]:12 -> [[@LINE-1]]:19, [[@LINE-1]]:36 -> [[@LINE-1]]:40 + } world: ^ { // comment + } object: ^ { int method = "]"; + } +]]; + // CHECK118: [[@LINE-11]]:14 -> [[@LINE-11]]:27, [[@LINE-4]]:5 -> [[@LINE-4]]:10, [[@LINE-3]]:5 -> [[@LINE-3]]:11 + // CHECK119: [[@LINE-52]]:10 -> [[@LINE-52]]:13, [[@LINE-52]]:30 -> [[@LINE-52]]:35, [[@LINE-50]]:2 -> [[@LINE-50]]:5, [[@LINE-49]]:2 -> [[@LINE-49]]:8 + return [] { return 12; + }; + if ([] { + some_type_t foo = [_undef_ivar perform: @"string literal" name: globalArray[i] * ']' + z_Z_42: "]" perform: globalArray[i] class: globalArray[i] +]; + // CHECK120: [[@LINE-3]]:35 -> [[@LINE-3]]:42, [[@LINE-3]]:62 -> [[@LINE-3]]:66, [[@LINE-2]]:2 -> [[@LINE-2]]:8, [[@LINE-2]]:14 -> [[@LINE-2]]:21, [[@LINE-2]]:38 -> [[@LINE-2]]:43 + + }) { + [globalObject message] = [self.undef_property withSomething: ']' test: @"string literal" onEntity: ^ { + return @{ @1, @3 }; + + } onEntity: "string"]; + // CHECK121: [[@LINE-4]]:53 -> [[@LINE-4]]:66, [[@LINE-4]]:72 -> [[@LINE-4]]:76, [[@LINE-4]]:96 -> [[@LINE-4]]:104, [[@LINE-1]]:4 -> [[@LINE-1]]:12 + + } + [globalObject message] = [self onEntity: "string" onEntity: ^ { call() = [self withSomething: ^ () { + } == ']' perform: ("string") bar: (']')]; + // CHECK122: [[@LINE-2]]:86 -> [[@LINE-2]]:99, [[@LINE-1]]:12 -> [[@LINE-1]]:19, [[@LINE-1]]:32 -> [[@LINE-1]]:35 + } +]; + // CHECK123: [[@LINE-5]]:34 -> [[@LINE-5]]:42, [[@LINE-5]]:53 -> [[@LINE-5]]:61 + globalArray[12] = [self withSomething: @"string literal" piece: ^ { + call() = [_undef_ivar perform: (12) bar: ^ { + + + } test: "]" name: "string" bar: "]"]; + // CHECK124: [[@LINE-4]]:26 -> [[@LINE-4]]:33, [[@LINE-4]]:40 -> [[@LINE-4]]:43, [[@LINE-1]]:4 -> [[@LINE-1]]:8, [[@LINE-1]]:14 -> [[@LINE-1]]:18, [[@LINE-1]]:29 -> [[@LINE-1]]:32 + + } z_Z_42: [] () { [self onEntity: [] () { + + + } piece: "string"]; + // CHECK125: [[@LINE-4]]:29 -> [[@LINE-4]]:37, [[@LINE-1]]:4 -> [[@LINE-1]]:9 + } + @"string literal" object: ^ { + [globalObject send: [self world: "]" onEntity: ']' + struct: [] () { + } perform: [self.undef_property piece: [] () { + + + } method: 12 foo: [] { + + + } onEntity: "string" * "]" method: "]"] < ']'] other: 42]; + // CHECK126: [[@LINE-7]]:35 -> [[@LINE-7]]:40, [[@LINE-4]]:4 -> [[@LINE-4]]:10, [[@LINE-4]]:15 -> [[@LINE-4]]:18, [[@LINE-1]]:4 -> [[@LINE-1]]:12, [[@LINE-1]]:29 -> [[@LINE-1]]:35 + // CHECK127: [[@LINE-10]]:30 -> [[@LINE-10]]:35, [[@LINE-10]]:41 -> [[@LINE-10]]:49, [[@LINE-9]]:2 -> [[@LINE-9]]:8, [[@LINE-8]]:5 -> [[@LINE-8]]:12 + + } method: ^ { // comment + }]; + // CHECK128: [[@LINE-27]]:27 -> [[@LINE-27]]:40, [[@LINE-27]]:60 -> [[@LINE-27]]:65, [[@LINE-20]]:4 -> [[@LINE-20]]:10, [[@LINE-15]]:25 -> [[@LINE-15]]:31, [[@LINE-2]]:4 -> [[@LINE-2]]:10 +} +// RUN: clang-refactor-test rename-indexed-file -name=class:struct:object:foo -new-name=onEntity:onEntity:struct:foo -indexed-file=%s -indexed-at=557:17 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK109 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:withSomething:usingThing -new-name=object:class:withSomething -indexed-file=%s -indexed-at=561:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK110 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:bar:usingThing -new-name=piece:class:test -indexed-file=%s -indexed-at=575:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK111 %s +// RUN: clang-refactor-test rename-indexed-file -name=part:name -new-name=bar:object -indexed-file=%s -indexed-at=574:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK112 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:struct:struct:class:class -new-name=name:onEntity:z_Z_42:piece:foo -indexed-file=%s -indexed-at=560:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK113 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:foo -new-name=foo:test -indexed-file=%s -indexed-at=560:13 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK114 %s +// RUN: clang-refactor-test rename-indexed-file -name=part:name:test:bar -new-name=class:z_Z_42:onEntity:usingThing -indexed-file=%s -indexed-at=579:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK115 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:a_200 -new-name=usingThing:bar -indexed-file=%s -indexed-at=587:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK116 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:world:foo:perform:name -new-name=withSomething:foo:world:test:test -indexed-file=%s -indexed-at=593:58 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK117 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:world:object -new-name=withSomething:class:part -indexed-file=%s -indexed-at=593:14 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK118 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:a_200:foo:struct -new-name=usingThing:foo:object:test -indexed-file=%s -indexed-at=553:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK119 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:name:z_Z_42:perform:class -new-name=method:usingThing:class:class:world -indexed-file=%s -indexed-at=609:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK120 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:test:onEntity:onEntity -new-name=onEntity:z_Z_42:a_200:piece -indexed-file=%s -indexed-at=615:53 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK121 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:perform:bar -new-name=foo:usingThing:world -indexed-file=%s -indexed-at=622:86 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK122 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:onEntity -new-name=foo:perform -indexed-file=%s -indexed-at=622:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK123 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:bar:test:name:bar -new-name=onEntity:withSomething:object:method:test -indexed-file=%s -indexed-at=629:26 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK124 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:piece -new-name=world:z_Z_42 -indexed-file=%s -indexed-at=635:29 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK125 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:method:foo:onEntity:method -new-name=foo:method:z_Z_42:piece:bar -indexed-file=%s -indexed-at=643:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK126 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:onEntity:struct:perform -new-name=test:object:z_Z_42:object -indexed-file=%s -indexed-at=641:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK127 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:piece:z_Z_42:object:method -new-name=test:test:withSomething:z_Z_42:name -indexed-file=%s -indexed-at=628:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK128 %s + ++(int) object { + [self usingThing: [] () { ([self.undef_property object: ^ { + } usingThing: @"string literal" + test: "string" usingThing: globalArray[i] + foo: /*]*/ globalArray[i]]); + // CHECK129: [[@LINE-4]]:54 -> [[@LINE-4]]:60, [[@LINE-3]]:5 -> [[@LINE-3]]:15, [[@LINE-2]]:2 -> [[@LINE-2]]:6, [[@LINE-2]]:17 -> [[@LINE-2]]:27, [[@LINE-1]]:2 -> [[@LINE-1]]:5 + } < ^ () { + [self z_Z_42: name: globalArray[i] test: globalArray[i] + perform: globalArray[i]]; + // CHECK130: [[@LINE-2]]:10 -> [[@LINE-2]]:16, [[@LINE-2]]:19 -> [[@LINE-2]]:23, [[@LINE-2]]:40 -> [[@LINE-2]]:44, [[@LINE-1]]:2 -> [[@LINE-1]]:9 + + } perform: a_200: ([] () { + [super z_Z_42: "string" z_Z_42: [self perform: [] () { + + + } withSomething: ^ { + + + } +] world: "]" onEntity: globalArray[i] foo: "]"]; + // CHECK131: [[@LINE-8]]:42 -> [[@LINE-8]]:49, [[@LINE-5]]:4 -> [[@LINE-5]]:17 + // CHECK132: [[@LINE-9]]:11 -> [[@LINE-9]]:17, [[@LINE-9]]:28 -> [[@LINE-9]]:34, [[@LINE-2]]:3 -> [[@LINE-2]]:8, [[@LINE-2]]:14 -> [[@LINE-2]]:22, [[@LINE-2]]:39 -> [[@LINE-2]]:42 + + } * "string") a_200: ']']; + // CHECK133: [[@LINE-23]]:9 -> [[@LINE-23]]:19, [[@LINE-13]]:4 -> [[@LINE-13]]:11, [[@LINE-13]]:14 -> [[@LINE-13]]:19, [[@LINE-1]]:16 -> [[@LINE-1]]:21 + if (12) { + int bar = [super piece: 12 == globalArray[i] method: [self class: [] () { return "string"; + } + class: @"string literal" test: ^ () { + globalArray[12] = [self class: globalArray[i] z_Z_42: [] () { + } method: [super test: [] () { + } struct: @"string literal" + withSomething: [] { + + + } == globalArray[i] class: "]" struct: @"string literal"]]; + // CHECK134: [[@LINE-6]]:20 -> [[@LINE-6]]:24, [[@LINE-5]]:5 -> [[@LINE-5]]:11, [[@LINE-4]]:2 -> [[@LINE-4]]:15, [[@LINE-1]]:22 -> [[@LINE-1]]:27, [[@LINE-1]]:33 -> [[@LINE-1]]:39 + // CHECK135: [[@LINE-8]]:28 -> [[@LINE-8]]:33, [[@LINE-8]]:50 -> [[@LINE-8]]:56, [[@LINE-7]]:5 -> [[@LINE-7]]:11 + + } struct: ']' * 12 + @"string literal"]]; + // CHECK136: [[@LINE-14]]:66 -> [[@LINE-14]]:71, [[@LINE-12]]:2 -> [[@LINE-12]]:7, [[@LINE-12]]:27 -> [[@LINE-12]]:31, [[@LINE-1]]:4 -> [[@LINE-1]]:10 + // CHECK137: [[@LINE-15]]:24 -> [[@LINE-15]]:29, [[@LINE-15]]:52 -> [[@LINE-15]]:58 + + } + [globalObject send: [super struct: ^ { + [globalObject message] = [self.undef_property test: "string" onEntity: "]"] other: 42]; + // CHECK138: [[@LINE-1]]:50 -> [[@LINE-1]]:54, [[@LINE-1]]:65 -> [[@LINE-1]]:73 + + } z_Z_42: @"string literal" perform: @"string literal" part: globalArray[i] < 12 struct: [self onEntity: @"string literal" part: "]"]]; + // CHECK139: [[@LINE-1]]:97 -> [[@LINE-1]]:105, [[@LINE-1]]:125 -> [[@LINE-1]]:129 + // CHECK140: [[@LINE-6]]:30 -> [[@LINE-6]]:36, [[@LINE-2]]:4 -> [[@LINE-2]]:10, [[@LINE-2]]:30 -> [[@LINE-2]]:37, [[@LINE-2]]:57 -> [[@LINE-2]]:61, [[@LINE-2]]:83 -> [[@LINE-2]]:89 + if ("]") { + [super onEntity: ^ { [self.undef_property withSomething: globalArray[i] + method: ^ () { + } onEntity: [self test: [_undef_ivar bar: @{ @1, @3 } < globalArray[i] < part: ] * "]" method: (([self struct: "string" piece: [] () { + } struct: "string"])) withSomething: [self a_200: "]" foo: ']' < [self a_200: @"string literal" object: ']' onEntity: "]"] part: 12 usingThing: globalArray[i] name: [self.undef_property perform: @"string literal" world: globalArray[i] method: (12) method: [self.undef_property foo: @"string literal" part: [] { + + + } * "string" withSomething: "string" +] perform: @"string literal"]] bar: ']' +]]; + // CHECK141: [[@LINE-7]]:40 -> [[@LINE-7]]:43, [[@LINE-7]]:77 -> [[@LINE-7]]:81 + // CHECK142: [[@LINE-8]]:107 -> [[@LINE-8]]:113, [[@LINE-8]]:124 -> [[@LINE-8]]:129, [[@LINE-7]]:5 -> [[@LINE-7]]:11 + // CHECK143: [[@LINE-8]]:74 -> [[@LINE-8]]:79, [[@LINE-8]]:99 -> [[@LINE-8]]:105, [[@LINE-8]]:111 -> [[@LINE-8]]:119 + // CHECK144: [[@LINE-9]]:280 -> [[@LINE-9]]:283, [[@LINE-9]]:303 -> [[@LINE-9]]:307, [[@LINE-6]]:15 -> [[@LINE-6]]:28 + // CHECK145: [[@LINE-10]]:189 -> [[@LINE-10]]:196, [[@LINE-10]]:216 -> [[@LINE-10]]:221, [[@LINE-10]]:238 -> [[@LINE-10]]:244, [[@LINE-10]]:251 -> [[@LINE-10]]:257, [[@LINE-6]]:3 -> [[@LINE-6]]:10 + // CHECK146: [[@LINE-11]]:46 -> [[@LINE-11]]:51, [[@LINE-11]]:57 -> [[@LINE-11]]:60, [[@LINE-11]]:126 -> [[@LINE-11]]:130, [[@LINE-11]]:135 -> [[@LINE-11]]:145, [[@LINE-11]]:162 -> [[@LINE-11]]:166 + // CHECK147: [[@LINE-13]]:21 -> [[@LINE-13]]:25, [[@LINE-13]]:91 -> [[@LINE-13]]:97, [[@LINE-12]]:25 -> [[@LINE-12]]:38, [[@LINE-8]]:32 -> [[@LINE-8]]:35 + // CHECK148: [[@LINE-16]]:53 -> [[@LINE-16]]:66, [[@LINE-15]]:2 -> [[@LINE-15]]:8, [[@LINE-14]]:5 -> [[@LINE-14]]:13 + } usingThing: +]; + // CHECK149: [[@LINE-19]]:14 -> [[@LINE-19]]:22, [[@LINE-2]]:5 -> [[@LINE-2]]:15 + + } + [self.undef_property test: [] { if (@"string literal") { + call() = [self z_Z_42: "string" usingThing: @"string literal" +]; + // CHECK150: [[@LINE-2]]:22 -> [[@LINE-2]]:28, [[@LINE-2]]:39 -> [[@LINE-2]]:49 + + } + } object: globalArray[i] + test: ^ () { if ("string" == @"string literal" + ^ () { + + + }) { + int name = 12 * 12; + + } + } a_200: "]"]; + // CHECK151: [[@LINE-15]]:24 -> [[@LINE-15]]:28, [[@LINE-9]]:5 -> [[@LINE-9]]:11, [[@LINE-8]]:2 -> [[@LINE-8]]:6, [[@LINE-1]]:5 -> [[@LINE-1]]:10 +} +// RUN: clang-refactor-test rename-indexed-file -name=object:usingThing:test:usingThing:foo -new-name=part:piece:object:a_200:name -indexed-file=%s -indexed-at=679:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK129 %s +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:name:test:perform -new-name=a_200:usingThing:usingThing:withSomething -indexed-file=%s -indexed-at=685:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK130 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:withSomething -new-name=piece:struct -indexed-file=%s -indexed-at=690:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK131 %s +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:z_Z_42:world:onEntity:foo -new-name=withSomething:method:perform:onEntity:bar -indexed-file=%s -indexed-at=690:11 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK132 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:perform:a_200:a_200 -new-name=a_200:world:usingThing:class -indexed-file=%s -indexed-at=679:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK133 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:struct:withSomething:class:struct -new-name=withSomething:part:a_200:method:perform -indexed-file=%s -indexed-at=708:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK134 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:z_Z_42:method -new-name=part:a_200:part -indexed-file=%s -indexed-at=707:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK135 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:class:test:struct -new-name=a_200:world:struct:world -indexed-file=%s -indexed-at=704:66 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK136 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:method -new-name=name:class -indexed-file=%s -indexed-at=704:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK137 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:onEntity -new-name=foo:piece -indexed-file=%s -indexed-at=723:50 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK138 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:part -new-name=class:name -indexed-file=%s -indexed-at=726:97 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK139 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:z_Z_42:perform:part:struct -new-name=onEntity:part:usingThing:struct:perform -indexed-file=%s -indexed-at=722:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK140 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:part -new-name=part:class -indexed-file=%s -indexed-at=732:40 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK141 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:piece:struct -new-name=part:onEntity:foo -indexed-file=%s -indexed-at=732:107 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK142 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:object:onEntity -new-name=world:bar:onEntity -indexed-file=%s -indexed-at=733:74 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK143 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:part:withSomething -new-name=usingThing:withSomething:perform -indexed-file=%s -indexed-at=733:280 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK144 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:world:method:method:perform -new-name=piece:bar:usingThing:class:piece -indexed-file=%s -indexed-at=733:189 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK145 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:foo:part:usingThing:name -new-name=z_Z_42:object:name:perform:foo -indexed-file=%s -indexed-at=733:46 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK146 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:method:withSomething:bar -new-name=piece:class:a_200:z_Z_42 -indexed-file=%s -indexed-at=732:21 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK147 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:method:onEntity -new-name=usingThing:foo:object -indexed-file=%s -indexed-at=730:53 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK148 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:usingThing -new-name=perform:piece -indexed-file=%s -indexed-at=730:14 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK149 %s +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:usingThing -new-name=test:usingThing -indexed-file=%s -indexed-at=753:22 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK150 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:object:test:a_200 -new-name=foo:world:piece:z_Z_42 -indexed-file=%s -indexed-at=752:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK151 %s + ++(void) onEntity { + int perform = [_undef_ivar onEntity: [] () { + /*comment*/([self z_Z_42: ']' + globalArray[i] a_200: method: "]" == "]" < ^ () { + + + }]); + // CHECK152: [[@LINE-4]]:22 -> [[@LINE-4]]:28, [[@LINE-4]]:51 -> [[@LINE-4]]:56, [[@LINE-4]]:59 -> [[@LINE-4]]:65 + + } method: "]" < globalArray[i] == @"string literal" struct: "string" == @{ @1, @3 } onEntity: @"string literal" + perform: ^ () { globalArray[12] = [self foo: ']' * @"string literal" piece: ^ { + } perform: "string" name: "]"]; + // CHECK153: [[@LINE-2]]:45 -> [[@LINE-2]]:48, [[@LINE-2]]:74 -> [[@LINE-2]]:79, [[@LINE-1]]:5 -> [[@LINE-1]]:12, [[@LINE-1]]:23 -> [[@LINE-1]]:27 + }]; + // CHECK154: [[@LINE-12]]:30 -> [[@LINE-12]]:38, [[@LINE-5]]:4 -> [[@LINE-5]]:10, [[@LINE-5]]:54 -> [[@LINE-5]]:60, [[@LINE-5]]:86 -> [[@LINE-5]]:94, [[@LINE-4]]:2 -> [[@LINE-4]]:9 + return [] { + [self part: (@"string literal") world: [self struct: [self a_200: "]" test: ']'] piece: "]" withSomething: @"string literal" struct: "]" object: ] bar: ']']; + // CHECK155: [[@LINE-1]]:63 -> [[@LINE-1]]:68, [[@LINE-1]]:74 -> [[@LINE-1]]:78 + // CHECK156: [[@LINE-2]]:49 -> [[@LINE-2]]:55, [[@LINE-2]]:85 -> [[@LINE-2]]:90, [[@LINE-2]]:96 -> [[@LINE-2]]:109, [[@LINE-2]]:129 -> [[@LINE-2]]:135, [[@LINE-2]]:141 -> [[@LINE-2]]:147 + // CHECK157: [[@LINE-3]]:10 -> [[@LINE-3]]:14, [[@LINE-3]]:36 -> [[@LINE-3]]:41, [[@LINE-3]]:151 -> [[@LINE-3]]:154 + + }; +} +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:a_200:method -new-name=name:world:part -indexed-file=%s -indexed-at=795:22 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK152 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:piece:perform:name -new-name=name:class:withSomething:method -indexed-file=%s -indexed-at=802:45 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK153 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:method:struct:onEntity:perform -new-name=withSomething:piece:bar:struct:piece -indexed-file=%s -indexed-at=794:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK154 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:test -new-name=part:z_Z_42 -indexed-file=%s -indexed-at=808:63 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK155 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:piece:withSomething:struct:object -new-name=foo:name:piece:z_Z_42:bar -indexed-file=%s -indexed-at=808:49 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK156 %s +// RUN: clang-refactor-test rename-indexed-file -name=part:world:bar -new-name=test:a_200:z_Z_42 -indexed-file=%s -indexed-at=808:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK157 %s + ++(const Object &) foo { + [self foo: "]" bar: ']' method: ^ () { ] } z_Z_42: [super usingThing: "string" world: [self world: globalArray[i] a_200: [] { [self z_Z_42: ']' usingThing: ("]")]; + // CHECK158: [[@LINE-1]]:139 -> [[@LINE-1]]:145, [[@LINE-1]]:151 -> [[@LINE-1]]:161 + } foo: (12)] usingThing: [] { + globalArray[12] = [self name: [] { + + + } foo: @"string literal"/*comment*/ withSomething: globalArray[i] test: [] { + }]; + // CHECK159: [[@LINE-8]]:95 -> [[@LINE-8]]:100, [[@LINE-8]]:117 -> [[@LINE-8]]:122, [[@LINE-6]]:5 -> [[@LINE-6]]:8 + // CHECK160: [[@LINE-6]]:28 -> [[@LINE-6]]:32, [[@LINE-3]]:4 -> [[@LINE-3]]:7, [[@LINE-3]]:38 -> [[@LINE-3]]:51, [[@LINE-3]]:68 -> [[@LINE-3]]:72 + + } foo: ^ { call() = [_undef_ivar world: @"string literal" usingThing: 12 onEntity: @"string literal" struct: ^ { + }]; + // CHECK161: [[@LINE-2]]:39 -> [[@LINE-2]]:44, [[@LINE-2]]:64 -> [[@LINE-2]]:74, [[@LINE-2]]:79 -> [[@LINE-2]]:87, [[@LINE-2]]:107 -> [[@LINE-2]]:113 + } < "]" test: ^ () { if ([] { + }) { + call() = [self withSomething: globalArray[i] z_Z_42: globalArray[i] + foo: 12 onEntity: @"string literal"]; + // CHECK162: [[@LINE-2]]:22 -> [[@LINE-2]]:35, [[@LINE-2]]:52 -> [[@LINE-2]]:58, [[@LINE-1]]:2 -> [[@LINE-1]]:5, [[@LINE-1]]:10 -> [[@LINE-1]]:18 + + } + }]]; + // CHECK163: [[@LINE-22]]:61 -> [[@LINE-22]]:71, [[@LINE-22]]:82 -> [[@LINE-22]]:87, [[@LINE-20]]:16 -> [[@LINE-20]]:26, [[@LINE-11]]:4 -> [[@LINE-11]]:7, [[@LINE-8]]:11 -> [[@LINE-8]]:15 + // CHECK164: [[@LINE-23]]:9 -> [[@LINE-23]]:12, [[@LINE-23]]:18 -> [[@LINE-23]]:21, [[@LINE-23]]:27 -> [[@LINE-23]]:33, [[@LINE-23]]:46 -> [[@LINE-23]]:52 + const Object & struct = ; + [globalObject message] = [self object: 12 + name: 12 a_200: ^ () { if ([self object: ^ { + } foo: "string" * @"string literal"]) { + [self withSomething: [] { + } +//comment + a_200: ^ { + } foo: "string" piece: [] () { + + + }]; + // CHECK165: [[@LINE-10]]:38 -> [[@LINE-10]]:44, [[@LINE-9]]:5 -> [[@LINE-9]]:8 + // CHECK166: [[@LINE-9]]:13 -> [[@LINE-9]]:26, [[@LINE-6]]:2 -> [[@LINE-6]]:7, [[@LINE-5]]:5 -> [[@LINE-5]]:8, [[@LINE-5]]:19 -> [[@LINE-5]]:24 + + } + }]; + // CHECK167: [[@LINE-16]]:34 -> [[@LINE-16]]:40, [[@LINE-15]]:2 -> [[@LINE-15]]:6, [[@LINE-15]]:11 -> [[@LINE-15]]:16 + [self world: @"string literal" withSomething: @{ @1, @3 }]; + // CHECK168: [[@LINE-1]]:9 -> [[@LINE-1]]:14, [[@LINE-1]]:34 -> [[@LINE-1]]:47 +} +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:usingThing -new-name=method:onEntity -indexed-file=%s -indexed-at=823:139 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK158 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:a_200:foo -new-name=class:test:bar -indexed-file=%s -indexed-at=823:95 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK159 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:foo:withSomething:test -new-name=a_200:perform:piece:method -indexed-file=%s -indexed-at=826:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK160 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:usingThing:onEntity:struct -new-name=usingThing:name:onEntity:method -indexed-file=%s -indexed-at=834:39 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK161 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:z_Z_42:foo:onEntity -new-name=perform:struct:bar:object -indexed-file=%s -indexed-at=839:22 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK162 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:world:usingThing:foo:test -new-name=method:onEntity:part:part:bar -indexed-file=%s -indexed-at=823:61 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK163 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:bar:method:z_Z_42 -new-name=class:onEntity:method:name -indexed-file=%s -indexed-at=823:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK164 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:foo -new-name=object:usingThing -indexed-file=%s -indexed-at=849:38 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK165 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:a_200:foo:piece -new-name=class:struct:bar:onEntity -indexed-file=%s -indexed-at=851:13 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK166 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:name:a_200 -new-name=piece:withSomething:withSomething -indexed-file=%s -indexed-at=848:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK167 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:withSomething -new-name=part:withSomething -indexed-file=%s -indexed-at=865:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK168 %s + ++(void) object { + int test = globalArray[i]; +} diff --git a/clang/test/Refactor/Rename/IndexedObjCMethod.m b/clang/test/Refactor/Rename/IndexedObjCMethod.m new file mode 100644 index 0000000000000..f9182c6c1eca5 --- /dev/null +++ b/clang/test/Refactor/Rename/IndexedObjCMethod.m @@ -0,0 +1,128 @@ +@interface Test + +- (int)performAction:(int)action with:(int)value; // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:34 -> [[@LINE]]:38 + +@end + +@implementation Test + +- (int)performAction:(int)action + with:(int)value { // CHECK: rename [[@LINE-1]]:8 -> [[@LINE-1]]:21, [[@LINE]]:8 -> [[@LINE]]:12 + return action + value; +} + ++ (void)foo:(Test*)t { + [t performAction: 2 with: 4]; // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:25 -> [[@LINE]]:29 + SEL s1 = @selector(performAction: + with:); // CHECK-NOT: selector [[@LINE-1]]:24 -> [[@LINE-1]]:37, [[@LINE]]:24 -> [[@LINE]]:28 + SEL s2 = @selector(performAction:); // CHECK-NOT: selector [[@LINE]] + SEL s3 = @selector(performAction); // CHECK-NOT: selector [[@LINE]] + // Not indexed + [t performAction: 1 with: 2]; // CHECK-NOT: rename [[@LINE]] +} + +@end + +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=performAction:with -new-name=foo:bar -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-im %s | FileCheck %s + +@interface SemicolonIsExcluded +-(void)/*A_propA4_set_decl*/setPropA4X:(int)value; +@end +// CHECK2: rename [[@LINE-2]]:29 -> [[@LINE-2]]:39 + +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=setPropA4X: -new-name=foo -indexed-file=%s -indexed-at=29:29 -indexed-symbol-kind=objc-im %s -x objective-c | FileCheck --check-prefix=CHECK2 %s + +// It should be possible to have the filename as one of the compilation arguments +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -ignore-filename-for-initiation-tu -name=performAction:with: -new-name=foo:bar: -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-cm %s -c %s -Wall | FileCheck %s +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -ignore-filename-for-initiation-tu -name=performAction:with: -new-name=foo:bar: -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-cm %s %s -fsyntax-only | FileCheck %s + +// -gmodules should be stripped to avoid -fmodule-format=obj in CC1 arguments: +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=performAction:with: -new-name=foo:bar: -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-cm %s -fmodules -gmodules | FileCheck %s + +// These texual matches should be reported as comment or selector occurrences: +// CHECK3: rename [[@LINE-40]]:8 -> [[@LINE-40]]:21, [[@LINE-40]]:34 -> [[@LINE-40]]:38 +// performAction +/* performAction: with: 2 performAction */ +/*! performAction+1 +// performAction with +!*/ +/// Hello performAction with World +/// \c performAction. + +// CHECK3: comment [[@LINE-8]]:4 -> [[@LINE-8]]:17 +// CHECK3-NEXT: comment [[@LINE-8]]:4 -> [[@LINE-8]]:17 +// CHECK3-NEXT: comment [[@LINE-9]]:27 -> [[@LINE-9]]:40 +// CHECK3-NEXT: documentation [[@LINE-9]]:5 -> [[@LINE-9]]:18 +// CHECK3-NEXT: documentation [[@LINE-9]]:4 -> [[@LINE-9]]:17 +// CHECK3-NEXT: documentation [[@LINE-8]]:11 -> [[@LINE-8]]:24 +// CHECK3-NEXT: documentation [[@LINE-8]]:8 -> [[@LINE-8]]:21 + +// "performAction:with:" +// 'performAction:'with: +// CHECK3-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:18 +// CHECK3-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:18 + +// CHECK3-NEXT: selector [[@LINE+1]]:11 -> [[@LINE+1]]:24, [[@LINE+1]]:25 -> [[@LINE+1]]:29 +@selector(performAction:with:); +// CHECK3-NEXT: selector [[@LINE+1]]:11 -> [[@LINE+1]]:24, [[@LINE+1]]:28 -> [[@LINE+1]]:32 +@selector(performAction : with ); +// CHECK3-NEXT: selector [[@LINE+2]]:19 -> [[@LINE+2]]:32, [[@LINE+2]]:46 -> [[@LINE+2]]:50 +SEL s = @selector(//comment + performAction: /*comment*/ with + ); +// CHECK3-NEXT: selector [[@LINE+1]]:33 -> [[@LINE+1]]:46, [[@LINE+2]]:33 -> [[@LINE+2]]:37 +void call = @selector(@selector(performAction: + with: )); + +// CHECK3-NEXT: comment [[@LINE+1]]:55 +// RUN: clang-refactor-test rename-indexed-file -name=performAction:with: -new-name=foo:bar -indexed-file=%s -indexed-at=objc-cm:3:8 %s | FileCheck --check-prefix=CHECK3 %s + +// These ones shouldn't: +// performAction2 PERFORMACTION performActionWith +const char *test = "performAction:with:"; + +@selector(performAction: with ::) +@selector(performAction:) +@selector(performAction) +@selector(performAction with) +@selector(performAction:without:) +@selector(performAction:with:somethingElse:) +@selector(performAction:with "") +@selector("performAction:with:") +@selector(with: performAction:) +selector(performAction:with) +(performAction:with:) + +// CHECK3-NOT: comment +// CHECK3-NOT: documentation +// CHECK3-NOT: selector + +// It should be possible to find a selector in a file without any indexed occurrences: + +// CHECK4: selector [[@LINE+1]]:11 +@selector(nonIndexedSelector) +// CHECK4-NEXT: comment +// CHECK4-NOT: selector + +// RUN: clang-refactor-test rename-indexed-file -indexed-symbol-kind=objc-im -name=nonIndexedSelector -new-name=test -indexed-file=%s %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-indexed-file -indexed-symbol-kind=objc-cm -name=nonIndexedSelector -new-name=test -indexed-file=%s %s | FileCheck --check-prefix=CHECK4 %s + +#define MACRO doStuff +#define MACRO2(x, y) doStuff:(x) with: (y) + +@interface I +- (void)doStuff:(int)x with: y; +@end + +@implementation I +- (void)MACRO:(int)x with: y { + [self MACRO2(x, y)]; +} +@end + +// CHECK-MACRO: macro [[@LINE-5]]:9 -> [[@LINE-5]]:9 +// CHECK-MACRO-NEXT: macro [[@LINE-5]]:9 -> [[@LINE-5]]:9 +// CHECK-MACRO-NEXT: rename [[@LINE-11]]:9 -> [[@LINE-11]]:16, [[@LINE-11]]:24 -> [[@LINE-11]]:28 +// CHECK-MACRO-NOT: macro + +// RUN: clang-refactor-test rename-indexed-file -indexed-symbol-kind=objc-im -name=doStuff:with: -new-name=foo:bar -indexed-file=%s -indexed-at=114:9 -indexed-at=118:9 -indexed-at=119:9 %s | FileCheck --check-prefix=CHECK-MACRO %s diff --git a/clang/test/Refactor/Rename/IndexedObjCMethodDecl.mm b/clang/test/Refactor/Rename/IndexedObjCMethodDecl.mm new file mode 100644 index 0000000000000..23e41ac4087ff --- /dev/null +++ b/clang/test/Refactor/Rename/IndexedObjCMethodDecl.mm @@ -0,0 +1,748 @@ +@implementation foo ++(some_type_t)a_200:(void)a_200 name:(int[1 + 2 - 3])z_Z_42 usingThing:(some_type_t)world method:(void)test +class:(int[1 + 2 - 3])method __attribute__((eval { int x = 0 + 1; })) method:(({}))method { + const Object & piece = 12; +} +// CHECK1: [[@LINE-4]]:15 -> [[@LINE-4]]:20, [[@LINE-4]]:33 -> [[@LINE-4]]:37, [[@LINE-4]]:61 -> [[@LINE-4]]:71, [[@LINE-4]]:91 -> [[@LINE-4]]:97, [[@LINE-3]]:1 -> [[@LINE-3]]:6, [[@LINE-3]]:71 -> [[@LINE-3]]:77 ++(some_type_t)world:(BOOL)withSomething class:(const Object &)name struct:(some_type_t)method __attribute__((test()))a_200:(int)name +a_200:(({}))perform withSomething:(Object * (^)(BOOL, Object *))onEntity +{ + const Object & class = globalArray[i]; +} +// CHECK2: [[@LINE-5]]:15 -> [[@LINE-5]]:20, [[@LINE-5]]:41 -> [[@LINE-5]]:46, [[@LINE-5]]:68 -> [[@LINE-5]]:74, [[@LINE-5]]:118 -> [[@LINE-5]]:123, [[@LINE-4]]:1 -> [[@LINE-4]]:6, [[@LINE-4]]:21 -> [[@LINE-4]]:34 +-(some_type_t)struct:(int[1 + 2 - 3])bar +class:(int[1 + 2 - 3])foo +part:(void)piece +{ + int onEntity = "]"; +} +// CHECK3: [[@LINE-6]]:15 -> [[@LINE-6]]:21, [[@LINE-5]]:1 -> [[@LINE-5]]:6, [[@LINE-4]]:1 -> [[@LINE-4]]:5 +-(some_type_t)test:(^ { })a_200 world:(BOOL)onEntity a_200:(BOOL)perform +withSomething:(({}))method +{ + [self part: [self z_Z_42: "]" world: ("]")] world: globalArray[i] class: "string" test: globalArray[i]]; +} +// CHECK4: [[@LINE-5]]:15 -> [[@LINE-5]]:19, [[@LINE-5]]:33 -> [[@LINE-5]]:38, [[@LINE-5]]:54 -> [[@LINE-5]]:59, [[@LINE-4]]:1 -> [[@LINE-4]]:14 +-(int)a_200:(BOOL)bar piece:(Object *)perform class:(^ { })onEntity { + [globalObject send: [self perform: ']' object: [] () { ([self name: ']' a_200: ']']) other: 42]; + } foo: ']']; +} +// CHECK5: [[@LINE-4]]:7 -> [[@LINE-4]]:12, [[@LINE-4]]:23 -> [[@LINE-4]]:28, [[@LINE-4]]:47 -> [[@LINE-4]]:52 +@end +// RUN: clang-refactor-test rename-indexed-file -name=a_200:name:usingThing:method:class:method -new-name=withSomething:struct:class:onEntity:withSomething:test -indexed-file=%s -indexed-at=2:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:class:struct:a_200:a_200:withSomething -new-name=onEntity:a_200:perform:onEntity:usingThing:onEntity -indexed-file=%s -indexed-at=7:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:class:part -new-name=z_Z_42:name:usingThing -indexed-file=%s -indexed-at=13:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:world:a_200:withSomething -new-name=part:withSomething:test:struct -indexed-file=%s -indexed-at=20:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:piece:class -new-name=a_200:a_200:piece -indexed-file=%s -indexed-at=26:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK5 %s + +@interface method ++(some_type_t)bar:(BOOL)bar bar:(Object *)usingThing struct:(int[1 + 2 - 3])usingThing +perform:(BOOL)onEntity ; +// CHECK6: [[@LINE-2]]:15 -> [[@LINE-2]]:18, [[@LINE-2]]:29 -> [[@LINE-2]]:32, [[@LINE-2]]:54 -> [[@LINE-2]]:60, [[@LINE-1]]:1 -> [[@LINE-1]]:8 ++(void)struct:(^ { })part __attribute__((test()))foo:(^ { })part part:(void)class __attribute__((eval { int x = 0 + 1; })) onEntity:(Object *)method foo:(const Object &)class /*comment*/ ; +// CHECK7: [[@LINE-1]]:8 -> [[@LINE-1]]:14, [[@LINE-1]]:50 -> [[@LINE-1]]:53, [[@LINE-1]]:66 -> [[@LINE-1]]:70, [[@LINE-1]]:124 -> [[@LINE-1]]:132, [[@LINE-1]]:150 -> [[@LINE-1]]:153 +@end +// RUN: clang-refactor-test rename-indexed-file -name=bar:bar:struct:perform -new-name=bar:withSomething:test:foo -indexed-file=%s -indexed-at=39:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:foo:part:onEntity:foo -new-name=z_Z_42:part:world:piece:perform -indexed-file=%s -indexed-at=42:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK7 %s + +@interface foo ++(BOOL)class:(Object *)method struct:(void)method +perform:(Object *)class +; +// CHECK8: [[@LINE-3]]:8 -> [[@LINE-3]]:13, [[@LINE-3]]:31 -> [[@LINE-3]]:37, [[@LINE-2]]:1 -> [[@LINE-2]]:8 ++(some_type_t)usingThing:(({}))struct withSomething:(const Object &)z_Z_42 method:(some_type_t)class +a_200:(int)a_200 ; +// CHECK9: [[@LINE-2]]:15 -> [[@LINE-2]]:25, [[@LINE-2]]:39 -> [[@LINE-2]]:52, [[@LINE-2]]:76 -> [[@LINE-2]]:82, [[@LINE-1]]:1 -> [[@LINE-1]]:6 ++(Object *)a_200:(const Object &)class //comment +class:(BOOL)bar usingThing:(void (*)(some_type_t, some_type_t))name +bar:(BOOL)onEntity method:(void)method +part:(BOOL)usingThing /*comment*/ ; +// CHECK10: [[@LINE-4]]:12 -> [[@LINE-4]]:17, [[@LINE-3]]:1 -> [[@LINE-3]]:6, [[@LINE-3]]:17 -> [[@LINE-3]]:27, [[@LINE-2]]:1 -> [[@LINE-2]]:4, [[@LINE-2]]:20 -> [[@LINE-2]]:26, [[@LINE-1]]:1 -> [[@LINE-1]]:5 +@end +// RUN: clang-refactor-test rename-indexed-file -name=class:struct:perform -new-name=withSomething:foo:a_200 -indexed-file=%s -indexed-at=49:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:withSomething:method:a_200 -new-name=piece:bar:test:perform -indexed-file=%s -indexed-at=53:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test rename-indexed-file -name=a_200:class:usingThing:bar:method:part -new-name=a_200:piece:class:z_Z_42:piece:world -indexed-file=%s -indexed-at=56:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK10 %s + +@implementation onEntity <object, onEntity, world> +-(const Object &)test:(some_type_t (*)(some_type_t))z_Z_42 //comment +perform:(Object *)name /*comment*/ method:(int)bar __attribute__((eval { int x = 0 + 1; })) part:(Object *)name z_Z_42:(BOOL)a_200 test:(^ { })usingThing __attribute__((test())){ + call() = [self test: "]" z_Z_42: ^ { [_undef_ivar foo: "string" piece: @{ @1, @3 } perform: @"string literal" world: ']']; + } == "]" a_200: 12/*comment*/ object: 12 test: 12]; +} +// CHECK11: [[@LINE-5]]:18 -> [[@LINE-5]]:22, [[@LINE-4]]:1 -> [[@LINE-4]]:8, [[@LINE-4]]:36 -> [[@LINE-4]]:42, [[@LINE-4]]:93 -> [[@LINE-4]]:97, [[@LINE-4]]:113 -> [[@LINE-4]]:119, [[@LINE-4]]:132 -> [[@LINE-4]]:136 +-(const Object &)z_Z_42:(({}))usingThing onEntity:(^ { })world +a_200:(const Object &)withSomething __attribute__((test()))onEntity:(({}))onEntity { + [_undef_ivar object: globalArray[i] perform: ^ { globalArray[12] = [_undef_ivar class: "]" onEntity: [] { + } bar: "string" < ^ { + } foo: "string" piece: @{ @1, @3 }]; + } test: /*]*/ method: globalArray[i] +]; +} +// CHECK12: [[@LINE-8]]:18 -> [[@LINE-8]]:24, [[@LINE-8]]:42 -> [[@LINE-8]]:50, [[@LINE-7]]:1 -> [[@LINE-7]]:6, [[@LINE-7]]:60 -> [[@LINE-7]]:68 +@end +// RUN: clang-refactor-test rename-indexed-file -name=test:perform:method:part:z_Z_42:test -new-name=a_200:piece:onEntity:withSomething:world:test -indexed-file=%s -indexed-at=67:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK11 %s +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:onEntity:a_200:onEntity -new-name=part:world:z_Z_42:onEntity -indexed-file=%s -indexed-at=73:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK12 %s + +@implementation method +-(BOOL)usingThing:(void)part part:(void)world +class:(int)part perform:(some_type_t)withSomething +z_Z_42:(const Object &)class //comment +usingThing:(({}))part { + some_type_t foo = [self.undef_property onEntity: "string" withSomething: ']']; +} +// CHECK13: [[@LINE-6]]:8 -> [[@LINE-6]]:18, [[@LINE-6]]:30 -> [[@LINE-6]]:34, [[@LINE-5]]:1 -> [[@LINE-5]]:6, [[@LINE-5]]:17 -> [[@LINE-5]]:24, [[@LINE-4]]:1 -> [[@LINE-4]]:7, [[@LINE-3]]:1 -> [[@LINE-3]]:11 +@end +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:part:class:perform:z_Z_42:usingThing -new-name=class:method:withSomething:world:name:name -indexed-file=%s -indexed-at=87:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK13 %s + +@interface withSomething +-(Object *)object:(^ { })onEntity __attribute__((test()))piece:(^ { })z_Z_42 /*comment*/ ; +// CHECK14: [[@LINE-1]]:12 -> [[@LINE-1]]:18, [[@LINE-1]]:58 -> [[@LINE-1]]:63 +@end +// RUN: clang-refactor-test rename-indexed-file -name=object:piece -new-name=foo:bar -indexed-file=%s -indexed-at=98:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK14 %s + +@interface usingThing <piece, withSomething, perform> ++(void)part:(some_type_t)onEntity +a_200:(int)bar perform:(some_type_t)struct +; +// CHECK15: [[@LINE-3]]:8 -> [[@LINE-3]]:12, [[@LINE-2]]:1 -> [[@LINE-2]]:6, [[@LINE-2]]:16 -> [[@LINE-2]]:23 +-(some_type_t)piece:(^ { })foo name:(({}))perform part:(^ { })name z_Z_42:(Object *)test +; +// CHECK16: [[@LINE-2]]:15 -> [[@LINE-2]]:20, [[@LINE-2]]:32 -> [[@LINE-2]]:36, [[@LINE-2]]:51 -> [[@LINE-2]]:55, [[@LINE-2]]:68 -> [[@LINE-2]]:74 ++(Object *)method:(const Object &)method onEntity:(^ { })withSomething +withSomething:(int (*)(const Object &, BOOL))piece ; +// CHECK17: [[@LINE-2]]:12 -> [[@LINE-2]]:18, [[@LINE-2]]:42 -> [[@LINE-2]]:50, [[@LINE-1]]:1 -> [[@LINE-1]]:14 +-(BOOL)foo:(int[1 + 2 - 3])world +foo:(int[1 + 2 - 3])struct method:(void (*)())piece foo:(BOOL)test __attribute__((eval { int x = 0 + 1; })) ; +// CHECK18: [[@LINE-2]]:8 -> [[@LINE-2]]:11, [[@LINE-1]]:1 -> [[@LINE-1]]:4, [[@LINE-1]]:28 -> [[@LINE-1]]:34, [[@LINE-1]]:53 -> [[@LINE-1]]:56 +@end +// RUN: clang-refactor-test rename-indexed-file -name=part:a_200:perform -new-name=part:test:part -indexed-file=%s -indexed-at=104:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK15 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:name:part:z_Z_42 -new-name=usingThing:struct:onEntity:piece -indexed-file=%s -indexed-at=108:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK16 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:onEntity:withSomething -new-name=z_Z_42:method:usingThing -indexed-file=%s -indexed-at=111:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK17 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:foo:method:foo -new-name=piece:usingThing:class:foo -indexed-file=%s -indexed-at=114:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK18 %s + +@interface foo +-(void)foo:(({}))part method:(({}))method ; +// CHECK19: [[@LINE-1]]:8 -> [[@LINE-1]]:11, [[@LINE-1]]:23 -> [[@LINE-1]]:29 +-(const Object &)foo:(({}))usingThing test:(Object *)struct __attribute__((test()))name:(const Object &)z_Z_42 /*comment*/ name:(^ { })onEntity a_200:(Object * (^)())usingThing +usingThing:(Object *)method /*comment*/ ; +// CHECK20: [[@LINE-2]]:18 -> [[@LINE-2]]:21, [[@LINE-2]]:39 -> [[@LINE-2]]:43, [[@LINE-2]]:84 -> [[@LINE-2]]:88, [[@LINE-2]]:124 -> [[@LINE-2]]:128, [[@LINE-2]]:145 -> [[@LINE-2]]:150, [[@LINE-1]]:1 -> [[@LINE-1]]:11 ++(BOOL)name:(Object *)a_200 part:(int)bar +perform:(some_type_t)method ; +// CHECK21: [[@LINE-2]]:8 -> [[@LINE-2]]:12, [[@LINE-2]]:29 -> [[@LINE-2]]:33, [[@LINE-1]]:1 -> [[@LINE-1]]:8 +-(some_type_t)usingThing:(void)test //comment +foo:(void)world z_Z_42:(Object *)bar ; +// CHECK22: [[@LINE-2]]:15 -> [[@LINE-2]]:25, [[@LINE-1]]:1 -> [[@LINE-1]]:4, [[@LINE-1]]:17 -> [[@LINE-1]]:23 +@end +// RUN: clang-refactor-test rename-indexed-file -name=foo:method -new-name=part:world -indexed-file=%s -indexed-at=124:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK19 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:test:name:name:a_200:usingThing -new-name=method:withSomething:struct:z_Z_42:bar:z_Z_42 -indexed-file=%s -indexed-at=126:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK20 %s +// RUN: clang-refactor-test rename-indexed-file -name=name:part:perform -new-name=withSomething:part:object -indexed-file=%s -indexed-at=129:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK21 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:foo:z_Z_42 -new-name=perform:test:class -indexed-file=%s -indexed-at=132:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK22 %s + +@implementation struct ++(void)bar:(void)piece a_200:(some_type_t)class { + [self name: [] () { + [globalObject send: [super name: [] () { + + + } perform: globalArray[i]] other: 42]; + + } usingThing: @"string literal"]; + + call() = [self usingThing: @"string literal" perform: "string"]; +} +// CHECK23: [[@LINE-11]]:8 -> [[@LINE-11]]:11, [[@LINE-11]]:24 -> [[@LINE-11]]:29 +-(void)perform:(BOOL)test +withSomething:(const Object &)foo { + return [_undef_ivar piece: globalArray[i] + globalArray[i] object: globalArray[i] + a_200: [self withSomething: [] () { [self.undef_property usingThing: ']' struct: "]" + perform: "string"]; + } + piece: ']' name: ']' a_200: "string" == ^ () { + globalArray[12] = [_undef_ivar foo: ']' foo: [] { + } + bar: ^ { + } + [] { + + + } +]; + + }] z_Z_42: ^ () { + if (^ () { + }) { + return @"string literal" < globalArray[i] == [self world: [] { + + + } object: @"string literal" part: ']']; + + } + + } a_200: "string"]; +} +// CHECK24: [[@LINE-28]]:8 -> [[@LINE-28]]:15, [[@LINE-27]]:1 -> [[@LINE-27]]:14 +@end +// RUN: clang-refactor-test rename-indexed-file -name=bar:a_200 -new-name=bar:name -indexed-file=%s -indexed-at=142:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK23 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:withSomething -new-name=withSomething:test -indexed-file=%s -indexed-at=154:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK24 %s + +@implementation object +-(int)a_200:(Object *)perform usingThing:(int)bar //comment +{ + [self bar: ']' withSomething: "string"]; + + Object * part = ']'; +} +// CHECK25: [[@LINE-6]]:7 -> [[@LINE-6]]:12, [[@LINE-6]]:31 -> [[@LINE-6]]:41 +@end +// RUN: clang-refactor-test rename-indexed-file -name=a_200:usingThing -new-name=object:bar -indexed-file=%s -indexed-at=188:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK25 %s + +@implementation test ++(void)method:(const Object &)z_Z_42 //comment +piece:(some_type_t (*)(Object *))withSomething __attribute__((eval { int x = 0 + 1; })) perform:(void)piece method:(BOOL)object { + [self piece: [] () { + ; ;[self part: 12 a_200: @"string literal"]; + + } struct: globalArray[i] part: "]"]; + + ; +} +// CHECK26: [[@LINE-9]]:8 -> [[@LINE-9]]:14, [[@LINE-8]]:1 -> [[@LINE-8]]:6, [[@LINE-8]]:89 -> [[@LINE-8]]:96, [[@LINE-8]]:109 -> [[@LINE-8]]:115 +-(some_type_t)world:(some_type_t)world +a_200:(void)bar //comment +{ + int piece = ; + + if ("string") { + // comment + + } +} +// CHECK27: [[@LINE-10]]:15 -> [[@LINE-10]]:20, [[@LINE-9]]:1 -> [[@LINE-9]]:6 +@end +// RUN: clang-refactor-test rename-indexed-file -name=method:piece:perform:method -new-name=perform:test:bar:object -indexed-file=%s -indexed-at=199:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK26 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:a_200 -new-name=z_Z_42:method -indexed-file=%s -indexed-at=209:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK27 %s + +@implementation perform +-(Object *)class:(Object * (*)(BOOL))onEntity perform:(({}))class +usingThing:(int (*)())object __attribute__((eval { int x = 0 + 1; })) piece:(BOOL (^)(BOOL, BOOL))class bar:(int)name withSomething:(const Object &)onEntity __attribute__((test())){ + // comment +} +// CHECK28: [[@LINE-4]]:12 -> [[@LINE-4]]:17, [[@LINE-4]]:47 -> [[@LINE-4]]:54, [[@LINE-3]]:1 -> [[@LINE-3]]:11, [[@LINE-3]]:71 -> [[@LINE-3]]:76, [[@LINE-3]]:105 -> [[@LINE-3]]:108, [[@LINE-3]]:119 -> [[@LINE-3]]:132 +-(int)test:(({}))onEntity +a_200:(BOOL)struct { + ([self name: ']' part: [] { + ; ;([_undef_ivar usingThing: globalArray[i] class: globalArray[i] method: globalArray[i] method: ']' class: 12]); + + } bar: 12 withSomething: "]" == [] { [self test: [self world: [_undef_ivar struct: "]" bar: @"string literal" method: globalArray[i]] usingThing: @"string literal" class: [] () { + } bar: @"string literal" +] withSomething: ([] { + + + } + "]" * ']') withSomething: @"string literal"]; + } usingThing: "]"]); + + call() = [self test: globalArray[i] name: @"string literal" perform: [self class: "]" + piece: globalArray[i] method: "string" +] +//comment + piece: "string"]; +} +// CHECK29: [[@LINE-19]]:7 -> [[@LINE-19]]:11, [[@LINE-18]]:1 -> [[@LINE-18]]:6 +-(Object *)test:(^ { })z_Z_42 class:(void)usingThing +{ + if ([self.undef_property class: globalArray[i] a_200: ^ () { ] } +]) { + if (globalArray[i]) { + some_type_t foo = [self foo: "]" perform: "string" +]; + + } + + } +} +// CHECK30: [[@LINE-12]]:12 -> [[@LINE-12]]:16, [[@LINE-12]]:31 -> [[@LINE-12]]:36 ++(int)struct:(void (^)())method +a_200:(BOOL)onEntity usingThing:(Object *)a_200 bar:(void)z_Z_42 z_Z_42:(Object *)struct perform:(BOOL (*)(Object *, Object *))method { + globalArray[12] = [self bar: ']' test: ^ () { ] } method: globalArray[i]]; + + if ([super test: "string" < globalArray[i] part: 12]) { + int bar = [self object: "]" + perform: [self a_200: "string" object: ("string") +//comment +]]; + + } +} +// CHECK31: [[@LINE-12]]:7 -> [[@LINE-12]]:13, [[@LINE-11]]:1 -> [[@LINE-11]]:6, [[@LINE-11]]:22 -> [[@LINE-11]]:32, [[@LINE-11]]:49 -> [[@LINE-11]]:52, [[@LINE-11]]:66 -> [[@LINE-11]]:72, [[@LINE-11]]:90 -> [[@LINE-11]]:97 +@end +// RUN: clang-refactor-test rename-indexed-file -name=class:perform:usingThing:piece:bar:withSomething -new-name=a_200:piece:foo:struct:onEntity:world -indexed-file=%s -indexed-at=225:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK28 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:a_200 -new-name=struct:world -indexed-file=%s -indexed-at=230:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK29 %s +// RUN: clang-refactor-test rename-indexed-file -name=test:class -new-name=perform:name -indexed-file=%s -indexed-at=250:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK30 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:a_200:usingThing:bar:z_Z_42:perform -new-name=world:piece:test:world:class:struct -indexed-file=%s -indexed-at=263:7 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK31 %s + +@implementation usingThing <object> +-(int)foo:(int)a_200 part:(Object *)name +object:(BOOL)world __attribute__((test()))method:(const Object & (*)())test name:(some_type_t)z_Z_42 bar:(Object *)class /*comment*/ { + [self piece: ^ { + [self.undef_property name: [self z_Z_42: [self object: @"string literal" + [] { + } + z_Z_42: [] () { + + + }] class: [self method: globalArray[i] usingThing: globalArray[i] part: 12] onEntity: @{ @1, @3 } piece: "string"] object: ([self foo: @"string literal" < [self object: ']' usingThing: usingThing: globalArray[i] a_200: [] () { + }] + name: "string" foo: [] () { + + + }]) struct: "string" struct: "]" + [] { + } usingThing: ]; + + } usingThing: (@"string literal") usingThing: 12 a_200: ^ { [globalObject message] = [self onEntity: [] { + } == ^ { + + + } foo: @{ @1, @3 }]; + } withSomething: "]" == ]; +} +// CHECK32: [[@LINE-23]]:7 -> [[@LINE-23]]:10, [[@LINE-23]]:22 -> [[@LINE-23]]:26, [[@LINE-22]]:1 -> [[@LINE-22]]:7, [[@LINE-22]]:43 -> [[@LINE-22]]:49, [[@LINE-22]]:77 -> [[@LINE-22]]:81, [[@LINE-22]]:102 -> [[@LINE-22]]:105 +-(Object *)withSomething:(void)class __attribute__((test()))onEntity:(void)part struct:(some_type_t (*)(Object *))bar __attribute__((eval { int x = 0 + 1; })) perform:(Object *)world { + globalArray[12] = [self struct: globalArray[i] bar: ^ { if (']' + "string") { + return ^ () { + }; + + } + } class: ^ () { some_type_t foo = [self.undef_property withSomething: ']' bar: "]"]; + } usingThing: ']' +]; +} +// CHECK33: [[@LINE-10]]:12 -> [[@LINE-10]]:25, [[@LINE-10]]:61 -> [[@LINE-10]]:69, [[@LINE-10]]:81 -> [[@LINE-10]]:87, [[@LINE-10]]:160 -> [[@LINE-10]]:167 +@end +// RUN: clang-refactor-test rename-indexed-file -name=foo:part:object:method:name:bar -new-name=z_Z_42:z_Z_42:part:part:usingThing:z_Z_42 -indexed-file=%s -indexed-at=283:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK32 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:onEntity:struct:perform -new-name=withSomething:name:foo:name -indexed-file=%s -indexed-at=307:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK33 %s + +@interface a_200 <foo> ++(void)bar:(int[1 + 2 - 3])a_200 object:(Object *)z_Z_42 world:(int[1 + 2 - 3])name name:(const Object &)name world:(Object *)world /*comment*/ ; +// CHECK34: [[@LINE-1]]:8 -> [[@LINE-1]]:11, [[@LINE-1]]:34 -> [[@LINE-1]]:40, [[@LINE-1]]:58 -> [[@LINE-1]]:63, [[@LINE-1]]:85 -> [[@LINE-1]]:89, [[@LINE-1]]:111 -> [[@LINE-1]]:116 +-(int)foo:(void (^)())part usingThing:(int)usingThing object:(({}))withSomething class:(some_type_t (^)())perform /*comment*/ method:(int)foo ; +// CHECK35: [[@LINE-1]]:7 -> [[@LINE-1]]:10, [[@LINE-1]]:28 -> [[@LINE-1]]:38, [[@LINE-1]]:55 -> [[@LINE-1]]:61, [[@LINE-1]]:82 -> [[@LINE-1]]:87, [[@LINE-1]]:127 -> [[@LINE-1]]:133 +-(void)z_Z_42:(({}))withSomething name:(void (*)())struct foo:(Object *)part ; +// CHECK36: [[@LINE-1]]:8 -> [[@LINE-1]]:14, [[@LINE-1]]:35 -> [[@LINE-1]]:39, [[@LINE-1]]:59 -> [[@LINE-1]]:62 +-(BOOL)onEntity:(const Object &)z_Z_42 +piece:(const Object &)world +onEntity:(BOOL)part ; +// CHECK37: [[@LINE-3]]:8 -> [[@LINE-3]]:16, [[@LINE-2]]:1 -> [[@LINE-2]]:6, [[@LINE-1]]:1 -> [[@LINE-1]]:9 +-(BOOL)method:(some_type_t)name +part:(BOOL)a_200 part:(void)test __attribute__((eval { int x = 0 + 1; })) ; +// CHECK38: [[@LINE-2]]:8 -> [[@LINE-2]]:14, [[@LINE-1]]:1 -> [[@LINE-1]]:5, [[@LINE-1]]:18 -> [[@LINE-1]]:22 +@end +// RUN: clang-refactor-test rename-indexed-file -name=bar:object:world:name:world -new-name=withSomething:part:struct:part:withSomething -indexed-file=%s -indexed-at=323:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK34 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:usingThing:object:class:method -new-name=part:onEntity:foo:test:struct -indexed-file=%s -indexed-at=325:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK35 %s +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:name:foo -new-name=piece:z_Z_42:world -indexed-file=%s -indexed-at=327:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK36 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:piece:onEntity -new-name=part:usingThing:foo -indexed-file=%s -indexed-at=329:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK37 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:part:part -new-name=perform:test:part -indexed-file=%s -indexed-at=333:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK38 %s + +@interface part <world, struct, part> ++(some_type_t)z_Z_42:(Object *)onEntity test:(int (*)())a_200 __attribute__((test())); +// CHECK39: [[@LINE-1]]:15 -> [[@LINE-1]]:21, [[@LINE-1]]:41 -> [[@LINE-1]]:45 +@end +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:test -new-name=name:onEntity -indexed-file=%s -indexed-at=344:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK39 %s + +@interface piece +-(some_type_t)part:(some_type_t)onEntity test:(Object *)world +bar:(BOOL)foo +method:(const Object & (^)(int, some_type_t))object +withSomething:(BOOL)part +; +// CHECK40: [[@LINE-5]]:15 -> [[@LINE-5]]:19, [[@LINE-5]]:42 -> [[@LINE-5]]:46, [[@LINE-4]]:1 -> [[@LINE-4]]:4, [[@LINE-3]]:1 -> [[@LINE-3]]:7, [[@LINE-2]]:1 -> [[@LINE-2]]:14 +-(some_type_t)onEntity:(void)object test:(int[1 + 2 - 3])object ; +// CHECK41: [[@LINE-1]]:15 -> [[@LINE-1]]:23, [[@LINE-1]]:37 -> [[@LINE-1]]:41 +-(some_type_t)struct:(void)name class:(some_type_t)foo name:(int (^)(BOOL, int))method +foo:(void)usingThing usingThing:(int)z_Z_42 /*comment*/ a_200:(int[1 + 2 - 3])object +; +// CHECK42: [[@LINE-3]]:15 -> [[@LINE-3]]:21, [[@LINE-3]]:33 -> [[@LINE-3]]:38, [[@LINE-3]]:56 -> [[@LINE-3]]:60, [[@LINE-2]]:1 -> [[@LINE-2]]:4, [[@LINE-2]]:22 -> [[@LINE-2]]:32, [[@LINE-2]]:57 -> [[@LINE-2]]:62 +-(BOOL)bar:(some_type_t (*)(int))part /*comment*/ object:(int[1 + 2 - 3])onEntity perform:(BOOL)usingThing /*comment*/ ; +// CHECK43: [[@LINE-1]]:8 -> [[@LINE-1]]:11, [[@LINE-1]]:51 -> [[@LINE-1]]:57, [[@LINE-1]]:83 -> [[@LINE-1]]:90 +@end +// RUN: clang-refactor-test rename-indexed-file -name=part:test:bar:method:withSomething -new-name=name:usingThing:perform:onEntity:bar -indexed-file=%s -indexed-at=350:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK40 %s +// RUN: clang-refactor-test rename-indexed-file -name=onEntity:test -new-name=onEntity:class -indexed-file=%s -indexed-at=356:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK41 %s +// RUN: clang-refactor-test rename-indexed-file -name=struct:class:name:foo:usingThing:a_200 -new-name=class:a_200:class:object:class:name -indexed-file=%s -indexed-at=358:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK42 %s +// RUN: clang-refactor-test rename-indexed-file -name=bar:object:perform -new-name=a_200:test:object -indexed-file=%s -indexed-at=362:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK43 %s + +@implementation piece ++(BOOL)name:(int (^)(BOOL, const Object &))onEntity /*comment*/ withSomething:(void)foo name:(const Object &)withSomething a_200:(Object *)struct foo:(^ { })object { + if (12) { + return 12; + + } +} +// CHECK44: [[@LINE-6]]:8 -> [[@LINE-6]]:12, [[@LINE-6]]:65 -> [[@LINE-6]]:78, [[@LINE-6]]:89 -> [[@LINE-6]]:93, [[@LINE-6]]:124 -> [[@LINE-6]]:129, [[@LINE-6]]:147 -> [[@LINE-6]]:150 ++(int)class:(some_type_t (*)(BOOL))name +name:(BOOL)onEntity __attribute__((test()))a_200:(^ { })withSomething __attribute__((eval { int x = 0 + 1; })) piece:(int)withSomething usingThing:(Object *)name { + ; ;[super method: part: ^ () { + // comment + + }]; + + [self struct: "string" == "]" withSomething: ']' class: [] () { + [globalObject send: [self.undef_property part: ^ () { + + + } class: (']' < ) + z_Z_42: ^ () { + + + } test: (12)] other: 42]; + + } object: 12 perform: ']']; +} +// CHECK45: [[@LINE-19]]:7 -> [[@LINE-19]]:12, [[@LINE-18]]:1 -> [[@LINE-18]]:5, [[@LINE-18]]:44 -> [[@LINE-18]]:49, [[@LINE-18]]:112 -> [[@LINE-18]]:117, [[@LINE-18]]:137 -> [[@LINE-18]]:147 ++(some_type_t)perform:(int[1 + 2 - 3])world //comment +withSomething:(const Object &)foo part:(int (^)(int))foo part:(int[1 + 2 - 3])perform //comment +{ + [globalObject send: [self object: ']' withSomething: 12 usingThing: ']' perform: @"string literal"] other: 42]; +} +// CHECK46: [[@LINE-5]]:15 -> [[@LINE-5]]:22, [[@LINE-4]]:1 -> [[@LINE-4]]:14, [[@LINE-4]]:35 -> [[@LINE-4]]:39, [[@LINE-4]]:58 -> [[@LINE-4]]:62 +@end +// RUN: clang-refactor-test rename-indexed-file -name=name:withSomething:name:a_200:foo -new-name=object:onEntity:world:world:piece -indexed-file=%s -indexed-at=371:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK44 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:name:a_200:piece:usingThing -new-name=method:test:z_Z_42:part:foo -indexed-file=%s -indexed-at=378:7 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK45 %s +// RUN: clang-refactor-test rename-indexed-file -name=perform:withSomething:part:part -new-name=a_200:object:perform:z_Z_42 -indexed-file=%s -indexed-at=398:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK46 %s + +@implementation z_Z_42 <struct> ++(BOOL)piece:(some_type_t (^)(int))class a_200:(BOOL (^)(some_type_t, BOOL))bar +world:(const Object & (*)(const Object &, const Object &))struct piece:(^ { })test z_Z_42:(BOOL)name +usingThing:(void (^)(Object *))struct { + [self object: ([self.undef_property struct: ^ { + /*comment*/[self.undef_property part: @"string literal" world: @"string literal" + object: "]" world: "string" world: @{ @1, @3 }]; + + } == ^ () { [super class: ^ () { + + + } * globalArray[i] piece: [] { + } bar: [self part: @"string literal" perform: "]" world: [] () { + } + world: [self.undef_property world: globalArray[i] perform: "string" struct: "]"]]]; + } + name: 12 usingThing: "]" bar: "]" +//comment +]) withSomething: ^ () { + if ("string") { + [self withSomething: [] { + + + } method: 12 piece: [] () { + + + } a_200: ']' method: "]"]; + + } + + } test: @{ @1, @3 } + class: 12 world: "string"]; +} +// CHECK47: [[@LINE-32]]:8 -> [[@LINE-32]]:13, [[@LINE-32]]:42 -> [[@LINE-32]]:47, [[@LINE-31]]:1 -> [[@LINE-31]]:6, [[@LINE-31]]:66 -> [[@LINE-31]]:71, [[@LINE-31]]:84 -> [[@LINE-31]]:90, [[@LINE-30]]:1 -> [[@LINE-30]]:11 +-(int)method:(Object *)bar //comment +part:(int)bar usingThing:(({}))name { + // comment + + [_undef_ivar world: "]" onEntity: [] { + ; ;[self part: globalArray[i] world: @{ @1, @3 }]; + + } bar: "string" == [super name: 12 piece: /*]*/ @"string literal" part: "string" method: ^ { + some_type_t object = "string"; + + } piece: (^ { + return /*]*/ "]"; + + })] onEntity: [] { + int bar = [self name: "string"/*comment*/ method: globalArray[i]]; + + }]; +} +// CHECK48: [[@LINE-18]]:7 -> [[@LINE-18]]:13, [[@LINE-17]]:1 -> [[@LINE-17]]:5, [[@LINE-17]]:15 -> [[@LINE-17]]:25 ++(Object *)class:(BOOL)object __attribute__((test()))method:(some_type_t)method +world:(Object *)struct +{ + [self method: ^ { call() = [self a_200: ']' a_200: "]"]; + } struct: ^ () { + return [] () { + }; + + } class: [_undef_ivar part: "string" name: [] { + int bar = [self withSomething: "string" bar: @"string literal" usingThing: @"string literal"]; + + }]]; + + [super foo: 12 foo: * "]" foo: ("string") foo: "string" method: 12]; +} +// CHECK49: [[@LINE-15]]:12 -> [[@LINE-15]]:17, [[@LINE-15]]:54 -> [[@LINE-15]]:60, [[@LINE-14]]:1 -> [[@LINE-14]]:6 +@end +// RUN: clang-refactor-test rename-indexed-file -name=piece:a_200:world:piece:z_Z_42:usingThing -new-name=struct:part:bar:struct:class:usingThing -indexed-file=%s -indexed-at=410:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK47 %s +// RUN: clang-refactor-test rename-indexed-file -name=method:part:usingThing -new-name=object:piece:method -indexed-file=%s -indexed-at=443:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK48 %s +// RUN: clang-refactor-test rename-indexed-file -name=class:method:world -new-name=struct:test:z_Z_42 -indexed-file=%s -indexed-at=462:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK49 %s + +@implementation a_200 <name, bar, a_200> ++(some_type_t)class:(void)z_Z_42 +object:(BOOL)perform +z_Z_42:(const Object &)usingThing name:(const Object &)a_200 test:(({}))world perform:(int[1 + 2 - 3])z_Z_42 +{ + if ("string") { + if ([super withSomething: [] { return [_undef_ivar usingThing: [] () { + } + world: [] () { + + + } foo: 12 perform: globalArray[i] name: @"string literal"]; + } world: ^ { // comment + } object: ^ { int method = "]"; + } +]) { + [globalObject send: [self.undef_property method: globalArray[i] object: [] { [self method: ']' test: [self foo: ("string") a_200: @"string literal" piece: 12 < [self class: @"string literal" piece: "]" + ']']] test: [self onEntity: @"string literal" + z_Z_42: ']'] name: globalArray[i] test: 12] other: 42]; + } class: ^ { ; ;[self perform: ("string") bar: (']') foo: "string" + foo: ^ { + + + }]; + } piece: ^ { + call() = [_undef_ivar perform: (12) bar: ^ { + + + } test: "]" name: "string" bar: "]"]; + + }]; + + } + + } + + [globalObject send: [self bar: globalArray[i] == ']' perform: [_undef_ivar z_Z_42: @"string literal" object: ^ { + [globalObject send: [self world: "]" onEntity: ']' + struct: [] () { + } perform: [self.undef_property piece: [] () { + + + } method: 12 foo: [] { + + + } onEntity: "string" * "]" method: "]"] < ']'] other: 42] other: 42]; + + } method: ^ { // comment + } test: ^ () { return [] { + }; + }] a_200: [] () { ([self.undef_property object: ^ { + } usingThing: @"string literal" + test: "string" usingThing: globalArray[i] + foo: /*]*/ globalArray[i]]); + } piece: 12]; +} +// CHECK50: [[@LINE-54]]:15 -> [[@LINE-54]]:20, [[@LINE-53]]:1 -> [[@LINE-53]]:7, [[@LINE-52]]:1 -> [[@LINE-52]]:7, [[@LINE-52]]:35 -> [[@LINE-52]]:39, [[@LINE-52]]:62 -> [[@LINE-52]]:66, [[@LINE-52]]:79 -> [[@LINE-52]]:86 +@end +// RUN: clang-refactor-test rename-indexed-file -name=class:object:z_Z_42:name:test:perform -new-name=world:foo:part:a_200:name:piece -indexed-file=%s -indexed-at=484:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK50 %s + +@implementation piece (part) ++(BOOL)method:(^ { })foo foo:(({}))foo +usingThing:(int)withSomething perform:(int[1 + 2 - 3])bar a_200:(some_type_t (^)())onEntity test:(const Object &)z_Z_42 __attribute__((eval { int x = 0 + 1; })) { + if ([self method: ']' struct: ^ { [globalObject send: [_undef_ivar world: "]" onEntity: globalArray[i] foo: "]" withSomething: < ']' onEntity: "string"] other: 42]; + } +]) { + call() = [self part: 12 + struct: "string" perform: "string" class: [] () { return "string"; + } +]; + + } +} +// CHECK51: [[@LINE-12]]:8 -> [[@LINE-12]]:14, [[@LINE-12]]:26 -> [[@LINE-12]]:29, [[@LINE-11]]:1 -> [[@LINE-11]]:11, [[@LINE-11]]:31 -> [[@LINE-11]]:38, [[@LINE-11]]:59 -> [[@LINE-11]]:64, [[@LINE-11]]:93 -> [[@LINE-11]]:97 ++(some_type_t)withSomething:(some_type_t (^)(int, Object *))z_Z_42 bar:(^ { })usingThing { + return 12; +} +// CHECK52: [[@LINE-3]]:15 -> [[@LINE-3]]:28, [[@LINE-3]]:68 -> [[@LINE-3]]:71 +-(some_type_t)object:(some_type_t (*)())bar +foo:(some_type_t (*)(const Object &))class +withSomething:(void)onEntity +usingThing:(^ { })bar a_200:(some_type_t (^)())usingThing +usingThing:(^ { })name { + call() = ([self part: "]" piece: "string" * "]" bar: globalArray[i] piece: [] () { int bar = [self test: "string" method: globalArray[i] method: "string" == /*]*/ "string" * @"string literal" object: @"string literal" usingThing: globalArray[i]]; + } < "string"]); + + if ("]" + [self part: @"string literal" class: "string"]) { + [super onEntity: "]" object: "]" piece: (12) test: ']' < ^ { + int bar = [self usingThing: [] () { + + + } z_Z_42: ^ { + } usingThing: "]" object: "]"]; + + } +]; + + } +} +// CHECK53: [[@LINE-21]]:15 -> [[@LINE-21]]:21, [[@LINE-20]]:1 -> [[@LINE-20]]:4, [[@LINE-19]]:1 -> [[@LINE-19]]:14, [[@LINE-18]]:1 -> [[@LINE-18]]:11, [[@LINE-18]]:23 -> [[@LINE-18]]:28, [[@LINE-17]]:1 -> [[@LINE-17]]:11 +@end +// RUN: clang-refactor-test rename-indexed-file -name=method:foo:usingThing:perform:a_200:test -new-name=object:z_Z_42:test:struct:perform:z_Z_42 -indexed-file=%s -indexed-at=543:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK51 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:bar -new-name=world:foo -indexed-file=%s -indexed-at=556:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK52 %s +// RUN: clang-refactor-test rename-indexed-file -name=object:foo:withSomething:usingThing:a_200:usingThing -new-name=part:a_200:method:perform:world:part -indexed-file=%s -indexed-at=560:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK53 %s + +@interface world +-(const Object &)class:(int[1 + 2 - 3])struct onEntity:(void (^)(Object *, int))withSomething __attribute__((test())); +// CHECK54: [[@LINE-1]]:18 -> [[@LINE-1]]:23, [[@LINE-1]]:47 -> [[@LINE-1]]:55 +-(int)world:(int)foo name:(const Object & (^)(int, Object *))piece a_200:(Object *)test object:(BOOL)onEntity __attribute__((eval { int x = 0 + 1; })) onEntity:(BOOL)z_Z_42 /*comment*/ ; +// CHECK55: [[@LINE-1]]:7 -> [[@LINE-1]]:12, [[@LINE-1]]:22 -> [[@LINE-1]]:26, [[@LINE-1]]:68 -> [[@LINE-1]]:73, [[@LINE-1]]:89 -> [[@LINE-1]]:95, [[@LINE-1]]:152 -> [[@LINE-1]]:160 +-(int)usingThing:(^ { })part name:(const Object &)usingThing usingThing:(^ { })perform __attribute__((test()))struct:(^ { })world +withSomething:(int[1 + 2 - 3])method //comment +; +// CHECK56: [[@LINE-3]]:7 -> [[@LINE-3]]:17, [[@LINE-3]]:30 -> [[@LINE-3]]:34, [[@LINE-3]]:62 -> [[@LINE-3]]:72, [[@LINE-3]]:111 -> [[@LINE-3]]:117, [[@LINE-2]]:1 -> [[@LINE-2]]:14 +@end +// RUN: clang-refactor-test rename-indexed-file -name=class:onEntity -new-name=usingThing:usingThing -indexed-file=%s -indexed-at=588:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK54 %s +// RUN: clang-refactor-test rename-indexed-file -name=world:name:a_200:object:onEntity -new-name=world:bar:onEntity:name:part -indexed-file=%s -indexed-at=590:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK55 %s +// RUN: clang-refactor-test rename-indexed-file -name=usingThing:name:usingThing:struct:withSomething -new-name=name:struct:method:method:usingThing -indexed-file=%s -indexed-at=592:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK56 %s + +@implementation onEntity +-(BOOL)piece:(some_type_t (^)(int))usingThing perform:(int)foo onEntity:(({}))withSomething __attribute__((test()))bar:(Object *)usingThing usingThing:(const Object &)bar class:(const Object & (^)(int, const Object &))perform { + [self object: "string" foo: ([self onEntity: "string" * [] { if (@"string literal") { + call() = [self z_Z_42: "string" usingThing: @"string literal" +]; + + } + } object: globalArray[i] + test: ^ () { if ("string" == @"string literal" + ^ () { + + + }) { + int name = 12 * 12; + + } + }]) foo: @{ @1, @3 }]; + + call() = [super foo: ^ () { ] } part: ']' onEntity: [self piece: ']' piece: [] () { [super name: "]" perform: "string"]; + }] + struct: "]" part: @"string literal"]; +} +// CHECK57: [[@LINE-20]]:8 -> [[@LINE-20]]:13, [[@LINE-20]]:47 -> [[@LINE-20]]:54, [[@LINE-20]]:64 -> [[@LINE-20]]:72, [[@LINE-20]]:116 -> [[@LINE-20]]:119, [[@LINE-20]]:141 -> [[@LINE-20]]:151, [[@LINE-20]]:172 -> [[@LINE-20]]:177 +-(int)withSomething:(int[1 + 2 - 3])foo usingThing:(Object * (^)(some_type_t, BOOL))a_200 perform:(some_type_t)onEntity __attribute__((test())){ + [globalObject message] = [self.undef_property struct: @"string literal" object: "]" < ']' withSomething: @"string literal" piece: [self.undef_property withSomething: 12 foo: @"string literal" withSomething: "]" object: [] () { Object * z_Z_42 = 12 + "string"; + } part: ^ () { + const Object & z_Z_42 = globalArray[i]; + + }]]; + + some_type_t foo = [self.undef_property foo: 12 * globalArray[i] method: @"string literal" a_200: ^ () { + globalArray[12] = [self onEntity: @"string literal" class: "string" onEntity: "]"]; + + }/*comment*/]; +} +// CHECK58: [[@LINE-12]]:7 -> [[@LINE-12]]:20, [[@LINE-12]]:41 -> [[@LINE-12]]:51, [[@LINE-12]]:91 -> [[@LINE-12]]:98 ++(int)piece:(int)test +onEntity:(int[1 + 2 - 3])test piece:(const Object &)piece { + [self z_Z_42: ']' usingThing: ("]")]; + + if ([self test: [] () { + // comment + + } a_200: [] () { BOOL foo = [self world: [self.undef_property test: globalArray[i] foo: [self method: "]" onEntity: [] { + + + } withSomething: globalArray[i]] piece: 12 struct: ']'] usingThing: [super test: globalArray[i] world: [_undef_ivar bar: @{ @1, @3 } z_Z_42: [_undef_ivar bar: z_Z_42: [self foo: ("string") object: ^ () { + + + } bar: "string"] struct: (12) withSomething: @"string literal" name: [self.undef_property part: @"string literal" onEntity: [] { + + + }]] onEntity: ']' name: 12 object: @{ @1, @3 }] +//comment +] part: globalArray[i] test: 12]; + } + ']' bar: [super piece: "]" world: @"string literal" object: globalArray[i] test: ']' + withSomething: @{ @1, @3 }] piece: [self a_200: ']' world: ']'] onEntity: @"string literal"] == ^ () { ] }) { + if (12) { + [self.undef_property withSomething: [self a_200: globalArray[i] withSomething: "string" + test: [] () { [self perform: "]" object: ']' foo: @{ @1, @3 } + [] () { + }]; + } object: "string" struct: /*]*/ "]"] piece: @{ @1, @3 } a_200: 12 bar: globalArray[i]]; + + } + + } +} +// CHECK59: [[@LINE-31]]:7 -> [[@LINE-31]]:12, [[@LINE-30]]:1 -> [[@LINE-30]]:9, [[@LINE-30]]:31 -> [[@LINE-30]]:36 ++(some_type_t)withSomething:(BOOL)bar +z_Z_42:(BOOL)struct { + [self usingThing: (globalArray[i]) world: @"string literal" name: [super test: globalArray[i] part: ^ () { + [super test: 12 z_Z_42: "string"]; + + } perform: ']' perform: "string"] object: "]" name: 12]; + + /*comment*/[self world: "string" bar: ^ { + [self.undef_property bar: @"string literal" name: ^ { + + + } z_Z_42: "string" + [_undef_ivar name: [self part: 12 struct: [] { + } +] withSomething: @"string literal" class: ']' < "string" == "]" < "string" == "]"] onEntity: globalArray[i] object: "]"]; + + } == ']' test: @"string literal" world: "]" struct: "string"]; +} +// CHECK60: [[@LINE-17]]:15 -> [[@LINE-17]]:28, [[@LINE-16]]:1 -> [[@LINE-16]]:7 +@end +// RUN: clang-refactor-test rename-indexed-file -name=piece:perform:onEntity:bar:usingThing:class -new-name=method:bar:piece:onEntity:foo:piece -indexed-file=%s -indexed-at=602:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK57 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:usingThing:perform -new-name=name:usingThing:foo -indexed-file=%s -indexed-at=623:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK58 %s +// RUN: clang-refactor-test rename-indexed-file -name=piece:onEntity:piece -new-name=class:part:world -indexed-file=%s -indexed-at=636:7 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK59 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:z_Z_42 -new-name=object:object -indexed-file=%s -indexed-at=668:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK60 %s + +@interface test (z_Z_42) +-(void)struct:(some_type_t)onEntity perform:(void)struct +usingThing:(BOOL)foo onEntity:(some_type_t)onEntity /*comment*/ a_200:(Object *)piece ; +// CHECK61: [[@LINE-2]]:8 -> [[@LINE-2]]:14, [[@LINE-2]]:37 -> [[@LINE-2]]:44, [[@LINE-1]]:1 -> [[@LINE-1]]:11, [[@LINE-1]]:22 -> [[@LINE-1]]:30, [[@LINE-1]]:65 -> [[@LINE-1]]:70 +@end +// RUN: clang-refactor-test rename-indexed-file -name=struct:perform:usingThing:onEntity:a_200 -new-name=method:world:foo:foo:perform -indexed-file=%s -indexed-at=693:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK61 %s + +@implementation part +-(some_type_t)bar:(some_type_t (^)(some_type_t, Object *))class piece:(some_type_t)z_Z_42 +test:(BOOL)z_Z_42 /*comment*/ class:(void)class __attribute__((test())){ + [globalObject send: [self onEntity: == ']' z_Z_42: [] { int bar = [_undef_ivar a_200: [super a_200: @"string literal" +//comment + withSomething: @"string literal" + ^ { + }] foo: "]"] other: 42]; + } test: ^ () { if ([] () { + + + }) { + int bar = [self part: ^ () { + } + class: [] () { + } object: @"string literal"]; + + } + }]; + + [globalObject message] = [_undef_ivar part: (@{ @1, @3 }) struct: "]" z_Z_42: [self perform: @"string literal" usingThing: ']' a_200: ^ { // comment + } struct: ^ () { + if ("string") { + int test = "string"; + + } + + }]]; +} +// CHECK62: [[@LINE-27]]:15 -> [[@LINE-27]]:18, [[@LINE-27]]:65 -> [[@LINE-27]]:70, [[@LINE-26]]:1 -> [[@LINE-26]]:5, [[@LINE-26]]:31 -> [[@LINE-26]]:36 +@end +// RUN: clang-refactor-test rename-indexed-file -name=bar:piece:test:class -new-name=perform:bar:struct:world -indexed-file=%s -indexed-at=700:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK62 %s + +@interface perform ++(void)piece:(BOOL)a_200 onEntity:(const Object & (^)())object bar:(int (*)())method //comment +method:(some_type_t)onEntity +; +// CHECK63: [[@LINE-3]]:8 -> [[@LINE-3]]:13, [[@LINE-3]]:26 -> [[@LINE-3]]:34, [[@LINE-3]]:64 -> [[@LINE-3]]:67, [[@LINE-2]]:1 -> [[@LINE-2]]:7 +-(void)withSomething:(const Object &)piece //comment +piece:(void)perform ; +// CHECK64: [[@LINE-2]]:8 -> [[@LINE-2]]:21, [[@LINE-1]]:1 -> [[@LINE-1]]:6 +-(some_type_t)z_Z_42:(int)method piece:(^ { })struct struct:(BOOL)world /*comment*/ a_200:(const Object &)piece +; +// CHECK65: [[@LINE-2]]:15 -> [[@LINE-2]]:21, [[@LINE-2]]:34 -> [[@LINE-2]]:39, [[@LINE-2]]:54 -> [[@LINE-2]]:60, [[@LINE-2]]:85 -> [[@LINE-2]]:90 ++(Object *)foo:(const Object &)part bar:(some_type_t)a_200 ; +// CHECK66: [[@LINE-1]]:12 -> [[@LINE-1]]:15, [[@LINE-1]]:37 -> [[@LINE-1]]:40 +@end +// RUN: clang-refactor-test rename-indexed-file -name=piece:onEntity:bar:method -new-name=onEntity:withSomething:class:piece -indexed-file=%s -indexed-at=732:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK63 %s +// RUN: clang-refactor-test rename-indexed-file -name=withSomething:piece -new-name=perform:foo -indexed-file=%s -indexed-at=736:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK64 %s +// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:piece:struct:a_200 -new-name=test:withSomething:piece:class -indexed-file=%s -indexed-at=739:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK65 %s +// RUN: clang-refactor-test rename-indexed-file -name=foo:bar -new-name=usingThing:a_200 -indexed-file=%s -indexed-at=742:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK66 %s diff --git a/clang/test/Refactor/Rename/IndexedObjCProperty.m b/clang/test/Refactor/Rename/IndexedObjCProperty.m new file mode 100644 index 0000000000000..d8725754932ea --- /dev/null +++ b/clang/test/Refactor/Rename/IndexedObjCProperty.m @@ -0,0 +1,30 @@ +@interface I1 + +@property int p1; + +@end + +@implementation I1 + +- (void)foo { + self.p1; // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:10 + [self p1]; // CHECK: rename [[@LINE]]:9 -> [[@LINE]]:11 + [self setP1: 2]; // CHECK: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14 + _p1 = 3; // CHECK: rename "_foo" [[@LINE]]:3 -> [[@LINE]]:6 +} + +@end + +// RUN: clang-refactor-test rename-indexed-file -name=p1 -name=p1 -name=setP1 -name=_p1 -new-name=foo -new-name=foo -new-name=setFoo -new-name=_foo -indexed-file=%s -indexed-at=10:8 -indexed-at=objc-im:1:11:9 -indexed-at=objc-im:2:12:9 -indexed-at=3:13:3 %s | FileCheck %s + +// p1 _p1 setP1 +// CHECK: comment [[@LINE-1]]:4 +// CHECK: comment "_foo" [[@LINE-2]]:7 +// CHECK: comment "setFoo" [[@LINE-3]]:11 + +@selector(p1) +@selector(setP1:) +@selector(_p1) +// CHECK: selector [[@LINE-3]]:11 +// CHECK: selector "setFoo" [[@LINE-3]]:11 +// CHECK-NOT: selector diff --git a/clang/test/Refactor/Rename/Inputs/MultiFileTUHeader.h b/clang/test/Refactor/Rename/Inputs/MultiFileTUHeader.h new file mode 100644 index 0000000000000..2e8d29fa5824c --- /dev/null +++ b/clang/test/Refactor/Rename/Inputs/MultiFileTUHeader.h @@ -0,0 +1,6 @@ +class Foo { // CHECK: rename "{{.*}}/Inputs/MultiFileTUHeader.h" [[@LINE]]:7 -> [[@LINE]]:10 +public: + Foo(); // CHECK: rename "{{.*}}/Inputs/MultiFileTUHeader.h" [[@LINE]]:3 -> [[@LINE]]:6 + + void method(); +}; diff --git a/clang/test/Refactor/Rename/Inputs/ObjCImplementationTURequestsImplementation.m b/clang/test/Refactor/Rename/Inputs/ObjCImplementationTURequestsImplementation.m new file mode 100644 index 0000000000000..249aa7f36a1ef --- /dev/null +++ b/clang/test/Refactor/Rename/Inputs/ObjCImplementationTURequestsImplementation.m @@ -0,0 +1,3 @@ +@implementation ExplicitIVarsInInterface + +@end diff --git a/clang/test/Refactor/Rename/Inputs/objc-system-header.h b/clang/test/Refactor/Rename/Inputs/objc-system-header.h new file mode 100644 index 0000000000000..23b252a726bab --- /dev/null +++ b/clang/test/Refactor/Rename/Inputs/objc-system-header.h @@ -0,0 +1,5 @@ +@interface MySystemClass + +- (void)someMethod:(int)x with:(int)y; + +@end diff --git a/clang/test/Refactor/Rename/Inputs/rename-indexed-file.cpp b/clang/test/Refactor/Rename/Inputs/rename-indexed-file.cpp new file mode 100644 index 0000000000000..ec6b78d1c9ef1 --- /dev/null +++ b/clang/test/Refactor/Rename/Inputs/rename-indexed-file.cpp @@ -0,0 +1,4 @@ +void Test::otherFile() { + Test m; + m.~Test(); +} diff --git a/clang/test/Refactor/Rename/Inputs/system-header.h b/clang/test/Refactor/Rename/Inputs/system-header.h new file mode 100644 index 0000000000000..ae1af94d1ba57 --- /dev/null +++ b/clang/test/Refactor/Rename/Inputs/system-header.h @@ -0,0 +1,11 @@ +void systemFunction(); + +struct SystemStruct { +}; +typedef struct SystemStruct SystemTypedef; + +enum SystemEnum { + SystemEnumCase +}; + +extern int systemVariable; diff --git a/clang/test/Refactor/Rename/LocalBlockSymbol.m b/clang/test/Refactor/Rename/LocalBlockSymbol.m new file mode 100644 index 0000000000000..93235aac15f19 --- /dev/null +++ b/clang/test/Refactor/Rename/LocalBlockSymbol.m @@ -0,0 +1,58 @@ +__auto_type escaping1 = ^ { + struct Local { // LOCAL1: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:2:10 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL1 %s + + int x;// ESCAPES1: rename [[@LINE]] + // NOESCAPE1: rename local [[@LINE-1]] +// RUN: clang-refactor-test rename-initiate -at=%s:5:9 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=ESCAPES1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:5:9 -new-name=name %s -fblocks -x objective-c | FileCheck --check-prefix=NOESCAPE1 %s + }; + struct Local result; + return result; +}; + +__auto_type escaping2 = ^ () { // no prototype, + struct Local { // LOCAL2: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:15:10 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL2 %s + + int x;// ESCAPES2: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:18:9 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=ESCAPES2 %s + }; + struct Local result; + return result; +}; + +__auto_type outer1 = ^ { + __auto_type escaping3 = ^ (int x) { // prototype with some arguments. + struct Local { // LOCAL3: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:27:12 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL3 %s + + int x;// ESCAPES3: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:30:11 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=ESCAPES3 %s + }; + struct Local result; + return result; + }; + return escaping3(0); +}; + +void outer2() { + __auto_type escaping1 = ^ { + struct Local { + int x;// LOCAL4: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:42:11 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL4 %s + }; + struct Local result; + return result; + }; +} + +__auto_type normalBlock = ^int (void) { + struct Local { // LOCAL5: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:51:10 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL5 %s + + int x;// LOCAL6: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:54:9 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL6 %s + }; + return 0; +}; diff --git a/clang/test/Refactor/Rename/LocalBlockSymbolCpp.mm b/clang/test/Refactor/Rename/LocalBlockSymbolCpp.mm new file mode 100644 index 0000000000000..d48f3b5035c20 --- /dev/null +++ b/clang/test/Refactor/Rename/LocalBlockSymbolCpp.mm @@ -0,0 +1,24 @@ +auto escaping1 = ^ { + struct Global { // ESCAPES1: rename [[@LINE]] + // NOESCAPE1: rename local [[@LINE-1]] +// RUN: clang-refactor-test rename-initiate -at=%s:2:10 -new-name=name %s -std=c++14 -fblocks -x objective-c++-header | FileCheck --check-prefix=ESCAPES1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:2:10 -new-name=name %s -std=c++14 -fblocks -x objective-c++ | FileCheck --check-prefix=NOESCAPE1 %s + }; + return Global(); +}; + +void outer1() { + auto escaping1 = ^ { + struct Local { // LOCAL1: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:12:12 -new-name=name %s -std=c++14 -fblocks -x objective-c++-header | FileCheck --check-prefix=LOCAL1 %s + }; + return Local(); + }; +} + +auto normalBlock = ^int () { + struct Local { // LOCAL2: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:20:10 -new-name=name %s -std=c++14 -fblocks -x objective-c++-header | FileCheck --check-prefix=LOCAL2 %s + }; + return 0; +}; diff --git a/clang/test/Refactor/Rename/LocalSymbol.cpp b/clang/test/Refactor/Rename/LocalSymbol.cpp new file mode 100644 index 0000000000000..4bc82ed56c915 --- /dev/null +++ b/clang/test/Refactor/Rename/LocalSymbol.cpp @@ -0,0 +1,232 @@ +static int staticIsGlobalVar = 0; // CHECK1: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:1:12 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK1 %s + +int globalVar = 0; // CHECK2: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:4:5 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK2 %s + +struct GlobalFoo { // CHECK3: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:7:8 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK3 %s + + struct GlobalBar { // CHECK4: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:10:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK4 %s + }; + + void foo() { // CHECK5: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK5 %s + + struct LocalFoo { }; // CHECK6: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:17:12 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK6 %s + } + + virtual void bar() { } // CHECK-BAR: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:21:16 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK-BAR %s + + int globalField; // CHECK7: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:24:7 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK7 %s +}; + +enum GlobalEnum { // CHECK8: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:28:6 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK8 %s + + GlobalEnumCase = 0 // CHECK9: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:31:3 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK9 %s +}; + +namespace { + struct AnonymousIsGlobalFoo { }; // CHECK10: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:36:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=CHECK10 %s + + void globalFoo() { } // CHECK11: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:39:8 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=CHECK11 %s +} + +namespace globalNamespace { // CHECK12: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:43:11 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK12 %s +} + +void func(int localParam) { // CHECK13: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:47:15 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK13 %s + + int localVar = 0; // CHECK14: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:50:7 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK14 %s + + enum LocalEnum { // CHECK15: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:53:8 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK15 %s + + LocalEnumCase = 0 // CHECK16: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:56:5 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK16 %s + }; + + struct LocalFoo: GlobalFoo { // CHECK17: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:60:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK17 %s + + struct LocalBar { }; // CHECK18: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:63:12 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK18 %s + + void foo() { } // CHECK19: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:66:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK19 %s + + // Bar is global since it overrides GlobalFoo::bar + void bar() { } // CHECK-BAR: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:70:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK-BAR %s + + int localField; // CHECK20: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:73:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK20 %s + }; +} + +auto escapable1 = []() -> auto { + struct Global { // ESCAPES1: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:79:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES1 %s + + int field = 2; // ESCAPES2: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:82:9 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES2 %s + + void foo() { } // ESCAPES3: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:85:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES3 %s + }; + return Global(); +}; + +auto escapable2() { + struct Global { // ESCAPES4: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:92:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES4 %s + + int field = 2; // ESCAPES5: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:95:9 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES5 %s + + void foo() { } // ESCAPES6: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:98:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES6 %s + }; + return Global(); +} + +template<typename T> +struct C { + T x; +}; + +decltype(auto) escapable4() { + struct Global { // ESCAPES7: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:110:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES7 %s + }; + return C<Global>(); +} + +auto escapable5() -> decltype(auto) { + struct Foo { + struct Global { // ESCAPES8: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:118:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES8 %s + }; + }; + return C<Foo::Global>(); +} + +auto escapable6() { + struct Foo { + struct Global { // ESCAPES9: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:127:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES9 %s + }; + }; + return Foo::Global(); +} + +auto escapableOuter1() { + struct Foo { + auto escapableInner() { + struct Global { // ESCAPES10: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:137:14 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES10 %s + }; + return Global(); + } + } + return Foo().escapableInner(); +} + +auto escapableOuter2() { + auto escapableInner = []() -> auto { + struct Global { // ESCAPES11: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:148:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES11 %s + }; + return Global(); + } + return escapableInner(); +} + +void outer() { + auto escapableInner2 = []() -> auto { + struct Local { // ESCAPES12: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:158:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES12 %s + }; + return Local(); + } + struct Foo { + auto foo() { + struct Local { // ESCAPES13: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:165:14 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES13 %s + }; + return Local(); + } + }; +} + +struct Escapable { + auto escapable() { + struct Global { // ESCAPES14: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:175:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES14 %s + }; + return Global(); + } +}; + +auto escapableViaTypedef() { + struct Global { // ESCAPES15: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:183:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES15 %s + }; + typedef Foo Global; + return Foo(); +} + +auto nonescapable1 = []() -> auto { + struct Local { // NOESCAPE1: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:191:10 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=NOESCAPE1 %s + + int field = 2; // NOESCAPE2: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:194:9 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=NOESCAPE2 %s + + void foo() { } // NOESCAPE3: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:197:10 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=NOESCAPE3 %s + }; + return Local(); +}; + +static void localOrGlobal1() { }; // ISGLOBAL1: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:203:13 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=ISGLOBAL1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:203:13 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ISGLOBAL1 %s + +namespace { + +struct LocalOrGlobal { }; // ISGLOBAL2: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:209:13 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=ISGLOBAL2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:209:13 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ISGLOBAL2 %s + + +void localOrGlobal2() { }; // ISGLOBAL3: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:214:13 -new-name=name %s -std=c++14 | FileCheck --check-prefix=ISGLOBAL3 %s +} + + +struct LocalOrGlobalWrapper1 { + + static void foo() { } // ISGLOBAL4: rename [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:221:15 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=ISGLOBAL4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:221:15 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ISGLOBAL4 %s +}; + +void func2(int x) { + auto lambda1 = [x] // CHECK21: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:226:16 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK21 %s + (int z) { // CHECK22: rename local [[@LINE]] +// RUN: clang-refactor-test rename-initiate -at=%s:229:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK22 %s + } +} diff --git a/clang/test/Refactor/Rename/MemberExprMacro.cpp b/clang/test/Refactor/Rename/MemberExprMacro.cpp new file mode 100644 index 0000000000000..dfd7ec013d44c --- /dev/null +++ b/clang/test/Refactor/Rename/MemberExprMacro.cpp @@ -0,0 +1,19 @@ +class Baz { +public: + int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +}; + +int qux(int x) { return 0; } +#define MACRO(a) qux(a) + +int main() { + Baz baz; + baz.Foo = 1; /* Test 2 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 + MACRO(baz.Foo); // CHECK: rename [[@LINE]]:13 -> [[@LINE]]:16 + int y = baz.Foo; // CHECK: rename [[@LINE]]:15 -> [[@LINE]]:18 +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:3:7 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:11:7 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/MultiFileTU.cpp b/clang/test/Refactor/Rename/MultiFileTU.cpp new file mode 100644 index 0000000000000..14b0be25ab620 --- /dev/null +++ b/clang/test/Refactor/Rename/MultiFileTU.cpp @@ -0,0 +1,9 @@ +#include "Inputs/MultiFileTUHeader.h" + +Foo::Foo() {} // CHECK: rename "{{.*}}MultiFileTU.cpp" [[@LINE]]:1 -> [[@LINE]]:4 +// CHECK: rename "{{.*}}MultiFileTU.cpp" [[@LINE-1]]:6 -> [[@LINE-1]]:9 + +void Foo::method() { } // CHECK: rename "{{.*}}MultiFileTU.cpp" [[@LINE]]:6 -> [[@LINE]]:9 + +// RUN: clang-refactor-test rename-initiate -at=%s:3:6 -at=%s:6:6 -new-name=Bar %s | FileCheck %s +// RUN: clang-refactor-test rename-initiate -at=%s:3:6 -at=%s:6:6 -new-name=Bar %s | FileCheck %S/Inputs/MultiFileTUHeader.h diff --git a/clang/test/Refactor/Rename/Namespace.cpp b/clang/test/Refactor/Rename/Namespace.cpp new file mode 100644 index 0000000000000..1a0900f4abd92 --- /dev/null +++ b/clang/test/Refactor/Rename/Namespace.cpp @@ -0,0 +1,22 @@ +namespace gcc /* Test 1 */ { // CHECK: rename [[@LINE]]:11 -> [[@LINE]]:14 + int x; +} + +void boo() { + gcc::x = 42; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:1:11 -new-name=clang %s | FileCheck %s + +namespace ns1 { +namespace ns2 { // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:14 +void f(); +} +} + +void testVisitTwice() { + ns1::ns2::f(); // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:11 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:19:8 -new-name=clang %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Rename/NoNewName.cpp b/clang/test/Refactor/Rename/NoNewName.cpp new file mode 100644 index 0000000000000..0fe0069af369c --- /dev/null +++ b/clang/test/Refactor/Rename/NoNewName.cpp @@ -0,0 +1,4 @@ +// Check for an error while -new-name argument has not been passed to +// clang-rename. +// RUN: not clang-refactor-test rename-initiate -at=%s:1:11 %s 2>&1 | FileCheck %s +// CHECK: clang-refactor-test: for the -new-name option: must be specified at least once diff --git a/clang/test/Refactor/Rename/ObjCClass.m b/clang/test/Refactor/Rename/ObjCClass.m new file mode 100644 index 0000000000000..aacfc2c6b31bb --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCClass.m @@ -0,0 +1,95 @@ +@class I1, // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + I2; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + +@interface I1 // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14 +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:2:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +@implementation I1 { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 + I1 *interfaceIVar; // CHECK1: rename [[@LINE]]:3 -> [[@LINE]]:5 + // CHECK4: rename [[@LINE-1]]:7 -> [[@LINE-1]]:20 + int ivar; // CHECK3: rename [[@LINE]]:7 -> [[@LINE]]:11 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:11:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:12:3 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:21:20 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +-(void)foo: (const I1 *)bar { // CHECK1: rename [[@LINE]]:20 -> [[@LINE]]:22 + + ivar = 1; // CHECK3: rename [[@LINE]]:3 -> [[@LINE]]:7 + self->ivar = 2; // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:13 + print(bar->ivar);// CHECK3: rename [[@LINE]]:14 -> [[@LINE]]:18 + interfaceIVar->ivar = 4; // CHECK4: rename [[@LINE]]:3 -> [[@LINE]]:16 + // CHECK3: rename [[@LINE-1]]:18 -> [[@LINE-1]]:22 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:14:7 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:23:3 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:24:9 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:25:14 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:26:18 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:12:7 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:26:3 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s + +@end + +@interface I1 (Category) // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14 +@end // CHECK5: rename [[@LINE-1]]:16 -> [[@LINE-1]]:24 + +@implementation I1 (Category) // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 +@end // CHECK5: rename [[@LINE-1]]:21 -> [[@LINE-1]]:29 + +// RUN: clang-refactor-test rename-initiate -at=%s:41:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:44:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:41:16 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:44:21 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s + +// Implementation only-category: + +@interface I3 // CHECK6: rename [[@LINE]]:12 -> [[@LINE]]:14 +@end + +@implementation I3 (DummyCategory) // CHECK6: rename [[@LINE]]:17 -> [[@LINE]]:19 +@end // CHECK7: rename [[@LINE-1]]:21 -> [[@LINE-1]]:34 + +// RUN: clang-refactor-test rename-initiate -at=%s:55:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:58:17 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:58:21 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s + +// Class extension: + +@interface I3 () // CHECK6: rename [[@LINE]]:12 -> [[@LINE]]:14 +@end + +@implementation I3 // CHECK6: rename [[@LINE]]:17 -> [[@LINE]]:19 +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:68:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:71:17 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s + +// Ivar declared in the interface: + +@interface I4 { + @public + int ivar1; // CHECK8: rename [[@LINE]]:7 -> [[@LINE]]:12 +} +@end + +@implementation I4 { +} + +- (void)foo { + ivar1 = 0; // CHECK8: rename [[@LINE]]:3 -> [[@LINE]]:8 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:81:7 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-initiate -at=%s:89:3 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s diff --git a/clang/test/Refactor/Rename/ObjCClassProperty.m b/clang/test/Refactor/Rename/ObjCClassProperty.m new file mode 100644 index 0000000000000..4c8c9e0267d1e --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCClassProperty.m @@ -0,0 +1,100 @@ +@interface ExplicitClassProperty + +@property(class) int p1; // CHECK1: rename [[@LINE]]:22 -> [[@LINE]]:24 +@property(class, readonly) int p2; // CHECK2: rename [[@LINE]]:32 -> [[@LINE]]:34 + ++ (int)p1; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + +@end + +@implementation ExplicitClassProperty + +@dynamic p1; // CHECK1: rename [[@LINE]]:10 -> [[@LINE]]:12 + +@dynamic p2; // CHECK2: rename [[@LINE]]:10 -> [[@LINE]]:12 + ++ (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 1; +} +// TODO: remove ++ (void)setP1:(int)x { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:14 +} + ++ (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 2; +} + +- (void)foo { + ExplicitClassProperty.p1 = // CHECK1: rename [[@LINE]]:25 -> [[@LINE]]:27 + ExplicitClassProperty.p2; // CHECK2: rename [[@LINE]]:27 -> [[@LINE]]:29 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:3:22 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:6:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:12:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:16:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:20:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:28:25 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:4:32 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:14:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:23:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:29:27 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +@interface ImplicitClassProperty + ++(int)p3; // CHECK3: rename [[@LINE]]:7 -> [[@LINE]]:9 ++(void)setP3:(int)x; // CHECK4: rename [[@LINE]]:8 -> [[@LINE]]:13 ++(int)p4; // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:9 + +@end + +@implementation ImplicitClassProperty + ++ (int)p3 { // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 0; +} + +- (void)foo { + ImplicitClassProperty.p3 = // CHECK3: implicit-property [[@LINE]]:25 -> [[@LINE]]:27 + // CHECK4: implicit-property [[@LINE-1]]:25 -> [[@LINE-1]]:30 + ImplicitClassProperty.p4; // CHECK5: implicit-property [[@LINE]]:31 -> [[@LINE]]:33 + (void)ImplicitClassProperty.p3; // CHECK3: implicit-property [[@LINE]]:31 -> [[@LINE]]:33 + // CHECK4: implicit-property [[@LINE-1]]:31 -> [[@LINE-1]]:36 + + int x = [ImplicitClassProperty p3]; // CHECK3: rename [[@LINE]]:34 -> [[@LINE]]:36 + [ImplicitClassProperty setP3: x]; // CHECK4: rename [[@LINE]]:26 -> [[@LINE]]:31 + x = [ImplicitClassProperty p4]; // CHECK5: rename [[@LINE]]:30 -> [[@LINE]]:32 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:48:7 -at=%s:64:31 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:49:8 -at=%s:61:25 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:50:7 -at=%s:63:31 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s + + + + + + + + +@interface ClassReceivers // CHECK-RECEIVER: rename [[@LINE]]:12 -> [[@LINE]]:26 + +@property(class) int p1; ++ (int)implicit; ++ (void)setImplicit:(int)x; + +@end + +void classReceivers() { + ClassReceivers.p1 = 0; // CHECK-RECEIVER: rename [[@LINE]]:3 -> [[@LINE]]:17 + int y = ClassReceivers.p1; // CHECK-RECEIVER: rename [[@LINE]]:11 -> [[@LINE]]:25 + ClassReceivers.implicit = 0; // CHECK-RECEIVER: rename [[@LINE]]:3 -> [[@LINE]]:17 + int x = ClassReceivers.implicit; // CHECK-RECEIVER: rename [[@LINE]]:11 -> [[@LINE]]:25 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:94:3 -at=%s:95:11 -at=%s:96:3 -at=%s:97:11 -new-name=x %s | FileCheck --check-prefix=CHECK-RECEIVER %s diff --git a/clang/test/Refactor/Rename/ObjCCompatibilityAlias.m b/clang/test/Refactor/Rename/ObjCCompatibilityAlias.m new file mode 100644 index 0000000000000..da98da63e8874 --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCCompatibilityAlias.m @@ -0,0 +1,44 @@ +@class I1, // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + I2; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + +@interface I1 // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14 +@end + +@compatibility_alias I1Alias I1; // CHECK3: rename [[@LINE]]:22 -> [[@LINE]]:29 + // CHECK1: rename [[@LINE-1]]:30 -> [[@LINE-1]]:32 + +@compatibility_alias I2Alias I2; // CHECK4: rename [[@LINE]]:22 -> [[@LINE]]:29 + // CHECK2: rename [[@LINE-1]]:30 -> [[@LINE-1]]:32 + +// RUN: clang-refactor-test rename-initiate -at=%s:7:30 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:10:30 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:7:22 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:10:22 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s + +// TODO: Implement TypeLocs for @compatibility_alias (rdar://29245831) +// XFAIL: * +void foo(I1Alias *object) { // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:17 +} + +@implementation I1 { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 + I1Alias *object; // CHECK3: rename [[@LINE]]:3 -> [[@LINE]]:10 +} + +-(const I1Alias *)foo:(I2Alias *)object { // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:16 + // CHECK4: rename [[@LINE-1]]:24 -> [[@LINE-1]]:31 + return (const I1Alias *)self->object; // CHECK3: rename [[@LINE]]:17 -> [[@LINE]]:24 +} + +@end + +@interface I3: I1Alias // CHECK3: rename [[@LINE]]:16 -> [[@LINE]]:23 +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:21:10 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:25:3 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:28:9 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:30:17 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:35:16 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:28:24 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s diff --git a/clang/test/Refactor/Rename/ObjCImplementationTURequests.m b/clang/test/Refactor/Rename/ObjCImplementationTURequests.m new file mode 100644 index 0000000000000..9486a8a5824e9 --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCImplementationTURequests.m @@ -0,0 +1,32 @@ +@interface ExplicitIVarsInInterface { + int _requiresImplementationTU; +} + +@property int requiresImplementationTU; + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/Inputs/ObjCImplementationTURequestsImplementation.m" -dump-symbols %s | FileCheck --check-prefix=CHECK1 %s +// CHECK1: Implementation TU USR: 'c:objc(cs)ExplicitIVarsInInterface@_requiresImplementationTU' + +// RUN: not clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/MissingFile.m" -dump-symbols %s 2>&1 | FileCheck --check-prefix=CHECK-ERR1 %s +// CHECK-ERR1: failed to load implementation TU + +@interface NoNeedForImplementationTUs { + int _p1; +} + +@property int p1; +@property int p2; + +@end + +@implementation NoNeedForImplementationTUs { + int _p2; +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:16:7 -new-name=foo %s | FileCheck --check-prefix=CHECK-NO %s +// RUN: clang-refactor-test rename-initiate -at=%s:25:7 -new-name=foo %s | FileCheck --check-prefix=CHECK-NO %s +// CHECK-NO-NOT: Implementation TU USR diff --git a/clang/test/Refactor/Rename/ObjCImplicitProperty.m b/clang/test/Refactor/Rename/ObjCImplicitProperty.m new file mode 100644 index 0000000000000..b587f41cdf23e --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCImplicitProperty.m @@ -0,0 +1,31 @@ +@interface I1 + +-(int)p1; // CHECK1: rename [[@LINE]]:7 -> [[@LINE]]:9 +-(void)setP1:(int)x; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:13 +-(int)p2; // CHECK3: rename [[@LINE]]:7 -> [[@LINE]]:9 + +@end + +@implementation I1 + +- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 0; +} + +- (void)foo: (I1 *)other { + self.p1 = // CHECK1: implicit-property [[@LINE]]:8 -> [[@LINE]]:10 + // CHECK2: implicit-property [[@LINE-1]]:8 -> [[@LINE-1]]:13 + self.p2; // CHECK3: implicit-property [[@LINE]]:19 -> [[@LINE]]:21 + (void)other.p1; // CHECK1: implicit-property [[@LINE]]:15 -> [[@LINE]]:17 + // CHECK2: implicit-property [[@LINE-1]]:15 -> [[@LINE-1]]:20 + + int x = [self p1]; // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 + [self setP1: x]; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:14 + x = [other p2]; // CHECK3: rename [[@LINE]]:14 -> [[@LINE]]:16 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:3:7 -at=%s:19:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -at=%s:16:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:5:7 -at=%s:18:19 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s diff --git a/clang/test/Refactor/Rename/ObjCMethod.m b/clang/test/Refactor/Rename/ObjCMethod.m new file mode 100644 index 0000000000000..7f3655d8979e3 --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCMethod.m @@ -0,0 +1,135 @@ +@interface Test + +- (void)foo; // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:12 +- (int)performAction:(int)action with:(int)value; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:34 -> [[@LINE]]:38 + +@end + +@implementation Test + +- (void)foo { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:12 +} + +- (int)performAction:(int)action with:(int)value { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:34 -> [[@LINE]]:38 + return action + value; +} + ++ (void)foo:(Test*)t { // CHECK1-NOT: rename [[@LINE]] + [t foo]; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:11 + SEL s = @selector(foo); + [Test foo:t]; // CHECK1-NOT: rename [[@LINE]] + [t performAction: 2 with: 4]; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:25 -> [[@LINE]]:29 + SEL s1 = @selector(foo:); + SEL s2 = @selector(performAction: + with:); + SEL s3 = @selector(performAction:); + SEL s4 = @selector(performAction); +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:3:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:10:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:18:8 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -new-name=doSomething:to %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:13:8 -new-name=doSomething:to: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:21:8 -new-name=doSomething:to: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK2 %s + + + + +@interface SuperClass + +- (void)foo; // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12 +- (int)compareTo:(SuperClass *)other with:(int)options; // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE]]:38 -> [[@LINE]]:42 + +@end + +@implementation SuperClass + +- (void)foo { // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12 + return; +} + +- (int)compareTo:(SuperClass *)other with:(int)options { // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE]]:38 -> [[@LINE]]:42 + return 0; +} + +@end + +@interface SubClass : SuperClass + +- (void)foo; // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12 + +@end + +@implementation SubClass + +- (void)foo { // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12 + [super foo]; // CHECK-OVERRIDEFOO: rename [[@LINE]]:10 -> [[@LINE]]:13 +} + +@end + +@interface SubClassTheSecond : SubClass + +- (void)foo; // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12 +- (int)compareTo:(SuperClass *)other with:(int)options; // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE]]:38 -> [[@LINE]]:42 + +@end + +@implementation SubClassTheSecond + +- (void)foo { // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12 + return; +} +- (int)compareTo:(SuperClass *)other // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE+1]]:8 -> [[@LINE+1]]:12 + with:(int)options { + [other foo]; // CHECK-OVERRIDEFOO: rename [[@LINE]]:10 -> [[@LINE]]:13 + return [super compareTo: other with: options]; // CHECK-OVERRIDECOMP: rename [[@LINE]]:17 -> [[@LINE]]:26, [[@LINE]]:34 -> [[@LINE]]:38 +} + +@end + +@interface UnrelatedClass + +- (void)foo; // CHECK-OVERRIDEFOO-NOT: rename [[@LINE]] + +@end + +@interface UnrelatedSubClass : UnrelatedClass +// This method doesn't override SuperClass.foo, so verify that this occurrence +// isn't renamed even though its selector is the same. +- (void)foo; // CHECK-OVERRIDEFOO-NOT: rename [[@LINE]] + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:44:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s +// RUN: clang-refactor-test rename-initiate -at=%s:51:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s +// RUN: clang-refactor-test rename-initiate -at=%s:63:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s +// RUN: clang-refactor-test rename-initiate -at=%s:69:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s +// RUN: clang-refactor-test rename-initiate -at=%s:70:10 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s +// RUN: clang-refactor-test rename-initiate -at=%s:77:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s +// RUN: clang-refactor-test rename-initiate -at=%s:84:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s +// RUN: clang-refactor-test rename-initiate -at=%s:89:10 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s + +// RUN: clang-refactor-test rename-initiate -at=%s:45:8 -new-name=a:b: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s +// RUN: clang-refactor-test rename-initiate -at=%s:55:8 -new-name=a:b: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s +// RUN: clang-refactor-test rename-initiate -at=%s:78:8 -new-name=a:b %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s +// RUN: clang-refactor-test rename-initiate -at=%s:87:9 -new-name=a:b: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s +// RUN: clang-refactor-test rename-initiate -at=%s:90:17 -new-name=a:b %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s +// RUN: clang-refactor-test rename-initiate -at=%s:90:34 -new-name=a:b %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s + +// Don't allow implicit parameters: +@interface Foo +- (void)foo; +@end + +@implementation Foo +- (void)foo { + self = 0; +} +@end +// RUN: not clang-refactor-test rename-initiate -at=%s:130:3 -new-name=foo %s -Wno-objc-root-class 2>&1 | FileCheck --check-prefix=CHECK-NORENAME %s +// CHECK-NORENAME: could not rename symbol at the given location diff --git a/clang/test/Refactor/Rename/ObjCMethodMacro.m b/clang/test/Refactor/Rename/ObjCMethodMacro.m new file mode 100644 index 0000000000000..c6d6322a9e99c --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCMethodMacro.m @@ -0,0 +1,28 @@ +#define FOO foo // CHECK1-NOT: rename [[@LINE]] +// CHECK1-NOT: macro [[@LINE-1]] + +@interface I + +- (void)FOO; // CHECK1: macro [[@LINE]]:9 -> [[@LINE]]:9 +- (void)foo: (int)x FOO: (int)y; // CHECK2: macro [[@LINE]]:21 -> [[@LINE]]:21 + +@end + +@implementation I + +- (void)foo { // CHECK1-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:12 + [self FOO: 1 FOO: 2]; // CHECK2-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9 +} + +- (void)foo: (int)x foo: (int)y { // CHECK2-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:12, [[@LINE]]:21 -> [[@LINE]]:24 + [self FOO]; // CHECK1-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:13:9 -new-name=bar %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:17:9 -new-name=foo:bar %s | FileCheck --check-prefix=CHECK2 %s + +// RUN: not clang-refactor-test rename-initiate -at=%s:1:13 -new-name=foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s +// RUN: not clang-refactor-test rename-initiate -at=%s:6:9 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s +// CHECK-ERROR: could not rename symbol at the given location diff --git a/clang/test/Refactor/Rename/ObjCProperty.m b/clang/test/Refactor/Rename/ObjCProperty.m new file mode 100644 index 0000000000000..f0ed85233b74d --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCProperty.m @@ -0,0 +1,303 @@ +// XFAIL: * +// TODO: Remove or cut it down to one symbol rename. + +@interface I1 + +@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property (readwrite, nonatomic) int p2; // CHECK2: rename [[@LINE]]:38 -> [[@LINE]]:40 + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:38 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +@implementation I1 + +- (void)foo:(I1 *)other { + self.p2 = // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + self.p1; // CHECK1: rename [[@LINE]]:18 -> [[@LINE]]:20 + (void)other.p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:15:18 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:16:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +@implementation I1 (gettersAndSetters) + +- (void)foo2:(I1 *)other { + int x = [self p1]; // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 + [self setP1: x]; // CHECK1: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14 + [other setP2: // CHECK2: rename "setFoo" [[@LINE]]:10 -> [[@LINE]]:15 + [other p2]]; // CHECK2: rename [[@LINE]]:12 -> [[@LINE]]:14 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:28:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:29:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:30:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:31:12 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +@interface I2 + +@property int noImplementation; // CHECK3: rename [[@LINE]]:15 -> [[@LINE]]:31 + +@end + +void noImplementationGetterSetters(I2 *object) { + object.noImplementation = 2; // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:26 + int x = object.noImplementation; // CHECK3: rename [[@LINE]]:18 -> [[@LINE]]:34 + [object setNoImplementation: x]; // CHECK3: rename "setFoo" [[@LINE]]:11 -> [[@LINE]]:30 + (void)[object noImplementation]; // CHECK3: rename [[@LINE]]:17 -> [[@LINE]]:33 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:43:15 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:48:10 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:49:18 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:50:11 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:51:17 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s + +@interface I3 + +@property (readonly) int noSetter; // CHECK4: rename [[@LINE]]:26 -> [[@LINE]]:34 + +@end + +void noPropertySetter(I3 *object) { + (void)object.noSetter; // CHECK4: rename [[@LINE]]:16 -> [[@LINE]]:24 + object.noSetter = 2; // CHECK4-NOT: rename [[@LINE]] + (void)[object noSetter]; // CHECK4: rename [[@LINE]]:17 -> [[@LINE]]:25 + [object setNoSetter: 2]; // CHECK4-NOT: rename "setFoo" [[@LINE]] +} + +// RUN: clang-refactor-test rename-initiate -at=%s:62:26 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:67:16 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:69:17 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s + +@interface PropertyOverrides1: I1 + +- (int)p1; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 +- (void)setP1:(int)x; // CHECK1: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14 +- (int)p2; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + +@end + +@implementation PropertyOverrides1 { + I1 *object; +} + +- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + return [super p1]; // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 +} +- (void)setP1:(int)x { // CHECK1: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14 + [object setP1: x]; // CHECK1: rename "setFoo" [[@LINE]]:11 -> [[@LINE]]:16 + [super setP1: x]; // CHECK1: rename "setFoo" [[@LINE]]:10 -> [[@LINE]]:15 +} +- (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + return super.p2; // CHECK2: rename [[@LINE]]:16 -> [[@LINE]]:18 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:79:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:80:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:89:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:90:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:92:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:93:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:94:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:81:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:96:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:97:16 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +@interface PropertyOverrides2: I3 + +- (int)noSetter; // CHECK4: rename [[@LINE]]:8 -> [[@LINE]]:16 +- (void)setNoSetter:(int)x; // CHECK4-NOT: rename "setFoo" [[@LINE]] + +@end + +void getterOnlyOverrideWithoutImplementation(PropertyOverrides2 *object) { + (void)object.noSetter; // CHECK4: rename [[@LINE]]:16 -> [[@LINE]]:24 + int _ = [object noSetter]; // CHECK4: rename [[@LINE]]:19 -> [[@LINE]]:27 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:115:8 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:121:16 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:122:19 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s + +@interface MismatchedPropertyOverrides: I1 + +- (void)p1; // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:11 + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:131:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +@interface ExplicitlyNamedGetterSetters1 + +@property (getter=getP3) int p3; // CHECK5: rename [[@LINE]]:30 -> [[@LINE]]:32 + // CHECK5GET: rename [[@LINE-1]]:19 -> [[@LINE-1]]:24 +@property (getter=a, setter=b:) int p4; // CHECK6: rename [[@LINE]]:37 -> [[@LINE]]:39 + // CHECK6GET: rename [[@LINE-1]]:19 -> [[@LINE-1]]:20 + // CHECK6SET: rename [[@LINE-2]]:29 -> [[@LINE-2]]:30 +@property (readonly, getter=local) bool isLocal; // CHECK7: rename [[@LINE]]:41 -> [[@LINE]]:48 + // CHECK7GET: rename [[@LINE-1]]:29 -> [[@LINE-1]]:34 +@end + +@implementation ExplicitlyNamedGetterSetters1 + +- (void)foo:(ExplicitlyNamedGetterSetters *)other { + self.p3 = 2; // CHECK5: rename [[@LINE]]:8 -> [[@LINE]]:10 + [self p3]; // CHECK5-NOT: rename [[@LINE]] + [self setP3: // CHECK5: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14 + [other getP3]]; // CHECK5-NOT: rename [[@LINE]] + // CHECK5GET: rename [[@LINE-1]]:12 -> [[@LINE-1]]:17 + + self.p4 = 3; // CHECK6: rename [[@LINE]]:8 -> [[@LINE]]:10 + [self p4]; // CHECK6-NOT: rename [[@LINE]] + [self setP4: 2]; // CHECK6-NOT: rename "setFoo" [[@LINE]] + [self b: // CHECK6-NOT: rename "setFoo" [[@LINE]] + // CHECK6SET: rename [[@LINE-1]]:9 -> [[@LINE-1]]:10 + [other a]]; // CHECK6-NOT: rename [[@LINE]] + // CHECK6GET: rename [[@LINE-1]]:12 -> [[@LINE-1]]:13 + + (void)self.isLocal; // CHECK7: rename [[@LINE]]:14 -> [[@LINE]]:21 + [self isLocal]; // CHECK7-NOT: rename [[@LINE]] + [other local]; // CHECK7-NOT: rename [[@LINE]] + // CHECK7GET: rename [[@LINE-1]]:10 -> [[@LINE-1]]:15 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:139:30 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:151:8 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:153:9 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:141:37 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:157:8 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:144:41 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s +// RUN: clang-refactor-test rename-initiate -at=%s:165:14 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:139:19 -new-name=foo %s | FileCheck --check-prefix=CHECK5GET %s +// RUN: clang-refactor-test rename-initiate -at=%s:154:12 -new-name=foo %s | FileCheck --check-prefix=CHECK5GET %s + +// RUN: clang-refactor-test rename-initiate -at=%s:141:19 -new-name=foo %s | FileCheck --check-prefix=CHECK6GET %s +// RUN: clang-refactor-test rename-initiate -at=%s:162:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6GET %s +// RUN: clang-refactor-test rename-initiate -at=%s:141:29 -new-name=foo %s | FileCheck --check-prefix=CHECK6SET %s +// RUN: clang-refactor-test rename-initiate -at=%s:160:9 -new-name=foo %s | FileCheck --check-prefix=CHECK6SET %s + +// RUN: clang-refactor-test rename-initiate -at=%s:144:29 -new-name=foo %s | FileCheck --check-prefix=CHECK7GET %s +// RUN: clang-refactor-test rename-initiate -at=%s:167:10 -new-name=foo %s | FileCheck --check-prefix=CHECK7GET %s + +void ivars1(I1 *object) { + object->_p1 = 2; // CHECK1: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14 + object->_p2 = // CHECK2: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14 + object->_p1; // CHECK1: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28 +} + +void ivars2(ExplicitlyNamedGetterSetters1 *object) { + object->_p3 = // CHECK5: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14 + object->_p4; // CHECK6: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28 + object->_isLocal = 0; // CHECK7: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:19 +} + +void ivarsNoImplementation(I2 *object) { + object->_noImplementation = 4; // CHECK2-NOT: rename "_foo" [[@LINE]] +} + +// RUN: clang-refactor-test rename-initiate -at=%s:195:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:197:25 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:196:11 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:201:11 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:202:25 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:203:11 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s + +@interface ExplicitIVars + +@property int p5; // CHECK8: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property(readonly) int p6; // CHECK9: rename [[@LINE]]:25 -> [[@LINE]]:27 + +@end + +@implementation ExplicitIVars { + int _p5; // CHECK8: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 + int _p6; // CHECK9: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 +} + +- (void)foo:(ExplicitIVars *)other { + _p5 = // CHECK8: rename "_foo" [[@LINE]]:3 -> [[@LINE]]:6 + other->_p6; // CHECK9: rename "_foo" [[@LINE]]:16 -> [[@LINE]]:19 + other->_p6 = // CHECK9: rename "_foo" [[@LINE]]:10 -> [[@LINE]]:13 + _p5; // CHECK8: rename "_foo" [[@LINE]]:16 -> [[@LINE]]:19 + self.p5 = // CHECK8: rename [[@LINE]]:8 -> [[@LINE]]:10 + other.p6; // CHECK9: rename [[@LINE]]:18 -> [[@LINE]]:20 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:220:15 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-initiate -at=%s:226:7 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-initiate -at=%s:231:3 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-initiate -at=%s:234:16 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-initiate -at=%s:235:8 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:221:25 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test rename-initiate -at=%s:227:7 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test rename-initiate -at=%s:232:16 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test rename-initiate -at=%s:233:10 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s +// RUN: clang-refactor-test rename-initiate -at=%s:236:18 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s + +@interface ExplicitIVarsInInterface { + int _p7; // CHECK10: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 + @public + int _p8; // CHECK11: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 +} + +@property int p7; // CHECK10: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property int p8; // CHECK11: rename [[@LINE]]:15 -> [[@LINE]]:17 + +@end + +@implementation ExplicitIVarsInInterface +@end + +void explicitIVarsInInterface(ExplicitIVarsInInterface* object) { + object->_p7 = // CHECK10: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14 + object->_p8; // CHECK11: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:254:7 -new-name=foo %s | FileCheck --check-prefix=CHECK10 %s +// RUN: clang-refactor-test rename-initiate -at=%s:259:15 -new-name=foo %s | FileCheck --check-prefix=CHECK10 %s +// RUN: clang-refactor-test rename-initiate -at=%s:268:11 -new-name=foo %s | FileCheck --check-prefix=CHECK10 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:256:7 -new-name=foo %s | FileCheck --check-prefix=CHECK11 %s +// RUN: clang-refactor-test rename-initiate -at=%s:260:15 -new-name=foo %s | FileCheck --check-prefix=CHECK11 %s +// RUN: clang-refactor-test rename-initiate -at=%s:269:25 -new-name=foo %s | FileCheck --check-prefix=CHECK11 %s + +@interface GetterSetterDefinedInInterfaceOnly + +@property int p9; // CHECK12: rename [[@LINE]]:15 -> [[@LINE]]:17 + +@end + +@implementation GetterSetterDefinedInInterfaceOnly + +- (int)p9 { return 0; } // CHECK12: rename [[@LINE]]:8 -> [[@LINE]]:10 +- (void)setP9:(int)x { } // CHECK12: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14 + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:288:8 -new-name=foo %s | FileCheck --check-prefix=CHECK12 %s +// RUN: clang-refactor-test rename-initiate -at=%s:289:9 -new-name=foo %s | FileCheck --check-prefix=CHECK12 %s + +void matchingGetterSetterSelector() { + @selector(p1); // CHECK1: selector [[@LINE]]:13 -> [[@LINE]]:15 + @selector(setP1:); // CHECK1: selector "setFoo" [[@LINE]]:13 -> [[@LINE]]:18 + @selector(setP1); // CHECK1-NOT: selector "setFoo" [[@LINE]] +} diff --git a/clang/test/Refactor/Rename/ObjCPropertyDynamic.m b/clang/test/Refactor/Rename/ObjCPropertyDynamic.m new file mode 100644 index 0000000000000..8ac7aac2d6b48 --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCPropertyDynamic.m @@ -0,0 +1,41 @@ +@interface DynamicProperty + +@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property(readonly) int p2; // CHECK2: rename [[@LINE]]:25 -> [[@LINE]]:27 + +@end + +@implementation DynamicProperty + +@dynamic p1; // CHECK1: rename [[@LINE]]:10 -> [[@LINE]]:12 + +@dynamic p2; // CHECK2: rename [[@LINE]]:10 -> [[@LINE]]:12 + +- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 1; +} +// TODO: Remove +- (void)setP1:(int)x { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:14 +} + +- (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 2; +} + +- (void)foo:(DynamicProperty *)other { + self.p1 = // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + other.p2; // CHECK2: rename [[@LINE]]:19 -> [[@LINE]]:21 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:10:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:18:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:26:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:4:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:12:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:21:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:27:19 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Rename/ObjCPropertyIVarInInterfaceWithoutImplementation.m b/clang/test/Refactor/Rename/ObjCPropertyIVarInInterfaceWithoutImplementation.m new file mode 100644 index 0000000000000..fda9e824a7e0a --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCPropertyIVarInInterfaceWithoutImplementation.m @@ -0,0 +1,28 @@ +@interface ExplicitIVarsInInterface { + int _p1; // CHECK1: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 + @public + int _p2; // CHECK2: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 +} + +@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property int p2; // CHECK2: rename [[@LINE]]:15 -> [[@LINE]]:17 + +@end + +void explicitIVarsInInterface(ExplicitIVarsInInterface* object) { + object->_p7 = // CHECK1: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14 + object->_p8; // CHECK2: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28 +} + +// XFAIL: * +// This test is currently disabled as renaming can't initiate a property +// renaming operation in a TU without @implementation. +// rdar://29329980 + +// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:7:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:13:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:4:7 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:8:15 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:14:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Rename/ObjCPropertyInCategory.m b/clang/test/Refactor/Rename/ObjCPropertyInCategory.m new file mode 100644 index 0000000000000..9c8bc79c17dae --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCPropertyInCategory.m @@ -0,0 +1,45 @@ +@interface I1 + +@end + +@interface I1 (Category) + +@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property(readonly) int p2; // CHECK2: rename [[@LINE]]:25 -> [[@LINE]]:27 + +- (int)p1; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + +@end + +@implementation I1 (Category) + +@dynamic p2; // CHECK2: rename [[@LINE]]:10 -> [[@LINE]]:12 + +- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 1; +} +// TODO: Remove +- (void)setP1:(int)x { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:14 +} + +- (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10 + return 2; +} + +- (void)foo:(I1 *)other { + self.p1 = // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + other.p2; // CHECK2: rename [[@LINE]]:19 -> [[@LINE]]:21 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:7:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:10:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:18:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:22:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:30:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:8:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:16:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:25:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:31:19 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/test/Refactor/Rename/ObjCPropertyMacro.m b/clang/test/Refactor/Rename/ObjCPropertyMacro.m new file mode 100644 index 0000000000000..9d3ffa314cbcd --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCPropertyMacro.m @@ -0,0 +1,21 @@ +#define IMPLICIT implicit +#define SETIMPLICIT setImplicit + +@interface I + +- (int)IMPLICIT; +- (void)setImplicit:(int)x; // CHECK1: rename [[@LINE]] + +@end + +@implementation I + +- (void)foo { + self.implicit; // CHECK1-NEXT: implicit-property [[@LINE]] + self.IMPLICIT; // CHECK1-NEXT: implicit-property in macro [[@LINE]] + self.IMPLICIT = 2; // CHECK1-NEXT: implicit-property in macro [[@LINE]] +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:7:9 -new-name=bar %s | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/Rename/ObjCPropertySynthesize.m b/clang/test/Refactor/Rename/ObjCPropertySynthesize.m new file mode 100644 index 0000000000000..2b12b6fe76b8b --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCPropertySynthesize.m @@ -0,0 +1,112 @@ +// XFAIL: * +// TODO: Remove or cut it down to one symbol rename. + +@interface SynthesizedIVars + +@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property(readonly) int p2; // CHECK2PROP: rename [[@LINE]]:25 -> [[@LINE]]:27 + +@end + +@implementation SynthesizedIVars + +@synthesize p1 = _p1; // CHECK1: rename [[@LINE]]:13 -> [[@LINE]]:15 + // CHECK1: rename "_foo" [[@LINE-1]]:18 -> [[@LINE-1]]:21 + +// The rename of ivar 'p2_' shouldn't initiate the rename of property 'p2' +// because it doesn't follow the default naming convention. +@synthesize p2 = p2_; // CHECK2PROP: rename [[@LINE]]:13 -> [[@LINE]]:15 + // CHECK2IVAR: rename [[@LINE-1]]:18 -> [[@LINE-1]]:21 + +- (void)foo:(SynthesizedIVars *)other { + _p1 = // CHECK1: rename "_foo" [[@LINE]]:3 -> [[@LINE]]:6 + other->p2_; // CHECK2IVAR: rename [[@LINE]]:16 -> [[@LINE]]:19 + other->p2_ = // CHECK2IVAR: rename [[@LINE]]:10 -> [[@LINE]]:13 + _p1; // CHECK1: rename "_foo" [[@LINE]]:16 -> [[@LINE]]:19 + self.p1 = // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + other.p2; // CHECK2PROP: rename [[@LINE]]:18 -> [[@LINE]]:20 +} + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:10:13 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:10:18 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:19:3 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:22:16 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:23:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:4:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2PROP %s +// RUN: clang-refactor-test rename-initiate -at=%s:15:13 -new-name=foo %s | FileCheck --check-prefix=CHECK2PROP %s +// RUN: clang-refactor-test rename-initiate -at=%s:24:18 -new-name=foo %s | FileCheck --check-prefix=CHECK2PROP %s + +// RUN: clang-refactor-test rename-initiate -at=%s:15:18 -new-name=foo %s | FileCheck --check-prefix=CHECK2IVAR %s +// RUN: clang-refactor-test rename-initiate -at=%s:20:16 -new-name=foo %s | FileCheck --check-prefix=CHECK2IVAR %s +// RUN: clang-refactor-test rename-initiate -at=%s:21:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2IVAR %s + +@interface SynthesizedExplicitIVars { + int _p3; // CHECK3: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 +} + +@property int p3; // CHECK3: rename [[@LINE]]:15 -> [[@LINE]]:17 +@property int p4; // CHECK4: rename [[@LINE]]:15 -> [[@LINE]]:17 + +@end + +@implementation SynthesizedExplicitIVars { + int _p4; // CHECK4: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10 +} + +@synthesize p3 = _p3; // CHECK3: rename [[@LINE]]:13 -> [[@LINE]]:15 + // CHECK3: rename "_foo" [[@LINE-1]]:18 -> [[@LINE-1]]:21 +@synthesize p4 = _p4; // CHECK4: rename [[@LINE]]:13 -> [[@LINE]]:15 + // CHECK4: rename "_foo" [[@LINE-1]]:18 -> [[@LINE-1]]:21 + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:45:7 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:48:15 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:57:13 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:57:18 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:49:15 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:54:7 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:59:13 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:59:18 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s + +@interface SynthesizedWithoutIVarName + +@property int p5; // CHECK5: rename [[@LINE]]:15 -> [[@LINE]]:17 + +@end + +@implementation SynthesizedWithoutIVarName { + int _p5; // CHECK5-NOT: rename "" [[@LINE]] + // CHECK5-NOT: rename [[@LINE-1]] + int p5; // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:9 +} + +@synthesize p5; // CHECK5: rename [[@LINE]]:13 -> [[@LINE]]:15 + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:76:15 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:83:7 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:86:13 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s + +@interface A +@property int p6; +@end + +@interface C : A +@end + +@implementation C + +@synthesize p6; +// CHECK6: Renaming 4 symbols +// CHECK6-NEXT: 'c:objc(cs)A(py)p6' + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:103:13 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK6 %s diff --git a/clang/test/Refactor/Rename/ObjCProtocol.m b/clang/test/Refactor/Rename/ObjCProtocol.m new file mode 100644 index 0000000000000..661d83b7b02b8 --- /dev/null +++ b/clang/test/Refactor/Rename/ObjCProtocol.m @@ -0,0 +1,67 @@ +@protocol P1, // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:13 + P2; // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:13 + +@protocol P1 // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:13 +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:1:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:2:11 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +void protocolExpressions(id foo) { + (void)@protocol(P1); // CHECK1: rename [[@LINE]]:19 -> [[@LINE]]:21 + [foo p: @protocol(P2)]; // CHECK2: rename [[@LINE]]:21 -> [[@LINE]]:23 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:12:19 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:13:21 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +void qualifiedId(id<P1> foo) { // CHECK1: rename [[@LINE]]:21 -> [[@LINE]]:23 + id<P2, P1> bar = // CHECK2: rename [[@LINE]]:6 -> [[@LINE]]:8 + // CHECK1: rename [[@LINE-1]]:10 -> [[@LINE-1]]:12 + (id<P1, P2, P1>)foo; // CHECK1: rename [[@LINE]]:24 -> [[@LINE]]:26 + // CHECK2: rename [[@LINE-1]]:28 -> [[@LINE-1]]:30 + // CHECK1: rename [[@LINE-2]]:32 -> [[@LINE-2]]:34 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:19:21 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:20:6 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:20:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:22:24 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:22:28 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:22:32 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +typedef id<P1> TypedefQualifiedID; // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14 + +// RUN: clang-refactor-test rename-initiate -at=%s:34:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s + +@interface I1<P1> // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17 +@end + +@protocol P3 < P1> // CHECK3: rename [[@LINE]]:11 -> [[@LINE]]:13 + // CHECK1: rename [[@LINE-1]]:16 -> [[@LINE-1]]:18 +@end + +@interface I1 (Cat) <P2, P3> // CHECK2: rename [[@LINE]]:22 -> [[@LINE]]:24 +@end // CHECK3: rename [[@LINE-1]]:26 -> [[@LINE-1]]:28 + +// RUN: clang-refactor-test rename-initiate -at=%s:38:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:41:16 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:45:22 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:41:11 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:45:26 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s + +typedef I1<P1, P2> * TypedefI1; // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14 + // CHECK2: rename [[@LINE-1]]:16 -> [[@LINE-1]]:18 + +void qualifiedClassPointer(I1<P1> *x) { // CHECK1: rename [[@LINE]]:31 -> [[@LINE]]:33 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:54:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:57:31 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:54:16 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s + +void protocolTypeof(typeof(@protocol(P1)) *bar) { // CHECK1: rename [[@LINE]]:38 -> [[@LINE]]:40 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:64:38 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/Rename/ProhibitedDeclarations.cpp b/clang/test/Refactor/Rename/ProhibitedDeclarations.cpp new file mode 100644 index 0000000000000..b93f11d193401 --- /dev/null +++ b/clang/test/Refactor/Rename/ProhibitedDeclarations.cpp @@ -0,0 +1,72 @@ +void dontRenameBuiltins(int x) { + __builtin_assume(x != 0); + __builtin_trap(); +} + +// RUN: not clang-refactor-test rename-initiate -at=%s:2:3 -at=%s:3:3 -new-name=foo %s 2>&1 | FileCheck %s +// CHECK: error: could not rename symbol at the given location + +// RUN: not clang-refactor-test list-actions -at=%s:2:3 %s 2>&1 | FileCheck --check-prefix=CHECK-BUILTIN %s +// CHECK-BUILTIN: Failed to initiate 1 actions because: +// CHECK-BUILTIN-NEXT: Rename: '__builtin_assume' is a builtin function that cannot be renamed +// CHECK-BUILTIN-NEXT: No refactoring actions are available at the given location + +#include <system-header.h> + +void dontRenameSystemSymbols() { + systemFunction(); +} +// RUN: not clang-refactor-test rename-initiate -at=%s:17:3 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s +// CHECK-SYSTEM: 'systemFunction' cannot be renamed because it is declared in a system header + +struct External { + static void foo(); +} __attribute__((external_source_symbol(language="Swift"))); + +void dontRenameExternalSourceSymbols() { + External::foo(); +} +// RUN: not clang-refactor-test rename-initiate -at=%s:27:3 -new-name=foo %s 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-EXTERNAL1 %s +// CHECK-EXTERNAL1: 'External' is declared in a Swift file; rename can be initiated in a Swift file only + +// RUN: not clang-refactor-test rename-initiate -at=%s:27:13 -new-name=foo %s 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-EXTERNAL2 %s +// CHECK-EXTERNAL2: 'foo' is declared in a Swift file; rename can be initiated in a Swift file only + +// Ensure that operators can't be renamed: +struct Stream { +}; + +Stream &operator <<(Stream &, int); + +void renameArgsNotOperator(Stream x) { // CHECK-OP-X: rename local [[@LINE]]:35 -> [[@LINE]]:36 + int y = 0; // CHECK-OP-Y: rename local [[@LINE]]:7 -> [[@LINE]]:8 + x << // CHECK-OP-X: rename local [[@LINE]]:3 -> [[@LINE]]:4 + y << // CHECK-OP-Y: rename local [[@LINE]]:3 -> [[@LINE]]:4 + y; // CHECK-OP-Y: rename local [[@LINE]]:3 -> [[@LINE]]:4 +} +// RUN: clang-refactor-test rename-initiate -at=%s:43:3 -new-name=foo %s | FileCheck --check-prefixes=CHECK-OP-X %s +// RUN: clang-refactor-test rename-initiate -at=%s:44:3 -at=%s:45:3 -new-name=foo %s | FileCheck --check-prefixes=CHECK-OP-Y %s + +struct SystemStruct; + +// RUN: not clang-refactor-test rename-initiate -at=%s:50:8 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM2 %s +// CHECK-SYSTEM2: 'SystemStruct' cannot be renamed because it is declared in a system header + +typedef struct SystemStruct SystemTypedef; + +// RUN: not clang-refactor-test rename-initiate -at=%s:55:29 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM3 %s +// CHECK-SYSTEM3: 'SystemTypedef' cannot be renamed because it is declared in a system header + +enum SystemEnum; + +// RUN: not clang-refactor-test rename-initiate -at=%s:60:6 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM4 %s +// CHECK-SYSTEM4: 'SystemEnum' cannot be renamed because it is declared in a system header + +void systemFunction(); + +// RUN: not clang-refactor-test rename-initiate -at=%s:65:6 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s + +int systemVariable; + +// RUN: not clang-refactor-test rename-initiate -at=%s:69:5 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM5 %s +// CHECK-SYSTEM5: 'systemVariable' cannot be renamed because it is declared in a system header diff --git a/clang/test/Refactor/Rename/ProhibitedDeclarations.m b/clang/test/Refactor/Rename/ProhibitedDeclarations.m new file mode 100644 index 0000000000000..cb91a6b5dd6b3 --- /dev/null +++ b/clang/test/Refactor/Rename/ProhibitedDeclarations.m @@ -0,0 +1,35 @@ +@protocol P1 +@end + +void dontRenameProtocol() { + Protocol *p = @protocol(P1); +} +// RUN: not clang-refactor-test rename-initiate -at=%s:5:3 -new-name=foo %s 2>&1 | FileCheck %s +// CHECK: error: could not rename symbol at the given location + +#include <objc-system-header.h> + +@interface MyClass: MySystemClass + +- (void)someMethod:(int)x with:(int)y; + +@end + +@implementation MyClass + +- (void)someMethod:(int)x with:(int)y { +} + +@end + +// RUN: not clang-refactor-test rename-initiate -at=%s:14:9 -at=%s:20:9 -at=%s:28:9 -new-name=foo:bar %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s +// CHECK-SYSTEM: method 'someMethod:with:' cannot be renamed because it overrides a method declared in a system framework + +@interface MySubClass: MyClass +@end +@implementation MySubClass +- (void)someMethod:(int)x with:(int)y { +} +@end + +// RUN: not clang-refactor-test rename-initiate -at=%s:31:9 -new-name=foo:bar %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s diff --git a/clang/test/Refactor/Rename/TemplateClassInstantiation.cpp b/clang/test/Refactor/Rename/TemplateClassInstantiation.cpp new file mode 100644 index 0000000000000..48b76030cda74 --- /dev/null +++ b/clang/test/Refactor/Rename/TemplateClassInstantiation.cpp @@ -0,0 +1,39 @@ +template <typename T> +class Foo { /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +public: + T foo(T arg, T& ref, T* ptr) { + T value; + int number = 42; + value = (T)number; + value = static_cast<T>(number); + return value; + } + static void foo(T value) {} + T member; +}; + +template <typename T> +void func() { + Foo<T> obj; /* Test 2 */ // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + obj.member = T(); + Foo<T>::foo(); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 +} + +int main() { + Foo<int> i; /* Test 3 */ // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + i.member = 0; + Foo<int>::foo(0); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + + Foo<bool> b; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + b.member = false; + Foo<bool>::foo(false); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + + return 0; +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=Bar %s -fno-delayed-template-parsing | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:17:3 -new-name=Bar %s -fno-delayed-template-parsing | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:25:3 -new-name=Bar %s -fno-delayed-template-parsing | FileCheck %s diff --git a/clang/test/Refactor/Rename/TemplateParameters.cpp b/clang/test/Refactor/Rename/TemplateParameters.cpp new file mode 100644 index 0000000000000..2dbff6995909f --- /dev/null +++ b/clang/test/Refactor/Rename/TemplateParameters.cpp @@ -0,0 +1,25 @@ +template <typename T, int x> // CHECK1: rename local [[@LINE]]:20 -> [[@LINE]]:21 +class Foo { // CHECK2: rename local [[@LINE-1]]:27 -> [[@LINE-1]]:28 + +T func(); // CHECK1-NEXT: rename local [[@LINE]]:1 -> [[@LINE]]:2 + +int array[x]; // CHECK2-NEXT: rename local [[@LINE]]:11 -> [[@LINE]]:12 +}; + +// CHECK1-NOT: rename +// CHECK2-NOT: rename + +// RUN: clang-refactor-test rename-initiate -at=%s:1:20 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:1:27 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK2 %s + +template<typename T, int x> +T Foo<T, x>::func() { return T(); } + +template <template<typename> class H, typename S> // CHECK3: rename local [[@LINE]]:36 -> [[@LINE]]:37 +// CHECK4: rename local [[@LINE-1]]:48 -> [[@LINE-1]]:49 +void templateTemplateParam(const H<S> &value) { // CHECK3-NEXT: rename local [[@LINE]]:34 -> [[@LINE]]:35 +// CHECK4-NEXT: rename local [[@LINE-1]]:36 -> [[@LINE-1]]:37 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:18:36 -at=%s:20:34 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:18:48 -at=%s:20:36 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK4 %s diff --git a/clang/test/Refactor/Rename/TemplateTypename.cpp b/clang/test/Refactor/Rename/TemplateTypename.cpp new file mode 100644 index 0000000000000..da79f67e9572b --- /dev/null +++ b/clang/test/Refactor/Rename/TemplateTypename.cpp @@ -0,0 +1,24 @@ +template <typename T /* Test 1 */> // CHECK: rename local [[@LINE]]:20 -> [[@LINE]]:21 +class Foo { +T foo(T arg, T& ref, T* /* Test 2 */ ptr) { // CHECK: rename local [[@LINE]]:1 -> [[@LINE]]:2 + // CHECK: rename local [[@LINE-1]]:7 -> [[@LINE-1]]:8 + // CHECK: rename local [[@LINE-2]]:14 -> [[@LINE-2]]:15 + // CHECK: rename local [[@LINE-3]]:22 -> [[@LINE-3]]:23 + T value; // CHECK: rename local [[@LINE]]:3 -> [[@LINE]]:4 + int number = 42; + value = (T)number; // CHECK: rename local [[@LINE]]:12 -> [[@LINE]]:13 + value = static_cast<T /* Test 3 */>(number); // CHECK: rename local [[@LINE]]:23 -> [[@LINE]]:24 + return value; +} + +static void foo(T value) {} // CHECK: rename local [[@LINE]]:17 -> [[@LINE]]:18 + +T member; // CHECK: rename local [[@LINE]]:1 -> [[@LINE]]:2 +}; + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:1:20 -new-name=U %s -fno-delayed-template-parsing | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:3:22 -new-name=U %s -fno-delayed-template-parsing | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:10:23 -new-name=U %s -fno-delayed-template-parsing | FileCheck %s diff --git a/clang/test/Refactor/Rename/TemplatedClassFunction.cpp b/clang/test/Refactor/Rename/TemplatedClassFunction.cpp new file mode 100644 index 0000000000000..7eb3e440decb1 --- /dev/null +++ b/clang/test/Refactor/Rename/TemplatedClassFunction.cpp @@ -0,0 +1,19 @@ +template <typename T> +class A { +public: + void foo() /* Test 1 */ {} // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11 +}; + +int main(int argc, char **argv) { + A<int> a; + a.foo(); /* Test 2 */ // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + return 0; +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -new-name=bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:9:5 -new-name=bar %s | FileCheck %s +// +// Currently unsupported test. +// XFAIL: * diff --git a/clang/test/Refactor/Rename/TransparentTypedef.m b/clang/test/Refactor/Rename/TransparentTypedef.m new file mode 100644 index 0000000000000..5717bb8636cd7 --- /dev/null +++ b/clang/test/Refactor/Rename/TransparentTypedef.m @@ -0,0 +1,40 @@ +#define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type +// CHECK1: Renaming 1 symbols +// CHECK1-NEXT: 'c:@E@AnotherEnum' +typedef NS_ENUM(AnotherEnum, int) { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:28 + AnotherEnumFirst = 0, +}; +AnotherEnum anotherT; // CHECK1: rename [[@LINE]]:1 -> [[@LINE]]:12 +enum AnotherEnum anotherE; // CHECK1: rename [[@LINE]]:6 -> [[@LINE]]:17 + +// RUN: clang-refactor-test rename-initiate -at=%s:4:17 -at=%s:7:1 -at=%s:8:6 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK1 %s + +#define TRANSPARENT(_name) struct _name _name; struct _name +#define OPAQUE(_name) struct _name *_name; struct _name + +// CHECK2: Renaming 1 symbols +// CHECK2-NEXT: 'c:@S@AStruct' +typedef TRANSPARENT(AStruct) { // CHECK2: rename [[@LINE]]:21 -> [[@LINE]]:28 + int x; +}; + +AStruct aStructT; // CHECK2: rename [[@LINE]]:1 -> [[@LINE]]:8 +struct AStruct aStructS; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:15 + +// RUN: clang-refactor-test rename-initiate -at=%s:17:21 -at=%s:21:1 -at=%s:22:8 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK2 %s + +// CHECK3: Renaming 1 symbols +// CHECK3-NEXT: 'c:{{.*}}TransparentTypedef.m@T@Separate' +// CHECK4: Renaming 1 symbols +// CHECK4-NEXT: 'c:@S@Separate' + +typedef OPAQUE(Separate) { // CHECK3: rename [[@LINE]]:16 -> [[@LINE]]:24 + int x; // CHECK4: rename [[@LINE-1]]:16 -> [[@LINE-1]]:24 +}; + + +Separate separateT; // CHECK3: rename [[@LINE]]:1 -> [[@LINE]]:9 +struct Separate separateE; // CHECK4: rename [[@LINE]]:8 -> [[@LINE]]:16 + +// RUN: clang-refactor-test rename-initiate -at=%s:31:16 -at=%s:36:1 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:37:8 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK4 %s diff --git a/clang/test/Refactor/Rename/TypedefTag.cpp b/clang/test/Refactor/Rename/TypedefTag.cpp new file mode 100644 index 0000000000000..390350c1969ef --- /dev/null +++ b/clang/test/Refactor/Rename/TypedefTag.cpp @@ -0,0 +1,21 @@ +struct S1 { }; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10 + +typedef struct S2 { } S3; // CHECK2: rename [[@LINE]]:16 -> [[@LINE]]:18 +// CHECK3: rename [[@LINE-1]]:23 -> [[@LINE-1]]:25 + +void func(struct S1, // CHECK1-NEXT: rename [[@LINE]]:18 -> [[@LINE]]:20 + struct S2, // CHECK2-NEXT: rename [[@LINE]]:18 -> [[@LINE]]:20 + S3) { // CHECK3-NEXT: rename [[@LINE]]:11 -> [[@LINE]]:13 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -at=%s:6:18 -new-name=Bar %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:3:16 -at=%s:7:18 -new-name=Bar %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:3:23 -at=%s:8:11 -new-name=Bar %s | FileCheck --check-prefix=CHECK3 %s + +// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -at=%s:6:18 -new-name=Bar %s -x c | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:3:16 -at=%s:7:18 -new-name=Bar %s -x c | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:3:23 -at=%s:8:11 -new-name=Bar %s -x c | FileCheck --check-prefix=CHECK3 %s + +// RUN: not clang-refactor-test rename-initiate -at=%s:3:9 -at=%s:6:11 -at=%s:7:11 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=ERROR %s +// RUN: not clang-refactor-test rename-initiate -at=%s:3:9 -at=%s:6:11 -at=%s:7:11 -new-name=Bar %s -x c 2>&1 | FileCheck --check-prefix=ERROR %s +// ERROR: could not rename symbol at the given location diff --git a/clang/test/Refactor/Rename/USRForSymbols.m b/clang/test/Refactor/Rename/USRForSymbols.m new file mode 100644 index 0000000000000..1f1efe89209ed --- /dev/null +++ b/clang/test/Refactor/Rename/USRForSymbols.m @@ -0,0 +1,16 @@ +@interface I1 + +@property int p1; + +@end + +@implementation I1 + +@end + +// CHECK: 3 symbols +// CHECK: 'c:objc(cs)I1(py)p1' +// CHECK: 'c:objc(cs)I1(im)p1' +// CHECK: 'c:objc(cs)I1(im)setP1:' + +// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo -dump-symbols %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/UserDefinedConversion.cpp b/clang/test/Refactor/Rename/UserDefinedConversion.cpp new file mode 100644 index 0000000000000..24614bf7440d3 --- /dev/null +++ b/clang/test/Refactor/Rename/UserDefinedConversion.cpp @@ -0,0 +1,23 @@ +class Foo { /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10 +public: + Foo() {} // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 +}; + +class Baz { +public: + operator Foo() /* Test 2 */ const { // CHECK: rename [[@LINE]]:12 -> [[@LINE]]:15 + Foo foo; // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 + return foo; + } +}; + +int main() { + Baz boo; + Foo foo = static_cast<Foo>(boo); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6 + return 0; // CHECK: rename [[@LINE-1]]:25 -> [[@LINE-1]]:28 +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:1:7 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:8:12 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/UsingDecl.cpp b/clang/test/Refactor/Rename/UsingDecl.cpp new file mode 100644 index 0000000000000..99eb1f44b1fd2 --- /dev/null +++ b/clang/test/Refactor/Rename/UsingDecl.cpp @@ -0,0 +1,46 @@ + +namespace ns { // CHECK4: rename [[@LINE]]:11 -> [[@LINE]]:13 + +struct Struct { }; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:14 + +void func(); // CHECK2: rename [[@LINE]]:6 -> [[@LINE]]:10 + +void overload1(int x); +void overload1(double y); // CHECK3: rename [[@LINE]]:6 -> [[@LINE]]:15 + +} + +using ns::Struct; // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:17 + +// RUN: clang-refactor-test rename-initiate -at=%s:13:11 -new-name=x %s | FileCheck --check-prefix=CHECK1 %s + +using ns::func; // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:15 + +// RUN: clang-refactor-test rename-initiate -at=%s:17:11 -new-name=x %s | FileCheck --check-prefix=CHECK2 %s + +using ns::overload1; // CHECK3: rename [[@LINE]]:11 -> [[@LINE]]:20 + +// RUN: clang-refactor-test rename-initiate -at=%s:21:11 -new-name=x %s | FileCheck --check-prefix=CHECK3 %s + +using namespace ns; // CHECK4: rename [[@LINE]]:17 -> [[@LINE]]:19 + +// RUN: clang-refactor-test rename-initiate -at=%s:25:17 -new-name=x %s | FileCheck --check-prefix=CHECK4 %s + +struct UsingConstructor { + template<class T> + UsingConstructor(T, typename T::Q); +}; +struct UsingConstructorHere : UsingConstructor { +using UsingConstructor::UsingConstructor; // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:23 +// CHECK5: rename [[@LINE-1]]:25 -> [[@LINE-1]]:41 +}; +// RUN: clang-refactor-test rename-initiate -at=%s:34:25 -new-name=x %s -std=c++14 | FileCheck --check-prefix=CHECK5 %s + +template<typename T> +struct S : T { + using T::T; // CHECK6: rename local [[@LINE]]:12 -> [[@LINE]]:13 +// RUN: clang-refactor-test rename-initiate -at=%s:41:12 -new-name=x %s -std=c++14 | FileCheck --check-prefix=CHECK6 %s + using typename T::Foo; +// RUN: not clang-refactor-test rename-initiate -at=%s:43:12 -new-name=x %s -std=c++14 2>&1 | FileCheck --check-prefix=CHECK-UNRESOLVED %s +// CHECK-UNRESOLVED: error: could not rename symbol at the given location +}; diff --git a/clang/test/Refactor/Rename/Variable.cpp b/clang/test/Refactor/Rename/Variable.cpp new file mode 100644 index 0000000000000..7d5c41e13461e --- /dev/null +++ b/clang/test/Refactor/Rename/Variable.cpp @@ -0,0 +1,29 @@ +namespace A { +int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8 +} +int Foo; +int Qux = Foo; +int Baz = A::Foo; /* Test 2 */ // CHECK-NEXT: rename [[@LINE]]:14 -> [[@LINE]]:17 +void fun() { + struct { + int Foo; + } b = {100}; + int Foo = 100; + Baz = Foo; + { + extern int Foo; + Baz = Foo; + Foo = A::Foo /* Test 3 */ + Baz; // CHECK-NEXT: rename [[@LINE]]:14 -> [[@LINE]]:17 + A::Foo /* Test 4 */ = b.Foo; // CHECK-NEXT: rename [[@LINE]]:8 -> [[@LINE]]:11 + } + Foo = b.Foo; // CHECK-NOT: rename [[@LINE]] +} + +// Test 1. +// RUN: clang-refactor-test rename-initiate -at=%s:2:5 -new-name=Bar %s | FileCheck %s +// Test 2. +// RUN: clang-refactor-test rename-initiate -at=%s:6:14 -new-name=Bar %s | FileCheck %s +// Test 3. +// RUN: clang-refactor-test rename-initiate -at=%s:16:14 -new-name=Bar %s | FileCheck %s +// Test 4. +// RUN: clang-refactor-test rename-initiate -at=%s:17:8 -new-name=Bar %s | FileCheck %s diff --git a/clang/test/Refactor/Rename/VariableMacro.cpp b/clang/test/Refactor/Rename/VariableMacro.cpp new file mode 100644 index 0000000000000..312d459323910 --- /dev/null +++ b/clang/test/Refactor/Rename/VariableMacro.cpp @@ -0,0 +1,69 @@ +#define Baz Foo // CHECK1-NOT: rename local [[@LINE]] +// CHECK1-NOT: macro [[@LINE-1]] + +void foo(int value) {} + +void macro() { + int Foo; // CHECK1: rename local [[@LINE]]:7 -> [[@LINE]]:10 + Foo = 42; // CHECK1-NEXT: rename local [[@LINE]]:3 -> [[@LINE]]:6 + Baz -= 0; // CHECK1-NEXT: macro [[@LINE]]:3 -> [[@LINE]]:3 + foo(Foo); // CHECK1-NEXT: rename local [[@LINE]]:7 -> [[@LINE]]:10 + foo(Baz); // CHECK1-NEXT: macro [[@LINE]]:7 -> [[@LINE]]:7 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:7:7 -at=%s:8:3 -at=%s:10:7 -new-name=Bar %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: not clang-refactor-test rename-initiate -at=%s:1:13 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s +// RUN: not clang-refactor-test rename-initiate -at=%s:9:3 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s +// CHECK-ERROR: could not rename symbol at the given location + +#define M var +#define MM M +void macro2() { + int M = 2; // CHECK2: macro [[@LINE]]:7 -> [[@LINE]]:7 + (void)var; // CHECK2-NEXT: rename local [[@LINE]]:9 -> [[@LINE]]:12 + (void)M; // CHECK2-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9 + (void)MM; // CHECK2-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:24:9 -new-name=Bar %s | FileCheck --check-prefix=CHECK2 %s + +// RUN: not clang-refactor-test rename-initiate -at=%s:20:11 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s +// RUN: not clang-refactor-test rename-initiate -at=%s:21:12 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s +// RUN: not clang-refactor-test rename-initiate -at=%s:23:7 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s +// RUN: not clang-refactor-test rename-initiate -at=%s:25:9 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s + +#define BAR(x) x +#define FOO(x) BAR(x) +int FOO(global) = 2; // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:15 +void macro3() { + (void)global; // CHECK3-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:15 + BAR(global) = 0; // CHECK3-NEXT: rename [[@LINE]]:7 -> [[@LINE]]:13 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:40:9 -at=%s:41:7 -new-name=Bar %s | FileCheck --check-prefix=CHECK3 %s + + + +#define CONCAT(x, y) x##_##y +int CONCAT(a, b) = 2; // CHECK4: macro [[@LINE]]:5 -> [[@LINE]]:5 +void macro3() { + (void)a_b; // CHECK4-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:12 + CONCAT(a, b) = 0; // CHECK4-NEXT: macro [[@LINE]]:3 -> [[@LINE]]:3 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:51:9 -new-name=Bar %s | FileCheck --check-prefix=CHECK4 %s + +void macroInFunc() { + #define VARNAME var + int VARNAME; +} + +// RUN: not clang-refactor-test rename-initiate -at=%s:58:19 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s + +void localVarArg() { + int var; // CHECK5: rename local [[@LINE]]:7 -> [[@LINE]]:10 + BAR(var) = 0; // CHECK5-NEXT: rename local [[@LINE]]:7 -> [[@LINE]]:10 +} + +// RUN: clang-refactor-test rename-initiate -at=%s:65:7 -at=%s:66:7 -new-name=Bar %s | FileCheck --check-prefix=CHECK5 %s diff --git a/clang/test/Refactor/Rename/invalid-indexed-name.m b/clang/test/Refactor/Rename/invalid-indexed-name.m new file mode 100644 index 0000000000000..b682bb3fba9ca --- /dev/null +++ b/clang/test/Refactor/Rename/invalid-indexed-name.m @@ -0,0 +1,37 @@ +// XFAIL: * +// TODO: Remove if unused +// Note: the run lines follow their respective tests, since line/column +// matter in this test + +int variable = 0; // CHECK1: rename [[@LINE]]:5 -> [[@LINE]]:13 + +// RUN: clang-refactor-test rename-indexed-file -name=variable -new-name=class -indexed-file=%s -indexed-at=4:5 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: not clang-refactor-test rename-indexed-file -name=variable -new-name=class -indexed-file=%s -indexed-at=4:5 %s -x objective-c++ 2>&1 | FileCheck --check-prefix=CHECK-ERR %s +// CHECK-ERR: error: invalid new name + +// RUN: not clang-refactor-test rename-indexed-file -name=variable -new-name=int -indexed-file=%s -indexed-at=4:5 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s + +@interface I + +- (void)some:(int)x selector:(int)y; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:21 -> [[@LINE]]:29 + +@end + +// RUN: clang-refactor-test rename-indexed-file -name=some:selector -new-name=struct:void -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-indexed-file -name=some:selector: -new-name=struct:void -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK2 %s + +// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=struct:void -indexed-file=%s -indexed-at=14:9 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR-FAIL %s +// CHECK-ERR-FAIL: failed to perform indexed file rename +// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=hello:123 -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s +// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=+:test -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s + +// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=justOnePiece -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s 2>&1 | FileCheck --check-prefix=CHECK-ERR2 %s +// CHECK-ERR2: error: the number of strings in the new name 'justOnePiece' doesn't match the the number of strings in the old name + +@interface I1 + +- (void)singlePiece; + +@end + +// RUN: not clang-refactor-test rename-indexed-file -name=singlePiece -new-name=struct -indexed-file=%s -indexed-at=31:9 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s diff --git a/clang/test/Refactor/Rename/invalid-name.cpp b/clang/test/Refactor/Rename/invalid-name.cpp new file mode 100644 index 0000000000000..703f8187d1293 --- /dev/null +++ b/clang/test/Refactor/Rename/invalid-name.cpp @@ -0,0 +1,13 @@ +// XFAIL: * +// TODO: Remove if unused +// Note: the run lines follow their respective tests, since line/column +// matter in this test + +int variable = 0; + +// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=class %s 2>&1 | FileCheck %s +// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=- %s 2>&1 | FileCheck %s +// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=var+ %s 2>&1 | FileCheck %s +// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name="var " %s 2>&1 | FileCheck %s + +// CHECK: error: invalid new name diff --git a/clang/test/Refactor/Rename/invalid-name.m b/clang/test/Refactor/Rename/invalid-name.m new file mode 100644 index 0000000000000..65014be0ab0e1 --- /dev/null +++ b/clang/test/Refactor/Rename/invalid-name.m @@ -0,0 +1,23 @@ +// XFAIL: * +// TODO: Remove if unused +// Note: the run lines follow their respective tests, since line/column +// matter in this test + +int variable = 0; // CHECK1: rename [[@LINE]]:5 -> [[@LINE]]:13 + +// RUN: clang-refactor-test rename-initiate -at=%s:4:5 -new-name=class %s | FileCheck --check-prefix=CHECK1 %s +// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=class %s -x objective-c++ 2>&1 | FileCheck --check-prefix=CHECK-ERR %s +// CHECK-ERR: error: invalid new name + +// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=int %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s + +@interface I + +- (void)some:(int)x selector:(int)y; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:21 -> [[@LINE]]:29 + +@end + +// RUN: clang-refactor-test rename-initiate -at=%s:14:9 -new-name=struct:void %s | FileCheck --check-prefix=CHECK2 %s + +// RUN: not clang-refactor-test rename-initiate -at=%s:14:9 -new-name=hello:123 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s +// RUN: not clang-refactor-test rename-initiate -at=%s:14:9 -new-name=+:test %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s diff --git a/clang/test/Refactor/Rename/rename-indexed-file.cpp b/clang/test/Refactor/Rename/rename-indexed-file.cpp new file mode 100644 index 0000000000000..4e64fdf511a5f --- /dev/null +++ b/clang/test/Refactor/Rename/rename-indexed-file.cpp @@ -0,0 +1,123 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test + +class Test { // CHECK1: rename [[@LINE]]:7 -> [[@LINE]]:11 +public: + Test() { } // CHECK1: rename [[@LINE]]:3 -> [[@LINE]]:7 + ~Test() { } // CHECK1: rename [[@LINE]]:4 -> [[@LINE]]:8 + + void doSomething() { return; } + void otherFile(); +}; + +void foo() { + Test test; // CHECK1: rename [[@LINE]]:3 -> [[@LINE]]:7 + (test).doSomething(); +} + +Test notIndexed; // CHECK1-NOT: rename [[@LINE]] + +// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 -indexed-at=6:3 -indexed-at=7:4 -indexed-at=14:3 %s | FileCheck --check-prefix=CHECK1 %s + +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%S/Inputs/rename-indexed-file.cpp -indexed-at=1:6 -indexed-at=2:3 -indexed-at=3:6 %s | FileCheck --check-prefix=CHECK2 %s +// CHECK2: rename 1:6 -> 1:10 +// CHECK2: rename 2:3 -> 2:7 +// CHECK2: rename 3:6 -> 3:10 + +// A valid location with an non-identifier token shouldn't produce an occurence +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=15:3 %s | FileCheck --check-prefix=CHECK3 %s + +// A invalid location shouldn't produce an occurence +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=999:1 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=0:1 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=1:0 %s | FileCheck --check-prefix=CHECK3 %s + + +// CHECK3: no replacements found +// CHECK3-NOT: rename + +// RUN: not clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s + +// CHECK-ERROR1: for the -indexed-file option: must be specified at least once! + +// It should be possible to have the filename as one of the compilation arguments +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -ignore-filename-for-initiation-tu -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 -indexed-at=6:3 -indexed-at=7:4 -indexed-at=14:3 %s -c %s -Wall | FileCheck --check-prefix=CHECK1 %s + +// -gmodules should be stripped to avoid -fmodule-format=obj in CC1 arguments: +// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 -indexed-at=6:3 -indexed-at=7:4 -indexed-at=14:3 %s -fmodules -gmodules | FileCheck --check-prefix=CHECK1 %s + +// These texual matches should be reported as comment occurrences: +// CHECK4-INIT: rename [[@LINE-46]]:7 -> [[@LINE-46]]:11 +// Test +/* Test 2 Test */ +/** Test+1 +// Test +**/ +/// Hello Test World +//! \c Test. + +// CHECK4: comment [[@LINE-8]]:4 -> [[@LINE-8]]:8 +// CHECK4-NEXT: comment [[@LINE-8]]:4 -> [[@LINE-8]]:8 +// CHECK4-NEXT: comment [[@LINE-9]]:11 -> [[@LINE-9]]:15 +// CHECK4-NEXT: documentation [[@LINE-9]]:5 -> [[@LINE-9]]:9 +// CHECK4-NEXT: documentation [[@LINE-9]]:4 -> [[@LINE-9]]:8 +// CHECK4-NEXT: documentation [[@LINE-8]]:11 -> [[@LINE-8]]:15 +// CHECK4-NEXT: documentation [[@LINE-8]]:8 -> [[@LINE-8]]:12 + +// "Test" +// 'Test' +// CHECK4-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:9 +// CHECK4-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:9 + +// CHECK4-NEXT: comment [[@LINE+1]]:55 +// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 %s | FileCheck --check-prefixes=CHECK4-INIT,CHECK4 %s +// We should find textual occurrences even without indexed occurrences: +// CHECK4-NEXT: comment [[@LINE+1]]:55 +// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=Foo -indexed-file=%s %s | FileCheck --check-prefix=CHECK4 %s + +// These ones shouldn't: +// Test2 test Testable +/// _Test +/// ATest_ +const char *test = "Test"; +void Test20() { } + +// CHECK4-NOT: comment +// CHECK4-NOT: documentation + + +class MyInclude { // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:16 +}; + + /*comment*/ #include "MyInclude.h" +#include <clang/myinclude.h> +#import <MyInclude/ThisIsMyInclude> +// CHECK5-NEXT: filename [[@LINE-3]]:24 -> [[@LINE-3]]:33 +// CHECK5-NEXT: filename [[@LINE-3]]:17 -> [[@LINE-3]]:26 +// CHECK5-NEXT: filename [[@LINE-3]]:26 -> [[@LINE-3]]:35 + +// CHECK5-NOT: filename +#include "My Include.h" +"MyInclude.h" + +// RUN: clang-refactor-test rename-indexed-file -name=MyInclude -new-name=Foo -indexed-file=%s -indexed-at=89:7 -indexed-at=include:92:1 -indexed-at=include:93:1 -indexed-at=include:94:1 -indexed-at=include:100:1 %s | FileCheck --check-prefix=CHECK5 %s + +#define MACRO variable + +void macroOccurrence() { + variable; + MACRO; + 22; + MACRO; +} +// CHECK-MACRO: rename [[@LINE-5]]:3 -> [[@LINE-5]]:11 +// CHECK-MACRO-NEXT: macro [[@LINE-5]]:3 -> [[@LINE-5]]:3 +// CHECK-MACRO-NOT: macro + +// RUN: clang-refactor-test rename-indexed-file -name=variable -new-name=foo -indexed-file=%s -indexed-at=108:3 -indexed-at=109:3 -indexed-at=110:3 -indexed-at=111:2 %s | FileCheck --check-prefix=CHECK-MACRO %s + +struct MyType { // CHECK-MACRO-PREFIX: rename [[@LINE]]:8 -> [[@LINE]]:14 +}; +MyType MyTypePrefix; // CHECK-MACRO-PREFIX: macro [[@LINE]]:8 -> [[@LINE]]:8 + +// RUN: clang-refactor-test rename-indexed-file -name=MyType -new-name=x -indexed-file=%s -indexed-at=119:8 -indexed-at=121:8 %s | FileCheck --check-prefix=CHECK-MACRO-PREFIX %s diff --git a/clang/test/Refactor/Rename/rename-initiate-usr.cpp b/clang/test/Refactor/Rename/rename-initiate-usr.cpp new file mode 100644 index 0000000000000..2f1546522edba --- /dev/null +++ b/clang/test/Refactor/Rename/rename-initiate-usr.cpp @@ -0,0 +1,20 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test + +class Test { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:11 +public: + Test() { } // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7 + ~Test() { } // CHECK: rename [[@LINE]]:4 -> [[@LINE]]:8 +}; + +void foo() { + Test test; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7 +} + +// RUN: clang-refactor-test rename-initiate-usr -usr="c:@S@Test" -new-name=Foo %s | FileCheck %s + +// RUN: not clang-refactor-test rename-initiate-usr -usr="c:@S@Foo" -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s +// CHECK-ERROR1: error: could not rename symbol with the given USR + +// RUN: not clang-refactor-test rename-initiate-usr -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR2 %s +// CHECK-ERROR2: for the -usr option: must be specified at least once diff --git a/clang/test/Refactor/Rename/rename-initiate.cpp b/clang/test/Refactor/Rename/rename-initiate.cpp new file mode 100644 index 0000000000000..30c918cc834e7 --- /dev/null +++ b/clang/test/Refactor/Rename/rename-initiate.cpp @@ -0,0 +1,27 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test + +class Test { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:11 +public: + Test() { } // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7 + ~Test() { } // CHECK: rename [[@LINE]]:4 -> [[@LINE]]:8 + void doSomething() { + return; + } +}; + +void foo() { + Test test; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7 + test.doSomething(); +} + +// RUN: clang-refactor-test rename-initiate -at=%s:4:7 -new-name=Foo %s | FileCheck %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -new-name=Foo %s | FileCheck %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:9 -new-name=Foo %s | FileCheck %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:10 -new-name=Foo %s | FileCheck %s + +// RUN: not clang-refactor-test rename-initiate -at=%s:1:10 -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s +// CHECK-ERROR1: error: could not rename symbol at the given location + +// RUN: not clang-refactor-test rename-initiate -at=%s -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR2 %s +// CHECK-ERROR2: error: The -at option must use the <file:line:column> format diff --git a/clang/test/Refactor/list-refactoring-actions.cpp b/clang/test/Refactor/list-refactoring-actions.cpp new file mode 100644 index 0000000000000..573a076bf1449 --- /dev/null +++ b/clang/test/Refactor/list-refactoring-actions.cpp @@ -0,0 +1,39 @@ +int +renamable = 0; + +// RUN: clang-refactor-test list-actions -at=%s:2:1 %s | FileCheck --check-prefix=CHECK-RENAME %s + +// CHECK-RENAME: Found {{[0-9]*}} actions: +// CHECK-RENAME-NEXT: Rename + +// RUN: not clang-refactor-test list-actions -at=%s:2:13 %s 2>&1 | FileCheck --check-prefix=CHECK-NONE %s + +// CHECK-NONE: No refactoring actions are available at the given location +// CHECK-NONE-NOT: Rename + +// RUN: not clang-refactor-test list-actions -at=%s %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s +// CHECK-ERR: error: The -at option must use the <file:line:column> format + +void localVsGlobalRename(int renamable) { } + +// RUN: clang-refactor-test list-actions -dump-raw-action-type -at=%s:17:30 %s | FileCheck --check-prefix=CHECK-LOCAL-RENAME %s + +// CHECK-LOCAL-RENAME: Found {{[0-9]*}} actions: +// CHECK-LOCAL-RENAME-NEXT: Rename(1) + +namespace nullDeclNamespace { + +template<template<typename T> class C> class NullNode {}; + +struct AfterNull { }; +// RUN: clang-refactor-test list-actions -at=%s:28:8 %s | FileCheck --check-prefix=CHECK-RENAME %s + +} + +#define MACRO(X) (void)X; +void macroArg() { + int variable = 0; + MACRO(variable); +} +// RUN: not clang-refactor-test list-actions -at=%s:26:9 -selected=%s:36:9-36:16 %s 2>&1 | FileCheck --check-prefix=CHECK-MACRO-ARG %s +// CHECK-MACRO-ARG: No refactoring actions are available at the given location diff --git a/clang/test/Sema/enum-attr.c b/clang/test/Sema/enum-attr.c index 933d8ccdcd89c..bf0dcb0bfe18f 100644 --- a/clang/test/Sema/enum-attr.c +++ b/clang/test/Sema/enum-attr.c @@ -44,7 +44,7 @@ void test() { enum Enum t0 = 100; // expected-warning{{integer constant not in range of enumerated type}} t0 = 1; - switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}} + switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}} expected-note {{add missing switch cases}} case A0: break; case 16: break; // expected-warning{{case value not in enumerated type}} } @@ -58,7 +58,7 @@ void test() { enum EnumClosed t1 = 100; // expected-warning{{integer constant not in range of enumerated type}} t1 = 1; - switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}} + switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}} expected-note {{add missing switch cases}} case B0: break; case 16: break; // expected-warning{{case value not in enumerated type}} } @@ -72,7 +72,7 @@ void test() { enum EnumOpen t2 = 100; t2 = 1; - switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}} + switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}} expected-note {{add missing switch cases}} case C0: break; case 16: break; } @@ -86,7 +86,7 @@ void test() { enum EnumFlag t3 = 5; // expected-warning{{integer constant not in range of enumerated type}} t3 = 9; - switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}} + switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}} expected-note {{add missing switch cases}} case D0: break; case 9: break; case 16: break; // expected-warning{{case value not in enumerated type}} @@ -101,7 +101,7 @@ void test() { enum EnumFlagClosed t4 = 5; // expected-warning{{integer constant not in range of enumerated type}} t4 = 9; - switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}} + switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}} expected-note {{add missing switch cases}} case E0: break; case 9: break; case 16: break; // expected-warning{{case value not in enumerated type}} @@ -116,7 +116,7 @@ void test() { enum EnumFlagOpen t5 = 5; t5 = 9; - switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}} + switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}} expected-note {{add missing switch cases}} case F0: break; case 9: break; case 16: break; diff --git a/clang/test/Sema/statements.c b/clang/test/Sema/statements.c index dbb4d56ee1d17..a17e8405b4947 100644 --- a/clang/test/Sema/statements.c +++ b/clang/test/Sema/statements.c @@ -56,7 +56,7 @@ int test12(enum Numbers num) { enum x { a, b, c, d, e, f, g }; void foo(enum x X) { - switch (X) { // expected-warning {{enumeration value 'g' not handled in switch}} + switch (X) { // expected-warning {{enumeration value 'g' not handled in switch}} expected-note {{add missing switch cases}} case a: case b: case c: @@ -66,7 +66,7 @@ void foo(enum x X) { break; } - switch (X) { // expected-warning {{enumeration values 'f' and 'g' not handled in switch}} + switch (X) { // expected-warning {{enumeration values 'f' and 'g' not handled in switch}} expected-note {{add missing switch cases}} case a: case b: case c: @@ -75,7 +75,7 @@ void foo(enum x X) { break; } - switch (X) { // expected-warning {{enumeration values 'e', 'f', and 'g' not handled in switch}} + switch (X) { // expected-warning {{enumeration values 'e', 'f', and 'g' not handled in switch}} expected-note {{add missing switch cases}} case a: case b: case c: @@ -83,7 +83,7 @@ void foo(enum x X) { break; } - switch (X) { // expected-warning {{5 enumeration values not handled in switch: 'c', 'd', 'e'...}} + switch (X) { // expected-warning {{5 enumeration values not handled in switch: 'c', 'd', 'e'...}} expected-note {{add missing switch cases}} case a: case b: break; diff --git a/clang/test/Sema/switch.c b/clang/test/Sema/switch.c index 7aa695d7cb919..5580209876ba3 100644 --- a/clang/test/Sema/switch.c +++ b/clang/test/Sema/switch.c @@ -97,7 +97,7 @@ void test7() { A = 1, B } a; - switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} + switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} expected-note {{add missing switch cases}} case A: break; } @@ -155,7 +155,7 @@ void test8() { case C: break; } - switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} + switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} expected-note {{add missing switch cases}} case A: break; } @@ -202,13 +202,13 @@ void test11() { B, C } a; - switch(a) { //expected-warning{{enumeration value 'A' not handled in switch}} + switch(a) { //expected-warning{{enumeration value 'A' not handled in switch}} expected-note {{add missing switch cases}} case B: case C: break; } - switch(a) { //expected-warning{{enumeration value 'A' not explicitly handled in switch}} + switch(a) { //expected-warning{{enumeration value 'A' not explicitly handled in switch}} expected-note {{add missing switch cases}} case B: case C: break; @@ -238,7 +238,7 @@ typedef enum { } my_type_t; int test13(my_type_t t) { - switch(t) { // expected-warning{{enumeration value 'val3' not handled in switch}} + switch(t) { // expected-warning{{enumeration value 'val3' not handled in switch}} expected-note {{add missing switch cases}} case val1: return 1; case val2: diff --git a/clang/test/SemaCXX/array-bounds.cpp b/clang/test/SemaCXX/array-bounds.cpp index 8ae92e7613010..92e8dd4e13613 100644 --- a/clang/test/SemaCXX/array-bounds.cpp +++ b/clang/test/SemaCXX/array-bounds.cpp @@ -164,7 +164,7 @@ enum enumB { enumB_X, enumB_Y, enumB_Z }; static enum enumB myVal = enumB_X; void test_nested_switch() { switch (enumA_E) { // expected-warning {{no case matching constant}} - switch (myVal) { // expected-warning {{enumeration values 'enumB_X' and 'enumB_Z' not handled in switch}} + switch (myVal) { // expected-warning {{enumeration values 'enumB_X' and 'enumB_Z' not handled in switch}} expected-note {{add missing switch cases}} case enumB_Y: ; } } diff --git a/clang/test/SemaCXX/enum-attr.cpp b/clang/test/SemaCXX/enum-attr.cpp index 7726aff4830a3..92027ae5bd238 100644 --- a/clang/test/SemaCXX/enum-attr.cpp +++ b/clang/test/SemaCXX/enum-attr.cpp @@ -27,7 +27,7 @@ enum __attribute__((flag_enum,enum_extensibility(open))) EnumFlagOpen { void test() { enum Enum t0; - switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}} + switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}} expected-note {{add missing switch cases}} case A0: break; case 16: break; // expected-warning{{case value not in enumerated type}} } @@ -40,7 +40,7 @@ void test() { enum EnumClosed t1; - switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}} + switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}} expected-note {{add missing switch cases}} case B0: break; case 16: break; // expected-warning{{case value not in enumerated type}} } @@ -53,7 +53,7 @@ void test() { enum EnumOpen t2; - switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}} + switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}} expected-note {{add missing switch cases}} case C0: break; case 16: break; } @@ -66,7 +66,7 @@ void test() { enum EnumFlag t3; - switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}} + switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}} expected-note {{add missing switch cases}} case D0: break; case 9: break; case 16: break; // expected-warning{{case value not in enumerated type}} @@ -80,7 +80,7 @@ void test() { enum EnumFlagClosed t4; - switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}} + switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}} expected-note {{add missing switch cases}} case E0: break; case 9: break; case 16: break; // expected-warning{{case value not in enumerated type}} @@ -94,7 +94,7 @@ void test() { enum EnumFlagOpen t5; - switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}} + switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}} expected-note {{add missing switch cases}} case F0: break; case 9: break; case 16: break; diff --git a/clang/test/SemaCXX/scope-check.cpp b/clang/test/SemaCXX/scope-check.cpp index 9e00332985276..256f01b8af2a8 100644 --- a/clang/test/SemaCXX/scope-check.cpp +++ b/clang/test/SemaCXX/scope-check.cpp @@ -193,7 +193,7 @@ namespace PR10462 { bool recurse() { MyEnum K; - switch (K) { // expected-warning {{enumeration value 'something_invalid' not handled in switch}} + switch (K) { // expected-warning {{enumeration value 'something_invalid' not handled in switch}} expected-note {{add missing switch cases}} case something_valid: case what_am_i_thinking: // expected-error {{use of undeclared identifier}} int *X = 0; diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index 2d78f06c5d331..910e2c1186fc8 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -411,7 +411,7 @@ class Figure { void testAccess() { Figure obj; - switch (obj.type()) { // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}} + switch (obj.type()) { // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}} expected-note {{add missing switch cases}} case SQUARE: // expected-error-re {{use of undeclared identifier 'SQUARE'{{$}}}} case TRIANGLE: // expected-error-re {{use of undeclared identifier 'TRIANGLE'{{$}}}} case CIRCE: // expected-error-re {{use of undeclared identifier 'CIRCE'{{$}}}} diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt index b0c97f0f1e4ca..ec3307b1415ca 100644 --- a/clang/tools/CMakeLists.txt +++ b/clang/tools/CMakeLists.txt @@ -30,3 +30,6 @@ add_llvm_external_project(clang-tools-extra extra) # libclang may require clang-tidy in clang-tools-extra. add_clang_subdirectory(libclang) + +add_clang_subdirectory(clang-refactor-test) + diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index d25ae117a68aa..f3684407bf83c 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -110,6 +110,10 @@ static void describeLibclangFailure(enum CXErrorCode Err) { case CXError_ASTReadError: fprintf(stderr, "Failure: AST deserialization error occurred\n"); return; + + default: + fprintf(stderr, "Failure (other)\n"); + return; } } diff --git a/clang/tools/clang-refactor-test/CMakeLists.txt b/clang/tools/clang-refactor-test/CMakeLists.txt new file mode 100644 index 0000000000000..f742327ea3989 --- /dev/null +++ b/clang/tools/clang-refactor-test/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +add_clang_executable(clang-refactor-test + ClangRefactorTest.cpp + ) + +if (LLVM_BUILD_STATIC) + target_link_libraries(clang-refactor-test + libclang_static + ) +else() + target_link_libraries(clang-refactor-test + libclang + clangBasic + clangFrontend + clangRewrite + clangTooling + clangToolingCore + clangToolingRefactor + ) +endif() diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp new file mode 100644 index 0000000000000..5f220e40adc2d --- /dev/null +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -0,0 +1,1371 @@ +//===--- ClangRefactorTest.cpp - ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a clang-refactor-test tool that is used to test the +// refactoring library in Clang. +// +//===----------------------------------------------------------------------===// + +#include "clang-c/Refactor.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Frontend/CommandLineSourceLoc.h" +#include "clang/Tooling/Refactor/SymbolName.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace clang; + +namespace opts { + +static cl::OptionCategory + ClangRefactorTestOptions("clang-refactor-test common options"); + +cl::SubCommand RenameInitiateSubcommand( + "rename-initiate", "Initiate renaming in an initial translation unit"); + +cl::SubCommand RenameInitiateUSRSubcommand( + "rename-initiate-usr", + "Initiate renaming in an translation unit on a specific declaration"); + +cl::SubCommand RenameIndexedFileSubcommand( + "rename-indexed-file", + "Initiate renaming and find occurrences in an indexed file"); + +cl::SubCommand ListRefactoringActionsSubcommand("list-actions", + "Print the list of the " + "refactoring actions that can " + "be performed at the specified " + "location"); + +cl::SubCommand InitiateActionSubcommand("initiate", + "Initiate a refactoring action"); + +cl::SubCommand + PerformActionSubcommand("perform", + "Initiate and perform a refactoring action"); + +const cl::desc + AtOptionDescription("The location at which the refactoring should be " + "initiated (<file>:<line>:<column>)"); + +const cl::desc InRangeOptionDescription( + "The location(s) at which the refactoring should be " + "initiated (<file>:<line>:<column>-<last-column>)"); + +const cl::desc SelectedRangeOptionDescription( + "The selected source range in which the refactoring should be " + "initiated (<file>:<line>:<column>-<line>:<column>)"); + +static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); + +namespace rename { +static cl::list<std::string> AtLocation("at", AtOptionDescription, cl::Required, + cl::cat(ClangRefactorTestOptions), + cl::sub(RenameInitiateSubcommand), + cl::OneOrMore); + +static cl::opt<std::string> + USR("usr", cl::desc("The USR of the declaration that should be renamed"), + cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateUSRSubcommand), + cl::Required); + +static cl::opt<std::string> + NewName("new-name", cl::desc("The new name to change the symbol to."), + cl::Required, cl::cat(ClangRefactorTestOptions), + cl::sub(RenameInitiateSubcommand), + cl::sub(RenameInitiateUSRSubcommand)); + +static cl::list<std::string> + IndexedNames("name", cl::desc("The names of the renamed symbols"), + cl::Required, cl::OneOrMore, cl::cat(ClangRefactorTestOptions), + cl::sub(RenameIndexedFileSubcommand)); + +static cl::list<std::string> IndexedNewNames( + "new-name", cl::desc("The new name to change the symbol to."), cl::Required, + cl::OneOrMore, cl::cat(ClangRefactorTestOptions), + cl::sub(RenameIndexedFileSubcommand)); + +static cl::opt<std::string> + IndexedSymbolKind("indexed-symbol-kind", + cl::desc("The kind of the indexed symbol."), cl::Optional, + cl::cat(ClangRefactorTestOptions), + cl::sub(RenameIndexedFileSubcommand)); + +static cl::opt<std::string> + IndexedFileName("indexed-file", cl::desc("The name of the indexed file"), + cl::Required, cl::cat(ClangRefactorTestOptions), + cl::sub(RenameIndexedFileSubcommand)); + +static cl::list<std::string> + IndexedLocations("indexed-at", + cl::desc("The location of an indexed occurrence " + "([<kind>|<symbol-index>:]<line>:<column>)"), + cl::ZeroOrMore, cl::cat(ClangRefactorTestOptions), + cl::sub(RenameIndexedFileSubcommand)); + +static cl::opt<bool> AvoidTextual( + "no-textual-matches", cl::desc("Avoid searching for textual matches"), + cl::cat(ClangRefactorTestOptions), cl::sub(RenameIndexedFileSubcommand)); + +static cl::opt<bool> DumpSymbols( + "dump-symbols", cl::desc("Dump the information about the renamed symbols"), + cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand), + cl::sub(RenameInitiateUSRSubcommand)); +} + +namespace listActions { +cl::opt<std::string> AtLocation("at", AtOptionDescription, cl::Required, + cl::cat(ClangRefactorTestOptions), + cl::sub(ListRefactoringActionsSubcommand)); + +cl::opt<std::string> SelectedRange("selected", SelectedRangeOptionDescription, + cl::cat(ClangRefactorTestOptions), + cl::sub(ListRefactoringActionsSubcommand)); + +cl::opt<bool> DumpRawActionType( + "dump-raw-action-type", + cl::desc("Prints the action type integer value for each listed action"), + cl::cat(ClangRefactorTestOptions), + cl::sub(ListRefactoringActionsSubcommand)); +} + +namespace initiateAndPerform { +cl::list<std::string> InLocationRanges("in", cl::ZeroOrMore, + InRangeOptionDescription, + cl::cat(ClangRefactorTestOptions), + cl::sub(InitiateActionSubcommand)); + +cl::list<std::string> AtLocations("at", cl::ZeroOrMore, AtOptionDescription, + cl::cat(ClangRefactorTestOptions), + cl::sub(InitiateActionSubcommand), + cl::sub(PerformActionSubcommand)); + +cl::list<std::string> SelectedRanges("selected", cl::ZeroOrMore, + SelectedRangeOptionDescription, + cl::cat(ClangRefactorTestOptions), + cl::sub(InitiateActionSubcommand), + cl::sub(PerformActionSubcommand)); + +cl::opt<std::string> ActionName("action", cl::Required, + cl::desc("The name of the refactoring action"), + cl::cat(ClangRefactorTestOptions), + cl::sub(InitiateActionSubcommand), + cl::sub(PerformActionSubcommand)); + +cl::opt<bool> LocationAgnostic( + "location-agnostic", + cl::desc( + "Ignore the location of initiation when verifying result consistency"), + cl::cat(ClangRefactorTestOptions), cl::sub(InitiateActionSubcommand)); + +cl::opt<unsigned> CandidateIndex( + "candidate", + cl::desc( + "The index of the refactoring candidate which should be performed"), + cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand)); + +cl::opt<std::string> ContinuationFile( + "continuation-file", + cl::desc("The source file in which the continuation should run"), + cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand)); + +cl::opt<std::string> QueryResults( + "query-results", cl::desc("The indexer query results that should be passed " + "into the continuation"), + cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand)); + +cl::opt<bool> EmitAssociatedInfo( + "emit-associated", cl::desc("Dump additional associated information"), + cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand)); +} + +cl::opt<bool> Apply( + "apply", + cl::desc( + "Apply the changes and print the modified file to standard output"), + cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand), + cl::sub(RenameInitiateSubcommand), cl::sub(RenameIndexedFileSubcommand)); + +cl::opt<bool> + Diff("diff", + cl::desc("Display the replaced text in red when -apply is specified"), + cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand), + cl::sub(RenameInitiateSubcommand), + cl::sub(RenameIndexedFileSubcommand)); + +cl::opt<int> Context("context", cl::desc("How many lines of context should be " + "displayed when -apply is specified"), + cl::cat(ClangRefactorTestOptions), + cl::sub(PerformActionSubcommand), + cl::sub(RenameInitiateSubcommand), + cl::sub(RenameIndexedFileSubcommand)); + +static cl::opt<std::string> FileName( + cl::Positional, cl::desc("<filename>"), cl::Required, + cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand), + cl::sub(RenameInitiateUSRSubcommand), cl::sub(RenameIndexedFileSubcommand), + cl::sub(ListRefactoringActionsSubcommand), + cl::sub(InitiateActionSubcommand), cl::sub(PerformActionSubcommand)); + +static cl::opt<bool> IgnoreFilenameForInitiationTU( + "ignore-filename-for-initiation-tu", cl::Optional, + cl::cat(ClangRefactorTestOptions), cl::sub(RenameIndexedFileSubcommand)); + +static cl::list<std::string> CompilerArguments( + cl::ConsumeAfter, cl::desc("<arguments to be passed to the compiler>"), + cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand), + cl::sub(RenameInitiateUSRSubcommand), cl::sub(RenameIndexedFileSubcommand), + cl::sub(ListRefactoringActionsSubcommand), + cl::sub(InitiateActionSubcommand), cl::sub(PerformActionSubcommand)); + +static cl::opt<std::string> ImplementationTU( + "implementation-tu", cl::desc("The name of the implementation TU"), + cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand)); +} + +static const char *renameOccurrenceKindString(CXSymbolOccurrenceKind Kind, + bool IsLocal, + bool IsMacroExpansion) { + switch (Kind) { + case CXSymbolOccurrence_MatchingSymbol: + return IsMacroExpansion ? "macro" : IsLocal ? "rename local" : "rename"; + case CXSymbolOccurrence_MatchingSelector: + assert(!IsLocal && "Objective-C selector renames must be global"); + return IsMacroExpansion ? "selector in macro" : "selector"; + case CXSymbolOccurrence_MatchingImplicitProperty: + assert(!IsLocal); + return IsMacroExpansion ? "implicit-property in macro" + : "implicit-property"; + case CXSymbolOccurrence_MatchingCommentString: + return "comment"; + case CXSymbolOccurrence_MatchingDocCommentString: + return "documentation"; + case CXSymbolOccurrence_MatchingFilename: + return "filename"; + case CXSymbolOccurrence_ExtractedDeclaration: + return "extracted-decl"; + case CXSymbolOccurrence_ExtractedDeclaration_Reference: + return "extracted-decl-ref"; + } +} + +static int apply(ArrayRef<CXRefactoringReplacement> Replacements, + StringRef Filename) { + // Assume that the replacements are sorted. + auto Result = MemoryBuffer::getFile(Filename); + if (!Result) { + errs() << "Failed to open " << Filename << "\n"; + return 1; + } + + raw_ostream &OS = outs(); + + int Context = opts::Context; + + MemoryBuffer &Buffer = **Result; + std::vector<std::pair<StringRef, std::vector<CXRefactoringReplacement>>> + Lines; + for (auto I = line_iterator(Buffer, /*SkipBlanks=*/false), + E = line_iterator(); + I != E; ++I) + Lines.push_back( + std::make_pair(*I, std::vector<CXRefactoringReplacement>())); + unsigned FlushedLine = 1; + auto FlushUntil = [&](unsigned Line) { + // Adjust the first flushed line if needed when printing in context mode. + if (FlushedLine == 1 && Context) + FlushedLine = std::max(int(Line) - Context, 1); + for (; FlushedLine < Line; ++FlushedLine) { + const auto &Line = Lines[FlushedLine - 1]; + if (Line.second.empty()) { + OS << Line.first << "\n"; + continue; + } + + unsigned I = 0; + for (const CXRefactoringReplacement &Replacement : Line.second) { + OS << Line.first.substr(I, Replacement.Range.Begin.Column - 1 - I); + if (opts::Diff) { + OS.changeColor(raw_ostream::RED, false, true); + OS << Line.first.substr(Replacement.Range.Begin.Column - 1, + Replacement.Range.End.Column - 1 - + (Replacement.Range.Begin.Column - 1)); + } + OS.changeColor(raw_ostream::GREEN); + OS << clang_getCString(Replacement.ReplacementString); + OS.resetColor(); + I = Replacement.Range.End.Column - 1; + } + OS << Line.first.substr(I); + if (I < Line.first.size() || opts::Diff) + OS << "\n"; + } + }; + + int EndLineMax = 0; + for (const CXRefactoringReplacement &Replacement : Replacements) { + EndLineMax = std::max(int(Replacement.Range.End.Line), EndLineMax); + unsigned StartingLine = Replacement.Range.Begin.Line; + FlushUntil(StartingLine); + if (Replacement.Range.End.Line == StartingLine) { + Lines[StartingLine - 1].second.push_back(Replacement); + continue; + } + // Multi-line replacements have to be split + for (unsigned I = StartingLine; I <= Replacement.Range.End.Line; ++I) { + CXRefactoringReplacement NewReplacement; + if (I == Replacement.Range.End.Line) + NewReplacement.ReplacementString = Replacement.ReplacementString; + else + // FIXME: This is a hack to workaround the fact that the API doesn't + // provide a way to create a null string. This should be fixed when + // upstreaming. + NewReplacement.ReplacementString = {0, 0}; + NewReplacement.Range.Begin.Line = I; + NewReplacement.Range.Begin.Column = + I == StartingLine ? Replacement.Range.Begin.Column : 1; + NewReplacement.Range.End.Line = I; + NewReplacement.Range.End.Column = I == Replacement.Range.End.Line + ? Replacement.Range.End.Column + : Lines[I - 1].first.size() + 1; + NewReplacement.AssociatedData = nullptr; + Lines[I - 1].second.push_back(NewReplacement); + } + } + FlushUntil(Context ? std::min(int(Lines.size()), EndLineMax + Context) + 1 + : Lines.size() + 2); + // Print out a dividor when printing in the context mode. + if (Context) { + for (int I = 0; I < 80; ++I) + OS << '-'; + OS << "\n"; + } + return 0; +} + +/// Converts the given renamed \p Occurrence into a string value that represents +/// this occurrence. +static std::string +occurrenceToString(const CXSymbolOccurrence &Occurrence, bool IsLocal, + const tooling::SymbolName &NewName, + const tooling::SymbolName &ExpectedReplacementStrings, + StringRef Filename) { + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << renameOccurrenceKindString(Occurrence.Kind, IsLocal, + Occurrence.IsMacroExpansion) + << ' '; + if (!Filename.empty()) + OS << '"' << Filename << "\" "; + + bool FirstRange = true; + for (unsigned J = 0; J != Occurrence.NumNamePieces; ++J) { + if (!FirstRange) // TODO + OS << ", "; + + // Print the replacement string if it doesn't match the expected string. + if (NewName[J] != ExpectedReplacementStrings[J]) + OS << '"' << NewName[J] << "\" "; + + CXFileRange Range = Occurrence.NamePieces[J]; + OS << Range.Begin.Line << ":" << Range.Begin.Column << " -> " + << Range.End.Line << ":" << Range.End.Column; + FirstRange = false; + } + return OS.str(); +} + +static CXCursorKind +renameIndexedOccurrenceKindStringToKind(StringRef Str, CXCursorKind Default) { + return llvm::StringSwitch<CXCursorKind>(Str) + .Case("objc-im", CXCursor_ObjCInstanceMethodDecl) + .Case("objc-cm", CXCursor_ObjCClassMethodDecl) + .Case("objc-message", CXCursor_ObjCMessageExpr) + .Case("include", CXCursor_InclusionDirective) + .Default(Default); +} + +/// Parses the string passed as the -indexed-at argument. +std::pair<CXRenamedIndexedSymbolLocation, unsigned> +parseIndexedOccurrence(StringRef IndexedOccurrence, + CXCursorKind DefaultCursorKind) { + StringRef LineColumnLoc = IndexedOccurrence; + CXCursorKind Kind = DefaultCursorKind; + unsigned SymbolIndex = 0; + if (LineColumnLoc.count(':') > 1) { + std::pair<StringRef, StringRef> Split = LineColumnLoc.split(':'); + // The first value is either the kind or the symbol index. + if (Split.first.getAsInteger(10, SymbolIndex)) { + if (Split.second.count(':') > 1) { + std::pair<StringRef, StringRef> SecondSplit = Split.second.split(':'); + if (SecondSplit.first.getAsInteger(10, SymbolIndex)) + assert(false && "expected symbol index"); + Split.second = SecondSplit.second; + } + Kind = renameIndexedOccurrenceKindStringToKind(Split.first, Kind); + } + LineColumnLoc = Split.second; + } + auto Loc = std::string("-:") + LineColumnLoc.str(); + auto Location = ParsedSourceLocation::FromString(Loc); + return std::make_pair( + CXRenamedIndexedSymbolLocation{{Location.Line, Location.Column}, Kind}, + SymbolIndex); +} + +/// Compare the produced occurrences to the expected occurrences that were +/// gathered at the first location. Return true if the occurrences are +/// different. +static bool compareOccurrences(ArrayRef<std::string> ExpectedReplacements, + CXSymbolOccurrencesResult Occurrences, + bool IsLocal, + const tooling::SymbolName &NewSymbolName, + bool PrintFilenames) { + unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Occurrences); + size_t ExpectedReplacementIndex = 0; + for (unsigned FileIndex = 0; FileIndex < NumFiles; ++FileIndex) { + CXSymbolOccurrencesInFile FileResult; + clang_SymbolOccurrences_getOccurrencesForFile(Occurrences, FileIndex, + &FileResult); + StringRef Filename = + PrintFilenames ? clang_getCString(FileResult.Filename) : ""; + + for (unsigned I = 0; I != FileResult.NumOccurrences; ++I) { + std::string Replacement = + occurrenceToString(FileResult.Occurrences[I], IsLocal, NewSymbolName, + NewSymbolName, Filename); + if (ExpectedReplacementIndex >= ExpectedReplacements.size() || + Replacement != ExpectedReplacements[ExpectedReplacementIndex]) + return true; + ++ExpectedReplacementIndex; + } + } + // Verify that all of the expected replacements were checked. + return ExpectedReplacementIndex != ExpectedReplacements.size(); +} + +struct ImplementationTUWrapper { + CXTranslationUnit TU = nullptr; + + ImplementationTUWrapper() {} + ~ImplementationTUWrapper() { clang_disposeTranslationUnit(TU); } + + ImplementationTUWrapper(const ImplementationTUWrapper &) = delete; + ImplementationTUWrapper &operator=(const ImplementationTUWrapper &) = delete; + + bool load(CXRefactoringAction Action, CXIndex CIdx, + ArrayRef<const char *> Args); +}; + +bool ImplementationTUWrapper::load(CXRefactoringAction Action, CXIndex CIdx, + ArrayRef<const char *> Args) { + if (!clang_RefactoringAction_requiresImplementationTU(Action)) + return false; + CXString USR = + clang_RefactoringAction_getUSRThatRequiresImplementationTU(Action); + outs() << "Implementation TU USR: '" << clang_getCString(USR) << "'\n"; + clang_disposeString(USR); + if (!TU) { + CXErrorCode Err = clang_parseTranslationUnit2( + CIdx, opts::ImplementationTU.c_str(), Args.data(), Args.size(), 0, 0, + CXTranslationUnit_KeepGoing, &TU); + if (Err != CXError_Success) { + errs() << "error: failed to load implementation TU '" + << opts::ImplementationTU << "'\n"; + return true; + } + } + CXErrorCode Err = clang_RefactoringAction_addImplementationTU(Action, TU); + if (Err != CXError_Success) { + errs() << "error: failed to add implementation TU '" + << opts::ImplementationTU << "'\n"; + return true; + } + return false; +} + +static bool reportNewNameError(CXErrorCode Err) { + std::string NewName = opts::RenameIndexedFileSubcommand + ? opts::rename::IndexedNewNames[0] + : opts::rename::NewName; + if (Err == CXError_RefactoringNameSizeMismatch) + errs() << "error: the number of strings in the new name '" << NewName + << "' doesn't match the the number of strings in the old name\n"; + else if (Err == CXError_RefactoringNameInvalid) + errs() << "error: invalid new name '" << NewName << "'\n"; + else + return true; + return false; +} + +int rename(CXTranslationUnit TU, CXIndex CIdx, ArrayRef<const char *> Args) { + assert(!opts::RenameIndexedFileSubcommand); + // Contains the renamed source replacements for the first location. It is + // compared to replacements from follow-up renames to ensure that all renames + // give the same result. + std::vector<std::string> ExpectedReplacements; + // Should we print out the filenames. False by default, but true when multiple + // files are modified. + bool PrintFilenames = false; + ImplementationTUWrapper ImplementationTU; + + auto RenameAt = [&](const ParsedSourceLocation &Location, + const std::string &USR) -> int { + CXRefactoringAction RenamingAction; + CXErrorCode Err; + CXDiagnosticSet Diags = nullptr; + if (USR.empty()) { + CXSourceLocation Loc = + clang_getLocation(TU, clang_getFile(TU, Location.FileName.c_str()), + Location.Line, Location.Column); + Err = clang_Refactoring_initiateAction( + TU, Loc, clang_getNullRange(), CXRefactor_Rename, + /*Options=*/nullptr, &RenamingAction, &Diags); + } else { + Err = clang_Refactoring_initiateActionOnDecl( + TU, USR.c_str(), CXRefactor_Rename, /*Options=*/nullptr, + &RenamingAction, nullptr); + } + if (Err != CXError_Success) { + errs() << "error: could not rename symbol " + << (USR.empty() ? "at the given location\n" + : "with the given USR\n"); + if (USR.empty()) { + unsigned NumDiags = clang_getNumDiagnosticsInSet(Diags); + for (unsigned DiagID = 0; DiagID < NumDiags; ++DiagID) { + CXDiagnostic Diag = clang_getDiagnosticInSet(Diags, DiagID); + CXString Spelling = clang_getDiagnosticSpelling(Diag); + errs() << clang_getCString(Spelling) << "\n"; + clang_disposeString(Spelling); + } + } + clang_disposeDiagnosticSet(Diags); + return 1; + } + clang_disposeDiagnosticSet(Diags); + + if (ImplementationTU.load(RenamingAction, CIdx, Args)) + return 1; + + Err = clang_Refactoring_initiateRenamingOperation(RenamingAction); + if (Err != CXError_Success) { + errs() << "error: failed to initiate the renaming operation!\n"; + return 1; + } + + bool IsLocal = clang_RefactoringAction_getInitiatedActionType( + RenamingAction) == CXRefactor_Rename_Local; + + unsigned NumSymbols = clang_RenamingOperation_getNumSymbols(RenamingAction); + if (opts::rename::DumpSymbols) { + outs() << "Renaming " << NumSymbols << " symbols\n"; + for (unsigned I = 0; I < NumSymbols; ++I) { + CXString USR = + clang_RenamingOperation_getUSRForSymbol(RenamingAction, I); + outs() << "'" << clang_getCString(USR) << "'\n"; + clang_disposeString(USR); + } + } + + CXSymbolOccurrencesResult Occurrences; + Occurrences = clang_Refactoring_findSymbolOccurrencesInInitiationTU( + RenamingAction, Args.data(), Args.size(), 0, 0); + + clang_RefactoringAction_dispose(RenamingAction); + + // FIXME: This is a hack + LangOptions LangOpts; + LangOpts.ObjC1 = true; + tooling::SymbolName NewSymbolName(opts::rename::NewName, LangOpts); + + if (ExpectedReplacements.empty()) { + if (opts::Apply) { + // FIXME: support --apply. + } + + unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Occurrences); + if (NumFiles > 1) + PrintFilenames = true; + // Convert the occurrences to strings + for (unsigned FileIndex = 0; FileIndex < NumFiles; ++FileIndex) { + CXSymbolOccurrencesInFile FileResult; + clang_SymbolOccurrences_getOccurrencesForFile(Occurrences, FileIndex, + &FileResult); + StringRef Filename = + PrintFilenames ? clang_getCString(FileResult.Filename) : ""; + for (unsigned I = 0; I != FileResult.NumOccurrences; ++I) + ExpectedReplacements.push_back( + occurrenceToString(FileResult.Occurrences[I], IsLocal, + NewSymbolName, NewSymbolName, Filename)); + } + clang_SymbolOccurrences_dispose(Occurrences); + return 0; + } + // Compare the produced occurrences to the expected occurrences that were + // gathered at the first location. + bool AreOccurrencesDifferent = + compareOccurrences(ExpectedReplacements, Occurrences, IsLocal, + NewSymbolName, PrintFilenames); + clang_SymbolOccurrences_dispose(Occurrences); + if (!AreOccurrencesDifferent) + return 0; + errs() << "error: occurrences for a rename at " << Location.FileName << ":" + << Location.Line << ":" << Location.Column + << " differ to occurrences from the rename at the first location!\n"; + return 1; + }; + + std::vector<ParsedSourceLocation> ParsedLocations; + for (const auto &I : enumerate(opts::rename::AtLocation)) { + auto Location = ParsedSourceLocation::FromString(I.value()); + if (Location.FileName.empty()) { + errs() + << "error: The -at option must use the <file:line:column> format\n"; + return 1; + } + ParsedLocations.push_back(Location); + } + + if (opts::RenameInitiateUSRSubcommand) { + if (RenameAt(ParsedSourceLocation(), opts::rename::USR)) + return 1; + } else { + assert(!ParsedLocations.empty() && "No -at locations"); + + for (const auto &Location : ParsedLocations) { + if (RenameAt(Location, "")) + return 1; + } + } + + // Print the produced renamed replacements + if (opts::Apply) + return 0; + for (const auto &Replacement : ExpectedReplacements) + outs() << Replacement << "\n"; + if (ExpectedReplacements.empty()) + outs() << "no replacements found\n"; + return 0; +} + +int renameIndexedFile(CXIndex CIdx, ArrayRef<const char *> Args) { + assert(opts::RenameIndexedFileSubcommand); + + // Compute the number of symbols. + unsigned NumSymbols = opts::rename::IndexedNames.size(); + + // Get the occurrences of a symbol. + CXCursorKind DefaultCursorKind = renameIndexedOccurrenceKindStringToKind( + opts::rename::IndexedSymbolKind, CXCursor_NotImplemented); + std::vector<std::vector<CXIndexedSymbolLocation>> IndexedOccurrences( + NumSymbols, std::vector<CXIndexedSymbolLocation>()); + for (const auto &IndexedOccurrence : opts::rename::IndexedLocations) { + auto Occurrence = + parseIndexedOccurrence(IndexedOccurrence, DefaultCursorKind); + unsigned SymbolIndex = Occurrence.second; + assert(SymbolIndex < IndexedOccurrences.size() && "Invalid symbol index"); + IndexedOccurrences[SymbolIndex].push_back(CXIndexedSymbolLocation{ + Occurrence.first.Location, Occurrence.first.CursorKind}); + } + + // Create the indexed symbols. + std::vector<CXIndexedSymbol> IndexedSymbols; + for (const auto &I : llvm::enumerate(IndexedOccurrences)) { + const auto &Occurrences = I.value(); + const char *Name = + opts::rename::IndexedNames[opts::rename::IndexedNames.size() < 2 + ? 0 + : I.index()] + .c_str(); + IndexedSymbols.push_back({Occurrences.data(), (unsigned)Occurrences.size(), + DefaultCursorKind, Name}); + } + + CXRefactoringOptionSet Options = nullptr; + if (opts::rename::AvoidTextual) { + Options = clang_RefactoringOptionSet_create(); + clang_RefactoringOptionSet_add(Options, + CXRefactorOption_AvoidTextualMatches); + } + + CXSymbolOccurrencesResult Occurrences; + CXErrorCode Err = clang_Refactoring_findSymbolOccurrencesInIndexedFile( + IndexedSymbols.data(), IndexedSymbols.size(), CIdx, + opts::rename::IndexedFileName.c_str(), Args.data(), Args.size(), 0, 0, + Options, &Occurrences); + if (Err != CXError_Success) { + if (reportNewNameError(Err)) + errs() << "error: failed to perform indexed file rename\n"; + return 1; + } + + if (Options) + clang_RefactoringOptionSet_dispose(Options); + + // Should we print out the filenames. False by default, but true when multiple + // files are modified. + bool PrintFilenames = false; + unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Occurrences); + if (NumFiles > 1) + PrintFilenames = true; + + LangOptions LangOpts; + LangOpts.ObjC1 = true; + tooling::SymbolName ExpectedReplacementStrings( + opts::rename::IndexedNewNames[0], LangOpts); + + // Print the occurrences. + bool HasReplacements = false; + for (unsigned FileIndex = 0; FileIndex < NumFiles; ++FileIndex) { + CXSymbolOccurrencesInFile FileResult; + clang_SymbolOccurrences_getOccurrencesForFile(Occurrences, FileIndex, + &FileResult); + StringRef Filename = + PrintFilenames ? clang_getCString(FileResult.Filename) : ""; + HasReplacements = FileResult.NumOccurrences; + for (unsigned I = 0; I != FileResult.NumOccurrences; ++I) { + unsigned SymbolIndex = FileResult.Occurrences[I].SymbolIndex; + const char *NewName = + opts::rename::IndexedNewNames[opts::rename::IndexedNewNames.size() < 2 + ? 0 + : SymbolIndex] + .c_str(); + LangOptions LangOpts; + LangOpts.ObjC1 = true; + tooling::SymbolName NewSymbolName(NewName, LangOpts); + + outs() << occurrenceToString(FileResult.Occurrences[I], /*IsLocal*/ false, + NewSymbolName, ExpectedReplacementStrings, + Filename) + << "\n"; + } + } + if (!HasReplacements) + outs() << "no replacements found\n"; + clang_SymbolOccurrences_dispose(Occurrences); + return 0; +} + +/// Returns the last column number of a line in a file. +static unsigned lastColumnForFile(StringRef Filename, unsigned LineNo) { + auto Buf = llvm::MemoryBuffer::getFile(Filename); + if (!Buf) + return 0; + unsigned LineCount = 1; + for (llvm::line_iterator Lines(**Buf, /*SkipBlanks=*/false); + !Lines.is_at_end(); ++Lines, ++LineCount) { + if (LineNo == LineCount) + return Lines->size() + 1; + } + return 0; +} + +struct ParsedSourceLineRange : ParsedSourceLocation { + unsigned MaxColumn; + + ParsedSourceLineRange() {} + ParsedSourceLineRange(const ParsedSourceLocation &Loc) + : ParsedSourceLocation(Loc), MaxColumn(Loc.Column) {} + + static Optional<ParsedSourceLineRange> FromString(StringRef Str) { + std::pair<StringRef, StringRef> RangeSplit = Str.rsplit('-'); + auto PSL = ParsedSourceLocation::FromString(RangeSplit.first); + ParsedSourceLineRange Result; + Result.FileName = std::move(PSL.FileName); + Result.Line = PSL.Line; + Result.Column = PSL.Column; + if (Result.FileName.empty()) + return None; + if (RangeSplit.second == "end") + Result.MaxColumn = lastColumnForFile(Result.FileName, Result.Line); + else if (RangeSplit.second.getAsInteger(10, Result.MaxColumn)) + return None; + if (Result.MaxColumn < Result.Column) + return None; + return Result; + } +}; + +struct ParsedSourceRange { + ParsedSourceLocation Begin, End; + + ParsedSourceRange(const ParsedSourceLocation &Begin, + const ParsedSourceLocation &End) + : Begin(Begin), End(End) {} + + static Optional<ParsedSourceRange> FromString(StringRef Str) { + std::pair<StringRef, StringRef> RangeSplit = Str.rsplit('-'); + auto Begin = ParsedSourceLocation::FromString(RangeSplit.first); + if (Begin.FileName.empty()) + return None; + std::string EndString = Begin.FileName + ":" + RangeSplit.second.str(); + auto End = ParsedSourceLocation::FromString(EndString); + if (End.FileName.empty()) + return None; + return ParsedSourceRange(Begin, End); + } +}; + +int listRefactoringActions(CXTranslationUnit TU) { + auto Location = + ParsedSourceLocation::FromString(opts::listActions::AtLocation); + if (Location.FileName.empty()) { + errs() << "error: The -at option must use the <file:line:column> format\n"; + return 1; + } + CXSourceRange Range; + if (!opts::listActions::SelectedRange.empty()) { + auto SelectionRange = + ParsedSourceRange::FromString(opts::listActions::SelectedRange); + if (!SelectionRange) { + errs() << "error: The -selected option must use the " + "<file:line:column-line:column> format\n"; + return 1; + } + auto Begin = SelectionRange.getValue().Begin; + auto End = SelectionRange.getValue().End; + CXFile File = clang_getFile(TU, Begin.FileName.c_str()); + Range = + clang_getRange(clang_getLocation(TU, File, Begin.Line, Begin.Column), + clang_getLocation(TU, File, End.Line, End.Column)); + } else + Range = clang_getNullRange(); + CXSourceLocation Loc = + clang_getLocation(TU, clang_getFile(TU, Location.FileName.c_str()), + Location.Line, Location.Column); + CXRefactoringActionSet ActionSet; + CXRefactoringActionSetWithDiagnostics FailedActionSet; + CXErrorCode Err = + clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( + TU, Loc, Range, /*Options=*/nullptr, &ActionSet, &FailedActionSet); + if (FailedActionSet.NumActions) { + errs() << "Failed to initiate " << FailedActionSet.NumActions + << " actions because:\n"; + for (unsigned I = 0; I < FailedActionSet.NumActions; ++I) { + errs() << clang_getCString(clang_RefactoringActionType_getName( + FailedActionSet.Actions[I].Action)) + << ":"; + CXDiagnosticSet Diags = FailedActionSet.Actions[I].Diagnostics; + unsigned NumDiags = clang_getNumDiagnosticsInSet(Diags); + for (unsigned DiagID = 0; DiagID < NumDiags; ++DiagID) { + CXDiagnostic Diag = clang_getDiagnosticInSet(Diags, DiagID); + CXString Spelling = clang_getDiagnosticSpelling(Diag); + errs() << ' ' << clang_getCString(Spelling); + clang_disposeString(Spelling); + } + errs() << "\n"; + } + } + if (Err == CXError_RefactoringActionUnavailable) + errs() << "No refactoring actions are available at the given location\n"; + if (Err != CXError_Success) + return 1; + // Print the list of refactoring actions. + outs() << "Found " << ActionSet.NumActions << " actions:\n"; + for (unsigned I = 0; I < ActionSet.NumActions; ++I) { + outs() << clang_getCString( + clang_RefactoringActionType_getName(ActionSet.Actions[I])); + if (opts::listActions::DumpRawActionType) + outs() << "(" << ActionSet.Actions[I] << ")"; + outs() << "\n"; + } + clang_RefactoringActionSet_dispose(&ActionSet); + clang_RefactoringActionSetWithDiagnostics_dispose(&FailedActionSet); + return 0; +} + +static std::string locationToString(CXSourceLocation Loc) { + unsigned Line, Column; + clang_getFileLocation(Loc, nullptr, &Line, &Column, nullptr); + std::string S; + llvm::raw_string_ostream OS(S); + OS << Line << ':' << Column; + return OS.str(); +} + +static std::string rangeToString(CXSourceRange Range) { + return locationToString(clang_getRangeStart(Range)) + " -> " + + locationToString(clang_getRangeEnd(Range)); +} + +static std::string +refactoringCandidatesToString(CXRefactoringCandidateSet Candidates) { + std::string Results = "with multiple candidates:"; + for (unsigned I = 0; I < Candidates.NumCandidates; ++I) { + Results += "\n"; + Results += clang_getCString(Candidates.Candidates[I].Description); + } + return Results; +} + +static void printEscaped(StringRef Str, raw_ostream &OS) { + size_t Pos = Str.find('\n'); + OS << Str.substr(0, Pos); + if (Pos == StringRef::npos) + return; + OS << "\\n"; + printEscaped(Str.substr(Pos + 1), OS); +} + +bool printRefactoringReplacements( + CXRefactoringResult Result, CXRefactoringContinuation Continuation, + CXRefactoringContinuation CurrentContinuation) { + CXRefactoringReplacements Replacements = + clang_RefactoringResult_getSourceReplacements(Result); + if (Replacements.NumFileReplacementSets == 0) { + if (CurrentContinuation) + return false; + errs() << "error: no replacements produced!\n"; + return true; + } + // Print out the produced results. + for (unsigned FileIndex = 0; FileIndex < Replacements.NumFileReplacementSets; + ++FileIndex) { + const CXRefactoringFileReplacementSet &FileSet = + Replacements.FileReplacementSets[FileIndex]; + if (opts::Apply) { + apply(llvm::makeArrayRef(FileSet.Replacements, FileSet.NumReplacements), + clang_getCString(FileSet.Filename)); + continue; + } + for (unsigned I = 0; I < FileSet.NumReplacements; ++I) { + const CXRefactoringReplacement &Replacement = FileSet.Replacements[I]; + + if (Continuation) { + // Always print the filenames in with continuations. + outs() << '"' << clang_getCString(FileSet.Filename) << "\" "; + } + outs() << '"'; + printEscaped(clang_getCString(Replacement.ReplacementString), outs()); + outs() << "\" "; + CXFileRange Range = Replacement.Range; + outs() << Range.Begin.Line << ":" << Range.Begin.Column << " -> " + << Range.End.Line << ":" << Range.End.Column; + if (opts::initiateAndPerform::EmitAssociatedInfo) { + CXRefactoringReplacementAssociatedSymbolOccurrences Info = + clang_RefactoringReplacement_getAssociatedSymbolOccurrences( + Replacement); + for (const CXSymbolOccurrence &SymbolOccurrence : + llvm::makeArrayRef(Info.AssociatedSymbolOccurrences, + Info.NumAssociatedSymbolOccurrences)) { + outs() << " [Symbol " << renameOccurrenceKindString( + SymbolOccurrence.Kind, /*IsLocal*/ false, + SymbolOccurrence.IsMacroExpansion) + << ' ' << SymbolOccurrence.SymbolIndex; + for (const auto &Piece : + llvm::makeArrayRef(SymbolOccurrence.NamePieces, + SymbolOccurrence.NumNamePieces)) { + outs() << ' ' << Piece.Begin.Line << ":" << Piece.Begin.Column + << " -> " << Piece.End.Line << ":" << Piece.End.Column; + } + outs() << ']'; + } + } + outs() << "\n"; + } + } + return false; +} + +/// Returns the last column number of a line in a file. +static std::string queryResultsForFile(StringRef Filename, StringRef Name, + StringRef FileSubstitution) { + auto Buf = llvm::MemoryBuffer::getFile(Filename); + if (!Buf) + return "<invalid>"; + StringRef Buffer = (*Buf)->getBuffer(); + std::string Label = Name.str() + ":"; + size_t I = Buffer.find(Label); + if (I == StringRef::npos) + return "<invalid>"; + I = I + Label.size(); + auto Result = Buffer.substr(I, Buffer.find('\n', I) - I); + std::string Sub1 = llvm::Regex("%s").sub(FileSubstitution, Result); + return llvm::Regex("%S").sub(llvm::sys::path::parent_path(FileSubstitution), + Sub1); +} + +static Optional<std::pair<unsigned, unsigned>> +findSelectionLocInSource(StringRef Buffer, StringRef Label) { + size_t I = Buffer.find(Label); + if (I == StringRef::npos) + return None; + I = I + Label.size(); + auto LocParts = + Buffer.substr(I, Buffer.find_first_of("\n/", I) - I).trim().split(":"); + unsigned CurrentLine = Buffer.take_front(I).count('\n') + 1; + if (LocParts.second.empty()) + return None; + StringRef LineString = LocParts.first; + unsigned Line, Column; + enum ExprKind { Literal, Add, Sub }; + ExprKind Expr = LineString.startswith("+") + ? Add + : LineString.startswith("-") ? Sub : Literal; + if (LineString.drop_front(Expr != Literal ? 1 : 0).getAsInteger(10, Line)) + return None; + if (Expr == Add) + Line += CurrentLine; + else if (Expr == Sub) + Line = CurrentLine - Line; + if (LocParts.second.getAsInteger(10, Column)) + return None; + return std::make_pair(Line, Column); +} + +static Optional<ParsedSourceLocation> selectionLocForFile(StringRef Filename, + StringRef Name) { + auto Buf = llvm::MemoryBuffer::getFile(Filename); + if (!Buf) + return None; + + StringRef Buffer = (*Buf)->getBuffer(); + std::string Label = Name.str() + ":"; + auto Start = findSelectionLocInSource(Buffer, Label); + if (!Start) + return None; + // Create the resulting source location. + // FIXME: Parse can be avoided. + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << Filename << ":" << Start->first << ":" << Start->second; + return ParsedSourceLocation::FromString(OS.str()); +} + +static Optional<ParsedSourceRange> selectionRangeForFile(StringRef Filename, + StringRef Name) { + auto Buf = llvm::MemoryBuffer::getFile(Filename); + if (!Buf) + return None; + + StringRef Buffer = (*Buf)->getBuffer(); + std::string BeginLabel = Name.str() + "-begin:"; + std::string EndLabel = Name.str() + "-end:"; + auto Start = findSelectionLocInSource(Buffer, BeginLabel); + auto End = findSelectionLocInSource(Buffer, EndLabel); + if (!Start || !End) + return None; + // Create the resulting source range. + // FIXME: Parse can be avoided. + std::string Str; + llvm::raw_string_ostream OS(Str); + OS << Filename << ":" << Start->first << ":" << Start->second << "-" + << End->first << ":" << End->second; + return ParsedSourceRange::FromString(OS.str()); +} + +bool performOperation(CXRefactoringAction Action, ArrayRef<const char *> Args, + CXIndex CIdx) { + if (opts::initiateAndPerform::CandidateIndex.getNumOccurrences()) { + if (clang_RefactoringAction_selectRefactoringCandidate( + Action, opts::initiateAndPerform::CandidateIndex)) { + errs() << "error: failed to select the refactoring candidate!\n"; + return true; + } + } + CXRefactoringOptionSet Options = nullptr; + CXString FailureReason; + CXRefactoringResult Result = clang_Refactoring_performOperation( + Action, Args.data(), Args.size(), nullptr, 0, Options, &FailureReason); + if (!Result) { + errs() << "error: failed to perform the refactoring operation"; + if (const char *Reason = clang_getCString(FailureReason)) + errs() << " (" << Reason << ')'; + errs() << "!\n"; + clang_disposeString(FailureReason); + return true; + } + CXRefactoringContinuation Continuation = + clang_RefactoringResult_getContinuation(Result); + bool AreReplacementsInvalid = + printRefactoringReplacements(Result, Continuation, Continuation); + clang_RefactoringResult_dispose(Result); + if (AreReplacementsInvalid) { + clang_RefactoringContinuation_dispose(Continuation); + return true; + } + if (!Continuation) + return false; + assert(clang_RefactoringContinuation_getNumIndexerQueries(Continuation) != + 0 && + "Missing indexer queries?"); + std::string QueryResults = queryResultsForFile( + opts::FileName, opts::initiateAndPerform::QueryResults, + /*FileSubstitution=*/opts::initiateAndPerform::ContinuationFile); + clang_RefactoringContinuation_loadSerializedIndexerQueryResults( + Continuation, /*Source=*/QueryResults.c_str()); + clang_RefactoringContinuation_finalizeEvaluationInInitationTU(Continuation); + // Load the continuation TU. + CXTranslationUnit ContinuationTU; + CXErrorCode Err = clang_parseTranslationUnit2( + CIdx, opts::initiateAndPerform::ContinuationFile.c_str(), Args.data(), + Args.size(), 0, 0, CXTranslationUnit_KeepGoing, &ContinuationTU); + if (Err != CXError_Success) { + errs() << "error: failed to load '" + << opts::initiateAndPerform::ContinuationFile.c_str() << "'\n"; + clang_RefactoringContinuation_dispose(Continuation); + return true; + } + Result = clang_RefactoringContinuation_continueOperationInTU( + Continuation, ContinuationTU, &FailureReason); + if (!Result) { + errs() << "error: failed to perform the refactoring continuation"; + if (const char *Reason = clang_getCString(FailureReason)) + errs() << " (" << Reason << ')'; + errs() << "!\n"; + clang_disposeString(FailureReason); + clang_disposeTranslationUnit(ContinuationTU); + clang_RefactoringContinuation_dispose(Continuation); + return true; + } + // FIXME: Continuations can be chained in the future. + AreReplacementsInvalid = + printRefactoringReplacements(Result, Continuation, nullptr); + clang_RefactoringResult_dispose(Result); + clang_disposeTranslationUnit(ContinuationTU); + clang_RefactoringContinuation_dispose(Continuation); + return AreReplacementsInvalid; +} + +int initiateAndPerformAction(CXTranslationUnit TU, ArrayRef<const char *> Args, + CXIndex CIdx) { + std::vector<ParsedSourceLineRange> Ranges; + std::vector<ParsedSourceRange> SelectionRanges; + for (const auto &Range : opts::initiateAndPerform::InLocationRanges) { + auto ParsedLineRange = ParsedSourceLineRange::FromString(Range); + if (!ParsedLineRange) { + errs() + << "error: The -in option must use the <file:line:column[-column]> " + "format\n"; + return 1; + } + Ranges.push_back(ParsedLineRange.getValue()); + } + for (const auto &Range : opts::initiateAndPerform::AtLocations) { + if (!StringRef(Range).contains(':')) { + auto ParsedLocation = selectionLocForFile(opts::FileName, Range); + if (!ParsedLocation) { + errs() << "error: The -at option must use the <file:line:column> " + "format\n"; + return 1; + } + Ranges.push_back(*ParsedLocation); + continue; + } + // TODO: Remove old location in arguments in favour of new testing + // locations. + auto ParsedLocation = ParsedSourceLocation::FromString(Range); + if (ParsedLocation.FileName.empty()) { + errs() << "error: The -at option must use the <file:line:column> " + "format\n"; + return 1; + } + Ranges.push_back(ParsedLocation); + } + for (const auto &Range : opts::initiateAndPerform::SelectedRanges) { + auto ParsedRange = StringRef(Range).contains(':') + ? ParsedSourceRange::FromString(Range) + : selectionRangeForFile(opts::FileName, Range); + if (!ParsedRange) { + errs() << "error: The -selected option must use the " + "<file:line:column-line:column> format or refer to the name of " + "the selection specifier in the source\n"; + return 1; + } + SelectionRanges.push_back(ParsedRange.getValue()); + } + if (Ranges.empty() && SelectionRanges.empty()) { + errs() << "error: -in or -at options must be specified at least once!"; + return 1; + } + if (!Ranges.empty() && !SelectionRanges.empty()) { + errs() << "error: -in or -at options can't be used with -selected!"; + return 1; + } + + auto ActionTypeOrNone = StringSwitch<Optional<CXRefactoringActionType>>( + opts::initiateAndPerform::ActionName) +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \ + .Case(Command, CXRefactor_##Name) +#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \ + .Case(Command, CXRefactor_##Parent##_##Name) +#include "clang/Tooling/Refactor/RefactoringActions.def" + .Default(None); + if (!ActionTypeOrNone) { + errs() << "error: invalid action '" << opts::initiateAndPerform::ActionName + << "'\n"; + return 1; + } + CXRefactoringActionType ActionType = *ActionTypeOrNone; + + Optional<bool> Initiated; + Optional<std::string> InitiationFailureReason; + Optional<std::string> LocationCandidateInformation; + auto InitiateAndPerform = + [&](const ParsedSourceLocation &Location, unsigned Column, + Optional<ParsedSourceRange> SelectionRange = None) -> bool { + CXSourceLocation Loc = + clang_getLocation(TU, clang_getFile(TU, Location.FileName.c_str()), + Location.Line, Column); + CXSourceRange Range; + if (SelectionRange) { + auto Begin = SelectionRange.getValue().Begin; + auto End = SelectionRange.getValue().End; + CXFile File = clang_getFile(TU, Begin.FileName.c_str()); + Range = + clang_getRange(clang_getLocation(TU, File, Begin.Line, Begin.Column), + clang_getLocation(TU, File, End.Line, End.Column)); + } else + Range = clang_getNullRange(); + CXRefactoringAction Action; + CXString FailureReason; + CXErrorCode Err = clang_Refactoring_initiateActionAt( + TU, Loc, Range, ActionType, /*Options=*/nullptr, &Action, + &FailureReason); + std::string ReasonString; + if (const char *Reason = clang_getCString(FailureReason)) + ReasonString = Reason; + clang_disposeString(FailureReason); + if (InitiationFailureReason.hasValue() && + InitiationFailureReason.getValue() != ReasonString) { + errs() << "error: inconsistent results in a single action range!\n"; + return true; + } + InitiationFailureReason = std::move(ReasonString); + if (Err == CXError_RefactoringActionUnavailable) { + if (Initiated.hasValue() && Initiated.getValue()) { + errs() << "error: inconsistent results in a single action range!\n"; + return true; + } + Initiated = false; + } else if (Err != CXError_Success) + return true; + else if (Initiated.hasValue() && !Initiated.getValue()) { + errs() << "error: inconsistent results in a single action range!\n"; + return true; + } else + Initiated = true; + + CXRefactoringCandidateSet Candidates; + if (clang_RefactoringAction_getRefactoringCandidates(Action, &Candidates) == + CXError_Success && + Candidates.NumCandidates > 1) { + std::string CandidateString = refactoringCandidatesToString(Candidates); + if (LocationCandidateInformation) { + if (*LocationCandidateInformation != CandidateString) { + errs() << "error: inconsistent results in a single action range!\n"; + return true; + } + } else + LocationCandidateInformation = CandidateString; + } else if (opts::InitiateActionSubcommand && + !opts::initiateAndPerform::LocationAgnostic) { + CXSourceRange Range = + clang_RefactoringAction_getSourceRangeOfInterest(Action); + std::string LocationString = + std::string("at ") + + (!clang_Range_isNull(Range) + ? SelectionRange ? rangeToString(Range) + : locationToString(clang_getRangeStart(Range)) + : "<unknown>"); + if (!LocationCandidateInformation.hasValue()) + LocationCandidateInformation = LocationString; + else if (LocationCandidateInformation.getValue() != LocationString) { + errs() << "error: inconsistent results in a single action range!\n"; + return true; + } + } + + if (!*Initiated) + return false; + + bool Failed = opts::PerformActionSubcommand + ? performOperation(Action, Args, CIdx) + : false; + clang_RefactoringAction_dispose(Action); + return Failed; + }; + + // Iterate over all of the possible locations and perform the initiation + // at each range. + for (const ParsedSourceLineRange &LineRange : Ranges) { + for (unsigned Column = LineRange.Column; Column <= LineRange.MaxColumn; + ++Column) { + if (InitiateAndPerform(LineRange, Column)) + return 1; + } + } + + for (const ParsedSourceRange &SelectionRange : SelectionRanges) { + if (InitiateAndPerform(SelectionRange.Begin, SelectionRange.Begin.Column, + SelectionRange)) + return 1; + } + + if (!Initiated.getValue()) { + errs() << "Failed to initiate the refactoring action"; + if (InitiationFailureReason.hasValue() && + !InitiationFailureReason.getValue().empty()) + errs() << " (" << InitiationFailureReason.getValue() << ')'; + errs() << "!\n"; + return 1; + } + if (opts::InitiateActionSubcommand) { + outs() << "Initiated the '" << opts::initiateAndPerform::ActionName + << "' action"; + if (!opts::initiateAndPerform::LocationAgnostic) + outs() << ' ' << LocationCandidateInformation.getValue(); + outs() << "\n"; + } + return 0; +} + +int main(int argc, const char **argv) { + cl::HideUnrelatedOptions(opts::ClangRefactorTestOptions); + + cl::ParseCommandLineOptions(argc, argv, "Clang refactoring test tool\n"); + cl::PrintOptionValues(); + + CXIndex CIdx = clang_createIndex(0, 0); + + std::vector<const char *> Args; + for (const auto &Arg : opts::CompilerArguments) { + Args.push_back(Arg.c_str()); + } + CXTranslationUnit TU; + CXErrorCode Err = clang_parseTranslationUnit2( + CIdx, + opts::IgnoreFilenameForInitiationTU ? nullptr : opts::FileName.c_str(), + Args.data(), Args.size(), 0, 0, CXTranslationUnit_KeepGoing, &TU); + if (Err != CXError_Success) { + errs() << "error: failed to load '" << opts::FileName << "'\n"; + return 1; + } + + if (opts::RenameInitiateSubcommand || opts::RenameInitiateUSRSubcommand) + return rename(TU, CIdx, Args); + else if (opts::RenameIndexedFileSubcommand) + return renameIndexedFile(CIdx, Args); + else if (opts::ListRefactoringActionsSubcommand) + return listRefactoringActions(TU); + else if (opts::InitiateActionSubcommand || opts::PerformActionSubcommand) + return initiateAndPerformAction(TU, Args, CIdx); + + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + + return 0; +} diff --git a/clang/tools/diagtool/DiagnosticNames.cpp b/clang/tools/diagtool/DiagnosticNames.cpp index a08da89577f1b..1f894898b3dea 100644 --- a/clang/tools/diagtool/DiagnosticNames.cpp +++ b/clang/tools/diagtool/DiagnosticNames.cpp @@ -41,6 +41,7 @@ static const DiagnosticRecord BuiltinDiagnosticsByID[] = { #include "clang/Basic/DiagnosticCommentKinds.inc" #include "clang/Basic/DiagnosticSemaKinds.inc" #include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" #undef DIAG }; diff --git a/clang/tools/libclang/CIndexDiagnostic.cpp b/clang/tools/libclang/CIndexDiagnostic.cpp index de223d3043a31..4cf1d18e5b3a0 100644 --- a/clang/tools/libclang/CIndexDiagnostic.cpp +++ b/clang/tools/libclang/CIndexDiagnostic.cpp @@ -152,7 +152,20 @@ class CXDiagnosticRenderer : public DiagnosticNoteRenderer { CXDiagnosticSetImpl *CurrentSet; CXDiagnosticSetImpl *MainSet; -}; +}; + +class CXStoredDiagnosticSet : public CXDiagnosticSetImpl { + llvm::SmallVector<StoredDiagnostic, 2> Diags; + +public: + CXStoredDiagnosticSet(ArrayRef<StoredDiagnostic> Diags, + const LangOptions &LangOpts) + : CXDiagnosticSetImpl(/*isManaged=*/true), + Diags(Diags.begin(), Diags.end()) { + for (const auto &Diag : this->Diags) + appendDiagnostic(llvm::make_unique<CXStoredDiagnostic>(Diag, LangOpts)); + } +}; } CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU, @@ -202,6 +215,11 @@ CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU, return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics); } +CXDiagnosticSetImpl *cxdiag::createStoredDiags(ArrayRef<StoredDiagnostic> Diags, + const LangOptions &LangOpts) { + return new CXStoredDiagnosticSet(Diags, LangOpts); +} + //----------------------------------------------------------------------------- // C Interface Routines //----------------------------------------------------------------------------- diff --git a/clang/tools/libclang/CIndexDiagnostic.h b/clang/tools/libclang/CIndexDiagnostic.h index 9f406987ebf1a..468f9bcde6227 100644 --- a/clang/tools/libclang/CIndexDiagnostic.h +++ b/clang/tools/libclang/CIndexDiagnostic.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_TOOLS_LIBCLANG_CINDEXDIAGNOSTIC_H #include "clang-c/Index.h" +#include "clang/Basic/LLVM.h" #include <memory> #include <vector> #include <assert.h> @@ -158,6 +159,9 @@ struct CXStoredDiagnostic : public CXDiagnosticImpl { namespace cxdiag { CXDiagnosticSetImpl *lazyCreateDiags(CXTranslationUnit TU, bool checkIfChanged = false); + +CXDiagnosticSetImpl *createStoredDiags(ArrayRef<StoredDiagnostic> Diags, + const LangOptions &LangOpts); } // end namespace cxdiag } // end namespace clang diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt index 5cad9dc3b3c67..dbad06aecfece 100644 --- a/clang/tools/libclang/CMakeLists.txt +++ b/clang/tools/libclang/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES CIndexInclusionStack.cpp CIndexUSRs.cpp CIndexer.cpp + CRefactor.cpp CXComment.cpp CXCursor.cpp CXIndexDataConsumer.cpp diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp new file mode 100644 index 0000000000000..7331c3a53abb0 --- /dev/null +++ b/clang/tools/libclang/CRefactor.cpp @@ -0,0 +1,1917 @@ +//===- CRefactor.cpp - Refactoring API hooks ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Clang-C refactoring library. +// +//===----------------------------------------------------------------------===// + +#include "CIndexDiagnostic.h" +#include "CIndexer.h" +#include "CLog.h" +#include "CXCursor.h" +#include "CXSourceLocation.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "clang-c/Refactor.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/DiagnosticCategories.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/Utils.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Tooling/Refactor/IndexerQuery.h" +#include "clang/Tooling/Refactor/RefactoringActionFinder.h" +#include "clang/Tooling/Refactor/RefactoringActions.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "clang/Tooling/Refactor/RefactoringOptions.h" +#include "clang/Tooling/Refactor/RenameIndexedFile.h" +#include "clang/Tooling/Refactor/RenamingOperation.h" +#include "clang/Tooling/Refactor/SymbolOccurrenceFinder.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringMap.h" +#include <set> +#include <vector> + +using namespace clang; +using namespace clang::tooling; + +static RefactoringActionType +translateRefactoringActionType(CXRefactoringActionType Action) { + switch (Action) { +#define REFACTORING_ACTION(Name, Spelling) \ + case CXRefactor_##Name: \ + return RefactoringActionType::Name; +#include "clang/Tooling/Refactor/RefactoringActions.def" + } +} + +static CXRefactoringActionType +translateRefactoringActionType(RefactoringActionType Action) { + switch (Action) { +#define REFACTORING_ACTION(Name, Spelling) \ + case RefactoringActionType::Name: \ + return CXRefactor_##Name; +#include "clang/Tooling/Refactor/RefactoringActions.def" + } +} + +static CXSymbolOccurrenceKind +translateOccurrenceKind(rename::SymbolOccurrence::OccurrenceKind Kind) { + switch (Kind) { + case rename::SymbolOccurrence::MatchingSymbol: + return CXSymbolOccurrence_MatchingSymbol; + case rename::SymbolOccurrence::MatchingSelector: + return CXSymbolOccurrence_MatchingSelector; + case rename::SymbolOccurrence::MatchingImplicitProperty: + return CXSymbolOccurrence_MatchingImplicitProperty; + case rename::SymbolOccurrence::MatchingComment: + return CXSymbolOccurrence_MatchingCommentString; + case rename::SymbolOccurrence::MatchingDocComment: + return CXSymbolOccurrence_MatchingDocCommentString; + case rename::SymbolOccurrence::MatchingFilename: + return CXSymbolOccurrence_MatchingFilename; + } +} + +namespace { + +// TODO: Remove +class RenamingResult { + struct RenamedNameString { + CXString NewString; + unsigned OldLength; + }; + typedef SmallVector<RenamedNameString, 4> SymbolNameInfo; + std::vector<SymbolNameInfo> NameInfo; + + /// The set of files that have to be modified. + llvm::SmallVector<CXString, 2> Filenames; + llvm::SpecificBumpPtrAllocator<CXRefactoringReplacement_Old> Replacements; + std::vector<std::vector<CXRenamedSymbolOccurrence>> Occurrences; + + void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, + const SourceManager &SM, const LangOptions &LangOpts) { + CXRefactoringReplacement_Old *OccurrenceReplacements = + Replacements.Allocate(RenamedOccurrence.Locations.size()); + + unsigned I = 0; + const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; + if (!RenamedOccurrence.IsMacroExpansion && + RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && + RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) + assert(RenamedOccurrence.Locations.size() == SymbolNameInfo.size()); + for (const auto &Location : RenamedOccurrence.Locations) { + CXSourceRange Range = cxloc::translateSourceRange( + SM, LangOpts, + CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange( + Location, SymbolNameInfo[I].OldLength))); + CXFileLocation Begin, End; + clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line, + &Begin.Column, nullptr); + clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, + &End.Column, nullptr); + + OccurrenceReplacements[I] = CXRefactoringReplacement_Old{ + {Begin, End}, + RenamedOccurrence.IsMacroExpansion ? cxstring::createNull() + : SymbolNameInfo[I].NewString}; + ++I; + } + + Occurrences.back().push_back(CXRenamedSymbolOccurrence{ + OccurrenceReplacements, I, + translateOccurrenceKind(RenamedOccurrence.Kind), + RenamedOccurrence.IsMacroExpansion}); + } + +public: + RenamingResult(ArrayRef<SymbolName> NewNames, + ArrayRef<rename::Symbol> Symbols) { + assert(NewNames.size() == Symbols.size()); + for (size_t I = 0, E = NewNames.size(); I != E; ++I) { + const auto &NewName = NewNames[I]; + const auto &OldName = Symbols[I].Name; + + assert(NewName.size() == OldName.size()); + SymbolNameInfo Info; + for (size_t I = 0, E = NewName.size(); I != E; ++I) + Info.push_back(RenamedNameString{cxstring::createDup(NewName[I]), + (unsigned)OldName[I].size()}); + NameInfo.push_back(std::move(Info)); + } + } + + // FIXME: Don't duplicate code, Use just one constructor. + RenamingResult(ArrayRef<SymbolName> NewNames, ArrayRef<SymbolName> OldNames) { + assert(NewNames.size() == OldNames.size()); + for (size_t I = 0, E = NewNames.size(); I != E; ++I) { + const auto &NewName = NewNames[I]; + const auto &OldName = OldNames[I]; + + assert(NewName.size() == OldName.size()); + SymbolNameInfo Info; + for (size_t I = 0, E = NewName.size(); I != E; ++I) + Info.push_back(RenamedNameString{cxstring::createDup(NewName[I]), + (unsigned)OldName[I].size()}); + NameInfo.push_back(std::move(Info)); + } + } + + ~RenamingResult() { + for (const auto &SymbolInfo : NameInfo) + for (const auto &NameString : SymbolInfo) + clang_disposeString(NameString.NewString); + for (const auto &Filename : Filenames) + clang_disposeString(Filename); + } + + void + handleTUResults(CXTranslationUnit TU, + llvm::MutableArrayRef<rename::SymbolOccurrence> Results) { + ASTUnit *Unit = cxtu::getASTUnit(TU); + assert(Unit && "Invalid TU"); + auto &Ctx = Unit->getASTContext(); + + // Find the set of files that have to be modified and gather the indices of + // the occurrences for each file. + const SourceManager &SM = Ctx.getSourceManager(); + typedef std::set<rename::SymbolOccurrence> OccurrenceSet; + llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; + for (auto &Occurrence : Results) { + const std::pair<FileID, unsigned> DecomposedLocation = + SM.getDecomposedLoc(Occurrence.Locations[0]); + const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); + assert(Entry && "Invalid file entry"); + auto &FileOccurrences = + FilenamesToSymbolOccurrences + .try_emplace(Entry->getName(), OccurrenceSet()) + .first->getValue(); + FileOccurrences.insert(std::move(Occurrence)); + } + + // Create the filenames + for (const auto &FilenameCount : FilenamesToSymbolOccurrences) + Filenames.push_back(cxstring::createDup(FilenameCount.getKey())); + + unsigned FileIndex = 0; + for (const auto &RenamedOccurrences : FilenamesToSymbolOccurrences) { + assert(clang_getCString(Filenames[FileIndex]) == + RenamedOccurrences.getKey() && + "Unstable iteration order"); + Occurrences.push_back(std::vector<CXRenamedSymbolOccurrence>()); + for (const auto &Occurrence : RenamedOccurrences.getValue()) + addOccurrence(Occurrence, SM, Ctx.getLangOpts()); + ++FileIndex; + } + } + + void addMainFilename(const SourceManager &SM) { + assert(Filenames.empty() && "Main filename should be added only once"); + Filenames.push_back(cxstring::createDup( + SM.getFileEntryForID(SM.getMainFileID())->getName())); + Occurrences.push_back(std::vector<CXRenamedSymbolOccurrence>()); + } + + void + handleSingleFileTUResults(const ASTContext &Ctx, + ArrayRef<rename::SymbolOccurrence> Occurrences) { + addMainFilename(Ctx.getSourceManager()); + for (const auto &Occurrence : Occurrences) + addOccurrence(Occurrence, Ctx.getSourceManager(), Ctx.getLangOpts()); + } + + void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence, + const SourceManager &SM, + const LangOptions &LangOpts) { + if (Filenames.empty()) { + addMainFilename(SM); + } + addOccurrence(Occurrence, SM, LangOpts); + } + + ArrayRef<CXRenamedSymbolOccurrence> getOccurrences(unsigned FileIndex) const { + return Occurrences[FileIndex]; + } + + ArrayRef<CXString> getFilenames() const { return Filenames; } +}; + +class SymbolOccurrencesResult { + struct SymbolNamePiece { + unsigned OldLength; + }; + typedef SmallVector<SymbolNamePiece, 4> SymbolNameInfo; + std::vector<SymbolNameInfo> NameInfo; + + /// The set of files that have to be modified. + llvm::SmallVector<CXString, 2> Filenames; + llvm::SpecificBumpPtrAllocator<CXFileRange> Ranges; + std::vector<std::vector<CXSymbolOccurrence>> SymbolOccurrences; + + void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, + const SourceManager &SM, const LangOptions &LangOpts) { + CXFileRange *OccurrenceRanges = + Ranges.Allocate(RenamedOccurrence.Locations.size()); + + unsigned I = 0; + const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; + if (!RenamedOccurrence.IsMacroExpansion && + RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && + RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) + assert(RenamedOccurrence.Locations.size() == SymbolNameInfo.size()); + for (const auto &Location : RenamedOccurrence.Locations) { + CXSourceRange Range = cxloc::translateSourceRange( + SM, LangOpts, + CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange( + Location, SymbolNameInfo[I].OldLength))); + CXFileLocation Begin, End; + clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line, + &Begin.Column, nullptr); + clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, + &End.Column, nullptr); + OccurrenceRanges[I] = CXFileRange{Begin, End}; + ++I; + } + + SymbolOccurrences.back().push_back(CXSymbolOccurrence{ + OccurrenceRanges, /*NumNamePieces=*/I, + translateOccurrenceKind(RenamedOccurrence.Kind), + RenamedOccurrence.IsMacroExpansion, RenamedOccurrence.SymbolIndex}); + } + +public: + SymbolOccurrencesResult(ArrayRef<rename::Symbol> Symbols) { + for (const auto &Symbol : Symbols) { + const SymbolName &Name = Symbol.Name; + SymbolNameInfo Info; + for (size_t I = 0, E = Name.size(); I != E; ++I) + Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()}); + NameInfo.push_back(std::move(Info)); + } + } + + SymbolOccurrencesResult(ArrayRef<SymbolName> Names) { + for (const SymbolName &Name : Names) { + SymbolNameInfo Info; + for (size_t I = 0, E = Name.size(); I != E; ++I) + Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()}); + NameInfo.push_back(std::move(Info)); + } + } + + ~SymbolOccurrencesResult() { + for (const auto &Filename : Filenames) + clang_disposeString(Filename); + } + + void + handleTUResults(CXTranslationUnit TU, + llvm::MutableArrayRef<rename::SymbolOccurrence> Results) { + ASTUnit *Unit = cxtu::getASTUnit(TU); + assert(Unit && "Invalid TU"); + auto &Ctx = Unit->getASTContext(); + + // Find the set of files that have to be modified and gather the indices of + // the occurrences for each file. + const SourceManager &SM = Ctx.getSourceManager(); + typedef std::set<rename::SymbolOccurrence> OccurrenceSet; + llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; + for (auto &Occurrence : Results) { + const std::pair<FileID, unsigned> DecomposedLocation = + SM.getDecomposedLoc(Occurrence.Locations[0]); + const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); + assert(Entry && "Invalid file entry"); + auto &FileOccurrences = + FilenamesToSymbolOccurrences + .try_emplace(Entry->getName(), OccurrenceSet()) + .first->getValue(); + FileOccurrences.insert(std::move(Occurrence)); + } + + // Create the filenames + for (const auto &FilenameCount : FilenamesToSymbolOccurrences) + Filenames.push_back(cxstring::createDup(FilenameCount.getKey())); + + unsigned FileIndex = 0; + for (const auto &RenamedOccurrences : FilenamesToSymbolOccurrences) { + assert(clang_getCString(Filenames[FileIndex]) == + RenamedOccurrences.getKey() && + "Unstable iteration order"); + SymbolOccurrences.push_back(std::vector<CXSymbolOccurrence>()); + for (const auto &Occurrence : RenamedOccurrences.getValue()) + addOccurrence(Occurrence, SM, Ctx.getLangOpts()); + ++FileIndex; + } + } + + void addMainFilename(const SourceManager &SM) { + assert(Filenames.empty() && "Main filename should be added only once"); + Filenames.push_back(cxstring::createDup( + SM.getFileEntryForID(SM.getMainFileID())->getName())); + SymbolOccurrences.push_back(std::vector<CXSymbolOccurrence>()); + } + + void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence, + const SourceManager &SM, + const LangOptions &LangOpts) { + if (Filenames.empty()) { + addMainFilename(SM); + } + addOccurrence(Occurrence, SM, LangOpts); + } + + ArrayRef<CXSymbolOccurrence> getOccurrences(unsigned FileIndex) const { + return SymbolOccurrences[FileIndex]; + } + + ArrayRef<CXString> getFilenames() const { return Filenames; } +}; + +class RenamingAction { +public: + LangOptions LangOpts; + IdentifierTable IDs; + // TODO: Remove + SmallVector<SymbolName, 4> NewNames; + SymbolOperation Operation; + + RenamingAction(const LangOptions &LangOpts, SymbolOperation Operation) + : LangOpts(LangOpts), IDs(LangOpts), Operation(std::move(Operation)) {} + + /// \brief Sets the new renaming name and returns CXError_Success on success. + // TODO: Remove + CXErrorCode setNewName(StringRef Name) { + SymbolName NewSymbolName(Name, LangOpts); + if (NewSymbolName.size() != Operation.symbols()[0].Name.size()) + return CXError_RefactoringNameSizeMismatch; + if (!rename::isNewNameValid(NewSymbolName, Operation, IDs, LangOpts)) + return CXError_RefactoringNameInvalid; + rename::determineNewNames(std::move(NewSymbolName), Operation, NewNames, + LangOpts); + return CXError_Success; + } + + // TODO: Remove + CXString usrForSymbolAt(unsigned Index) { + llvm::SmallVector<char, 128> Buff; + if (index::generateUSRForDecl(Operation.symbols()[Index].FoundDecl, Buff)) + return cxstring::createNull(); + return cxstring::createDup(StringRef(Buff.begin(), Buff.size())); + } + + // TODO: Remove + CXString getUSRThatRequiresImplementationTU() { + llvm::SmallVector<char, 128> Buff; + if (!Operation.requiresImplementationTU() || + index::generateUSRForDecl(Operation.declThatRequiresImplementationTU(), + Buff)) + return cxstring::createNull(); + return cxstring::createDup(StringRef(Buff.begin(), Buff.size())); + } + + // TODO: Remove + RenamingResult *handlePrimaryTU(CXTranslationUnit TU, ASTUnit &Unit) { + // Perform the renaming. + if (NewNames.empty()) + return nullptr; + + const ASTContext &Context = Unit.getASTContext(); + auto Occurrences = rename::findSymbolOccurrences( + Operation, Context.getTranslationUnitDecl()); + auto *Result = new RenamingResult(NewNames, Operation.symbols()); + Result->handleTUResults(TU, Occurrences); + return Result; + } + + SymbolOccurrencesResult *findSymbolsInInitiationTU(CXTranslationUnit TU, + ASTUnit &Unit) { + const ASTContext &Context = Unit.getASTContext(); + auto Occurrences = rename::findSymbolOccurrences( + Operation, Context.getTranslationUnitDecl()); + auto *Result = new SymbolOccurrencesResult(Operation.symbols()); + Result->handleTUResults(TU, Occurrences); + return Result; + } +}; + +static bool isObjCSelectorKind(CXCursorKind Kind) { + return Kind == CXCursor_ObjCInstanceMethodDecl || + Kind == CXCursor_ObjCClassMethodDecl || + Kind == CXCursor_ObjCMessageExpr; +} + +// TODO: Remove +static bool isObjCSelector(const CXRenamedIndexedSymbol &Symbol) { + if (isObjCSelectorKind(Symbol.CursorKind)) + return true; + for (const auto &Occurrence : llvm::makeArrayRef( + Symbol.IndexedLocations, Symbol.IndexedLocationCount)) { + if (isObjCSelectorKind(Occurrence.CursorKind)) + return true; + } + return false; +} + +static bool isObjCSelector(const CXIndexedSymbol &Symbol) { + if (isObjCSelectorKind(Symbol.CursorKind)) + return true; + for (const auto &Occurrence : llvm::makeArrayRef( + Symbol.IndexedLocations, Symbol.IndexedLocationCount)) { + if (isObjCSelectorKind(Occurrence.CursorKind)) + return true; + } + return false; +} + +// New names are initialized and verified after the LangOptions are created. +CXErrorCode computeNewNames(ArrayRef<CXRenamedIndexedSymbol> Symbols, + ArrayRef<SymbolName> SymbolNames, + const LangOptions &LangOpts, + SmallVectorImpl<SymbolName> &NewNames) { + IdentifierTable IDs(LangOpts); + for (const auto &Symbol : Symbols) { + SymbolName NewSymbolName(Symbol.NewName, LangOpts); + if (NewSymbolName.size() != SymbolNames[0].size()) + return CXError_RefactoringNameSizeMismatch; + if (!rename::isNewNameValid(NewSymbolName, isObjCSelector(Symbol), IDs, + LangOpts)) + return CXError_RefactoringNameInvalid; + NewNames.push_back(std::move(NewSymbolName)); + } + return CXError_Success; +} + +static rename::IndexedOccurrence::OccurrenceKind +translateIndexedOccurrenceKind(CXCursorKind Kind) { + switch (Kind) { + case CXCursor_ObjCMessageExpr: + return rename::IndexedOccurrence::IndexedObjCMessageSend; + case CXCursor_InclusionDirective: + return rename::IndexedOccurrence::InclusionDirective; + default: + return rename::IndexedOccurrence::IndexedSymbol; + } +} + +// TODO: Remove +CXErrorCode performIndexedFileRename( + ArrayRef<CXRenamedIndexedSymbol> Symbols, StringRef Filename, + ArrayRef<const char *> Arguments, CXIndex CIdx, + MutableArrayRef<CXUnsavedFile> UnsavedFiles, + const RefactoringOptionSet *Options, CXRenamingResult &Result) { + Result = nullptr; + + // Adjust the given command line arguments to ensure that any positional + // arguments in them are stripped. + std::vector<const char *> ClangToolArguments; + ClangToolArguments.push_back("--"); + for (const auto &Arg : Arguments) { + // Remove the '-gmodules' option, as the -fmodules-format=obj isn't + // supported without the linked object reader. + if (StringRef(Arg) == "-gmodules") + continue; + ClangToolArguments.push_back(Arg); + } + int Argc = ClangToolArguments.size(); + std::string ErrorMessage; + std::unique_ptr<CompilationDatabase> Compilations = + FixedCompilationDatabase::loadFromCommandLine( + Argc, ClangToolArguments.data(), ErrorMessage); + if (!Compilations) { + llvm::errs() << "CRefactor: Failed to load command line: " << ErrorMessage + << "\n"; + return CXError_Failure; + } + + // Translate the symbols. + llvm::SmallVector<rename::IndexedSymbol, 4> IndexedSymbols; + for (const auto &Symbol : Symbols) { + + // Parse the symbol name. + bool IsObjCSelector = false; + // Selectors have to be parsed. + if (isObjCSelector(Symbol)) + IsObjCSelector = true; + // Ensure that we don't get selectors with incorrect symbol kind. + else if (StringRef(Symbol.Name).contains(':')) + return CXError_InvalidArguments; + + std::vector<rename::IndexedOccurrence> IndexedOccurrences; + for (const auto &Loc : llvm::makeArrayRef(Symbol.IndexedLocations, + Symbol.IndexedLocationCount)) { + rename::IndexedOccurrence Result; + Result.Line = Loc.Location.Line; + Result.Column = Loc.Location.Column; + Result.Kind = translateIndexedOccurrenceKind(Loc.CursorKind); + IndexedOccurrences.push_back(Result); + } + + IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector), + IndexedOccurrences, + /*IsObjCSelector=*/IsObjCSelector); + } + + class ToolRunner final : public FrontendActionFactory, + public rename::IndexedFileOccurrenceConsumer { + ArrayRef<CXRenamedIndexedSymbol> Symbols; + ArrayRef<rename::IndexedSymbol> IndexedSymbols; + const RefactoringOptionSet *Options; + + public: + RenamingResult *Result; + CXErrorCode Err; + + ToolRunner(ArrayRef<CXRenamedIndexedSymbol> Symbols, + ArrayRef<rename::IndexedSymbol> IndexedSymbols, + const RefactoringOptionSet *Options) + : Symbols(Symbols), IndexedSymbols(IndexedSymbols), Options(Options), + Result(nullptr), Err(CXError_Success) {} + + clang::FrontendAction *create() override { + return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, + Options); + } + + void handleOccurrence(const rename::SymbolOccurrence &Occurrence, + SourceManager &SM, + const LangOptions &LangOpts) override { + if (Err != CXError_Success) + return; + if (!Result) { + SmallVector<SymbolName, 4> SymbolNames; + for (const auto &Symbol : IndexedSymbols) + SymbolNames.push_back(Symbol.Name); + SmallVector<SymbolName, 4> NewNames; + Err = computeNewNames(Symbols, SymbolNames, LangOpts, NewNames); + if (Err != CXError_Success) + return; + Result = new RenamingResult(NewNames, SymbolNames); + } + Result->handleIndexedFileOccurrence(Occurrence, SM, LangOpts); + } + }; + + auto Runner = llvm::make_unique<ToolRunner>(Symbols, IndexedSymbols, Options); + + // Run a clang tool on the input file. + std::string Name = Filename.str(); + ClangTool Tool(*Compilations, Name); + Tool.run(Runner.get()); + if (Runner->Err != CXError_Success) + return Runner->Err; + Result = Runner->Result; + return CXError_Success; +} + +CXErrorCode performIndexedSymbolSearch( + ArrayRef<CXIndexedSymbol> Symbols, StringRef Filename, + ArrayRef<const char *> Arguments, CXIndex CIdx, + MutableArrayRef<CXUnsavedFile> UnsavedFiles, + const RefactoringOptionSet *Options, CXSymbolOccurrencesResult &Result) { + Result = nullptr; + + // Adjust the given command line arguments to ensure that any positional + // arguments in them are stripped. + std::vector<const char *> ClangToolArguments; + ClangToolArguments.push_back("--"); + for (const auto &Arg : Arguments) { + // Remove the '-gmodules' option, as the -fmodules-format=obj isn't + // supported without the linked object reader. + if (StringRef(Arg) == "-gmodules") + continue; + ClangToolArguments.push_back(Arg); + } + int Argc = ClangToolArguments.size(); + std::string ErrorMessage; + std::unique_ptr<CompilationDatabase> Compilations = + FixedCompilationDatabase::loadFromCommandLine( + Argc, ClangToolArguments.data(), ErrorMessage); + if (!Compilations) { + llvm::errs() << "CRefactor: Failed to load command line: " << ErrorMessage + << "\n"; + return CXError_Failure; + } + + // Translate the symbols. + llvm::SmallVector<rename::IndexedSymbol, 4> IndexedSymbols; + for (const auto &Symbol : Symbols) { + + // Parse the symbol name. + bool IsObjCSelector = false; + // Selectors have to be parsed. + if (isObjCSelector(Symbol)) + IsObjCSelector = true; + // Ensure that we don't get selectors with incorrect symbol kind. + else if (StringRef(Symbol.Name).contains(':')) + return CXError_InvalidArguments; + + std::vector<rename::IndexedOccurrence> IndexedOccurrences; + for (const auto &Loc : llvm::makeArrayRef(Symbol.IndexedLocations, + Symbol.IndexedLocationCount)) { + rename::IndexedOccurrence Result; + Result.Line = Loc.Location.Line; + Result.Column = Loc.Location.Column; + Result.Kind = translateIndexedOccurrenceKind(Loc.CursorKind); + IndexedOccurrences.push_back(Result); + } + + IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector), + IndexedOccurrences, + /*IsObjCSelector=*/IsObjCSelector); + } + + class ToolRunner final : public FrontendActionFactory, + public rename::IndexedFileOccurrenceConsumer { + ArrayRef<rename::IndexedSymbol> IndexedSymbols; + const RefactoringOptionSet *Options; + + public: + SymbolOccurrencesResult *Result; + + ToolRunner(ArrayRef<rename::IndexedSymbol> IndexedSymbols, + const RefactoringOptionSet *Options) + : IndexedSymbols(IndexedSymbols), Options(Options), Result(nullptr) {} + + clang::FrontendAction *create() override { + return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, + Options); + } + + void handleOccurrence(const rename::SymbolOccurrence &Occurrence, + SourceManager &SM, + const LangOptions &LangOpts) override { + if (!Result) { + SmallVector<SymbolName, 4> SymbolNames; + for (const auto &Symbol : IndexedSymbols) + SymbolNames.push_back(Symbol.Name); + Result = new SymbolOccurrencesResult(SymbolNames); + } + Result->handleIndexedFileOccurrence(Occurrence, SM, LangOpts); + } + }; + + auto Runner = llvm::make_unique<ToolRunner>(IndexedSymbols, Options); + + // Run a clang tool on the input file. + std::string Name = Filename.str(); + ClangTool Tool(*Compilations, Name); + for (const CXUnsavedFile &File : UnsavedFiles) + Tool.mapVirtualFile(File.Filename, StringRef(File.Contents, File.Length)); + if (Tool.run(Runner.get())) + return CXError_Failure; + Result = Runner->Result; + return CXError_Success; +} + +class RefactoringAction { + std::unique_ptr<RefactoringOperation> Operation; + std::unique_ptr<RenamingAction> Rename; + + SmallVector<CXRefactoringCandidate, 2> RefactoringCandidates; + CXRefactoringCandidateSet CandidateSet = {nullptr, 0}; + bool HasCandidateSet = false; + +public: + CXRefactoringActionType Type; + unsigned SelectedCandidate = 0; + CXTranslationUnit InitiationTU; + // TODO: Remove (no longer needed due to continuations). + CXTranslationUnit ImplementationTU; + + RefactoringAction(std::unique_ptr<RefactoringOperation> Operation, + CXRefactoringActionType Type, + CXTranslationUnit InitiationTU) + : Operation(std::move(Operation)), Type(Type), InitiationTU(InitiationTU), + ImplementationTU(nullptr) {} + + RefactoringAction(std::unique_ptr<RenamingAction> Rename, + CXTranslationUnit InitiationTU) + : Rename(std::move(Rename)), + Type(this->Rename->Operation.isLocal() ? CXRefactor_Rename_Local + : CXRefactor_Rename), + InitiationTU(InitiationTU), ImplementationTU(nullptr) {} + + ~RefactoringAction() { + for (const auto &Candidate : RefactoringCandidates) + clang_disposeString(Candidate.Description); + } + + RefactoringOperation *getOperation() const { return Operation.get(); } + + RenamingAction *getRenamingAction() const { return Rename.get(); } + + CXRefactoringCandidateSet getRefactoringCandidates() { + if (HasCandidateSet) + return CandidateSet; + HasCandidateSet = true; + RefactoringOperation *Operation = getOperation(); + if (!Operation) + return CandidateSet; + auto Candidates = Operation->getRefactoringCandidates(); + if (Candidates.empty()) + return CandidateSet; + for (const auto &Candidate : Candidates) + RefactoringCandidates.push_back({cxstring::createDup(Candidate)}); + CandidateSet = {RefactoringCandidates.data(), + (unsigned)RefactoringCandidates.size()}; + return CandidateSet; + } + + CXErrorCode selectCandidate(unsigned Index) { + RefactoringOperation *Operation = getOperation(); + if (!Operation) + return CXError_InvalidArguments; + if (Index != 0 && Index >= getRefactoringCandidates().NumCandidates) + return CXError_InvalidArguments; + SelectedCandidate = Index; + return CXError_Success; + } +}; + +static bool operator==(const CXFileLocation &LHS, const CXFileLocation &RHS) { + return LHS.Line == RHS.Line && LHS.Column == RHS.Column; +} + +static CXFileRange translateOffsetToRelativeRange(unsigned Offset, + unsigned Size, + StringRef Source) { + assert(Source.drop_front(Offset).take_front(Size).count('\n') == 0 && + "Newlines in translated range?"); + StringRef Prefix = Source.take_front(Offset); + unsigned StartLines = Prefix.count('\n') + 1; + if (StartLines > 1) + Offset -= Prefix.rfind('\n') + 1; + return CXFileRange{{StartLines, Offset + 1}, {StartLines, Offset + 1 + Size}}; +} + +class RefactoringResultWrapper { +public: + CXRefactoringReplacements_Old Replacements; // TODO: Remove. + CXRefactoringReplacements SourceReplacements; + std::unique_ptr<RefactoringContinuation> Continuation; + llvm::BumpPtrAllocator Allocator; + CXTranslationUnit TU; + + struct AssociatedReplacementInfo { + CXSymbolOccurrence *AssociatedSymbolOccurrences; + unsigned NumAssociatedSymbolOccurrences; + }; + + ~RefactoringResultWrapper() { + // TODO: Remove. + for (unsigned I = 0; I < Replacements.NumFileReplacementSets; ++I) { + const CXRefactoringFileReplacementSet_Old &FileSet = + Replacements.FileReplacementSets[I]; + clang_disposeString(FileSet.Filename); + for (unsigned J = 0; J < FileSet.NumReplacements; ++J) + clang_disposeString(FileSet.Replacements[J].ReplacementString); + delete[] FileSet.Replacements; + } + delete[] Replacements.FileReplacementSets; + + for (unsigned I = 0; I < SourceReplacements.NumFileReplacementSets; ++I) { + const CXRefactoringFileReplacementSet &FileSet = + SourceReplacements.FileReplacementSets[I]; + clang_disposeString(FileSet.Filename); + for (unsigned J = 0; J < FileSet.NumReplacements; ++J) + clang_disposeString(FileSet.Replacements[J].ReplacementString); + } + } + + RefactoringResultWrapper( + ArrayRef<RefactoringReplacement> Replacements, + ArrayRef<std::unique_ptr<RefactoringResultAssociatedSymbol>> + AssociatedSymbols, + std::unique_ptr<RefactoringContinuation> Continuation, + ASTContext &Context, CXTranslationUnit TU) + : Continuation(std::move(Continuation)), TU(TU) { + SourceManager &SM = Context.getSourceManager(); + + if (Replacements.empty()) { + assert(AssociatedSymbols.empty() && "Symbols without replacements??"); + // TODO: Remove begin + this->Replacements.NumFileReplacementSets = 0; + this->Replacements.FileReplacementSets = nullptr; + // Remove end + this->SourceReplacements.NumFileReplacementSets = 0; + this->SourceReplacements.FileReplacementSets = nullptr; + return; + } + llvm::SmallDenseMap<const RefactoringResultAssociatedSymbol *, unsigned> + AssociatedSymbolToIndex; + for (const auto &Symbol : llvm::enumerate(AssociatedSymbols)) + AssociatedSymbolToIndex[Symbol.value().get()] = Symbol.index(); + + // Find the set of files that have to be modified and gather the indices of + // the occurrences for each file. + llvm::DenseMap<const FileEntry *, std::vector<unsigned>> + FilesToReplacements; + for (const auto &Replacement : llvm::enumerate(Replacements)) { + SourceLocation Loc = Replacement.value().Range.getBegin(); + const std::pair<FileID, unsigned> DecomposedLocation = + SM.getDecomposedLoc(Loc); + const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); + FilesToReplacements.try_emplace(Entry, std::vector<unsigned>()) + .first->second.push_back(Replacement.index()); + } + + // TODO: Remove + unsigned NumFiles = FilesToReplacements.size(); + auto *FileReplacementSets = + new CXRefactoringFileReplacementSet_Old[NumFiles]; + + unsigned FileIndex = 0; + for (const auto &Entry : FilesToReplacements) { + CXRefactoringFileReplacementSet_Old &FileSet = + FileReplacementSets[FileIndex]; + ++FileIndex; + ArrayRef<unsigned> ReplacementIndices = Entry.second; + FileSet.Filename = cxstring::createDup(Entry.first->getName()); + FileSet.NumReplacements = ReplacementIndices.size(); + auto *FileReplacements = + new CXRefactoringReplacement_Old[ReplacementIndices.size()]; + FileSet.Replacements = FileReplacements; + + unsigned NumRemoved = 0; + for (unsigned I = 0; I < FileSet.NumReplacements; ++I) { + const RefactoringReplacement &RefReplacement = + Replacements[ReplacementIndices[I]]; + CXSourceRange Range = cxloc::translateSourceRange( + SM, Context.getLangOpts(), + CharSourceRange::getCharRange(RefReplacement.Range.getBegin(), + RefReplacement.Range.getEnd())); + CXFileLocation Begin, End; + clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line, + &Begin.Column, nullptr); + clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, + &End.Column, nullptr); + + if (I && FileReplacements[I - NumRemoved - 1].Range.End == Begin) { + // Merge the previous and the current replacement. + FileReplacements[I - NumRemoved - 1].Range.End = End; + std::string Replacement = + std::string(clang_getCString( + FileReplacements[I - NumRemoved - 1].ReplacementString)) + + RefReplacement.ReplacementString; + FileReplacements[I - NumRemoved - 1].ReplacementString = + cxstring::createDup(Replacement); + NumRemoved++; + continue; + } + + CXRefactoringReplacement_Old &Replacement = + FileReplacements[I - NumRemoved]; + Replacement.ReplacementString = + cxstring::createDup(RefReplacement.ReplacementString); + Replacement.Range.Begin = Begin; + Replacement.Range.End = End; + } + FileSet.NumReplacements -= NumRemoved; + } + + this->Replacements.FileReplacementSets = FileReplacementSets; + this->Replacements.NumFileReplacementSets = NumFiles; + + // TODO: Outdent. + { + unsigned NumFiles = FilesToReplacements.size(); + auto *FileReplacementSets = + Allocator.Allocate<CXRefactoringFileReplacementSet>(NumFiles); + SourceReplacements.FileReplacementSets = FileReplacementSets; + SourceReplacements.NumFileReplacementSets = NumFiles; + unsigned FileIndex = 0; + for (const auto &Entry : FilesToReplacements) { + CXRefactoringFileReplacementSet &FileSet = + FileReplacementSets[FileIndex]; + ++FileIndex; + ArrayRef<unsigned> ReplacementIndices = Entry.second; + FileSet.Filename = cxstring::createDup(Entry.first->getName()); + FileSet.NumReplacements = ReplacementIndices.size(); + auto *FileReplacements = Allocator.Allocate<CXRefactoringReplacement>( + ReplacementIndices.size()); + FileSet.Replacements = FileReplacements; + + unsigned NumRemoved = 0; + for (unsigned I = 0; I < FileSet.NumReplacements; ++I) { + const RefactoringReplacement &RefReplacement = + Replacements[ReplacementIndices[I]]; + CXSourceRange Range = cxloc::translateSourceRange( + SM, Context.getLangOpts(), + CharSourceRange::getCharRange(RefReplacement.Range.getBegin(), + RefReplacement.Range.getEnd())); + CXFileLocation Begin, End; + clang_getFileLocation(clang_getRangeStart(Range), nullptr, + &Begin.Line, &Begin.Column, nullptr); + clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, + &End.Column, nullptr); + + if (I && FileReplacements[I - NumRemoved - 1].Range.End == Begin) { + // Merge the previous and the current replacement. + FileReplacements[I - NumRemoved - 1].Range.End = End; + std::string Replacement = + std::string(clang_getCString( + FileReplacements[I - NumRemoved - 1].ReplacementString)) + + RefReplacement.ReplacementString; + FileReplacements[I - NumRemoved - 1].ReplacementString = + cxstring::createDup(Replacement); + NumRemoved++; + continue; + } + + CXRefactoringReplacement &Replacement = + FileReplacements[I - NumRemoved]; + Replacement.ReplacementString = + cxstring::createDup(RefReplacement.ReplacementString); + Replacement.Range.Begin = Begin; + Replacement.Range.End = End; + unsigned NumAssociatedSymbols = RefReplacement.SymbolLocations.size(); + if (!NumAssociatedSymbols) { + Replacement.AssociatedData = nullptr; + continue; + } + AssociatedReplacementInfo *AssociatedData = + Allocator.Allocate<AssociatedReplacementInfo>(); + Replacement.AssociatedData = AssociatedData; + AssociatedData->AssociatedSymbolOccurrences = + Allocator.Allocate<CXSymbolOccurrence>(NumAssociatedSymbols); + AssociatedData->NumAssociatedSymbolOccurrences = NumAssociatedSymbols; + unsigned SymbolIndex = 0; + for (const auto &AssociatedSymbol : RefReplacement.SymbolLocations) { + unsigned Index = AssociatedSymbolToIndex[AssociatedSymbol.first]; + const RefactoringReplacement::AssociatedSymbolLocation &Loc = + AssociatedSymbol.second; + CXFileRange *NamePieces = + Allocator.Allocate<CXFileRange>(Loc.Offsets.size()); + assert(AssociatedSymbol.first->getName().size() == + Loc.Offsets.size() && + "mismatching symbol name and offsets"); + for (const auto &Offset : llvm::enumerate(Loc.Offsets)) { + StringRef NamePiece = + AssociatedSymbol.first->getName()[Offset.index()]; + NamePieces[Offset.index()] = translateOffsetToRelativeRange( + Offset.value(), NamePiece.size(), + RefReplacement.ReplacementString); + } + AssociatedData->AssociatedSymbolOccurrences[SymbolIndex] = + CXSymbolOccurrence{ + NamePieces, (unsigned)Loc.Offsets.size(), + Loc.IsDeclaration + ? CXSymbolOccurrence_ExtractedDeclaration + : CXSymbolOccurrence_ExtractedDeclaration_Reference, + /*IsMacroExpansion=*/0, Index}; + ++SymbolIndex; + } + } + FileSet.NumReplacements -= NumRemoved; + } + } + } +}; + +class RefactoringContinuationWrapper { +public: + std::unique_ptr<RefactoringContinuation> Continuation; + struct QueryWrapper { + indexer::IndexerQuery *Query; + CXTranslationUnit TU; + std::vector<PersistentDeclRef<Decl>> DeclResults; + unsigned ConsumedResults = 0; + + QueryWrapper(indexer::IndexerQuery *Query, CXTranslationUnit TU) + : Query(Query), TU(TU) {} + }; + SmallVector<QueryWrapper, 4> Queries; + bool IsInitiationTUAbandoned = false; + + RefactoringContinuationWrapper( + std::unique_ptr<RefactoringContinuation> Continuation, + CXTranslationUnit TU) + : Continuation(std::move(Continuation)) { + Queries.emplace_back(this->Continuation->getASTUnitIndexerQuery(), TU); + assert(Queries.back().Query && "Invalid ast query"); + std::vector<indexer::IndexerQuery *> AdditionalQueries = + this->Continuation->getAdditionalIndexerQueries(); + for (indexer::IndexerQuery *IQ : AdditionalQueries) + Queries.emplace_back(IQ, TU); + } +}; + +class RefactoringDiagnosticConsumer : public DiagnosticConsumer { + const ASTContext &Context; + DiagnosticConsumer *PreviousClient; + std::unique_ptr<DiagnosticConsumer> PreviousClientPtr; + llvm::SmallVector<StoredDiagnostic, 2> RenameDiagnostics; + +public: + RefactoringDiagnosticConsumer(ASTContext &Context) : Context(Context) { + PreviousClient = Context.getDiagnostics().getClient(); + PreviousClientPtr = Context.getDiagnostics().takeClient(); + Context.getDiagnostics().setClient(this, /*ShouldOwnClient=*/false); + } + + ~RefactoringDiagnosticConsumer() { + if (PreviousClientPtr) + Context.getDiagnostics().setClient(PreviousClientPtr.release()); + else + Context.getDiagnostics().setClient(PreviousClient, + /*ShouldOwnClient=*/false); + } + + void HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) override { + if (DiagnosticIDs::getCategoryNumberForDiag(Info.getID()) == + diag::DiagCat_Rename_Issue) + RenameDiagnostics.push_back(StoredDiagnostic(Level, Info)); + else + assert(false && "Unhandled refactoring category"); + } + + CXDiagnosticSetImpl *createDiags() const { + if (RenameDiagnostics.empty()) + return nullptr; + return cxdiag::createStoredDiags(RenameDiagnostics, Context.getLangOpts()); + } + + CXRefactoringActionSetWithDiagnostics createActionSet() const { + if (RenameDiagnostics.empty()) + return {nullptr, 0}; + CXRefactoringActionWithDiagnostics *Actions = + new CXRefactoringActionWithDiagnostics[1]; + Actions[0].Action = CXRefactor_Rename; + Actions[0].Diagnostics = + cxdiag::createStoredDiags(RenameDiagnostics, Context.getLangOpts()); + return {Actions, 1}; + } +}; + +} // end anonymous namespace + +template <typename T> +static T withRenamingAction(CXRefactoringAction Action, T DefaultValue, + llvm::function_ref<T(RenamingAction &)> Callback) { + if (!Action) + return DefaultValue; + RenamingAction *Rename = + static_cast<RefactoringAction *>(Action)->getRenamingAction(); + if (!Rename) + return DefaultValue; + return Callback(*Rename); +} + +static enum CXIndexerQueryKind +translateDeclPredicate(const indexer::DeclPredicate &Predicate) { + indexer::DeclEntity Entity; + if (Predicate == Entity.isDefined().Predicate) + return CXIndexerQuery_Decl_IsDefined; + return CXIndexerQuery_Unknown; +} + +extern "C" { + +CXString +clang_RefactoringActionType_getName(enum CXRefactoringActionType Action) { + return cxstring::createRef( + getRefactoringActionTypeName(translateRefactoringActionType(Action))); +} + +void clang_RefactoringActionSet_dispose(CXRefactoringActionSet *Set) { + if (Set && Set->Actions) + delete[] Set->Actions; +} + +void clang_RefactoringActionSetWithDiagnostics_dispose( + CXRefactoringActionSetWithDiagnostics *Set) { + if (Set && Set->Actions) { + for (auto &S : llvm::makeArrayRef(Set->Actions, Set->NumActions)) + clang_disposeDiagnosticSet(S.Diagnostics); + delete[] Set->Actions; + } +} + +CXRefactoringOptionSet clang_RefactoringOptionSet_create() { + return new RefactoringOptionSet; +} + +CXRefactoringOptionSet +clang_RefactoringOptionSet_createFromString(const char *String) { + RefactoringOptionSet *Result = new RefactoringOptionSet; + auto Options = RefactoringOptionSet::parse(String); + if (Options) { + *Result = std::move(*Options); + return Result; + } + llvm::handleAllErrors(Options.takeError(), + [](const llvm::StringError &Error) {}); + return clang_RefactoringOptionSet_create(); +} + +void clang_RefactoringOptionSet_add(CXRefactoringOptionSet Set, + enum CXRefactoringOption Option) { + if (!Set) + return; + switch (Option) { + case CXRefactorOption_AvoidTextualMatches: + static_cast<RefactoringOptionSet *>(Set)->add( + option::AvoidTextualMatches::getTrue()); + break; + } +} + +CXString clang_RefactoringOptionSet_toString(CXRefactoringOptionSet Set) { + if (!Set) + return cxstring::createNull(); + std::string Result; + llvm::raw_string_ostream OS(Result); + static_cast<RefactoringOptionSet *>(Set)->print(OS); + return cxstring::createDup(OS.str()); +} + +void clang_RefactoringOptionSet_dispose(CXRefactoringOptionSet Set) { + if (Set) + delete static_cast<RefactoringOptionSet *>(Set); +} + +enum CXErrorCode +clang_Refactoring_findActionsAt(CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, + CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet) { + return clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( + TU, Location, SelectionRange, Options, OutSet, /*OutFailureSet=*/nullptr); +} + +enum CXErrorCode clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet, + CXRefactoringActionSetWithDiagnostics *OutFailureSet) { + LOG_FUNC_SECTION { *Log << TU << ' '; } + if (OutFailureSet) { + OutFailureSet->Actions = nullptr; + OutFailureSet->NumActions = 0; + } + + if (!OutSet) + return CXError_InvalidArguments; + + OutSet->Actions = nullptr; + OutSet->NumActions = 0; + + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXError_InvalidArguments; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return CXError_InvalidArguments; + + SourceLocation Loc = cxloc::translateSourceLocation(Location); + if (Loc.isInvalid()) + return CXError_InvalidArguments; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + (void)Options; // FIXME: handle options + ASTContext &Context = CXXUnit->getASTContext(); + RefactoringDiagnosticConsumer DiagConsumer(Context); + RefactoringActionSet ActionSet = findActionSetAt( + Loc, cxloc::translateCXSourceRange(SelectionRange), Context); + if (OutFailureSet) + *OutFailureSet = DiagConsumer.createActionSet(); + if (ActionSet.Actions.empty()) + return CXError_RefactoringActionUnavailable; + + CXRefactoringActionType *Actions = + new CXRefactoringActionType[ActionSet.Actions.size()]; + OutSet->Actions = Actions; + OutSet->NumActions = ActionSet.Actions.size(); + for (const auto &Action : llvm::enumerate(ActionSet.Actions)) + Actions[Action.index()] = translateRefactoringActionType(Action.value()); + return CXError_Success; +} + +void clang_RefactoringAction_dispose(CXRefactoringAction Action) { + if (Action) + delete static_cast<RefactoringAction *>(Action); +} + +CXSourceRange +clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action) { + if (Action) { + RefactoringOperation *Operation = + static_cast<RefactoringAction *>(Action)->getOperation(); + if (Operation) { + ASTUnit *CXXUnit = cxtu::getASTUnit( + static_cast<RefactoringAction *>(Action)->InitiationTU); + if (const Stmt *S = Operation->getTransformedStmt()) { + SourceRange Range = S->getSourceRange(); + if (const Stmt *Last = Operation->getLastTransformedStmt()) + Range.setEnd(Last->getLocEnd()); + return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range); + } else if (const Decl *D = Operation->getTransformedDecl()) { + SourceRange Range = D->getSourceRange(); + if (const Decl *Last = Operation->getLastTransformedDecl()) + Range.setEnd(Last->getLocEnd()); + return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range); + } + } + } + return clang_getNullRange(); +} + +int clang_RefactoringAction_requiresImplementationTU( + CXRefactoringAction Action) { + return withRenamingAction<int>(Action, 0, [](RenamingAction &Action) { + return Action.Operation.requiresImplementationTU(); + }); +} + +CXString clang_RefactoringAction_getUSRThatRequiresImplementationTU( + CXRefactoringAction Action) { + return withRenamingAction<CXString>( + Action, cxstring::createNull(), [](RenamingAction &Action) { + return Action.getUSRThatRequiresImplementationTU(); + }); +} + +enum CXErrorCode +clang_RefactoringAction_addImplementationTU(CXRefactoringAction Action, + CXTranslationUnit TU) { + if (!Action || !TU) + return CXError_InvalidArguments; + // Prohibit multiple additions of implementation TU. + if (static_cast<RefactoringAction *>(Action)->ImplementationTU) + return CXError_Failure; + static_cast<RefactoringAction *>(Action)->ImplementationTU = TU; + return CXError_Success; +} + +enum CXErrorCode clang_RefactoringAction_getRefactoringCandidates( + CXRefactoringAction Action, + CXRefactoringCandidateSet *OutRefactoringCandidateSet) { + if (!Action || !OutRefactoringCandidateSet) + return CXError_InvalidArguments; + *OutRefactoringCandidateSet = + static_cast<RefactoringAction *>(Action)->getRefactoringCandidates(); + return CXError_Success; +} + +enum CXErrorCode +clang_RefactoringAction_selectRefactoringCandidate(CXRefactoringAction Action, + unsigned Index) { + if (!Action) + return CXError_InvalidArguments; + return static_cast<RefactoringAction *>(Action)->selectCandidate(Index); +} + +// TODO: Remove. +enum CXErrorCode clang_Refactoring_initiateActionAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXString *OutFailureReason) { + CXDiagnosticSet Diags; + CXErrorCode Result = clang_Refactoring_initiateAction( + TU, Location, SelectionRange, ActionType, Options, OutAction, &Diags); + if (OutFailureReason && Diags && clang_getNumDiagnosticsInSet(Diags) == 1) { + CXString Spelling = + clang_getDiagnosticSpelling(clang_getDiagnosticInSet(Diags, 0)); + *OutFailureReason = cxstring::createDup(clang_getCString(Spelling)); + clang_disposeString(Spelling); + } else if (OutFailureReason) + *OutFailureReason = cxstring::createEmpty(); + clang_disposeDiagnosticSet(Diags); + return Result; +} + +enum CXErrorCode clang_Refactoring_initiateAction( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXDiagnosticSet *OutDiagnostics) { + if (!OutAction) + return CXError_InvalidArguments; + *OutAction = nullptr; + if (OutDiagnostics) + *OutDiagnostics = nullptr; + + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXError_InvalidArguments; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return CXError_InvalidArguments; + + SourceLocation Loc = cxloc::translateSourceLocation(Location); + if (Loc.isInvalid()) + return CXError_InvalidArguments; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + (void)Options; // FIXME: handle options + ASTContext &Context = CXXUnit->getASTContext(); + RefactoringDiagnosticConsumer DiagConsumer(Context); + auto Operation = initiateRefactoringOperationAt( + Loc, cxloc::translateCXSourceRange(SelectionRange), Context, + translateRefactoringActionType(ActionType)); + if (!Operation.Initiated) { + if (OutDiagnostics) { + if (!Operation.FailureReason.empty()) { + // TODO: Remove when other actions migrate to diagnostics. + StoredDiagnostic Diag(DiagnosticsEngine::Error, /*ID=*/0, + Operation.FailureReason); + *OutDiagnostics = + cxdiag::createStoredDiags(Diag, Context.getLangOpts()); + } else + *OutDiagnostics = DiagConsumer.createDiags(); + } + return CXError_RefactoringActionUnavailable; + } + if (Operation.RefactoringOp) + *OutAction = new RefactoringAction(std::move(Operation.RefactoringOp), + ActionType, TU); + else + *OutAction = new RefactoringAction( + llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(), + std::move(*Operation.SymbolOp)), + TU); + return CXError_Success; +} + +enum CXErrorCode clang_Refactoring_initiateActionOnDecl( + CXTranslationUnit TU, const char *DeclUSR, + enum CXRefactoringActionType ActionType, CXRefactoringOptionSet Options, + CXRefactoringAction *OutAction, CXString *OutFailureReason) { + if (!OutAction) + return CXError_InvalidArguments; + *OutAction = nullptr; + if (OutFailureReason) + *OutFailureReason = cxstring::createNull(); + + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXError_InvalidArguments; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return CXError_InvalidArguments; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + (void)Options; // FIXME: handle options + auto Operation = initiateRefactoringOperationOnDecl( + DeclUSR, CXXUnit->getASTContext(), + translateRefactoringActionType(ActionType)); + if (!Operation.Initiated) + return CXError_RefactoringActionUnavailable; + // FIXME: Don't dupe with above + if (Operation.RefactoringOp) + *OutAction = new RefactoringAction(std::move(Operation.RefactoringOp), + ActionType, TU); + else + *OutAction = new RefactoringAction( + llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(), + std::move(*Operation.SymbolOp)), + TU); + return CXError_Success; +} + +enum CXErrorCode +clang_Refactoring_initiateRenamingOperation(CXRefactoringAction Action) { + if (!Action) + return CXError_InvalidArguments; + RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); + RenamingAction *Rename = RefAction->getRenamingAction(); + if (!Rename) + return CXError_InvalidArguments; + // TODO + return CXError_Success; +} + +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findRenamedCursor( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXCursor *OutCursor) { + if (!OutCursor) + return CXError_InvalidArguments; + + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXError_InvalidArguments; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return CXError_InvalidArguments; + SourceLocation Loc = cxloc::translateSourceLocation(Location); + if (Loc.isInvalid()) + return CXError_InvalidArguments; + + const NamedDecl *ND = rename::getNamedDeclAt(CXXUnit->getASTContext(), Loc); + if (!ND) { + *OutCursor = cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound, TU); + return CXError_RefactoringActionUnavailable; + } + + *OutCursor = cxcursor::MakeCXCursor(ND, TU); + return CXError_Success; +} + +enum CXErrorCode clang_RenamingOperation_setNewName(CXRefactoringAction Action, + const char *NewName) { + return withRenamingAction<CXErrorCode>( + Action, CXError_InvalidArguments, + [=](RenamingAction &Action) -> CXErrorCode { + if (!NewName) + return CXError_InvalidArguments; + StringRef Name = NewName; + if (Name.empty()) + return CXError_InvalidArguments; + return Action.setNewName(Name); + }); +} + +enum CXRefactoringActionType +clang_RefactoringAction_getInitiatedActionType(CXRefactoringAction Action) { + return static_cast<RefactoringAction *>(Action)->Type; +} + +unsigned clang_RenamingOperation_getNumSymbols(CXRefactoringAction Action) { + return withRenamingAction<unsigned>(Action, 0, [](RenamingAction &Action) { + return Action.Operation.symbols().size(); + }); +} + +CXString clang_RenamingOperation_getUSRForSymbol(CXRefactoringAction Action, + unsigned Index) { + return withRenamingAction<CXString>( + Action, cxstring::createNull(), + [=](RenamingAction &Action) { return Action.usrForSymbolAt(Index); }); +} + +CXRenamingResult clang_Refactoring_findRenamedOccurrencesInPrimaryTUs( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles) { + if (!Action) + return nullptr; + RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); + RenamingAction *Rename = RefAction->getRenamingAction(); + if (!Rename) + return nullptr; + + // TODO: Handle implementation TU + if (cxtu::isNotUsableTU(RefAction->InitiationTU)) { + LOG_BAD_TU(RefAction->InitiationTU); + return nullptr; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU); + if (!CXXUnit) + return nullptr; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + return Rename->handlePrimaryTU(RefAction->InitiationTU, *CXXUnit); +} + +CXSymbolOccurrencesResult clang_Refactoring_findSymbolOccurrencesInInitiationTU( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles) { + if (!Action) + return nullptr; + RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); + RenamingAction *Rename = RefAction->getRenamingAction(); + if (!Rename) + return nullptr; + + if (cxtu::isNotUsableTU(RefAction->InitiationTU)) { + LOG_BAD_TU(RefAction->InitiationTU); + return nullptr; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU); + if (!CXXUnit) + return nullptr; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + return Rename->findSymbolsInInitiationTU(RefAction->InitiationTU, *CXXUnit); +} + +CXErrorCode clang_Refactoring_findRenamedOccurrencesInIndexedFile( + const CXRenamedIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXRenamingResult *OutResult) { + if (!OutResult) + return CXError_InvalidArguments; + if (!Symbols || !NumSymbols || !Filename) + return CXError_InvalidArguments; + return performIndexedFileRename( + llvm::makeArrayRef(Symbols, NumSymbols), StringRef(Filename), + llvm::makeArrayRef(CommandLineArgs, NumCommandLineArgs), CIdx, + MutableArrayRef<CXUnsavedFile>(UnsavedFiles, NumUnsavedFiles), + Options ? static_cast<RefactoringOptionSet *>(Options) : nullptr, + *OutResult); +} + +CXErrorCode clang_Refactoring_findSymbolOccurrencesInIndexedFile( + const CXIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXSymbolOccurrencesResult *OutResult) { + if (!OutResult) + return CXError_InvalidArguments; + if (!Symbols || !NumSymbols || !Filename) + return CXError_InvalidArguments; + return performIndexedSymbolSearch( + llvm::makeArrayRef(Symbols, NumSymbols), StringRef(Filename), + llvm::makeArrayRef(CommandLineArgs, NumCommandLineArgs), CIdx, + MutableArrayRef<CXUnsavedFile>(UnsavedFiles, NumUnsavedFiles), + Options ? static_cast<RefactoringOptionSet *>(Options) : nullptr, + *OutResult); +} + +unsigned clang_RenamingResult_getNumModifiedFiles(CXRenamingResult Result) { + if (Result) + return static_cast<RenamingResult *>(Result)->getFilenames().size(); + return 0; +} + +void clang_RenamingResult_getResultForFile(CXRenamingResult Result, + unsigned FileIndex, + CXFileRenamingResult *OutResult) { + if (!Result || + FileIndex >= + static_cast<RenamingResult *>(Result)->getFilenames().size()) { + OutResult->Filename = cxstring::createNull(); + OutResult->NumOccurrences = 0; + OutResult->Occurrences = nullptr; + return; + } + auto &RenameResult = *static_cast<RenamingResult *>(Result); + OutResult->Filename = RenameResult.getFilenames()[FileIndex]; + OutResult->NumOccurrences = RenameResult.getOccurrences(FileIndex).size(); + OutResult->Occurrences = RenameResult.getOccurrences(FileIndex).data(); +} + +void clang_RenamingResult_dispose(CXRenamingResult Result) { + if (Result) + delete static_cast<RenamingResult *>(Result); +} + +unsigned clang_SymbolOccurrences_getNumFiles(CXSymbolOccurrencesResult Result) { + if (Result) + return static_cast<SymbolOccurrencesResult *>(Result) + ->getFilenames() + .size(); + return 0; +} + +void clang_SymbolOccurrences_getOccurrencesForFile( + CXSymbolOccurrencesResult Result, unsigned FileIndex, + CXSymbolOccurrencesInFile *OutResult) { + if (!Result || + FileIndex >= static_cast<SymbolOccurrencesResult *>(Result) + ->getFilenames() + .size()) { + OutResult->Filename = cxstring::createNull(); + OutResult->NumOccurrences = 0; + OutResult->Occurrences = nullptr; + return; + } + auto &RenameResult = *static_cast<SymbolOccurrencesResult *>(Result); + OutResult->Filename = RenameResult.getFilenames()[FileIndex]; + OutResult->NumOccurrences = RenameResult.getOccurrences(FileIndex).size(); + OutResult->Occurrences = RenameResult.getOccurrences(FileIndex).data(); +} + +void clang_SymbolOccurrences_dispose(CXSymbolOccurrencesResult Result) { + if (Result) + delete static_cast<SymbolOccurrencesResult *>(Result); +} + +CXRefactoringResult clang_Refactoring_performOperation( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXString *OutFailureReason) { + if (OutFailureReason) + *OutFailureReason = cxstring::createNull(); + if (!Action) + return nullptr; + RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); + if (!RefAction->getOperation()) + return nullptr; + + ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU); + if (!CXXUnit) + return nullptr; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + RefactoringOptionSet EmptyOptionSet; + const RefactoringOptionSet &OptionSet = + Options ? *static_cast<RefactoringOptionSet *>(Options) : EmptyOptionSet; + llvm::Expected<RefactoringResult> Result = RefAction->getOperation()->perform( + CXXUnit->getASTContext(), CXXUnit->getPreprocessor(), OptionSet, + RefAction->SelectedCandidate); + if (!Result) { + if (OutFailureReason) { + (void)!llvm::handleErrors( + Result.takeError(), [&](const RefactoringOperationError &Error) { + *OutFailureReason = cxstring::createDup(Error.FailureReason); + }); + } + return nullptr; + } + return new RefactoringResultWrapper( + Result.get().Replacements, Result.get().AssociatedSymbols, + std::move(Result.get().Continuation), CXXUnit->getASTContext(), + RefAction->InitiationTU); +} + +void clang_RefactoringResult_getReplacements( + CXRefactoringResult Result, + CXRefactoringReplacements_Old *OutReplacements) { + if (!OutReplacements) + return; + if (!Result) { + OutReplacements->FileReplacementSets = nullptr; + OutReplacements->NumFileReplacementSets = 0; + return; + } + *OutReplacements = static_cast<RefactoringResultWrapper *>(Result)->Replacements; +} + +CXRefactoringReplacements +clang_RefactoringResult_getSourceReplacements(CXRefactoringResult Result) { + if (!Result) + return CXRefactoringReplacements{nullptr, 0}; + return static_cast<RefactoringResultWrapper *>(Result)->SourceReplacements; +} + +CXRefactoringReplacementAssociatedSymbolOccurrences +clang_RefactoringReplacement_getAssociatedSymbolOccurrences( + CXRefactoringReplacement Replacement) { + if (!Replacement.AssociatedData) + return CXRefactoringReplacementAssociatedSymbolOccurrences{nullptr, 0}; + auto *Data = + static_cast<RefactoringResultWrapper::AssociatedReplacementInfo *>( + Replacement.AssociatedData); + return CXRefactoringReplacementAssociatedSymbolOccurrences{ + Data->AssociatedSymbolOccurrences, Data->NumAssociatedSymbolOccurrences}; +} + +void clang_RefactoringResult_dispose(CXRefactoringResult Result) { + if (Result) + delete static_cast<RefactoringResultWrapper *>(Result); +} + +CXRefactoringContinuation +clang_RefactoringResult_getContinuation(CXRefactoringResult Result) { + if (!Result) + return nullptr; + auto *Wrapper = static_cast<RefactoringResultWrapper *>(Result); + if (!Wrapper->Continuation) + return nullptr; + return new RefactoringContinuationWrapper(std::move(Wrapper->Continuation), + Wrapper->TU); +} + +enum CXErrorCode +clang_RefactoringContinuation_loadSerializedIndexerQueryResults( + CXRefactoringContinuation Continuation, const char *Source) { + if (!Continuation) + return CXError_InvalidArguments; + auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); + llvm::SmallVector<indexer::IndexerQuery *, 4> Queries; + for (const auto &Query : Wrapper->Queries) + Queries.push_back(Query.Query); + auto Err = indexer::IndexerQuery::loadResultsFromYAML(Source, Queries); + if (Err) { + consumeError(std::move(Err)); + return CXError_Failure; + } + return CXError_Success; +} + +unsigned clang_RefactoringContinuation_getNumIndexerQueries( + CXRefactoringContinuation Continuation) { + if (Continuation) + return static_cast<RefactoringContinuationWrapper *>(Continuation) + ->Queries.size(); + return 0; +} + +CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery( + CXRefactoringContinuation Continuation, unsigned Index) { + if (!Continuation) + return nullptr; + auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); + if (Index >= Wrapper->Queries.size()) + return nullptr; + return &Wrapper->Queries[Index]; +} + +void clang_RefactoringContinuation_finalizeEvaluationInInitationTU( + CXRefactoringContinuation Continuation) { + if (!Continuation) + return; + auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); + Wrapper->Queries.clear(); + Wrapper->Continuation->persistTUSpecificState(); + Wrapper->IsInitiationTUAbandoned = true; +} + +CXRefactoringResult clang_RefactoringContinuation_continueOperationInTU( + CXRefactoringContinuation Continuation, CXTranslationUnit TU, + CXString *OutFailureReason) { + if (!Continuation || !TU) + return nullptr; + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return nullptr; + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + const auto *Wrapper = + static_cast<RefactoringContinuationWrapper *>(Continuation); + if (!Wrapper->IsInitiationTUAbandoned) { + // FIXME: We can avoid conversions of TU-specific state if the given TU is + // the same as the initiation TU. + clang_RefactoringContinuation_finalizeEvaluationInInitationTU(Continuation); + } + auto Result = + Wrapper->Continuation->runInExternalASTUnit(CXXUnit->getASTContext()); + if (!Result) { + if (OutFailureReason) { + (void)!llvm::handleErrors( + Result.takeError(), [&](const RefactoringOperationError &Error) { + *OutFailureReason = cxstring::createDup(Error.FailureReason); + }); + } + return nullptr; + } + return new RefactoringResultWrapper( + Result.get().Replacements, Result.get().AssociatedSymbols, + std::move(Result.get().Continuation), CXXUnit->getASTContext(), TU); +} + +void clang_RefactoringContinuation_dispose( + CXRefactoringContinuation Continuation) { + if (Continuation) + delete static_cast<RefactoringContinuationWrapper *>(Continuation); +} + +enum CXIndexerQueryKind clang_IndexerQuery_getKind(CXIndexerQuery Query) { + if (!Query) + return CXIndexerQuery_Unknown; + const auto *IQ = + static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query; + if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) { + const indexer::detail::DeclPredicateNode &Node = DQ->getPredicateNode(); + if (const auto *NP = + dyn_cast<indexer::detail::DeclPredicateNotPredicate>(&Node)) + return translateDeclPredicate( + cast<indexer::detail::DeclPredicateNodePredicate>(NP->getChild()) + .getPredicate()); + return translateDeclPredicate( + cast<indexer::detail::DeclPredicateNodePredicate>(Node).getPredicate()); + } else if (isa<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) + return CXIndexerQuery_Decl_FileThatShouldImplement; + return CXIndexerQuery_Unknown; +} + +unsigned clang_IndexerQuery_getNumCursors(CXIndexerQuery Query) { + if (!Query) + return 0; + const auto *IQ = + static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query; + if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) + return DQ->getInputs().size(); + else if (isa<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) + return 1; + return 0; +} + +CXCursor clang_IndexerQuery_getCursor(CXIndexerQuery Query, + unsigned CursorIndex) { + if (Query) { + const auto *Wrapper = + static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query); + const indexer::IndexerQuery *IQ = Wrapper->Query; + CXTranslationUnit TU = Wrapper->TU; + if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) { + if (CursorIndex < DQ->getInputs().size()) + return cxcursor::MakeCXCursor(DQ->getInputs()[CursorIndex], TU); + } else if (const auto *ASTQuery = dyn_cast< + indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) { + if (CursorIndex == 0) + return cxcursor::MakeCXCursor(ASTQuery->getDecl(), TU); + } + } + return cxcursor::MakeCXCursorInvalid(CXCursor_InvalidCode); +} + +enum CXIndexerQueryAction +clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex, + int Value) { + if (!Query) + return CXIndexerQueryAction_None; + auto *Wrapper = + static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query); + auto *DQ = dyn_cast<indexer::DeclarationsQuery>(Wrapper->Query); + if (!DQ) + return CXIndexerQueryAction_None; + if (CursorIndex >= DQ->getInputs().size() || + Wrapper->ConsumedResults == DQ->getInputs().size()) + return CXIndexerQueryAction_None; + if (Wrapper->DeclResults.empty()) + Wrapper->DeclResults.resize(DQ->getInputs().size(), + PersistentDeclRef<Decl>::create(nullptr)); + // Filter the declarations! + bool IsNot = false; + if (isa<indexer::detail::DeclPredicateNotPredicate>(DQ->getPredicateNode())) + IsNot = true; + Wrapper->DeclResults[CursorIndex] = PersistentDeclRef<Decl>::create( + (IsNot ? !Value : !!Value) ? DQ->getInputs()[CursorIndex] : nullptr); + Wrapper->ConsumedResults++; + if (Wrapper->ConsumedResults == Wrapper->DeclResults.size()) { + // We've received all the results, pass them back to the query. + DQ->setOutput(std::move(Wrapper->DeclResults)); + } + return CXIndexerQueryAction_None; +} + +enum CXIndexerQueryAction +clang_IndexerQuery_consumeFileResult(CXIndexerQuery Query, unsigned CursorIndex, + const char *Filename) { + if (!Query || !Filename) + return CXIndexerQueryAction_None; + auto *IQ = + static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query; + if (auto *ASTQuery = + dyn_cast<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) { + if (CursorIndex != 0) + return CXIndexerQueryAction_None; + ASTQuery->setResult(PersistentFileID(Filename)); + return CXIndexerQueryAction_RunContinuationInTUThatHasThisFile; + } + return CXIndexerQueryAction_None; +} +} diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports index 187d4749ebc13..4f2335ce77f35 100644 --- a/clang/tools/libclang/libclang.exports +++ b/clang/tools/libclang/libclang.exports @@ -350,3 +350,56 @@ clang_EvalResult_isUnsignedInt clang_EvalResult_getAsDouble clang_EvalResult_getAsStr clang_EvalResult_dispose +clang_RefactoringActionType_getName +clang_RefactoringActionSet_dispose +clang_RefactoringActionSetWithDiagnostics_dispose +clang_RefactoringOptionSet_create +clang_RefactoringOptionSet_createFromString +clang_RefactoringOptionSet_add +clang_RefactoringOptionSet_toString +clang_RefactoringOptionSet_dispose +clang_Refactoring_findActionsAt +clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt +clang_RefactoringAction_dispose +clang_RefactoringAction_getSourceRangeOfInterest +clang_RefactoringAction_getInitiatedActionType +clang_RefactoringAction_requiresImplementationTU +clang_RefactoringAction_getUSRThatRequiresImplementationTU +clang_RefactoringAction_addImplementationTU +clang_RefactoringAction_getRefactoringCandidates +clang_RefactoringAction_selectRefactoringCandidate +clang_Refactoring_initiateActionAt +clang_Refactoring_initiateAction +clang_Refactoring_initiateActionOnDecl +clang_Refactoring_initiateRenamingOperation +clang_RenamingOperation_setNewName +clang_RenamingOperation_getNumSymbols +clang_RenamingOperation_getUSRForSymbol +clang_Refactoring_findRenamedCursor +clang_Refactoring_findRenamedOccurrencesInPrimaryTUs +clang_Refactoring_findSymbolOccurrencesInInitiationTU +clang_Refactoring_findRenamedOccurrencesInIndexedFile +clang_Refactoring_findSymbolOccurrencesInIndexedFile +clang_RenamingResult_getNumModifiedFiles +clang_RenamingResult_getResultForFile +clang_RenamingResult_dispose +clang_SymbolOccurrences_getNumFiles +clang_SymbolOccurrences_getOccurrencesForFile +clang_SymbolOccurrences_dispose +clang_Refactoring_performOperation +clang_RefactoringResult_getReplacements +clang_RefactoringResult_getSourceReplacements +clang_RefactoringReplacement_getAssociatedSymbolOccurrences +clang_RefactoringResult_getContinuation +clang_RefactoringResult_dispose +clang_RefactoringContinuation_loadSerializedIndexerQueryResults +clang_RefactoringContinuation_getNumIndexerQueries +clang_RefactoringContinuation_getIndexerQuery +clang_RefactoringContinuation_finalizeEvaluationInInitationTU +clang_RefactoringContinuation_continueOperationInTU +clang_RefactoringContinuation_dispose +clang_IndexerQuery_getKind +clang_IndexerQuery_getNumCursors +clang_IndexerQuery_getCursor +clang_IndexerQuery_consumeIntResult +clang_IndexerQuery_consumeFileResult diff --git a/clang/unittests/Tooling/RefactoringTest.cpp b/clang/unittests/Tooling/RefactoringTest.cpp index 495ac755b39d9..10849af7ac74f 100644 --- a/clang/unittests/Tooling/RefactoringTest.cpp +++ b/clang/unittests/Tooling/RefactoringTest.cpp @@ -25,6 +25,9 @@ #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Refactor/IndexerQuery.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "clang/Tooling/Refactor/RefactoringOptions.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Refactoring/AtomicChange.h" #include "clang/Tooling/Tooling.h" @@ -1090,6 +1093,51 @@ TEST(DeduplicateByFileTest, NonExistingFilePath) { EXPECT_TRUE(FileToReplaces.empty()); } +namespace { +struct TestRefactoringValueOption final : RefactoringOption { + int Value; + TestRefactoringValueOption(int Value) : Value(Value) {} + + static constexpr const char *Name = "test value option"; +}; +} // end anonymous namespace + +TEST(RefactoringOptionSet, AddGet) { + RefactoringOptionSet Options; + const TestRefactoringValueOption Kind(21); + const TestRefactoringValueOption DefaultKind(42); + + EXPECT_EQ(Options.get<TestRefactoringValueOption>(), nullptr); + EXPECT_EQ(Options.get(DefaultKind).Value, DefaultKind.Value); + + Options.add(Kind); + + auto *Ptr = Options.get<TestRefactoringValueOption>(); + ASSERT_TRUE(Ptr); + EXPECT_EQ(Ptr->Value, Kind.Value); + EXPECT_EQ(Options.get(DefaultKind).Value, Kind.Value); +} + +namespace { +struct TestRefactoringOption final : RefactoringOption { + int &Counter; + TestRefactoringOption(int &Counter) : Counter(Counter) {} + ~TestRefactoringOption() { ++Counter; } + + static constexpr const char *Name = "test option"; +}; +} // end anonymous namespace + +TEST(RefactoringOptionSet, OptionDestroyed) { + int Counter = 0; + { + RefactoringOptionSet Options; + Options.add(TestRefactoringOption(Counter)); + Options.add(TestRefactoringOption(Counter)); + } + EXPECT_EQ(Counter, 3); +} + class AtomicChangeTest : public ::testing::Test { protected: void SetUp() override { @@ -1291,5 +1339,127 @@ TEST_F(AtomicChangeTest, InsertAfterWithInvalidLocation) { Replacement(Context.Sources, SourceLocation(), 0, "b"))); } +namespace { + +class RefactoringOperationTest { + RefactoringActionType Type; + unsigned Line, Column; + bool Success = true; + std::function<void(const RefactoringResult &Result)> ResultHandler; + +public: + RefactoringOperationTest( + RefactoringActionType Type, unsigned Line, unsigned Column, + std::function<void(const RefactoringResult &Result)> ResultHandler) + : Type(Type), Line(Line), Column(Column), + ResultHandler(std::move(ResultHandler)) {} + + bool runOver(StringRef Code) { + return runToolOnCode(new TestAction(this), Code); + } + + bool succeeded() const { return Success; } + + void run() { + assert(PP && Context && "Invalid state"); + SourceLocation Loc = Context->getSourceManager().translateLineCol( + Context->getSourceManager().getMainFileID(), Line, Column); + if (Loc.isInvalid()) { + Success = false; + return; + } + RefactoringOperationResult Op = + initiateRefactoringOperationAt(Loc, SourceRange(), *Context, Type); + if (!Op.Initiated) { + Success = false; + return; + } + RefactoringOptionSet Options; + llvm::Expected<RefactoringResult> Result = + Op.RefactoringOp->perform(*Context, *PP, Options); + if (!Result) { + (void)!llvm::handleErrors( + Result.takeError(), + [&](const RefactoringOperationError &Error) { Success = false; }); + return; + } + ResultHandler(Result.get()); + } + +protected: + clang::Preprocessor *PP; + clang::ASTContext *Context; + +private: + class TestConsumer : public clang::ASTConsumer { + public: + TestConsumer(RefactoringOperationTest *Test) : Test(Test) {} + + void HandleTranslationUnit(clang::ASTContext &Context) override { + Test->run(); + } + + private: + RefactoringOperationTest *Test; + }; + + class TestAction : public clang::ASTFrontendAction { + public: + TestAction(RefactoringOperationTest *Test) : Test(Test) {} + + std::unique_ptr<clang::ASTConsumer> + CreateASTConsumer(clang::CompilerInstance &Compiler, + llvm::StringRef) override { + Test->PP = &Compiler.getPreprocessor(); + Test->Context = &Compiler.getASTContext(); + return llvm::make_unique<TestConsumer>(Test); + } + + private: + RefactoringOperationTest *Test; + }; +}; + +} // end anonymous namespace + +TEST(RefactoringContinuation, ContinuationAndQueriesExist) { + using namespace clang::tooling::indexer; + using namespace clang::tooling::indexer::detail; + RefactoringOperationTest Test( + RefactoringActionType::ImplementDeclaredMethods, 2, 1, + [](const RefactoringResult &Result) { + EXPECT_TRUE(Result.Replacements.empty()); + ASSERT_NE(Result.Continuation, nullptr); + RefactoringContinuation &Continuation = *Result.Continuation; + + ASTProducerQuery *ASTQuery = Continuation.getASTUnitIndexerQuery(); + ASSERT_NE(ASTQuery, nullptr); + EXPECT_TRUE(isa<ASTProducerQuery>(ASTQuery)); + EXPECT_TRUE(isa<ASTUnitForImplementationOfDeclarationQuery>(ASTQuery)); + EXPECT_FALSE(isa<DeclarationsQuery>(ASTQuery)); + + auto AdditionalQueries = Continuation.getAdditionalIndexerQueries(); + ASSERT_EQ(AdditionalQueries.size(), (size_t)1); + EXPECT_FALSE(isa<ASTProducerQuery>(AdditionalQueries[0])); + EXPECT_FALSE(isa<ASTUnitForImplementationOfDeclarationQuery>( + AdditionalQueries[0])); + ASSERT_TRUE(isa<DeclarationsQuery>(AdditionalQueries[0])); + + const DeclPredicateNode &Predicate = + cast<DeclarationsQuery>(AdditionalQueries[0])->getPredicateNode(); + ASSERT_TRUE(isa<DeclPredicateNotPredicate>(Predicate)); + const DeclPredicateNode &SubPredicate = + cast<DeclPredicateNotPredicate>(Predicate).getChild(); + ASSERT_TRUE(isa<DeclPredicateNodePredicate>(SubPredicate)); + EXPECT_EQ(cast<DeclPredicateNodePredicate>(SubPredicate).getPredicate(), + DeclEntity().isDefined().Predicate); + + ASTQuery->invalidateTUSpecificState(); + AdditionalQueries[0]->invalidateTUSpecificState(); + }); + Test.runOver("class Foo {\nvoid method();\n};\n"); + EXPECT_TRUE(Test.succeeded()); +} + } // end namespace tooling } // end namespace clang diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp index f2a96d6be6c17..d5a9cbf1222fa 100644 --- a/clang/unittests/libclang/LibclangTest.cpp +++ b/clang/unittests/libclang/LibclangTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "clang-c/Index.h" +#include "clang-c/Refactor.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -572,3 +573,71 @@ TEST_F(LibclangReparseTest, clang_parseTranslationUnit2FullArgv) { EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU)); DisplayDiagnostics(); } + +TEST(libclang, RefactoringAction) { + std::string Name = + clang_getCString(clang_RefactoringActionType_getName(CXRefactor_Rename)); + EXPECT_EQ(Name, "Rename"); +} + +TEST_F(LibclangParseTest, RefactoringFindRenamedCursor) { + std::string Filename = "test.cpp"; + WriteFile(Filename, "int renamable = 0;\n"); + + ClangTU = clang_parseTranslationUnit(Index, Filename.c_str(), nullptr, 0, + nullptr, 0, TUFlags); + CXSourceLocation Loc = clang_getLocation( + ClangTU, clang_getFile(ClangTU, Filename.c_str()), 1, 5); + CXSourceRange Range = clang_getRange(Loc, Loc); + CXCursor Cursor; + EXPECT_EQ(CXError_Success, + clang_Refactoring_findRenamedCursor(ClangTU, Loc, Range, &Cursor)); + EXPECT_EQ(Cursor.kind, CXCursor_VarDecl); +} + +TEST_F(LibclangParseTest, RefactoringRenameIndexedUnsavedFiles) { + std::string Filename = "test.cpp"; + std::string PartialSource = "class Test { };\n"; + WriteFile(Filename, PartialSource); + std::string FullSource = PartialSource + "Test t;\n"; + + CXIndexedSymbolLocation IndexedLocations[2] = { + {{1, 7}, CXCursor_DeclRefExpr}, {{2, 1}, CXCursor_DeclRefExpr}}; + CXIndexedSymbol Symbols[1] = { + {IndexedLocations, 2, CXCursor_DeclRefExpr, /*Name=*/"Test"}}; + + CXIndex Idx = clang_createIndex(0, 0); + + auto test = [&](CXUnsavedFile *File = nullptr) -> CXSymbolOccurrencesInFile { + CXSymbolOccurrencesResult Result; + CXErrorCode Err = clang_Refactoring_findSymbolOccurrencesInIndexedFile( + Symbols, 1, Idx, Filename.c_str(), nullptr, 0, File, File ? 1 : 0, + /*Options=*/nullptr, &Result); + EXPECT_EQ(CXError_Success, Err); + unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Result); + EXPECT_EQ(NumFiles, 1u); + CXSymbolOccurrencesInFile Occurrences; + clang_SymbolOccurrences_getOccurrencesForFile(Result, 0, &Occurrences); + return Occurrences; + }; + CXSymbolOccurrencesInFile FileOccurrences = test(); + EXPECT_EQ(FileOccurrences.NumOccurrences, 1u); + EXPECT_EQ(clang_getCString(FileOccurrences.Filename), Filename); + EXPECT_EQ(FileOccurrences.Occurrences[0].NumNamePieces, 1u); + EXPECT_EQ(FileOccurrences.Occurrences[0].NamePieces[0].Begin.Line, 1u); + EXPECT_EQ(FileOccurrences.Occurrences[0].NamePieces[0].Begin.Column, 7u); + + CXUnsavedFile UnsavedFile = {Filename.c_str(), FullSource.c_str(), + FullSource.size()}; + CXSymbolOccurrencesInFile UnsavedFileOccurrences = test(&UnsavedFile); + EXPECT_EQ(UnsavedFileOccurrences.NumOccurrences, 2u); + EXPECT_EQ(clang_getCString(UnsavedFileOccurrences.Filename), Filename); + EXPECT_EQ(UnsavedFileOccurrences.Occurrences[0].NumNamePieces, 1u); + EXPECT_EQ(UnsavedFileOccurrences.Occurrences[0].NamePieces[0].Begin.Line, 1u); + EXPECT_EQ(UnsavedFileOccurrences.Occurrences[0].NamePieces[0].Begin.Column, + 7u); + EXPECT_EQ(UnsavedFileOccurrences.Occurrences[1].NumNamePieces, 1u); + EXPECT_EQ(UnsavedFileOccurrences.Occurrences[1].NamePieces[0].Begin.Line, 2u); + EXPECT_EQ(UnsavedFileOccurrences.Occurrences[1].NamePieces[0].Begin.Column, + 1u); +} From 18694022ed9988793305f4bf559176561c464ae4 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 15 Jun 2017 17:04:51 -0700 Subject: [PATCH 157/582] Remove .rej file accidentally committed in the previous commit apple-llvm-split-commit: 1fa921f1e3fe009ac6dbf374fae13ac82721e102 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h.rej | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 clang/include/clang/Basic/DiagnosticIDs.h.rej diff --git a/clang/include/clang/Basic/DiagnosticIDs.h.rej b/clang/include/clang/Basic/DiagnosticIDs.h.rej deleted file mode 100644 index f81c0605abf59..0000000000000 --- a/clang/include/clang/Basic/DiagnosticIDs.h.rej +++ /dev/null @@ -1,18 +0,0 @@ -*************** -*** 37,43 **** - DIAG_START_COMMENT = DIAG_START_AST + 110, - DIAG_START_SEMA = DIAG_START_COMMENT + 100, - DIAG_START_ANALYSIS = DIAG_START_SEMA + 3500, -- DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100 - }; - - class CustomDiagInfo; ---- 37,44 ---- - DIAG_START_COMMENT = DIAG_START_AST + 110, - DIAG_START_SEMA = DIAG_START_COMMENT + 100, - DIAG_START_ANALYSIS = DIAG_START_SEMA + 3500, -+ DIAG_START_REFACTORING = DIAG_START_ANALYSIS + 100, -+ DIAG_UPPER_LIMIT = DIAG_START_REFACTORING + 100 - }; - - class CustomDiagInfo; From 35854d47b627af2900228564fb23b896040db54a Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 21 Jun 2017 10:56:08 +0100 Subject: [PATCH 158/582] [refactor] Add missing internal change to include/clang/Basic/DiagnosticIDs.h This change ensures that 'upstream-with-swift' can build again rdar://32878541 apple-llvm-split-commit: 94152766d32a3a9de8f9c891c6e82a659001137c apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 22e95fee95381..b964595076eae 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -37,7 +37,8 @@ namespace clang { DIAG_START_COMMENT = DIAG_START_AST + 110, DIAG_START_SEMA = DIAG_START_COMMENT + 100, DIAG_START_ANALYSIS = DIAG_START_SEMA + 4000, - DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100 + DIAG_START_REFACTORING = DIAG_START_ANALYSIS + 100, + DIAG_UPPER_LIMIT = DIAG_START_REFACTORING + 100 }; class CustomDiagInfo; From 912350b72a8d58da22c7b3e6f0a42a5c86dfb9a7 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 20 Jun 2017 13:43:17 +0100 Subject: [PATCH 159/582] [refactor] "Wrap in NSLocalizedString" should work with macro arguments rdar://32015671 apple-llvm-split-commit: 31fe591c33b051dca234ed3928df6dbdcea396a5 apple-llvm-split-dir: clang/ --- .../Tooling/Refactor/LocalizeObjCStringLiteral.cpp | 8 ++++++-- .../LocalizeObjCStringLiteral/localize-objc-perform.m | 11 +++++++++++ clang/tools/libclang/CRefactor.cpp | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp index d2ad11ec15366..08cf41dbbb1c1 100644 --- a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp +++ b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp @@ -69,11 +69,15 @@ LocalizeObjCStringLiteralOperation::perform(ASTContext &Context, const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { std::vector<RefactoringReplacement> Replacements; - SourceLocation LocStart = E->getLocStart(); + // TODO: New API: Replace by something like Node.wrap("NSLocalizedString(", ", + // @""") + SourceLocation LocStart = + Context.getSourceManager().getSpellingLoc(E->getLocStart()); Replacements.emplace_back(SourceRange(LocStart, LocStart), StringRef("NSLocalizedString(")); SourceLocation LocEnd = getPreciseTokenLocEnd( - E->getLocEnd(), Context.getSourceManager(), Context.getLangOpts()); + Context.getSourceManager().getSpellingLoc(E->getLocEnd()), + Context.getSourceManager(), Context.getLangOpts()); Replacements.emplace_back(SourceRange(LocEnd, LocEnd), StringRef(", @\"\")")); return std::move(Replacements); } diff --git a/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m b/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m index 8c0de42faa39a..96095deffc4e5 100644 --- a/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m +++ b/clang/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m @@ -7,3 +7,14 @@ void perform() { // CHECK1-NEXT: ", @"")" [[@LINE-3]]:30 -> [[@LINE-3]]:30 // RUN: clang-refactor-test perform -action localize-objc-string-literal -at=%s:4:22 %s | FileCheck --check-prefix=CHECK1 %s // RUN: clang-refactor-test perform -action localize-objc-string-literal -selected=%s:4:23-4:30 %s | FileCheck --check-prefix=CHECK1 %s + +#define MACRO(x, y) x + +void performInMacroArgument() { + // macro-arg: +2:9 + // macro-arg-range-begin: +1:9 + MACRO(@"hello", 1); // CHECK2: "NSLocalizedString(" [[@LINE]]:9 -> [[@LINE]]:9 + // macro-arg-range-end: -1:17 // CHECK2: ", @"")" [[@LINE-1]]:17 -> [[@LINE-1]]:17 +} +// RUN: clang-refactor-test perform -action localize-objc-string-literal -at=macro-arg %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action localize-objc-string-literal -selected=macro-arg-range %s | FileCheck --check-prefix=CHECK2 %s diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index 7331c3a53abb0..098e685f880e3 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -859,6 +859,7 @@ class RefactoringResultWrapper { SourceLocation Loc = Replacement.value().Range.getBegin(); const std::pair<FileID, unsigned> DecomposedLocation = SM.getDecomposedLoc(Loc); + assert(DecomposedLocation.first.isValid() && "Invalid file!"); const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); FilesToReplacements.try_emplace(Entry, std::vector<unsigned>()) .first->second.push_back(Replacement.index()); From eaccec298b5f24cc69d07e60c38a58fa7e7b98fd Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 20 Jun 2017 14:03:24 +0100 Subject: [PATCH 160/582] [refactor] "Fill in missing switch cases" should work with macro arguments rdar://32015671 apple-llvm-split-commit: 06e56e53a08d1221fce62d75b33293bc5f5a1210 apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingSwitchEnumCases.cpp | 3 ++- .../FillInEnumSwitchCases/fill-in-cases-perform.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp index e2dcdb1f36d65..ec10c1ebd451d 100644 --- a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -136,7 +136,8 @@ void edit::fillInMissingSwitchEnumCases( else InsertionLoc = Switch->getBody()->getLocEnd(); } - Consumer(FixItHint::CreateInsertion(InsertionLoc, OS.str())); + Consumer(FixItHint::CreateInsertion( + Context.getSourceManager().getSpellingLoc(InsertionLoc), OS.str())); }; // Determine which enum cases are uncovered. diff --git a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp index 76ab1c2e0a547..70aa585cad20c 100644 --- a/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp +++ b/clang/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp @@ -78,3 +78,16 @@ void perform1(PREFIX Color c) { // RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 -D NESTED1NS -D ENUMCLASS | FileCheck --check-prefix=CHECK6 %s // RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D NESTEDANON | FileCheck --check-prefix=CHECK1 %s // RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D NESTEDANON -D ENUMCLASS | FileCheck --check-prefix=CHECK2 %s + +#define MACROARG(X) X + +void macroArg(PREFIX Color c) { + // macro-arg: +2:12 + // macro-arg-range-begin: +1:12 + MACROARG(switch (c) { + }); // MACRO-ARG: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE]] + // macro-arg-range-end: -1:4 +} + +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=macro-arg %s | FileCheck --check-prefix=MACRO-ARG %s +// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -selected=macro-arg-range %s | FileCheck --check-prefix=MACRO-ARG %s From 08d72d76752d3e31855f4d0b5ddc3c89912648e5 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 20 Jun 2017 14:22:55 +0100 Subject: [PATCH 161/582] [refactor] "Convert to switch" should work with macro arguments rdar://32015671 apple-llvm-split-commit: 2590c521bb0ca983c3bc11d9d8394542f47143c5 apple-llvm-split-dir: clang/ --- .../Tooling/Refactor/IfSwitchConversion.cpp | 40 +++++++++++-------- .../if-switch-conversion-perform.cpp | 14 +++++++ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp index 1a284b8d49814..5ce39552876ab 100644 --- a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -302,8 +302,9 @@ struct CasePlacement { CasePlacement(const IfStmt *If, const SourceManager &SM, bool AreBracesNeeded) { - CaseStartLoc = isa<CompoundStmt>(If->getThen()) ? If->getThen()->getLocEnd() - : If->getElseLoc(); + CaseStartLoc = SM.getSpellingLoc(isa<CompoundStmt>(If->getThen()) + ? If->getThen()->getLocEnd() + : If->getElseLoc()); SourceLocation BodyEndLoc = findLastNonCompoundLocation(If->getThen()); NeedsNewLine = BodyEndLoc.isValid() ? areOnSameLine(CaseStartLoc, BodyEndLoc, SM) @@ -343,16 +344,18 @@ addCaseReplacements(const IfStmt *If, const CasePlacement &CaseInfo, gatherCaseValues(If->getCond(), CaseValues); assert(!CaseValues.empty()); Replacements.emplace_back( - SourceRange(CaseInfo.CaseStartLoc, CaseValues[0]->getLocStart()), + SourceRange(CaseInfo.CaseStartLoc, + SM.getSpellingLoc(CaseValues[0]->getLocStart())), CaseInfo.getCaseReplacementString()); - SourceLocation PrevCaseEnd = - getPreciseTokenLocEnd(CaseValues[0]->getLocEnd(), SM, LangOpts); + SourceLocation PrevCaseEnd = getPreciseTokenLocEnd( + SM.getSpellingLoc(CaseValues[0]->getLocEnd()), SM, LangOpts); for (const Expr *CaseValue : llvm::makeArrayRef(CaseValues).drop_front()) { Replacements.emplace_back( - SourceRange(PrevCaseEnd, CaseValue->getLocStart()), + SourceRange(PrevCaseEnd, SM.getSpellingLoc(CaseValue->getLocStart())), StringRef(":\ncase ")); - PrevCaseEnd = getPreciseTokenLocEnd(CaseValue->getLocEnd(), SM, LangOpts); + PrevCaseEnd = getPreciseTokenLocEnd( + SM.getSpellingLoc(CaseValue->getLocEnd()), SM, LangOpts); } AreBracesNeeded = areBracesNeeded(If->getThen()); @@ -361,12 +364,13 @@ addCaseReplacements(const IfStmt *If, const CasePlacement &CaseInfo, Replacements.emplace_back( SourceRange( PrevCaseEnd, - getPreciseTokenLocEnd(If->getThen()->getLocStart(), SM, LangOpts)), + getPreciseTokenLocEnd( + SM.getSpellingLoc(If->getThen()->getLocStart()), SM, LangOpts)), ColonReplacement); } else { // Find the location of the if's ')' - SourceLocation End = - findClosingParenLocEnd(If->getCond()->getLocEnd(), SM, LangOpts); + SourceLocation End = findClosingParenLocEnd( + SM.getSpellingLoc(If->getCond()->getLocEnd()), SM, LangOpts); if (!End.isValid()) return llvm::make_error<RefactoringOperationError>( "couldn't find the location of ')'"); @@ -388,13 +392,14 @@ IfSwitchConversionOperation::perform(ASTContext &Context, // should be preserved. const Expr *LHS = getConditionFirstLHS(If->getCond()); assert(LHS && "Missing == expression"); - Replacements.emplace_back(SourceRange(If->getLocStart(), LHS->getLocStart()), + Replacements.emplace_back(SourceRange(SM.getSpellingLoc(If->getLocStart()), + SM.getSpellingLoc(LHS->getLocStart())), StringRef("switch (")); bool AreBracesNeeded = false; if (auto Error = addCaseReplacements( - If, - CasePlacement(getPreciseTokenLocEnd(LHS->getLocEnd(), SM, LangOpts)), + If, CasePlacement(getPreciseTokenLocEnd( + SM.getSpellingLoc(LHS->getLocEnd()), SM, LangOpts)), AreBracesNeeded, Replacements, SM, LangOpts)) return std::move(Error); @@ -415,8 +420,10 @@ IfSwitchConversionOperation::perform(ASTContext &Context, if (const Stmt *Else = CurrentIf->getElse()) { CasePlacement DefaultInfo(CurrentIf, SM, AreBracesNeeded); AreBracesNeeded = areBracesNeeded(Else); + SourceLocation EndLoc = getPreciseTokenLocEnd( - isa<CompoundStmt>(Else) ? Else->getLocStart() : CurrentIf->getElseLoc(), + SM.getSpellingLoc(isa<CompoundStmt>(Else) ? Else->getLocStart() + : CurrentIf->getElseLoc()), SM, LangOpts); Replacements.emplace_back(SourceRange(DefaultInfo.CaseStartLoc, EndLoc), DefaultInfo.getCaseReplacementString( @@ -450,11 +457,12 @@ IfSwitchConversionOperation::perform(ASTContext &Context, OS << "}\n"; } - if (!OS.str().empty()) + if (!OS.str().empty()) { + TerminatingReplacementLoc = SM.getSpellingLoc(TerminatingReplacementLoc); Replacements.emplace_back( SourceRange(TerminatingReplacementLoc, TerminatingReplacementLoc), std::move(OS.str())); + } - // TODO: verify replacements (no macro locs + ordered). return std::move(Replacements); } diff --git a/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp b/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp index a16e165498696..9874795cb9045 100644 --- a/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp +++ b/clang/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp @@ -305,3 +305,17 @@ void noBracesNeeded(int x) { } // RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:253:3 -at=%s:269:3 -at=%s:275:3 -at=%s:288:3 -at=%s:295:3 %s | FileCheck --check-prefix=CHECK8 %s + +#define MACRO(X) X + +void macroArg(int x) { + // macro-arg: +1:9 + MACRO(if (x == 2) { // MACRO-ARG: "switch (" [[@LINE]]:9 -> [[@LINE]]:13 + ; // MACRO-ARG: ") {\ncase " [[@LINE-1]]:14 -> [[@LINE-1]]:18 + // MACRO-ARG: ":" [[@LINE-2]]:19 -> [[@LINE-2]]:22 + } else if (x == 3) { // MACRO-ARG: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:19 + ; // MACRO-ARG: ":" [[@LINE-1]]:20 -> [[@LINE-1]]:23 + }); // MACRO-ARG: "break;\n" [[@LINE]]:3 -> [[@LINE]]:3 +} + +// RUN: clang-refactor-test perform -action if-switch-conversion -at=macro-arg %s | FileCheck --check-prefix=MACRO-ARG %s From a6667e8741dec44e06859df432b0c8c51eb2dab7 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 20 Jun 2017 15:00:02 +0100 Subject: [PATCH 162/582] [refactor] "Extract Repeated Expression" should work with macro arguments Also we shouldn't count repeated expressions in macros. rdar://32015671 apple-llvm-split-commit: 392c96e4e08f8f6bd21903e0a032d15bcd2581d1 apple-llvm-split-dir: clang/ --- .../ExtractRepeatedExpressionIntoVariable.cpp | 31 ++++++++++++++----- .../extract-repeated-expr-initiate.m | 12 +++++++ .../extract-repeated-expr-perform.m | 18 +++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index 87b65a325c28f..2f46e0e18c70c 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -88,6 +88,7 @@ class DuplicateExprSemanticProfiler class DuplicateExprFinder : public RecursiveASTVisitor<DuplicateExprFinder>, PrinterHelper { const Expr *Target; + const ASTContext &Context; const PrintingPolicy &PP; Stmt::StmtClass ExprKind; QualType T; @@ -102,8 +103,10 @@ class DuplicateExprFinder : public RecursiveASTVisitor<DuplicateExprFinder>, public: SmallVector<const Expr *, 4> DuplicateExpressions; - DuplicateExprFinder(const Expr *E, const PrintingPolicy &PP) - : Target(E), PP(PP), ExprKind(E->getStmtClass()), T(E->getType()) { + DuplicateExprFinder(const Expr *E, const ASTContext &Context, + const PrintingPolicy &PP) + : Target(E), Context(Context), PP(PP), ExprKind(E->getStmtClass()), + T(E->getType()) { printExpr(ExprString, E); DuplicateExprSemanticProfiler(ExprDecls).TraverseStmt( const_cast<Expr *>(E)); @@ -127,6 +130,16 @@ class DuplicateExprFinder : public RecursiveASTVisitor<DuplicateExprFinder>, DuplicateExpressions.push_back(E); return true; } + // The expression should not be in a macro. + SourceRange R = E->getSourceRange(); + if (R.getBegin().isMacroID()) { + if (!Context.getSourceManager().isMacroArgExpansion(R.getBegin())) + return true; + } + if (R.getEnd().isMacroID()) { + if (!Context.getSourceManager().isMacroArgExpansion(R.getEnd())) + return true; + } // The expression types should match. if (E->getType() != T) return true; @@ -204,7 +217,7 @@ clang::tooling::initiateExtractRepeatedExpressionIntoVariableOperation( (!T->isAnyPointerType() && !T->isReferenceType())) return None; - DuplicateExprFinder DupFinder(E, Context.getPrintingPolicy()); + DuplicateExprFinder DupFinder(E, Context, Context.getPrintingPolicy()); DupFinder.TraverseDecl(const_cast<Decl *>(ParentDecl)); if (DupFinder.DuplicateExpressions.size() < 2) return None; @@ -354,15 +367,17 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( OS << " = "; E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy()); OS << ";\n"; - Replacements.emplace_back(SourceRange(LocFinder.Loc, LocFinder.Loc), - OS.str()); + SourceLocation InsertionLoc = LocFinder.Loc; + if (InsertionLoc.isMacroID()) + InsertionLoc = SM.getExpansionLoc(InsertionLoc); + Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc), OS.str()); // Replace the duplicates with a reference to the variable. for (const Expr *E : DuplicateExpressions) Replacements.emplace_back( - SourceRange( - E->getLocStart(), - getPreciseTokenLocEnd(E->getLocEnd(), SM, Context.getLangOpts())), + SourceRange(SM.getSpellingLoc(E->getLocStart()), + getPreciseTokenLocEnd(SM.getSpellingLoc(E->getLocEnd()), SM, + Context.getLangOpts())), Name); return std::move(Replacements); diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m index 1cd28efa13c56..11df71f6f4347 100644 --- a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m @@ -92,3 +92,15 @@ void implicitPropertyWithoutGetter(ImplicitPropertyWithoutGetter *x) { } // RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -at=%s:90:3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s + +// Prohibit ininiation in macros: + +#define MACROREF(X) X.object + +void prohibitMacroExpr(Wrapper *wrapper) { + // macro-prohibited: +1:3 + wrapper.object.prop = 0; + MACROREF(wrapper).prop = 1; +} + +// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -at=macro-prohibited %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m index 723438d59345b..23ed359b61df6 100644 --- a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m @@ -78,3 +78,21 @@ void worksOnClassProperty() { // CHECK5-NEXT: "classObject" [[@LINE-4]]:3 -> [[@LINE-4]]:22 // RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:73:3 %s | FileCheck --check-prefix=CHECK5 %s + +#define MACROARG1(X, Y) (X) +#define MACROARG2(X) X; ref.object.prop = 0 + +void macroArgument(Wrapper *ref) { + // macro-arg1: +1:13 + MACROARG1(Wrapper.classObject->ivar, 1); // MACRO-ARG1: "Object *classObject = Wrapper.classObject;\n" [[@LINE]]:3 -> [[@LINE]]:3 + // MACRO-ARG1-NEXT: "classObject" [[@LINE-1]]:13 -> [[@LINE-1]]:32 + MACROARG1(Wrapper.classObject.prop, 0) = 0;// MACRO-ARG1-NEXT: "classObject" [[@LINE]]:13 -> [[@LINE]]:32 + + // macro-arg2: +3:13 + MACROARG2(int x = 0; ref.object->ivar); // MACRO-ARG2: "Object *object = ref.object;\n" [[@LINE]]:3 -> [[@LINE]]:3 + // MACRO-ARG2-NEXT: "object" [[@LINE-1]]:24 -> [[@LINE-1]]:34 + MACROARG2(ref.object.prop);// MARO-ARG2-NEXT: "object" [[@LINE]]:13 -> [[@LINE]]:23 +} + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=macro-arg1 %s | FileCheck --check-prefix=MACRO-ARG1 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=macro-arg2 %s | FileCheck --check-prefix=MACRO-ARG2 %s From 29931ec135aaca97ce86b043f07c22a967127dd0 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 20 Jun 2017 15:35:28 +0100 Subject: [PATCH 163/582] [refactor] "Generate Missing Function Definitions" should generate stubs in @implementation for a declaration in a class extension rdar://32869347 apple-llvm-split-commit: 3845806e35f24d0dd0fb842ab62f1ce6532ee955 apple-llvm-split-dir: clang/ --- .../Tooling/Refactor/ImplementDeclaredMethods.cpp | 6 ++++++ .../implement-declared-methods.m | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp index 5055aac32d158..ba4fb903c392c 100644 --- a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -149,6 +149,12 @@ clang::tooling::initiateImplementDeclaredMethodsOperation( CharSourceRange::getTokenRange(M->getSourceRange()))) SelectedMethods.push_back(M); } + // Method declarations from class extensions should be defined in class + // @implementations. + if (const auto *Category = dyn_cast<ObjCCategoryDecl>(Container)) { + if (Category->IsClassExtension()) + Container = Category->getClassInterface(); + } return ImplementDeclaredObjCMethodsOperation::initiate( Container, SelectedMethods, CreateOperation); } diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m index 3949e32650c48..1c330392d2066 100644 --- a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m @@ -13,6 +13,18 @@ - (void)method:(int)x with:(int)y; @end +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s + +@interface MyClass () + +// extension-methods-begin: +1:1 +- (void)anExtensionMethod; +// extension-methods-end: +0:1 + +@end + +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=extension-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-EXT %s + #ifndef NO_IMPL @implementation MyClass @@ -21,6 +33,7 @@ - (void)someOtherMethod { } @end // CHECK1: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n+ (void)classMethod { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n- (void)method:(int)x with:(int)y { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 // CHECK2: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n" [[@LINE-2]]:1 +// CHECK-EXT: "{{.*}}implement-declared-methods.m" "- (void)anExtensionMethod { \n <#code#>;\n}\n\n" [[@LINE-3]]:1 #endif // RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s // RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK2 %s From 72ae067f999825507752a0096db3571406c66c5a Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 20 Jun 2017 17:41:44 +0100 Subject: [PATCH 164/582] [refactor] Fix a crash that happens when generating a "Add Missing Switch Cases" fixit while parsing an incomplete switch rdar://32854328 apple-llvm-split-commit: c73d7189e087914fabe9acd22049af091f8e8851 apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingSwitchEnumCases.cpp | 2 ++ clang/test/FixIt/fixit-fill-in-switch-crash.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 clang/test/FixIt/fixit-fill-in-switch-crash.cpp diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp index ec10c1ebd451d..c6d59340d30ad 100644 --- a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -49,6 +49,8 @@ bool useBraces(const SwitchStmt *S) { unsigned CaseCount = 0, CompoundCasesCount = 0; for (const SwitchCase *Case = S->getSwitchCaseList(); Case; Case = Case->getNextSwitchCase(), ++CaseCount) { + if (!Case->getSubStmt()) + continue; if (isa<CompoundStmt>(Case->getSubStmt())) ++CompoundCasesCount; } diff --git a/clang/test/FixIt/fixit-fill-in-switch-crash.cpp b/clang/test/FixIt/fixit-fill-in-switch-crash.cpp new file mode 100644 index 0000000000000..635fe818addd9 --- /dev/null +++ b/clang/test/FixIt/fixit-fill-in-switch-crash.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s +// RUN: not %clang_cc1 -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s + +enum Color { + Black, Red +}; + +void dontCrashOnEmptySubStmt(Color c) { // expected-note {{to match this '{'}} + switch (c) { // expected-note {{to match this '{'}} \ + // expected-warning {{enumeration value 'Red' not handled in switch}} \ + // expected-note {{add missing switch cases}} + case Black: // CHECK: fix-it:{{.*}}:{[[@LINE+3]]:10-[[@LINE+3]]:10}:"case Red:\n<#code#>\nbreak;\n" + // expected-error@+2 {{expected expression}} + // expected-error@+1 2 {{expected '}'}} + case // From bfe258c8f7a643d0b17c66c9887c07c9587e076c Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 21 Jun 2017 13:52:53 +0100 Subject: [PATCH 165/582] [refactor] "Fill in abstract methods" should not place method groups after an implicit method that overrides a method in the abstract class This commit fixes an issue where the refactoring action "Fill in abstract methods" inserted method declarations outside of the class body because Clang decided to place them after the implicit destructor. rdar://32857076 apple-llvm-split-commit: 5463b61ddb27add0f828bb174e8f105aa1c3eaa1 apple-llvm-split-dir: clang/ --- ...nMissingMethodStubsFromAbstractClasses.cpp | 2 +- ...ll-in-missing-abstract-methods-perform.cpp | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp index 42497907ee936..ba730f68a8a20 100644 --- a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp +++ b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp @@ -156,7 +156,7 @@ static SourceLocation findInsertionLocationForMethodsFromAbstractClass( const SourceManager &SM, const LangOptions &LangOpts) { SourceLocation Loc; for (const CXXMethodDecl *M : Class->methods()) { - if (!M->isVirtual() || M->isPure()) + if (!M->isVirtual() || M->isPure() || M->isImplicit()) continue; for (const CXXMethodDecl *OM : M->overridden_methods()) { OM = OM->getCanonicalDecl(); diff --git a/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp index cc6e8e59933f6..8ae223b67ea96 100644 --- a/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp +++ b/clang/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp @@ -99,3 +99,28 @@ struct GenericSubType : GenericType<int> { // CHECK8: "void firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n\nvoid fourthMethod() override;\n\n" [[@LINE-1]]:1 // RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:91:1 -at=%s:96:1 %s | FileCheck --check-prefix=CHECK8 %s + + +struct BaseClass2 +{ + virtual ~BaseClass2(); + virtual int load() = 0; +}; + +// correct-implicit-destructor-placement: +1:1 +struct DerivedImplicitDestructorClass2 +: public BaseClass2 +{ + +}; // CHECK-DESTRUCTOR: "int load() override;\n\n" [[@LINE]]:1 + +// Don't insert methods after the destructor: +// correct-destructor-placement: +1:1 +struct DerivedExplicitDestructorClass2 +: public BaseClass2 { + ~DerivedImplicitDestructorClass2(); + + +}; // CHECK-DESTRUCTOR: "int load() override;\n\n" [[@LINE]]:1 + +// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=correct-implicit-destructor-placement -at=correct-destructor-placement %s | FileCheck --check-prefix=CHECK-DESTRUCTOR %s From d0f81be7748d5d7d9a2dea9304cc93f537a22970 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 14 Jun 2017 17:49:15 -0700 Subject: [PATCH 166/582] Revert "Temporary hack to allow making progress on master-next build failures." This reverts commit d1cd5fdf4f31d14fb20e40b4d3565fc865076e7b. apple-llvm-split-commit: b1404c0d6dca1e93d796e42244d567789effb15d apple-llvm-split-dir: clang/ --- clang/include/clang/Lex/MacroInfo.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index e5ca15f2e7170..7da1e7b41ab8d 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -266,11 +266,6 @@ class MacroInfo { void setUsedForHeaderGuard(bool Val) { UsedForHeaderGuard = Val; } - // FIXME: hack to get past build failures - unsigned getOwningModuleID() const { - return 0; - } - void dump() const; private: From 0ebc64037f659cde986f324f3f70a7c0bb29bb1c Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Mon, 19 Jun 2017 10:59:31 -0700 Subject: [PATCH 167/582] [Lex] Expose a ModuleMacro's IdentifierInfo. Usually the caller already has this information, but not always. apple-llvm-split-commit: 3f87783b0429be1d4329a7b091351eae9b6e6d7a apple-llvm-split-dir: clang/ --- clang/include/clang/Lex/MacroInfo.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/include/clang/Lex/MacroInfo.h b/clang/include/clang/Lex/MacroInfo.h index 7da1e7b41ab8d..b1621b8d2280d 100644 --- a/clang/include/clang/Lex/MacroInfo.h +++ b/clang/include/clang/Lex/MacroInfo.h @@ -510,6 +510,9 @@ class ModuleMacro : public llvm::FoldingSetNode { ID.AddPointer(II); } + /// Get the name of the macro. + IdentifierInfo *getName() const { return II; } + /// Get the ID of the module that exports this macro. Module *getOwningModule() const { return OwningModule; } From 7efef4eec59be4433f8ced542c234acdb13fdb5d Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 22 Jun 2017 16:37:34 +0100 Subject: [PATCH 168/582] NFC, move nameForExtractedVariable to ExtractionUtils Prep for rdar://32793311 apple-llvm-split-commit: 3e1c0b75f652ee4ec90262b81c56e427e254c7e1 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/CMakeLists.txt | 1 + .../ExtractRepeatedExpressionIntoVariable.cpp | 21 +++--------- .../lib/Tooling/Refactor/ExtractionUtils.cpp | 33 +++++++++++++++++++ clang/lib/Tooling/Refactor/ExtractionUtils.h | 31 +++++++++++++++++ 4 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 clang/lib/Tooling/Refactor/ExtractionUtils.cpp create mode 100644 clang/lib/Tooling/Refactor/ExtractionUtils.h diff --git a/clang/lib/Tooling/Refactor/CMakeLists.txt b/clang/lib/Tooling/Refactor/CMakeLists.txt index b65f794de4062..9828c1ac6813c 100644 --- a/clang/lib/Tooling/Refactor/CMakeLists.txt +++ b/clang/lib/Tooling/Refactor/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangToolingRefactor ASTStateSerialization.cpp Extract.cpp ExtractRepeatedExpressionIntoVariable.cpp + ExtractionUtils.cpp FillInEnumSwitchCases.cpp FillInMissingMethodStubsFromAbstractClasses.cpp FillInMissingProtocolStubs.cpp diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index 2f46e0e18c70c..4427c14bbf381 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "ExtractionUtils.h" #include "RefactoringOperations.h" #include "SourceLocationUtilities.h" #include "clang/AST/AST.h" @@ -322,22 +323,10 @@ class ExtractedVariableInsertionLocFinder } // end anonymous namespace static StringRef nameForExtractedVariable(const Expr *E) { - if (const auto *Call = dyn_cast<CallExpr>(E)) { - if (const auto *Fn = Call->getDirectCallee()) - return Fn->getName(); - } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) { - if (const auto *M = Msg->getMethodDecl()) { - if (M->getSelector().isUnarySelector()) - return M->getSelector().getNameForSlot(0); - } - } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { - if (PRE->isImplicitProperty()) { - if (const auto *M = PRE->getImplicitPropertyGetter()) - return M->getSelector().getNameForSlot(0); - } else if (const auto *Prop = PRE->getExplicitProperty()) - return Prop->getName(); - } - return "duplicate"; + auto SuggestedName = extract::nameForExtractedVariable(E); + if (!SuggestedName) + return "duplicate"; + return *SuggestedName; } llvm::Expected<RefactoringResult> diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp new file mode 100644 index 0000000000000..f1917e51926a8 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp @@ -0,0 +1,33 @@ +//===--- ExtractionUtils.cpp - Extraction helper functions ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExtractionUtils.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" + +using namespace clang; + +Optional<StringRef> tooling::extract::nameForExtractedVariable(const Expr *E) { + if (const auto *Call = dyn_cast<CallExpr>(E)) { + if (const auto *Fn = Call->getDirectCallee()) + return Fn->getName(); + } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) { + if (const auto *M = Msg->getMethodDecl()) { + if (M->getSelector().isUnarySelector()) + return M->getSelector().getNameForSlot(0); + } + } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { + if (PRE->isImplicitProperty()) { + if (const auto *M = PRE->getImplicitPropertyGetter()) + return M->getSelector().getNameForSlot(0); + } else if (const auto *Prop = PRE->getExplicitProperty()) + return Prop->getName(); + } + return None; +} diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.h b/clang/lib/Tooling/Refactor/ExtractionUtils.h new file mode 100644 index 0000000000000..5607e46d6d59f --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.h @@ -0,0 +1,31 @@ +//===--- ExtractionUtils.h - Extraction helper functions ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H + +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" + +namespace clang { + +class Expr; + +namespace tooling { +namespace extract { + +/// Returns a good name for an extracted variable based on the declaration +/// that's used in the given expression \p E. +Optional<StringRef> nameForExtractedVariable(const Expr *E); + +} // end namespace extract +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H From f5487a150c76ec96cbe47654e2422ba2e9585b2b Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 22 Jun 2017 16:59:22 +0100 Subject: [PATCH 169/582] NFC, extract expression checking logic to StmtUtils Prep for rdar://32793311 apple-llvm-split-commit: 47f2d1f7afce429be53d3906f028805db0e01ad8 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/Extract.cpp | 15 +-------------- clang/lib/Tooling/Refactor/StmtUtils.cpp | 18 ++++++++++++++++++ clang/lib/Tooling/Refactor/StmtUtils.h | 5 +++++ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index ac381065709c9..8a807d34fa35c 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -1282,14 +1282,6 @@ PrintingPolicy getPrintingPolicy(const ASTContext &Context, return Policy; } -static bool isAssignmentOperator(const Stmt *S) { - if (const auto *PseudoExpr = dyn_cast<PseudoObjectExpr>(S)) - return isAssignmentOperator(PseudoExpr->getSyntacticForm()); - if (const auto *BO = dyn_cast<BinaryOperator>(S)) - return BO->isAssignmentOp(); - return false; -} - static QualType getFunctionLikeParentDeclReturnType(const Decl *D) { // FIXME: might need to handle ObjC blocks in the future. if (const auto *M = dyn_cast<ObjCMethodDecl>(D)) @@ -1468,12 +1460,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( } else Visitor.InspectExtractedStmt(const_cast<Stmt *>(S), Context); // Compute the return type. - bool IsExpr = isa<Expr>(S); - // Assignment operators should be treated as statements unless they are a part - // of an expression. - if (IsExpr && isAssignmentOperator(S) && - (!ParentStmt || !isa<Expr>(ParentStmt))) - IsExpr = false; + bool IsExpr = isLexicalExpression(S, ParentStmt); QualType ReturnType; if (IsExpr || Visitor.HasReturnInExtracted) { if (const auto *E = dyn_cast<Expr>(S)) { diff --git a/clang/lib/Tooling/Refactor/StmtUtils.cpp b/clang/lib/Tooling/Refactor/StmtUtils.cpp index 53d426b8b39c0..ed8ae30543a48 100644 --- a/clang/lib/Tooling/Refactor/StmtUtils.cpp +++ b/clang/lib/Tooling/Refactor/StmtUtils.cpp @@ -52,3 +52,21 @@ bool clang::tooling::isSemicolonRequiredAfter(const Stmt *S) { return true; } } + +static bool isAssignmentOperator(const Stmt *S) { + if (const auto *PseudoExpr = dyn_cast<PseudoObjectExpr>(S)) + return isAssignmentOperator(PseudoExpr->getSyntacticForm()); + if (const auto *BO = dyn_cast<BinaryOperator>(S)) + return BO->isAssignmentOp(); + return false; +} + +bool clang::tooling::isLexicalExpression(const Stmt *S, const Stmt *Parent) { + if (!isa<Expr>(S)) + return false; + // Assignment operators should be treated as statements unless they are a part + // of an expression. + if (isAssignmentOperator(S) && (!Parent || !isa<Expr>(Parent))) + return false; + return true; +} diff --git a/clang/lib/Tooling/Refactor/StmtUtils.h b/clang/lib/Tooling/Refactor/StmtUtils.h index 8f458770ae23b..5bc319528e469 100644 --- a/clang/lib/Tooling/Refactor/StmtUtils.h +++ b/clang/lib/Tooling/Refactor/StmtUtils.h @@ -27,6 +27,11 @@ SourceLocation getLexicalEndLocForDecl(const Decl *D, const SourceManager &SM, /// statement. bool isSemicolonRequiredAfter(const Stmt *S); +/// Returns true if the given statement \p S is an actual expression in the +/// source. Assignment expressions are considered to be statements unless they +/// are a part of an expression. +bool isLexicalExpression(const Stmt *S, const Stmt *Parent); + } // end namespace tooling } // end namespace clang From 6f314c97e6b9d0a7e72d1dc4231f0b03ebf63620 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 22 Jun 2017 17:43:08 +0100 Subject: [PATCH 170/582] NFC, move variable placement logic to ExtractionUtils Prep for rdar://32793311 apple-llvm-split-commit: 3c6cc10ce4508826bab65510c13b75215d30b712 apple-llvm-split-dir: clang/ --- .../ExtractRepeatedExpressionIntoVariable.cpp | 99 +---------------- .../lib/Tooling/Refactor/ExtractionUtils.cpp | 102 ++++++++++++++++++ clang/lib/Tooling/Refactor/ExtractionUtils.h | 9 ++ 3 files changed, 115 insertions(+), 95 deletions(-) diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index 4427c14bbf381..dc90d9ea93c1e 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -19,7 +19,6 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" -#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace clang::tooling; @@ -234,94 +233,6 @@ clang::tooling::initiateExtractRepeatedExpressionIntoVariableOperation( return Result; } -namespace { - -/// Checks if a set of expressions is directly contained in some AST region. -class StmtReachabilityChecker - : public RecursiveASTVisitor<StmtReachabilityChecker> { - const llvm::SmallPtrSetImpl<const Stmt *> &Expressions; - unsigned Count = 0; - - StmtReachabilityChecker( - const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) - : Expressions(Expressions) {} - - bool areAllExpressionsReached() const { return Count == Expressions.size(); } - -public: - bool VisitStmt(const Stmt *S) { - if (Expressions.count(S)) { - ++Count; - if (areAllExpressionsReached()) - return false; - } - return true; - } - - static bool areAllExpressionsReachableFrom( - CompoundStmt *S, const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) { - StmtReachabilityChecker Checker(Expressions); - Checker.TraverseStmt(S); - return Checker.areAllExpressionsReached(); - } -}; - -/// Figures out where the extracted variable should go. -class ExtractedVariableInsertionLocFinder - : public RecursiveASTVisitor<ExtractedVariableInsertionLocFinder> { - llvm::SmallPtrSet<const Stmt *, 4> Expressions; - llvm::SmallVector<std::pair<CompoundStmt *, const Stmt *>, 4> - InsertionCandidateStack; - bool IsPrevCompoundStmt = false; - -public: - SourceLocation Loc; - - /// Initializes the insertion location finder using the set of duplicate - /// \p Expressions from one function. - ExtractedVariableInsertionLocFinder(ArrayRef<const Expr *> Expressions) { - for (const Expr *E : Expressions) - this->Expressions.insert(E); - } - - bool TraverseStmt(Stmt *S) { - if (!S) - return RecursiveASTVisitor::TraverseStmt(S); - if (IsPrevCompoundStmt && !InsertionCandidateStack.empty()) - InsertionCandidateStack.back().second = S; - llvm::SaveAndRestore<bool> IsPrevCompoundStmtTracker(IsPrevCompoundStmt, - false); - if (auto *CS = dyn_cast<CompoundStmt>(S)) { - IsPrevCompoundStmt = true; - InsertionCandidateStack.emplace_back(CS, nullptr); - return RecursiveASTVisitor::TraverseStmt(S); - } - return RecursiveASTVisitor::TraverseStmt(S); - } - - bool VisitStmt(const Stmt *S) { - if (Expressions.count(S)) { - // The insertion location should be in the first compound statement that - // includes all of the expressions as descendants as we want the new - // variable to be visible to all uses. - for (auto I = InsertionCandidateStack.rbegin(), - E = InsertionCandidateStack.rend(); - I != E; ++I) { - if (StmtReachabilityChecker::areAllExpressionsReachableFrom( - I->first, Expressions) && - I->second) { - Loc = I->second->getLocStart(); - break; - } - } - return false; - } - return true; - } -}; - -} // end anonymous namespace - static StringRef nameForExtractedVariable(const Expr *E) { auto SuggestedName = extract::nameForExtractedVariable(E); if (!SuggestedName) @@ -336,9 +247,10 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( std::vector<RefactoringReplacement> Replacements; const SourceManager &SM = Context.getSourceManager(); - ExtractedVariableInsertionLocFinder LocFinder(DuplicateExpressions); - LocFinder.TraverseDecl(const_cast<Decl *>(ParentDecl)); - if (LocFinder.Loc.isInvalid()) + SourceLocation InsertionLoc = + extract::locationForExtractedVariableDeclaration(DuplicateExpressions, + ParentDecl, SM); + if (InsertionLoc.isInvalid()) return llvm::make_error<RefactoringOperationError>( "no appropriate insertion location found"); @@ -356,9 +268,6 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( OS << " = "; E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy()); OS << ";\n"; - SourceLocation InsertionLoc = LocFinder.Loc; - if (InsertionLoc.isMacroID()) - InsertionLoc = SM.getExpansionLoc(InsertionLoc); Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc), OS.str()); // Replace the duplicates with a reference to the variable. diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp index f1917e51926a8..208685ed7b7d0 100644 --- a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp @@ -10,6 +10,9 @@ #include "ExtractionUtils.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; @@ -31,3 +34,102 @@ Optional<StringRef> tooling::extract::nameForExtractedVariable(const Expr *E) { } return None; } + +namespace { + +/// Checks if a set of expressions is directly contained in some AST region. +class StmtReachabilityChecker + : public RecursiveASTVisitor<StmtReachabilityChecker> { + const llvm::SmallPtrSetImpl<const Stmt *> &Expressions; + unsigned Count = 0; + + StmtReachabilityChecker( + const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) + : Expressions(Expressions) {} + + bool areAllExpressionsReached() const { return Count == Expressions.size(); } + +public: + bool VisitStmt(const Stmt *S) { + if (Expressions.count(S)) { + ++Count; + if (areAllExpressionsReached()) + return false; + } + return true; + } + + static bool areAllExpressionsReachableFrom( + CompoundStmt *S, const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) { + StmtReachabilityChecker Checker(Expressions); + Checker.TraverseStmt(S); + return Checker.areAllExpressionsReached(); + } +}; + +/// Figures out where the extracted variable should go. +class ExtractedVariableInsertionLocFinder + : public RecursiveASTVisitor<ExtractedVariableInsertionLocFinder> { + llvm::SmallPtrSet<const Stmt *, 4> Expressions; + llvm::SmallVector<std::pair<CompoundStmt *, const Stmt *>, 4> + InsertionCandidateStack; + bool IsPrevCompoundStmt = false; + +public: + SourceLocation Loc; + + /// Initializes the insertion location finder using the set of duplicate + /// \p Expressions from one function. + ExtractedVariableInsertionLocFinder(ArrayRef<const Expr *> Expressions) { + for (const Expr *E : Expressions) + this->Expressions.insert(E); + } + + bool TraverseStmt(Stmt *S) { + if (!S) + return RecursiveASTVisitor::TraverseStmt(S); + if (IsPrevCompoundStmt && !InsertionCandidateStack.empty()) + InsertionCandidateStack.back().second = S; + llvm::SaveAndRestore<bool> IsPrevCompoundStmtTracker(IsPrevCompoundStmt, + false); + if (auto *CS = dyn_cast<CompoundStmt>(S)) { + IsPrevCompoundStmt = true; + InsertionCandidateStack.emplace_back(CS, nullptr); + return RecursiveASTVisitor::TraverseStmt(S); + } + return RecursiveASTVisitor::TraverseStmt(S); + } + + bool VisitStmt(const Stmt *S) { + if (Expressions.count(S)) { + // The insertion location should be in the first compound statement that + // includes all of the expressions as descendants as we want the new + // variable to be visible to all uses. + for (auto I = InsertionCandidateStack.rbegin(), + E = InsertionCandidateStack.rend(); + I != E; ++I) { + if (StmtReachabilityChecker::areAllExpressionsReachableFrom( + I->first, Expressions) && + I->second) { + Loc = I->second->getLocStart(); + break; + } + } + return false; + } + return true; + } +}; + +} // end anonymous namespace + +SourceLocation tooling::extract::locationForExtractedVariableDeclaration( + ArrayRef<const Expr *> Expressions, const Decl *ParentDecl, + const SourceManager &SM) { + ExtractedVariableInsertionLocFinder LocFinder(Expressions); + LocFinder.TraverseDecl(const_cast<Decl *>(ParentDecl)); + SourceLocation Result = LocFinder.Loc; + if (Result.isValid() && Result.isMacroID()) + return SM.getExpansionLoc(Result); + return Result; +} diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.h b/clang/lib/Tooling/Refactor/ExtractionUtils.h index 5607e46d6d59f..816a0815106de 100644 --- a/clang/lib/Tooling/Refactor/ExtractionUtils.h +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.h @@ -16,6 +16,8 @@ namespace clang { class Expr; +class Decl; +class SourceManager; namespace tooling { namespace extract { @@ -24,6 +26,13 @@ namespace extract { /// that's used in the given expression \p E. Optional<StringRef> nameForExtractedVariable(const Expr *E); +/// Returns an appropriate location for a variable declaration that will be +/// visible to all the given expressions. +SourceLocation +locationForExtractedVariableDeclaration(ArrayRef<const Expr *> Expressions, + const Decl *ParentDecl, + const SourceManager &SM); + } // end namespace extract } // end namespace tooling } // end namespace clang From e7213dac7b52f5833304914fd7e327de041e45aa Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 22 Jun 2017 18:18:10 +0100 Subject: [PATCH 171/582] Add "Extract Expression" action rdar://32793311 apple-llvm-split-commit: 64fda69e359299f65557802094464a1334a1564c apple-llvm-split-dir: clang/ --- clang/include/clang-c/Refactor.h | 6 + .../Tooling/Refactor/RefactoringActions.def | 2 + clang/lib/Tooling/Refactor/Extract.cpp | 133 ++++++++++++++---- .../lib/Tooling/Refactor/ExtractionUtils.cpp | 4 +- .../Extract/extract-expression-into-var.cpp | 39 +++++ .../Extract/extract-expression-into-var.m | 12 ++ 6 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 clang/test/Refactor/Extract/extract-expression-into-var.cpp create mode 100644 clang/test/Refactor/Extract/extract-expression-into-var.m diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h index 869f562726b6d..63b01a4175450 100644 --- a/clang/include/clang-c/Refactor.h +++ b/clang/include/clang-c/Refactor.h @@ -156,6 +156,12 @@ enum CXRefactoringActionType { * declarations without respective definitions. */ CXRefactor_ImplementDeclaredMethods = 10, + + /** + * \brief The sub-action of 'extract' that extracts source expression into a + * new variable. + */ + CXRefactor_Extract_Expression = 11, }; /** diff --git a/clang/include/clang/Tooling/Refactor/RefactoringActions.def b/clang/include/clang/Tooling/Refactor/RefactoringActions.def index 9b7e7e04d037f..f5c2f668e0141 100644 --- a/clang/include/clang/Tooling/Refactor/RefactoringActions.def +++ b/clang/include/clang/Tooling/Refactor/RefactoringActions.def @@ -32,6 +32,8 @@ REFACTORING_SUB_ACTION(Local, Rename, "Rename") REFACTORING_OPERATION_ACTION(Extract, "Extract Function", "extract") REFACTORING_OPERATION_SUB_ACTION(Method, Extract, "Extract Method", "extract-method") +REFACTORING_OPERATION_SUB_ACTION(Expression, Extract, "Extract Expression", + "extract-expression") REFACTORING_OPERATION_ACTION(IfSwitchConversion, "Convert to Switch", "if-switch-conversion") diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index 8a807d34fa35c..624b18dc772a3 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "ExtractionUtils.h" #include "RefactoringOperations.h" #include "SourceLocationUtilities.h" #include "StmtUtils.h" @@ -56,6 +57,8 @@ struct CompoundStatementRange { CompoundStmt::const_body_iterator end() const { return Last + 1; } }; +enum class ExtractionKind { Function, Method, Expression }; + class ExtractOperation : public RefactoringOperation { public: struct CandidateInfo { @@ -80,12 +83,11 @@ class ExtractOperation : public RefactoringOperation { std::vector<std::string> Candidates, Optional<CompoundStatementRange> ExtractedStmtRange, Optional<CandidateInfo> FirstCandidateInfo, - bool IsMethodExtraction) + ExtractionKind Kind) : S(S), ParentStmt(ParentStmt), FunctionLikeParentDecl(FunctionLikeParentDecl), Candidates(std::move(Candidates)), - ExtractedStmtRange(ExtractedStmtRange), - IsMethodExtraction(IsMethodExtraction) { + ExtractedStmtRange(ExtractedStmtRange), Kind(Kind) { if (FirstCandidateInfo) CandidateExtractionInfo.push_back(*FirstCandidateInfo); } @@ -107,23 +109,35 @@ class ExtractOperation : public RefactoringOperation { } std::vector<RefactoringActionType> getAvailableSubActions() override { + std::vector<RefactoringActionType> SubActions; if (isa<CXXMethodDecl>(FunctionLikeParentDecl) || isa<ObjCMethodDecl>(FunctionLikeParentDecl)) - return {RefactoringActionType::Extract_Method}; - return {}; + SubActions.push_back(RefactoringActionType::Extract_Method); + if (isLexicalExpression(S, ParentStmt)) + SubActions.push_back(RefactoringActionType::Extract_Expression); + return SubActions; + } + + bool isMethodExtraction() const { return Kind == ExtractionKind::Method; } + + bool isExpressionExtraction() const { + return Kind == ExtractionKind::Expression; } llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) override; + llvm::Expected<RefactoringResult> + performExpressionExtraction(ASTContext &Context, PrintingPolicy &PP); + const Stmt *S, *ParentStmt; const Decl *FunctionLikeParentDecl; std::vector<std::string> Candidates; /// A set of extraction candidates that correspond to the extracted code. SmallVector<CandidateInfo, 2> CandidateExtractionInfo; Optional<CompoundStatementRange> ExtractedStmtRange; - bool IsMethodExtraction; + ExtractionKind Kind; }; } // end anonymous namespace @@ -167,7 +181,7 @@ static RefactoringOperationResult initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, SourceLocation Location, SourceRange SelectionRange, bool CreateOperation, - bool IsMethodExtraction = false) { + ExtractionKind Kind = ExtractionKind::Function) { auto SelectedStmtsOpt = Slice.getSelectedStmtSet(); if (!SelectedStmtsOpt) return None; @@ -196,6 +210,12 @@ initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, return RefactoringOperationResult("property setter can't be extracted"); } + const Stmt *ParentStmt = + Slice.parentStmtForIndex(*Stmts.containsSelectionRangeIndex); + if (Kind == ExtractionKind::Expression && + !isLexicalExpression(Selected, ParentStmt)) + return None; + RefactoringOperationResult Result; Result.Initiated = true; if (!CreateOperation) @@ -244,9 +264,8 @@ initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, } auto Operation = llvm::make_unique<ExtractOperation>( - Selected, Slice.parentStmtForIndex(*Stmts.containsSelectionRangeIndex), - ParentDecl, std::move(Candidates), ExtractedStmtRange, FirstCandidateInfo, - IsMethodExtraction); + Selected, ParentStmt, ParentDecl, std::move(Candidates), + ExtractedStmtRange, FirstCandidateInfo, Kind); auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo; SourceRange Range; if (ExtractedStmtRange) @@ -290,8 +309,16 @@ RefactoringOperationResult clang::tooling::initiateExtractMethodOperation( SourceRange SelectionRange, bool CreateOperation) { // TODO: Verify that method extraction is actually possible. return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, - CreateOperation, - /*IsMethodExtraction=*/true); + CreateOperation, ExtractionKind::Method); +} + +RefactoringOperationResult clang::tooling::initiateExtractExpressionOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + RefactoringOperationResult R = + initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, + CreateOperation, ExtractionKind::Expression); + return R; } using ReferencedEntity = @@ -1429,6 +1456,54 @@ static bool isInHeader(SourceLocation Loc, const SourceManager &SM) { .Default(false); } +llvm::Expected<RefactoringResult> +ExtractOperation::performExpressionExtraction(ASTContext &Context, + PrintingPolicy &PP) { + assert(isExpressionExtraction() && "Not an expression extraction"); + std::vector<RefactoringReplacement> Replacements; + const Expr *E = cast<Expr>(S); + QualType VarType = findExpressionLexicalType(FunctionLikeParentDecl, E, + E->getType(), PP, Context); + StringRef VarName = "extractedExpr"; + auto CreatedSymbol = + llvm::make_unique<RefactoringResultAssociatedSymbol>(SymbolName(VarName)); + + SourceRange ExtractedTokenRange = CandidateExtractionInfo[0].Range; + SourceRange ExtractedCharRange = SourceRange( + ExtractedTokenRange.getBegin(), + getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(), + Context.getSourceManager(), Context.getLangOpts())); + + // Create the variable that will hold the value of the duplicate expression. + std::string VariableDeclarationString; + llvm::raw_string_ostream OS(VariableDeclarationString); + VarType.print(OS, PP, /*PlaceHolder*/ VarName); + // FIXME: We should hook into the TypePrinter when moving over to llvm.org + // instead and get the offset from it. + unsigned NameOffset = StringRef(OS.str()).find(VarName); + OS << " = "; + OS << Lexer::getSourceText(CharSourceRange::getCharRange(ExtractedCharRange), + Context.getSourceManager(), Context.getLangOpts()); + OS << ";\n"; + + // Variable declaration. + SourceLocation InsertionLoc = + extract::locationForExtractedVariableDeclaration( + E, FunctionLikeParentDecl, Context.getSourceManager()); + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), OS.str(), CreatedSymbol.get(), + RefactoringReplacement::AssociatedSymbolLocation( + llvm::makeArrayRef(NameOffset), /*IsDeclaration=*/true))); + // Replace the expression with the variable. + Replacements.push_back( + RefactoringReplacement(ExtractedCharRange, VarName, CreatedSymbol.get(), + /*NameOffset=*/llvm::makeArrayRef(unsigned(0)))); + + RefactoringResult Result(std::move(Replacements)); + Result.AssociatedSymbols.push_back(std::move(CreatedSymbol)); + return std::move(Result); +} + llvm::Expected<RefactoringResult> ExtractOperation::perform( ASTContext &Context, const Preprocessor &ThePreprocessor, const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { @@ -1442,6 +1517,9 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( PP.SuppressLifetimeQualifiers = true; PP.SuppressUnwrittenScope = true; + if (isExpressionExtraction()) + return performExpressionExtraction(Context, PP); + const Stmt *S = CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement ? CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement @@ -1573,7 +1651,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( } // Capture any fields if necessary. bool IsThisConstInCapturedFieldUses = true; - if (!IsMethodExtraction) { + if (!isMethodExtraction()) { for (const auto &I : Visitor.CapturedFields) { if (I.getSecond().isPassedByRefOrPtr() && !I.getSecond().isRefOrPtrConst()) @@ -1592,7 +1670,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( return X.getName() < Y.getName(); }); // 'This'/'self' should be passed-in first. - if (!IsMethodExtraction && Visitor.CaptureThis) { + if (!isMethodExtraction() && Visitor.CaptureThis) { CapturedVariables.insert( CapturedVariables.begin(), CapturedVariable::getThis( @@ -1612,7 +1690,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); } else PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S)); - } else if (!IsMethodExtraction && Visitor.CaptureSelf && + } else if (!isMethodExtraction() && Visitor.CaptureSelf && EnclosingObjCMethod) { if (EnclosingObjCMethod->isInstanceMethod()) { // Instance methods rewrite 'self' into an 'object' parameter. @@ -1638,7 +1716,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( SelfRewriter.TraverseStmt(const_cast<Stmt *>(S)); } } - if (!IsMethodExtraction && Visitor.CaptureSuper && EnclosingObjCMethod) { + if (!isMethodExtraction() && Visitor.CaptureSuper && EnclosingObjCMethod) { if (EnclosingObjCMethod->isInstanceMethod()) // Instance methods rewrite 'super' into an 'superObject' parameter. CapturedVariables.insert(Visitor.CaptureSelf @@ -1701,7 +1779,8 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( StringRef ExtractedName = "extracted"; llvm::SmallVector<StringRef, 4> ExtractedNamePieces; ExtractedNamePieces.push_back(ExtractedName); - if (IsMethodExtraction && EnclosingObjCMethod && !CapturedVariables.empty()) { + if (isMethodExtraction() && EnclosingObjCMethod && + !CapturedVariables.empty()) { for (const auto &Var : llvm::makeArrayRef(CapturedVariables).drop_front()) ExtractedNamePieces.push_back(Var.getName()); } @@ -1710,7 +1789,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( SymbolName(ExtractedNamePieces)); SourceLocation FunctionExtractionLoc = computeFunctionExtractionLocation( - FunctionLikeParentDecl, IsMethodExtraction); + FunctionLikeParentDecl, isMethodExtraction()); FunctionExtractionLoc = getLocationOfPrecedingComment(FunctionExtractionLoc, SM, LangOpts); @@ -1719,7 +1798,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( [&](llvm::raw_string_ostream &OS, bool IsDefinition = true) -> RefactoringReplacement::AssociatedSymbolLocation { - if (IsMethodExtraction && EnclosingObjCMethod) { + if (isMethodExtraction() && EnclosingObjCMethod) { OS << (EnclosingObjCMethod->isClassMethod() ? '+' : '-') << " ("; ReturnType.print(OS, PP); OS << ')'; @@ -1742,7 +1821,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( NameOffsets, /*IsDeclaration=*/true); } auto *FD = dyn_cast<FunctionDecl>(FunctionLikeParentDecl); - if (IsMethodExtraction && IsDefinition && + if (isMethodExtraction() && IsDefinition && !FD->getDescribedFunctionTemplate()) { // Print the class template parameter lists for an out-of-line method. for (unsigned I = 0, @@ -1752,13 +1831,13 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( OS << "\n"; } } - if (IsMethodExtraction && isEnclosingMethodStatic(FunctionLikeParentDecl)) + if (isMethodExtraction() && isEnclosingMethodStatic(FunctionLikeParentDecl)) OS << "static "; - else if (!IsMethodExtraction) + else if (!isMethodExtraction()) OS << (isInHeader(FunctionExtractionLoc, SM) ? "inline " : "static "); ReturnType.print(OS, PP); OS << ' '; - if (IsMethodExtraction && IsDefinition) + if (isMethodExtraction() && IsDefinition) printEnclosingMethodScope(FunctionLikeParentDecl, OS, PP); unsigned NameOffset = OS.str().size(); OS << ExtractedName << '('; @@ -1770,14 +1849,14 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( Var.ParameterType.print(OS, PP, /*PlaceHolder=*/Var.getName()); } OS << ')'; - if (IsMethodExtraction && isEnclosingMethodConst(FunctionLikeParentDecl)) + if (isMethodExtraction() && isEnclosingMethodConst(FunctionLikeParentDecl)) OS << " const"; return RefactoringReplacement::AssociatedSymbolLocation( NameOffset, /*IsDeclaration=*/true); ; }; - if (IsMethodExtraction && + if (isMethodExtraction() && isEnclosingMethodOutOfLine(FunctionLikeParentDecl)) { // The location of the declaration should be either before the original // declararation, or, if this method has not declaration, somewhere @@ -1786,7 +1865,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( SourceLocation DeclarationLoc; if (FunctionLikeParentDecl->getCanonicalDecl() != FunctionLikeParentDecl) { DeclarationLoc = computeFunctionExtractionLocation( - FunctionLikeParentDecl->getCanonicalDecl(), IsMethodExtraction); + FunctionLikeParentDecl->getCanonicalDecl(), isMethodExtraction()); Placement = MethodDeclarationPlacement::Before; } else { auto LocAndPlacement = @@ -1856,7 +1935,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( } InsertedOS << CandidateExtractionInfo[SelectedCandidateIndex].PreInsertedText; llvm::SmallVector<unsigned, 4> NameOffsets; - if (IsMethodExtraction && EnclosingObjCMethod) { + if (isMethodExtraction() && EnclosingObjCMethod) { InsertedOS << "[self "; NameOffsets.push_back(InsertedOS.str().size()); InsertedOS << ExtractedName; diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp index 208685ed7b7d0..a733add982ed6 100644 --- a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp @@ -95,7 +95,9 @@ class ExtractedVariableInsertionLocFinder if (auto *CS = dyn_cast<CompoundStmt>(S)) { IsPrevCompoundStmt = true; InsertionCandidateStack.emplace_back(CS, nullptr); - return RecursiveASTVisitor::TraverseStmt(S); + RecursiveASTVisitor::TraverseStmt(S); + InsertionCandidateStack.pop_back(); + return true; } return RecursiveASTVisitor::TraverseStmt(S); } diff --git a/clang/test/Refactor/Extract/extract-expression-into-var.cpp b/clang/test/Refactor/Extract/extract-expression-into-var.cpp new file mode 100644 index 0000000000000..f1ae8f5ae41f1 --- /dev/null +++ b/clang/test/Refactor/Extract/extract-expression-into-var.cpp @@ -0,0 +1,39 @@ +int call(int x); + +void extractExpressionIntoVar(int x, int y, int z) { + { + // expr1-begin: +1:13 + int p = x + y - z; // CHECK: "int extractedExpr = x + y;\n" [[@LINE]]:5 -> [[@LINE]]:5 [Symbol extracted-decl 0 1:5 -> 1:18] + // expr1-end: -1:19 // CHECK: "extractedExpr" [[@LINE-1]]:13 -> [[@LINE-1]]:18 [Symbol extracted-decl-ref 0 1:1 -> 1:14] + // expr2-begin: +1:6 // CHECK: "std::function<void (int)> extractedExpr = [&](int x) {\n p = x;\n };\n" [[@LINE+1]]:5 -> [[@LINE+1]]:5 [Symbol extracted-decl 0 1:27 -> 1:40] + ([&](int x) { + p = x; + })(0); + // expr2-end: -1:6 // CHECK: "extractedExpr" [[@LINE-3]]:6 -> [[@LINE-1]]:6 + } + #define MACROARG(x) (x) + // expr3-begin: +1:20 + int p = MACROARG(y + z) - x; // CHECK: "int extractedExpr = y + z;\n" [[@LINE]]:3 -> [[@LINE]]:3 + // expr3-end: -1:25 // CHECK: "extractedExpr" [[@LINE-1]]:20 -> [[@LINE-1]]:25 + #define MACROSTMT(x, y) int var = (x) + (y); + // expr4-begin: +1:16 + MACROSTMT(0, call(p * z)) // CHECK: "int extractedExpr = call(p * z);\n" [[@LINE]]:3 -> [[@LINE]]:3 + // expr4-end: -1:27 // CHECK: "extractedExpr" [[@LINE-1]]:16 -> [[@LINE-1]]:27 +} + +// RUN: clang-refactor-test perform -action extract-expression -selected=expr1 -selected=expr2 -selected=expr3 -selected=expr4 -emit-associated %s -std=c++11 | FileCheck %s + +// RUN: clang-refactor-test list-actions -at=%s:6:13 -selected=%s:6:13-6:18 %s -std=c++11 | FileCheck --check-prefix=CHECK-ACTION %s +// CHECK-ACTION: Extract Expression + +void dontExtractStatement(int x, int y) { + // stmt1-begin: +1:3 + x = y; + // stmt1-end: -1:8 + // stmt2-begin: +1:3 + return; + // stmt2-end: +0:1 +} + +// RUN: not clang-refactor-test perform -action extract-expression -selected=stmt1 -selected=stmt2 %s -std=c++11 2>&1 | FileCheck --check-prefix=CHECK-FAIL %s +// CHECK-FAIL: Failed to initiate the refactoring action! diff --git a/clang/test/Refactor/Extract/extract-expression-into-var.m b/clang/test/Refactor/Extract/extract-expression-into-var.m new file mode 100644 index 0000000000000..ec94dab8a8f4f --- /dev/null +++ b/clang/test/Refactor/Extract/extract-expression-into-var.m @@ -0,0 +1,12 @@ +// RUN: clang-refactor-test perform -action extract-expression -selected=expr1 -selected=expr2 %s | FileCheck %s + +void extractExpressionIntoVar(int x, int y, int z) { + // expr1-begin: +1:9 + (void)@selector(foo:bar:); // CHECK: "SEL extractedExpr = @selector(foo:bar:);\n" [[@LINE]]:3 -> [[@LINE]]:3 + // expr1-end: -1:28 // CHECK: "extractedExpr" [[@LINE-1]]:9 -> [[@LINE-1]]:28 + // expr2-begin: +1:4 // CHECK: "void (^extractedExpr)(void) = ^ {\n // do nothing\n };\n" [[@LINE+1]]:3 -> [[@LINE+1]]:3 + (^ { + // do nothing + })(); + // expr2-end: -1:4 // CHECK: "extractedExpr" [[@LINE-3]]:4 -> [[@LINE-1]]:4 +} From 2714f2fd8148d9b786ee0c4547cafb683c51f981 Mon Sep 17 00:00:00 2001 From: David Farler <dfarler@apple.com> Date: Mon, 26 Jun 2017 17:57:22 -0700 Subject: [PATCH 172/582] [index/build] Upstream Clang index-while-building This patch upstreams previously AppleInternal index-while-building changes released in the Xcode 9 timeframe. apple-llvm-split-commit: 0524768abaa7caa7630b7d1f3dc46c52f716962c apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticFrontendKinds.td | 4 + clang/include/clang/Basic/DiagnosticGroups.td | 1 + .../clang/DirectoryWatcher/DirectoryWatcher.h | 47 ++ clang/include/clang/Driver/Job.h | 6 +- clang/include/clang/Driver/Options.td | 7 + .../include/clang/Frontend/FrontendOptions.h | 9 +- clang/include/clang/Index/IndexDataStore.h | 102 +++ .../clang/Index/IndexDataStoreSymbolUtils.h | 53 ++ clang/include/clang/Index/IndexRecordReader.h | 109 +++ clang/include/clang/Index/IndexRecordWriter.h | 102 +++ clang/include/clang/Index/IndexUnitReader.h | 85 +++ clang/include/clang/Index/IndexUnitWriter.h | 140 ++++ clang/include/clang/Index/IndexingAction.h | 30 + clang/include/indexstore/IndexStoreCXX.h | 502 ++++++++++++++ clang/include/indexstore/indexstore.h | 487 +++++++++++++ clang/lib/CMakeLists.txt | 1 + clang/lib/DirectoryWatcher/CMakeLists.txt | 8 + .../lib/DirectoryWatcher/DirectoryWatcher.cpp | 275 ++++++++ clang/lib/Driver/Driver.cpp | 4 +- clang/lib/Driver/Job.cpp | 21 + clang/lib/Driver/ToolChains/Clang.cpp | 20 + clang/lib/Driver/ToolChains/Darwin.cpp | 4 + clang/lib/Frontend/CMakeLists.txt | 1 + clang/lib/Frontend/CompilerInstance.cpp | 15 +- clang/lib/Frontend/CompilerInvocation.cpp | 4 + .../ExecuteCompilerInvocation.cpp | 7 + clang/lib/Index/BitstreamVisitor.h | 163 +++++ clang/lib/Index/CMakeLists.txt | 9 + clang/lib/Index/ClangIndexRecordWriter.cpp | 128 ++++ clang/lib/Index/ClangIndexRecordWriter.h | 55 ++ clang/lib/Index/FileIndexRecord.cpp | 59 ++ clang/lib/Index/FileIndexRecord.h | 69 ++ clang/lib/Index/IndexDataStore.cpp | 259 +++++++ clang/lib/Index/IndexDataStoreUtils.cpp | 398 +++++++++++ clang/lib/Index/IndexDataStoreUtils.h | 116 ++++ clang/lib/Index/IndexRecordHasher.cpp | 468 +++++++++++++ clang/lib/Index/IndexRecordHasher.h | 58 ++ clang/lib/Index/IndexRecordReader.cpp | 407 +++++++++++ clang/lib/Index/IndexRecordWriter.cpp | 366 ++++++++++ clang/lib/Index/IndexUnitReader.cpp | 516 ++++++++++++++ clang/lib/Index/IndexUnitWriter.cpp | 628 +++++++++++++++++ clang/lib/Index/IndexingAction.cpp | 648 +++++++++++++++++- clang/lib/Index/IndexingContext.cpp | 66 +- clang/lib/Index/IndexingContext.h | 15 +- clang/test/Index/Store/Inputs/head.h | 3 + clang/test/Index/Store/Inputs/json.c.json | 151 ++++ clang/test/Index/Store/Inputs/module/ModDep.h | 3 + .../Index/Store/Inputs/module/ModSystem.h | 4 + clang/test/Index/Store/Inputs/module/ModTop.h | 4 + .../Index/Store/Inputs/module/ModTopSub1.h | 1 + .../Index/Store/Inputs/module/ModTopSub2.h | 1 + .../Store/Inputs/module/module.modulemap | 12 + clang/test/Index/Store/Inputs/overlay.yaml | 6 + clang/test/Index/Store/Inputs/print-unit.h | 2 + clang/test/Index/Store/Inputs/sys/another.h | 2 + clang/test/Index/Store/Inputs/sys/syshead.h | 4 + clang/test/Index/Store/Inputs/test1.c | 3 + clang/test/Index/Store/Inputs/test2.c | 3 + clang/test/Index/Store/Inputs/test3.cpp | 2 + clang/test/Index/Store/Inputs/using-overlay.h | 1 + clang/test/Index/Store/assembly-invocation.c | 3 + .../Index/Store/external-source-symbol-hash.m | 47 ++ .../test/Index/Store/handle-prebuilt-module.m | 25 + clang/test/Index/Store/json-with-module.m | 9 + .../test/Index/Store/json-with-module.m.json | 151 ++++ clang/test/Index/Store/json-with-pch.c | 10 + clang/test/Index/Store/json-with-pch.c.json | 96 +++ clang/test/Index/Store/json.c | 10 + clang/test/Index/Store/print-record.mm | 28 + clang/test/Index/Store/print-unit.c | 39 ++ .../Index/Store/print-units-with-modules.m | 59 ++ clang/test/Index/Store/print-units-with-pch.c | 29 + clang/test/Index/Store/record-hash-crash.cpp | 12 + clang/test/Index/Store/record-hash.cpp | 12 + clang/test/Index/Store/relative-out-path.c | 19 + clang/test/Index/Store/syntax-only.c | 11 + clang/test/Index/Store/unit-with-vfs.c | 12 + clang/test/Index/Store/unit-workdir-prefix.c | 30 + clang/test/Index/Store/using-libstdcpp-arc.mm | 10 + clang/tools/CMakeLists.txt | 1 + clang/tools/IndexStore/CMakeLists.txt | 94 +++ clang/tools/IndexStore/IndexStore.cpp | 647 +++++++++++++++++ clang/tools/IndexStore/IndexStore.exports | 69 ++ clang/tools/c-index-test/CMakeLists.txt | 20 + clang/tools/c-index-test/JSONAggregation.cpp | 409 +++++++++++ clang/tools/c-index-test/JSONAggregation.h | 24 + clang/tools/c-index-test/core_main.cpp | 607 +++++++++++++++- 87 files changed, 9127 insertions(+), 30 deletions(-) create mode 100644 clang/include/clang/DirectoryWatcher/DirectoryWatcher.h create mode 100644 clang/include/clang/Index/IndexDataStore.h create mode 100644 clang/include/clang/Index/IndexDataStoreSymbolUtils.h create mode 100644 clang/include/clang/Index/IndexRecordReader.h create mode 100644 clang/include/clang/Index/IndexRecordWriter.h create mode 100644 clang/include/clang/Index/IndexUnitReader.h create mode 100644 clang/include/clang/Index/IndexUnitWriter.h create mode 100644 clang/include/indexstore/IndexStoreCXX.h create mode 100644 clang/include/indexstore/indexstore.h create mode 100644 clang/lib/DirectoryWatcher/CMakeLists.txt create mode 100644 clang/lib/DirectoryWatcher/DirectoryWatcher.cpp create mode 100644 clang/lib/Index/BitstreamVisitor.h create mode 100644 clang/lib/Index/ClangIndexRecordWriter.cpp create mode 100644 clang/lib/Index/ClangIndexRecordWriter.h create mode 100644 clang/lib/Index/FileIndexRecord.cpp create mode 100644 clang/lib/Index/FileIndexRecord.h create mode 100644 clang/lib/Index/IndexDataStore.cpp create mode 100644 clang/lib/Index/IndexDataStoreUtils.cpp create mode 100644 clang/lib/Index/IndexDataStoreUtils.h create mode 100644 clang/lib/Index/IndexRecordHasher.cpp create mode 100644 clang/lib/Index/IndexRecordHasher.h create mode 100644 clang/lib/Index/IndexRecordReader.cpp create mode 100644 clang/lib/Index/IndexRecordWriter.cpp create mode 100644 clang/lib/Index/IndexUnitReader.cpp create mode 100644 clang/lib/Index/IndexUnitWriter.cpp create mode 100644 clang/test/Index/Store/Inputs/head.h create mode 100644 clang/test/Index/Store/Inputs/json.c.json create mode 100644 clang/test/Index/Store/Inputs/module/ModDep.h create mode 100644 clang/test/Index/Store/Inputs/module/ModSystem.h create mode 100644 clang/test/Index/Store/Inputs/module/ModTop.h create mode 100644 clang/test/Index/Store/Inputs/module/ModTopSub1.h create mode 100644 clang/test/Index/Store/Inputs/module/ModTopSub2.h create mode 100644 clang/test/Index/Store/Inputs/module/module.modulemap create mode 100644 clang/test/Index/Store/Inputs/overlay.yaml create mode 100644 clang/test/Index/Store/Inputs/print-unit.h create mode 100644 clang/test/Index/Store/Inputs/sys/another.h create mode 100644 clang/test/Index/Store/Inputs/sys/syshead.h create mode 100644 clang/test/Index/Store/Inputs/test1.c create mode 100644 clang/test/Index/Store/Inputs/test2.c create mode 100644 clang/test/Index/Store/Inputs/test3.cpp create mode 100644 clang/test/Index/Store/Inputs/using-overlay.h create mode 100644 clang/test/Index/Store/assembly-invocation.c create mode 100644 clang/test/Index/Store/external-source-symbol-hash.m create mode 100644 clang/test/Index/Store/handle-prebuilt-module.m create mode 100644 clang/test/Index/Store/json-with-module.m create mode 100644 clang/test/Index/Store/json-with-module.m.json create mode 100644 clang/test/Index/Store/json-with-pch.c create mode 100644 clang/test/Index/Store/json-with-pch.c.json create mode 100644 clang/test/Index/Store/json.c create mode 100644 clang/test/Index/Store/print-record.mm create mode 100644 clang/test/Index/Store/print-unit.c create mode 100644 clang/test/Index/Store/print-units-with-modules.m create mode 100644 clang/test/Index/Store/print-units-with-pch.c create mode 100644 clang/test/Index/Store/record-hash-crash.cpp create mode 100644 clang/test/Index/Store/record-hash.cpp create mode 100644 clang/test/Index/Store/relative-out-path.c create mode 100644 clang/test/Index/Store/syntax-only.c create mode 100644 clang/test/Index/Store/unit-with-vfs.c create mode 100644 clang/test/Index/Store/unit-workdir-prefix.c create mode 100644 clang/test/Index/Store/using-libstdcpp-arc.mm create mode 100644 clang/tools/IndexStore/CMakeLists.txt create mode 100644 clang/tools/IndexStore/IndexStore.cpp create mode 100644 clang/tools/IndexStore/IndexStore.exports create mode 100644 clang/tools/c-index-test/JSONAggregation.cpp create mode 100644 clang/tools/c-index-test/JSONAggregation.h diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 9e4eeee94f1f9..5045cc53a0766 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -214,6 +214,10 @@ def err_modules_embed_file_not_found : Error<"file '%0' specified by '-fmodules-embed-file=' not found">, DefaultFatal; +def remark_index_producing_module_file_data : Remark<"producing index data for " + "module file '%0'">, + InGroup<IndexStore>; + def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " "(%3.%4)">; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index ea3ba2f04e610..5b6806cbb55a6 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -292,6 +292,7 @@ def MissingFieldInitializers : DiagGroup<"missing-field-initializers">; def ModuleBuild : DiagGroup<"module-build">; def ModuleConflict : DiagGroup<"module-conflict">; def ModuleFileExtension : DiagGroup<"module-file-extension">; +def IndexStore : DiagGroup<"index-store">; def NewlineEOF : DiagGroup<"newline-eof">; def Nullability : DiagGroup<"nullability">; def NullabilityDeclSpec : DiagGroup<"nullability-declspec">; diff --git a/clang/include/clang/DirectoryWatcher/DirectoryWatcher.h b/clang/include/clang/DirectoryWatcher/DirectoryWatcher.h new file mode 100644 index 0000000000000..09d17a996126c --- /dev/null +++ b/clang/include/clang/DirectoryWatcher/DirectoryWatcher.h @@ -0,0 +1,47 @@ +//===- DirectoryWatcher.h - Listens for directory file changes --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// \brief Utility class for listening for file system changes in a directory. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H +#define LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Index/IndexDataStore.h" +#include <functional> +#include <memory> +#include <string> + +namespace clang { + +/// Provides notifications for file system changes in a directory. +/// +/// Guarantees that the first time the directory is processed, the receiver will +/// be invoked even if the directory is empty. +class DirectoryWatcher : public index::AbstractDirectoryWatcher { + struct Implementation; + Implementation &Impl; + + DirectoryWatcher(); + + DirectoryWatcher(const DirectoryWatcher&) = delete; + DirectoryWatcher &operator =(const DirectoryWatcher&) = delete; + +public: + ~DirectoryWatcher(); + + static std::unique_ptr<DirectoryWatcher> + create(StringRef Path, EventReceiver Receiver, bool waitInitialSync, + std::string &Error); +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h index ff88256b8c0d6..09f7ab50ce574 100644 --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -34,9 +34,11 @@ using llvm::opt::ArgStringList; struct CrashReportInfo { StringRef Filename; StringRef VFSPath; + StringRef IndexStorePath; - CrashReportInfo(StringRef Filename, StringRef VFSPath) - : Filename(Filename), VFSPath(VFSPath) {} + CrashReportInfo(StringRef Filename, StringRef VFSPath, + StringRef IndexStorePath) + : Filename(Filename), VFSPath(VFSPath), IndexStorePath(IndexStorePath) {} }; /// Command - An executable path/name and argument vector to diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 4a9fd1f9700a8..18754243c21a1 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -310,6 +310,13 @@ def objcmt_whitelist_dir_path: Joined<["-"], "objcmt-whitelist-dir-path=">, Flag def : Joined<["-"], "objcmt-white-list-dir-path=">, Flags<[CC1Option]>, Alias<objcmt_whitelist_dir_path>; +def index_store_path : Separate<["-"], "index-store-path">, Flags<[CC1Option]>, + HelpText<"Enable indexing with the specified data store path">; +def index_ignore_system_symbols : Flag<["-"], "index-ignore-system-symbols">, Flags<[CC1Option]>, + HelpText<"Ignore symbols from system headers">; +def index_record_codegen_name : Flag<["-"], "index-record-codegen-name">, Flags<[CC1Option]>, + HelpText<"Record the codegen name for symbols">; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<["-"], "ccc-">, Group<internal_Group>, Flags<[Unsupported]>; diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index e757a7e397e35..b784a6f03697e 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -256,6 +256,10 @@ class FrontendOptions { std::string MTMigrateDir; std::string ARCMTMigrateReportOut; + std::string IndexStorePath; + unsigned IndexIgnoreSystemSymbols : 1; + unsigned IndexRecordCodegenName : 1; + /// The input files and their types. std::vector<FrontendInputFile> Inputs; @@ -333,8 +337,9 @@ class FrontendOptions { SkipFunctionBodies(false), UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), ModulesEmbedAllFiles(false), - IncludeTimestamps(true), ARCMTAction(ARCMT_None), - ObjCMTAction(ObjCMT_None), ProgramAction(frontend::ParseSyntaxOnly) + IncludeTimestamps(true), ARCMTAction(ARCMT_None), ObjCMTAction(ObjCMT_None), + IndexIgnoreSystemSymbols(false), IndexRecordCodegenName(false), + ProgramAction(frontend::ParseSyntaxOnly) {} /// getInputKindForExtension - Return the appropriate input kind for a file diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h new file mode 100644 index 0000000000000..714ccddc8c450 --- /dev/null +++ b/clang/include/clang/Index/IndexDataStore.h @@ -0,0 +1,102 @@ +//===--- IndexDataStore.h - Index data store info -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATASTORE_H +#define LLVM_CLANG_INDEX_INDEXDATASTORE_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include <functional> +#include <memory> +#include <string> +#include <vector> + +namespace clang { +namespace index { + +class AbstractDirectoryWatcher { +public: + enum class EventKind { + /// A file was added. + Added, + /// A file was removed. + Removed, + /// A file was modified. + Modified, + /// The watched directory got deleted. No more events will follow. + DirectoryDeleted, + }; + + struct Event { + EventKind Kind; + std::string Filename; + timespec ModTime; + }; + + typedef std::function<void(ArrayRef<Event> Events, bool isInitial)> EventReceiver; + typedef std::unique_ptr<AbstractDirectoryWatcher>(CreateFnTy) + (StringRef Path, EventReceiver Receiver, bool waitInitialSync, std::string &Error); + + virtual ~AbstractDirectoryWatcher() {} +}; + +class IndexDataStore { +public: + ~IndexDataStore(); + + static std::unique_ptr<IndexDataStore> + create(StringRef IndexStorePath, std::string &Error); + + StringRef getFilePath() const; + bool foreachUnitName(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver); + + static unsigned getFormatVersion(); + + enum class UnitEventKind { + Added, + Removed, + Modified, + /// The directory got deleted. No more events will follow. + DirectoryDeleted, + }; + struct UnitEvent { + UnitEventKind Kind; + StringRef UnitName; + timespec ModTime; + }; + struct UnitEventNotification { + bool IsInitial; + ArrayRef<UnitEvent> Events; + }; + typedef std::function<void(UnitEventNotification)> UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler Handler); + /// \returns true if an error occurred. + bool startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, + bool waitInitialSync, std::string &Error); + void stopEventListening(); + + void discardUnit(StringRef UnitName); + void discardRecord(StringRef RecordName); + + void purgeStaleData(); + +private: + IndexDataStore(void *Impl) : Impl(Impl) {} + + void *Impl; // An IndexDataStoreImpl. +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexDataStoreSymbolUtils.h b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h new file mode 100644 index 0000000000000..e1d982de094cd --- /dev/null +++ b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h @@ -0,0 +1,53 @@ +//===--- IndexDataStoreSymbolUtils.h - Utilities for indexstore symbols ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H +#define LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H + +#include "indexstore/indexstore.h" +#include "clang/Index/IndexSymbol.h" + +namespace clang { +namespace index { + +/// Map an indexstore_symbol_kind_t to a SymbolKind, handling unknown values. +SymbolKind getSymbolKind(indexstore_symbol_kind_t K); + +SymbolSubKind getSymbolSubKind(indexstore_symbol_subkind_t K); + +/// Map an indexstore_symbol_language_t to a SymbolLanguage, handling unknown +/// values. +SymbolLanguage getSymbolLanguage(indexstore_symbol_language_t L); + +/// Map an indexstore representation to a SymbolPropertySet, handling +/// unknown values. +SymbolPropertySet getSymbolProperties(uint64_t Props); + +/// Map an indexstore representation to a SymbolRoleSet, handling unknown +/// values. +SymbolRoleSet getSymbolRoles(uint64_t Roles); + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_kind_t getIndexStoreKind(SymbolKind K); + +indexstore_symbol_subkind_t getIndexStoreSubKind(SymbolSubKind K); + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_language_t getIndexStoreLang(SymbolLanguage L); + +/// Map a SymbolPropertySet to its indexstore representation. +uint64_t getIndexStoreProperties(SymbolPropertySet Props); + +/// Map a SymbolRoleSet to its indexstore representation. +uint64_t getIndexStoreRoles(SymbolRoleSet Roles); + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H diff --git a/clang/include/clang/Index/IndexRecordReader.h b/clang/include/clang/Index/IndexRecordReader.h new file mode 100644 index 0000000000000..ef8edff2db86c --- /dev/null +++ b/clang/include/clang/Index/IndexRecordReader.h @@ -0,0 +1,109 @@ +//===--- IndexRecordReader.h - Index record deserialization ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDREADER_H +#define LLVM_CLANG_INDEX_INDEXRECORDREADER_H + +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include <memory> + +namespace llvm { + class MemoryBuffer; +} + +namespace clang { +namespace index { + +struct IndexRecordDecl { + unsigned DeclID; + SymbolInfo SymInfo; + SymbolRoleSet Roles; + SymbolRoleSet RelatedRoles; + StringRef Name; + StringRef USR; + StringRef CodeGenName; +}; + +struct IndexRecordRelation { + SymbolRoleSet Roles; + const IndexRecordDecl *Dcl = nullptr; + + IndexRecordRelation() = default; + IndexRecordRelation(SymbolRoleSet Roles, const IndexRecordDecl *Dcl) + : Roles(Roles), Dcl(Dcl) {} +}; + +struct IndexRecordOccurrence { + const IndexRecordDecl *Dcl; + SmallVector<IndexRecordRelation, 4> Relations; + SymbolRoleSet Roles; + unsigned Line; + unsigned Column; +}; + +class IndexRecordReader { + IndexRecordReader(); + +public: + static std::unique_ptr<IndexRecordReader> + createWithRecordFilename(StringRef RecordFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr<IndexRecordReader> + createWithFilePath(StringRef FilePath, std::string &Error); + static std::unique_ptr<IndexRecordReader> + createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer, + std::string &Error); + + ~IndexRecordReader(); + + struct DeclSearchReturn { + bool AcceptDecl; + bool ContinueSearch; + }; + typedef DeclSearchReturn(DeclSearchCheck)(const IndexRecordDecl &); + + /// Goes through and passes record decls, after filtering using a \c Checker + /// function. + /// + /// Resulting decls can be used as filter for \c foreachOccurrence. This + /// allows allocating memory only for the record decls that the caller is + /// interested in. + bool searchDecls(llvm::function_ref<DeclSearchCheck> Checker, + llvm::function_ref<void(const IndexRecordDecl *)> Receiver); + + /// \param NoCache if true, avoids allocating memory for the decls. + /// Useful when the caller does not intend to keep \c IndexRecordReader + /// for more queries. + bool foreachDecl(bool NoCache, + llvm::function_ref<bool(const IndexRecordDecl *)> Receiver); + + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. An empty array indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + bool foreachOccurrence(ArrayRef<const IndexRecordDecl *> DeclsFilter, + ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter, + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver); + bool foreachOccurrence( + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver); + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount, + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver); + + struct Implementation; +private: + Implementation &Impl; +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexRecordWriter.h b/clang/include/clang/Index/IndexRecordWriter.h new file mode 100644 index 0000000000000..8a9720aa98fc6 --- /dev/null +++ b/clang/include/clang/Index/IndexRecordWriter.h @@ -0,0 +1,102 @@ +//===--- IndexRecordWriter.h - Index record serialization -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDWRITER_H +#define LLVM_CLANG_INDEX_INDEXRECORDWRITER_H + +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/SmallString.h" + +namespace clang { +namespace index { + +namespace writer { +/// An opaque pointer to a declaration or other symbol used by the +/// IndexRecordWriter to identify when two occurrences refer to the same symbol, +/// and as a token for getting information about a symbol from the caller. +typedef const void *OpaqueDecl; + +/// An indexer symbol suitable for serialization. +/// +/// This includes all the information about the symbol that will be serialized +/// except for roles, which are synthesized by looking at all the occurrences. +/// +/// \seealso IndexRecordDecl +/// \note this struct is generally accompanied by a buffer that owns the string +/// storage. It should not be stored permanently. +struct Symbol { + SymbolInfo SymInfo; + StringRef Name; + StringRef USR; + StringRef CodeGenName; +}; + +/// An relation to an opaque symbol. +/// \seealso IndexRecordRelation +struct SymbolRelation { + OpaqueDecl RelatedSymbol; + SymbolRoleSet Roles; +}; + +typedef llvm::function_ref<Symbol(OpaqueDecl, SmallVectorImpl<char> &Scratch)> + SymbolWriterCallback; +} // end namespace writer + +/// A language-independent utility for serializing index record files. +/// +/// Internally, this class is a small state machine. Users should first call +/// beginRecord, and if the file does not already exist, then proceed to add +/// all symbol occurrences (addOccurrence) and finally finish with endRecord. +class IndexRecordWriter { + SmallString<64> RecordsPath; ///< The records directory path. + void *Record = nullptr; ///< The state of the current record. +public: + IndexRecordWriter(StringRef IndexPath); + + enum class Result { + Success, + Failure, + AlreadyExists, + }; + + /// Begin writing a record for the file \p Filename with contents uniquely + /// identified by \p RecordHash. + /// + /// \param Filename the name of the file this is a record for. + /// \param RecordHash the unique hash of the record contents. + /// \param Error on failure, set to the error message. + /// \param RecordFile if non-null, this is set to the name of the record file. + /// + /// \returns Success if we should continue writing this record, AlreadyExists + /// if the record file has already been written, or Failure if there was an + /// error, in which case \p Error will be set. + Result beginRecord(StringRef Filename, llvm::hash_code RecordHash, + std::string &Error, std::string *RecordFile = nullptr); + + /// Finish writing the record file. + /// + /// \param Error on failure, set to the error message. + /// \param GetSymbolForDecl a callback mapping an writer::OpaqueDecl to its + /// writer::Symbol. This is how the language-specific symbol information is + /// provided to the IndexRecordWriter. The scratch parameter can be used for + /// any necessary storage. + /// + /// \return Success, or Failure and sets \p Error. + Result endRecord(std::string &Error, + writer::SymbolWriterCallback GetSymbolForDecl); + + /// Add an occurrence of the symbol \p D with the given \p Roles and location. + void addOccurrence(writer::OpaqueDecl D, SymbolRoleSet Roles, unsigned Line, + unsigned Column, ArrayRef<writer::SymbolRelation> Related); +}; + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXRECORDWRITER_H diff --git a/clang/include/clang/Index/IndexUnitReader.h b/clang/include/clang/Index/IndexUnitReader.h new file mode 100644 index 0000000000000..ccd2dceb21bce --- /dev/null +++ b/clang/include/clang/Index/IndexUnitReader.h @@ -0,0 +1,85 @@ +//===--- IndexUnitReader.h - Index unit deserialization -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITREADER_H +#define LLVM_CLANG_INDEX_INDEXUNITREADER_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Chrono.h" + +namespace clang { +namespace index { + +class IndexUnitReader { +public: + enum class DependencyKind { + Unit, + Record, + File, + }; + + ~IndexUnitReader(); + + static std::unique_ptr<IndexUnitReader> + createWithUnitFilename(StringRef UnitFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr<IndexUnitReader> + createWithFilePath(StringRef FilePath, std::string &Error); + + static Optional<llvm::sys::TimePoint<>> + getModificationTimeForUnit(StringRef UnitFilename, StringRef StorePath, + std::string &Error); + + StringRef getProviderIdentifier() const; + StringRef getProviderVersion() const; + + llvm::sys::TimePoint<> getModificationTime() const; + StringRef getWorkingDirectory() const; + StringRef getOutputFile() const; + StringRef getSysrootPath() const; + StringRef getMainFilePath() const; + StringRef getModuleName() const; + StringRef getTarget() const; + bool hasMainFile() const; + bool isSystemUnit() const; + bool isModuleUnit() const; + bool isDebugCompilation() const; + + struct DependencyInfo { + DependencyKind Kind; + bool IsSystem; + StringRef UnitOrRecordName; + StringRef FilePath; + StringRef ModuleName; + size_t FileSize; + time_t ModTime; + }; + struct IncludeInfo { + StringRef SourcePath; + unsigned SourceLine; + StringRef TargetPath; + }; + /// Unit dependencies are provided ahead of record ones, record ones + /// ahead of the file ones. + bool foreachDependency(llvm::function_ref<bool(const DependencyInfo &Info)> Receiver); + + bool foreachInclude(llvm::function_ref<bool(const IncludeInfo &Info)> Receiver); + +private: + IndexUnitReader(void *Impl) : Impl(Impl) {} + + void *Impl; // An IndexUnitReaderImpl. +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexUnitWriter.h b/clang/include/clang/Index/IndexUnitWriter.h new file mode 100644 index 0000000000000..40d2c112ec0f5 --- /dev/null +++ b/clang/include/clang/Index/IndexUnitWriter.h @@ -0,0 +1,140 @@ +//===--- IndexUnitWriter.h - Index unit serialization ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITWRITER_H +#define LLVM_CLANG_INDEX_INDEXUNITWRITER_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include <string> +#include <vector> + +namespace llvm { + class BitstreamWriter; +} + +namespace clang { + class FileEntry; + class FileManager; + +namespace index { + +namespace writer { +/// An opaque pointer to a module used by the IndexUnitWriter to associate +/// record and file dependencies with a module, and as a token for getting +/// information about the module from the caller. +typedef const void *OpaqueModule; + +/// Module info suitable for serialization. +/// +/// This is used for top-level modules and sub-modules. +struct ModuleInfo { + /// Full, dot-separate, module name. + StringRef Name; +}; + +typedef llvm::function_ref<ModuleInfo(OpaqueModule, SmallVectorImpl<char> &Scratch)> + ModuleInfoWriterCallback; +} // end namespace writer + +class IndexUnitWriter { + FileManager &FileMgr; + SmallString<64> UnitsPath; + std::string ProviderIdentifier; + std::string ProviderVersion; + std::string OutputFile; + std::string ModuleName; + const FileEntry *MainFile; + bool IsSystemUnit; + bool IsModuleUnit; + bool IsDebugCompilation; + std::string TargetTriple; + std::string WorkDir; + std::string SysrootPath; + std::function<writer::ModuleInfo(writer::OpaqueModule, + SmallVectorImpl<char> &Scratch)> GetInfoForModuleFn; + struct FileInclude { + int Index; + unsigned Line; + }; + struct FileEntryData { + const FileEntry *File; + bool IsSystem; + int ModuleIndex; + std::vector<FileInclude> Includes; + }; + std::vector<FileEntryData> Files; + std::vector<writer::OpaqueModule> Modules; + llvm::DenseMap<const FileEntry *, int> IndexByFile; + llvm::DenseMap<writer::OpaqueModule, int> IndexByModule; + llvm::DenseSet<const FileEntry *> SeenASTFiles; + struct RecordOrUnitData { + std::string Name; + int FileIndex; + int ModuleIndex; + bool IsSystem; + }; + std::vector<RecordOrUnitData> Records; + std::vector<RecordOrUnitData> ASTFileUnits; + +public: + /// \param MainFile the main file for a compiled source file. This should be + /// null for PCH and module units. + /// \param IsSystem true for system module units, false otherwise. + IndexUnitWriter(FileManager &FileMgr, + StringRef StorePath, + StringRef ProviderIdentifier, StringRef ProviderVersion, + StringRef OutputFile, + StringRef ModuleName, + const FileEntry *MainFile, + bool IsSystem, + bool IsModuleUnit, + bool IsDebugCompilation, + StringRef TargetTriple, + StringRef SysrootPath, + writer::ModuleInfoWriterCallback GetInfoForModule); + ~IndexUnitWriter(); + + int addFileDependency(const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod); + void addRecordFile(StringRef RecordFile, const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod); + void addASTFileDependency(const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod, bool withoutUnitName = false); + void addUnitDependency(StringRef UnitFile, const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod); + bool addInclude(const FileEntry *Source, unsigned Line, const FileEntry *Target); + + bool write(std::string &Error); + + void getUnitNameForOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str); + void getUnitPathForOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str); + /// If the unit file exists and \p timeCompareFilePath is provided, it will + /// return true if \p timeCompareFilePath is older than the unit file. + Optional<bool> isUnitUpToDateForOutputFile(StringRef FilePath, + Optional<StringRef> TimeCompareFilePath, + std::string &Error); + static void getUnitNameForAbsoluteOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str); + static bool initIndexDirectory(StringRef StorePath, std::string &Error); + +private: + class PathStorage; + int addModule(writer::OpaqueModule Mod); + void writeUnitInfo(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeDependencies(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeIncludes(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writePaths(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeModules(llvm::BitstreamWriter &Stream); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexingAction.h b/clang/include/clang/Index/IndexingAction.h index 8eed33c612275..7dd25b8ec0b3a 100644 --- a/clang/include/clang/Index/IndexingAction.h +++ b/clang/include/clang/Index/IndexingAction.h @@ -12,11 +12,15 @@ #include "clang/Basic/LLVM.h" #include <memory> +#include <string> namespace clang { class ASTReader; class ASTUnit; + class CompilerInstance; class FrontendAction; + class FrontendOptions; + class Module; namespace serialization { class ModuleFile; @@ -24,6 +28,7 @@ namespace serialization { namespace index { class IndexDataConsumer; + class IndexUnitWriter; struct IndexingOptions { enum class SystemSymbolFilterKind { @@ -37,6 +42,19 @@ struct IndexingOptions { bool IndexFunctionLocals = false; }; +struct RecordingOptions { + enum class IncludesRecordingKind { + None, + UserOnly, // only record includes inside non-system files. + All, + }; + + std::string DataDirPath; + bool RecordSymbolCodeGenName = false; + bool RecordSystemDependencies = true; + IncludesRecordingKind RecordIncludes = IncludesRecordingKind::UserOnly; +}; + /// \param WrappedAction another frontend action to wrap over or null. std::unique_ptr<FrontendAction> createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, @@ -52,6 +70,18 @@ void indexModuleFile(serialization::ModuleFile &Mod, std::shared_ptr<IndexDataConsumer> DataConsumer, IndexingOptions Opts); +/// \param WrappedAction another frontend action to wrap over or null. +std::unique_ptr<FrontendAction> +createIndexDataRecordingAction(const FrontendOptions &FEOpts, + std::unique_ptr<FrontendAction> WrappedAction); + +/// Checks if the unit file exists for the module file, if it doesn't it +/// generates index data for it. +/// +/// \returns true if the index data were generated, false otherwise. +bool emitIndexDataForModuleFile(const Module *Mod, const CompilerInstance &CI, + IndexUnitWriter &ParentUnitWriter); + } // namespace index } // namespace clang diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h new file mode 100644 index 0000000000000..addaa86f130c6 --- /dev/null +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -0,0 +1,502 @@ +//===--- IndexStoreCXX.h - C++ wrapper for the Index Store C API. ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Header-only C++ wrapper for the Index Store C API. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H +#define LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H + +#include "indexstore/indexstore.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include <ctime> + +namespace indexstore { + using llvm::ArrayRef; + using llvm::Optional; + using llvm::StringRef; + +static inline StringRef stringFromIndexStoreStringRef(indexstore_string_ref_t str) { + return StringRef(str.data, str.length); +} + +class IndexRecordSymbol { + indexstore_symbol_t obj; + friend class IndexRecordReader; + +public: + IndexRecordSymbol(indexstore_symbol_t obj) : obj(obj) {} + + indexstore_symbol_language_t getLanguage() { + return indexstore_symbol_get_language(obj); + } + indexstore_symbol_kind_t getKind() { return indexstore_symbol_get_kind(obj); } + indexstore_symbol_subkind_t getSubKind() { return indexstore_symbol_get_subkind(obj); } + uint64_t getProperties() { + return indexstore_symbol_get_properties(obj); + } + uint64_t getRoles() { return indexstore_symbol_get_roles(obj); } + uint64_t getRelatedRoles() { return indexstore_symbol_get_related_roles(obj); } + StringRef getName() { return stringFromIndexStoreStringRef(indexstore_symbol_get_name(obj)); } + StringRef getUSR() { return stringFromIndexStoreStringRef(indexstore_symbol_get_usr(obj)); } + StringRef getCodegenName() { return stringFromIndexStoreStringRef(indexstore_symbol_get_codegen_name(obj)); } +}; + +class IndexSymbolRelation { + indexstore_symbol_relation_t obj; + +public: + IndexSymbolRelation(indexstore_symbol_relation_t obj) : obj(obj) {} + + uint64_t getRoles() { return indexstore_symbol_relation_get_roles(obj); } + IndexRecordSymbol getSymbol() { return indexstore_symbol_relation_get_symbol(obj); } +}; + +class IndexRecordOccurrence { + indexstore_occurrence_t obj; + +public: + IndexRecordOccurrence(indexstore_occurrence_t obj) : obj(obj) {} + + IndexRecordSymbol getSymbol() { return indexstore_occurrence_get_symbol(obj); } + uint64_t getRoles() { return indexstore_occurrence_get_roles(obj); } + + bool foreachRelation(llvm::function_ref<bool(IndexSymbolRelation)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_occurrence_relations_apply(obj, ^bool(indexstore_symbol_relation_t sym_rel) { + return receiver(sym_rel); + }); +#else + return false; +#endif + } + + std::pair<unsigned, unsigned> getLineCol() { + unsigned line, col; + indexstore_occurrence_get_line_col(obj, &line, &col); + return std::make_pair(line, col); + } +}; + +class IndexStore; +typedef std::shared_ptr<IndexStore> IndexStoreRef; + +class IndexStore { + indexstore_t obj; + friend class IndexRecordReader; + friend class IndexUnitReader; + +public: + IndexStore(StringRef path, std::string &error) { + llvm::SmallString<64> buf = path; + indexstore_error_t c_err = nullptr; + obj = indexstore_store_create(buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexStore(IndexStore &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexStore() { + indexstore_store_dispose(obj); + } + + static IndexStoreRef create(StringRef path, std::string &error) { + auto storeRef = std::make_shared<IndexStore>(path, error); + if (storeRef->isInvalid()) + return nullptr; + return storeRef; + } + + static unsigned formatVersion() { + return indexstore_format_version(); + } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + bool foreachUnit(bool sorted, llvm::function_ref<bool(StringRef unitName)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_store_units_apply(obj, sorted, ^bool(indexstore_string_ref_t unit_name) { + return receiver(stringFromIndexStoreStringRef(unit_name)); + }); +#else + return false; +#endif + } + + class UnitEvent { + indexstore_unit_event_t obj; + public: + UnitEvent(indexstore_unit_event_t obj) : obj(obj) {} + + enum class Kind { + Added, + Removed, + Modified, + DirectoryDeleted, + }; + Kind getKind() const { + indexstore_unit_event_kind_t c_k = indexstore_unit_event_get_kind(obj); + Kind K; + switch (c_k) { + case INDEXSTORE_UNIT_EVENT_ADDED: K = Kind::Added; break; + case INDEXSTORE_UNIT_EVENT_REMOVED: K = Kind::Removed; break; + case INDEXSTORE_UNIT_EVENT_MODIFIED: K = Kind::Modified; break; + case INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED: K = Kind::DirectoryDeleted; break; + } + return K; + } + + StringRef getUnitName() const { + return stringFromIndexStoreStringRef(indexstore_unit_event_get_unit_name(obj)); + } + + timespec getModificationTime() const { return indexstore_unit_event_get_modification_time(obj); } + }; + + class UnitEventNotification { + indexstore_unit_event_notification_t obj; + public: + UnitEventNotification(indexstore_unit_event_notification_t obj) : obj(obj) {} + + bool isInitial() const { return indexstore_unit_event_notification_is_initial(obj); } + size_t getEventsCount() const { return indexstore_unit_event_notification_get_events_count(obj); } + UnitEvent getEvent(size_t index) const { return indexstore_unit_event_notification_get_event(obj, index); } + }; + + typedef std::function<void(UnitEventNotification)> UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler handler) { +#if INDEXSTORE_HAS_BLOCKS + if (!handler) { + indexstore_store_set_unit_event_handler(obj, nullptr); + return; + } + + indexstore_store_set_unit_event_handler(obj, ^(indexstore_unit_event_notification_t evt_note) { + handler(UnitEventNotification(evt_note)); + }); +#endif + } + + bool startEventListening(bool waitInitialSync, std::string &error) { + indexstore_unit_event_listen_options_t opts; + opts.wait_initial_sync = waitInitialSync; + indexstore_error_t c_err = nullptr; + bool ret = indexstore_store_start_unit_event_listening(obj, &opts, sizeof(opts), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + return ret; + } + + void stopEventListening() { + return indexstore_store_stop_unit_event_listening(obj); + } + + void discardUnit(StringRef UnitName) { + llvm::SmallString<64> buf = UnitName; + indexstore_store_discard_unit(obj, buf.c_str()); + } + + void discardRecord(StringRef RecordName) { + llvm::SmallString<64> buf = RecordName; + indexstore_store_discard_record(obj, buf.c_str()); + } + + void getUnitNameFromOutputPath(StringRef outputPath, llvm::SmallVectorImpl<char> &nameBuf) { + llvm::SmallString<256> buf = outputPath; + size_t nameLen = indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), nameBuf.data(), nameBuf.size()); + if (nameLen+1 > nameBuf.size()) { + nameBuf.resize(nameLen+1); + indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), nameBuf.data(), nameBuf.size()); + } + } + + llvm::Optional<timespec> + getUnitModificationTime(StringRef unitName, std::string &error) { + llvm::SmallString<64> buf = unitName; + int64_t seconds, nanoseconds; + indexstore_error_t c_err = nullptr; + bool err = indexstore_store_get_unit_modification_time(obj, buf.c_str(), + &seconds, &nanoseconds, &c_err); + if (err && c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + return llvm::None; + } + timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; + return ts; + } + + void purgeStaleData() { + indexstore_store_purge_stale_data(obj); + } +}; + +class IndexRecordReader { + indexstore_record_reader_t obj; + +public: + IndexRecordReader(IndexStore &store, StringRef recordName, std::string &error) { + llvm::SmallString<64> buf = recordName; + indexstore_error_t c_err = nullptr; + obj = indexstore_record_reader_create(store.obj, buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexRecordReader(IndexRecordReader &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexRecordReader() { + indexstore_record_reader_dispose(obj); + } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + /// Goes through and passes record decls, after filtering using a \c Checker + /// function. + /// + /// Resulting decls can be used as filter for \c foreachOccurrence. This + /// allows allocating memory only for the record decls that the caller is + /// interested in. + bool searchSymbols(llvm::function_ref<bool(IndexRecordSymbol, bool &stop)> filter, + llvm::function_ref<void(IndexRecordSymbol)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_search_symbols(obj, ^bool(indexstore_symbol_t symbol, bool *stop) { + return filter(symbol, *stop); + }, ^(indexstore_symbol_t symbol) { + receiver(symbol); + }); +#else + return false; +#endif + } + + bool foreachSymbol(bool noCache, llvm::function_ref<bool(IndexRecordSymbol)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_symbols_apply(obj, noCache, ^bool(indexstore_symbol_t sym) { + return receiver(sym); + }); +#else + return false; +#endif + } + + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. An empty array indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + bool foreachOccurrence(ArrayRef<IndexRecordSymbol> symbolsFilter, + ArrayRef<IndexRecordSymbol> relatedSymbolsFilter, + llvm::function_ref<bool(IndexRecordOccurrence)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + llvm::SmallVector<indexstore_symbol_t, 16> c_symbolsFilter; + c_symbolsFilter.reserve(symbolsFilter.size()); + for (IndexRecordSymbol sym : symbolsFilter) { + c_symbolsFilter.push_back(sym.obj); + } + llvm::SmallVector<indexstore_symbol_t, 16> c_relatedSymbolsFilter; + c_relatedSymbolsFilter.reserve(relatedSymbolsFilter.size()); + for (IndexRecordSymbol sym : relatedSymbolsFilter) { + c_relatedSymbolsFilter.push_back(sym.obj); + } + return indexstore_record_reader_occurrences_of_symbols_apply(obj, + c_symbolsFilter.data(), c_symbolsFilter.size(), + c_relatedSymbolsFilter.data(), + c_relatedSymbolsFilter.size(), + ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return false; +#endif + } + + bool foreachOccurrence( + llvm::function_ref<bool(IndexRecordOccurrence)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_apply(obj, ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return false; +#endif + } + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineEnd, + llvm::function_ref<bool(IndexRecordOccurrence)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_in_line_range_apply(obj, + lineStart, + lineEnd, + ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return false; +#endif + } +}; + +class IndexUnitDependency { + indexstore_unit_dependency_t obj; + friend class IndexUnitReader; + +public: + IndexUnitDependency(indexstore_unit_dependency_t obj) : obj(obj) {} + + enum class DependencyKind { + Unit, + Record, + File, + }; + DependencyKind getKind() { + switch (indexstore_unit_dependency_get_kind(obj)) { + case INDEXSTORE_UNIT_DEPENDENCY_UNIT: return DependencyKind::Unit; + case INDEXSTORE_UNIT_DEPENDENCY_RECORD: return DependencyKind::Record; + case INDEXSTORE_UNIT_DEPENDENCY_FILE: return DependencyKind::File; + } + } + bool isSystem() { return indexstore_unit_dependency_is_system(obj); } + StringRef getName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_name(obj)); } + StringRef getFilePath() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_filepath(obj)); } + StringRef getModuleName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_modulename(obj)); } + time_t getModificationTime() { return indexstore_unit_dependency_get_modification_time(obj); } + size_t getFileSize() { return indexstore_unit_dependency_get_file_size(obj); } + +}; + +class IndexUnitInclude { + indexstore_unit_include_t obj; + friend class IndexUnitReader; + +public: + IndexUnitInclude(indexstore_unit_include_t obj) : obj(obj) {} + + StringRef getSourcePath() { + return stringFromIndexStoreStringRef(indexstore_unit_include_get_source_path(obj)); + } + StringRef getTargetPath() { + return stringFromIndexStoreStringRef(indexstore_unit_include_get_target_path(obj)); + } + unsigned getSourceLine() { + return indexstore_unit_include_get_source_line(obj); + } +}; + +class IndexUnitReader { + indexstore_unit_reader_t obj; + +public: + IndexUnitReader(IndexStore &store, StringRef unitName, std::string &error) { + llvm::SmallString<64> buf = unitName; + indexstore_error_t c_err = nullptr; + obj = indexstore_unit_reader_create(store.obj, buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexUnitReader(IndexUnitReader &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexUnitReader() { + indexstore_unit_reader_dispose(obj); + } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + StringRef getProviderIdentifier() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_provider_identifier(obj)); + } + StringRef getProviderVersion() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_provider_version(obj)); + } + + timespec getModificationTime() { + int64_t seconds, nanoseconds; + indexstore_unit_reader_get_modification_time(obj, &seconds, &nanoseconds); + timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; + return ts; + } + + bool isSystemUnit() { return indexstore_unit_reader_is_system_unit(obj); } + bool isModuleUnit() { return indexstore_unit_reader_is_module_unit(obj); } + bool isDebugCompilation() { return indexstore_unit_reader_is_debug_compilation(obj); } + bool hasMainFile() { return indexstore_unit_reader_has_main_file(obj); } + + StringRef getMainFilePath() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_main_file(obj)); + } + StringRef getModuleName() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_module_name(obj)); + } + StringRef getWorkingDirectory() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_working_dir(obj)); + } + StringRef getOutputFile() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_output_file(obj)); + } + StringRef getSysrootPath() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_sysroot_path(obj)); + } + StringRef getTarget() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_target(obj)); + } + + bool foreachDependency(llvm::function_ref<bool(IndexUnitDependency)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_dependencies_apply(obj, ^bool(indexstore_unit_dependency_t dep) { + return receiver(dep); + }); +#else + return false; +#endif + } + + bool foreachInclude(llvm::function_ref<bool(IndexUnitInclude)> receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_includes_apply(obj, ^bool(indexstore_unit_include_t inc) { + return receiver(inc); + }); +#else + return false; +#endif + } +}; + +} // namespace indexstore + +#endif diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h new file mode 100644 index 0000000000000..024cdf6c7967d --- /dev/null +++ b/clang/include/indexstore/indexstore.h @@ -0,0 +1,487 @@ +/*===-- indexstore/indexstore.h - Index Store C API ----------------- C -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a C API for the index store. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H +#define LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H + +#include <stdint.h> +#include <stddef.h> +#include <ctime> + +/** + * \brief The version constants for the Index Store C API. + * INDEXSTORE_VERSION_MINOR should increase when there are API additions. + * INDEXSTORE_VERSION_MAJOR is intended for "major" source/ABI breaking changes. + */ +#define INDEXSTORE_VERSION_MAJOR 0 +#define INDEXSTORE_VERSION_MINOR 9 + +#define INDEXSTORE_VERSION_ENCODE(major, minor) ( \ + ((major) * 10000) \ + + ((minor) * 1)) + +#define INDEXSTORE_VERSION INDEXSTORE_VERSION_ENCODE( \ + INDEXSTORE_VERSION_MAJOR, \ + INDEXSTORE_VERSION_MINOR ) + +#define INDEXSTORE_VERSION_STRINGIZE_(major, minor) \ + #major"."#minor +#define INDEXSTORE_VERSION_STRINGIZE(major, minor) \ + INDEXSTORE_VERSION_STRINGIZE_(major, minor) + +#define INDEXSTORE_VERSION_STRING INDEXSTORE_VERSION_STRINGIZE( \ + INDEXSTORE_VERSION_MAJOR, \ + INDEXSTORE_VERSION_MINOR) + +#ifdef __cplusplus +# define INDEXSTORE_BEGIN_DECLS extern "C" { +# define INDEXSTORE_END_DECLS } +#else +# define INDEXSTORE_BEGIN_DECLS +# define INDEXSTORE_END_DECLS +#endif + +#ifndef INDEXSTORE_PUBLIC +# if defined (_MSC_VER) +# define INDEXSTORE_PUBLIC __declspec(dllimport) +# else +# define INDEXSTORE_PUBLIC +# endif +#endif + +#ifndef __has_feature +# define __has_feature(x) 0 +#endif + +#if __has_feature(blocks) +# define INDEXSTORE_HAS_BLOCKS 1 +#else +# define INDEXSTORE_HAS_BLOCKS 0 +#endif + +INDEXSTORE_BEGIN_DECLS + +typedef void *indexstore_error_t; + +INDEXSTORE_PUBLIC const char * +indexstore_error_get_description(indexstore_error_t); + +INDEXSTORE_PUBLIC void +indexstore_error_dispose(indexstore_error_t); + +typedef struct { + const char *data; + size_t length; +} indexstore_string_ref_t; + +INDEXSTORE_PUBLIC unsigned +indexstore_format_version(void); + +typedef void *indexstore_t; + +INDEXSTORE_PUBLIC indexstore_t +indexstore_store_create(const char *store_path, indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_store_dispose(indexstore_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool +indexstore_store_units_apply(indexstore_t, unsigned sorted, + bool(^applier)(indexstore_string_ref_t unit_name)); +#endif + +typedef void *indexstore_unit_event_notification_t; +typedef void *indexstore_unit_event_t; + +INDEXSTORE_PUBLIC size_t +indexstore_unit_event_notification_get_events_count(indexstore_unit_event_notification_t); + +INDEXSTORE_PUBLIC indexstore_unit_event_t +indexstore_unit_event_notification_get_event(indexstore_unit_event_notification_t, size_t index); + +INDEXSTORE_PUBLIC bool +indexstore_unit_event_notification_is_initial(indexstore_unit_event_notification_t); + +typedef enum { + INDEXSTORE_UNIT_EVENT_ADDED = 1, + INDEXSTORE_UNIT_EVENT_REMOVED = 2, + INDEXSTORE_UNIT_EVENT_MODIFIED = 3, + INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 4, +} indexstore_unit_event_kind_t; + +INDEXSTORE_PUBLIC indexstore_unit_event_kind_t +indexstore_unit_event_get_kind(indexstore_unit_event_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_event_get_unit_name(indexstore_unit_event_t); + +INDEXSTORE_PUBLIC timespec +indexstore_unit_event_get_modification_time(indexstore_unit_event_t); + +#if INDEXSTORE_HAS_BLOCKS +typedef void (^indexstore_unit_event_handler_t)(indexstore_unit_event_notification_t); + +INDEXSTORE_PUBLIC void +indexstore_store_set_unit_event_handler(indexstore_t, + indexstore_unit_event_handler_t handler); +#endif + +typedef struct { + /// If true, \c indexstore_store_start_unit_event_listening will block until + /// the initial set of units is passed to the unit event handler, otherwise + /// the function will return and the initial set will be passed asynchronously. + bool wait_initial_sync; +} indexstore_unit_event_listen_options_t; + +INDEXSTORE_PUBLIC bool +indexstore_store_start_unit_event_listening(indexstore_t, + indexstore_unit_event_listen_options_t *, + size_t listen_options_struct_size, + indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_store_stop_unit_event_listening(indexstore_t); + +INDEXSTORE_PUBLIC void +indexstore_store_discard_unit(indexstore_t, const char *unit_name); + +INDEXSTORE_PUBLIC void +indexstore_store_discard_record(indexstore_t, const char *record_name); + +INDEXSTORE_PUBLIC void +indexstore_store_purge_stale_data(indexstore_t); + +/// Determines the unit name from the \c output_path and writes it out in the +/// \c name_buf buffer. It doesn't write more than \c buf_size. +/// \returns the length of the name. If this is larger than \c buf_size, the +/// caller should call the function again with a buffer of the appropriate size. +INDEXSTORE_PUBLIC size_t +indexstore_store_get_unit_name_from_output_path(indexstore_t store, + const char *output_path, + char *name_buf, + size_t buf_size); + +/// \returns true if an error occurred, false otherwise. +INDEXSTORE_PUBLIC bool +indexstore_store_get_unit_modification_time(indexstore_t store, + const char *unit_name, + int64_t *seconds, + int64_t *nanoseconds, + indexstore_error_t *error); + +typedef void *indexstore_symbol_t; + +typedef enum { + INDEXSTORE_SYMBOL_KIND_UNKNOWN = 0, + INDEXSTORE_SYMBOL_KIND_MODULE = 1, + INDEXSTORE_SYMBOL_KIND_NAMESPACE = 2, + INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS = 3, + INDEXSTORE_SYMBOL_KIND_MACRO = 4, + INDEXSTORE_SYMBOL_KIND_ENUM = 5, + INDEXSTORE_SYMBOL_KIND_STRUCT = 6, + INDEXSTORE_SYMBOL_KIND_CLASS = 7, + INDEXSTORE_SYMBOL_KIND_PROTOCOL = 8, + INDEXSTORE_SYMBOL_KIND_EXTENSION = 9, + INDEXSTORE_SYMBOL_KIND_UNION = 10, + INDEXSTORE_SYMBOL_KIND_TYPEALIAS = 11, + INDEXSTORE_SYMBOL_KIND_FUNCTION = 12, + INDEXSTORE_SYMBOL_KIND_VARIABLE = 13, + INDEXSTORE_SYMBOL_KIND_FIELD = 14, + INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT = 15, + INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD = 16, + INDEXSTORE_SYMBOL_KIND_CLASSMETHOD = 17, + INDEXSTORE_SYMBOL_KIND_STATICMETHOD = 18, + INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY = 19, + INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY = 20, + INDEXSTORE_SYMBOL_KIND_STATICPROPERTY = 21, + INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR = 22, + INDEXSTORE_SYMBOL_KIND_DESTRUCTOR = 23, + INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION = 24, + INDEXSTORE_SYMBOL_KIND_PARAMETER = 25, + + INDEXSTORE_SYMBOL_KIND_COMMENTTAG = 1000, +} indexstore_symbol_kind_t; + +typedef enum { + INDEXSTORE_SYMBOL_SUBKIND_NONE = 0, + INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR = 1, + INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR = 2, + INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER = 3, + INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER = 4, + + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET = 1000, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET = 1001, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR = 1002, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR = 1003, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT = 1004, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS = 1005, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM = 1006, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL = 1007, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR = 1008, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR = 1009, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR = 1010, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT = 1011, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE = 1012, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM = 1013, +} indexstore_symbol_subkind_t; + +typedef enum { + INDEXSTORE_SYMBOL_PROPERTY_GENERIC = 1 << 0, + INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION = 1 << 1, + INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION = 1 << 2, + INDEXSTORE_SYMBOL_PROPERTY_UNITTEST = 1 << 3, + INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED = 1 << 4, + INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION = 1 << 5, + INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE = 1 << 6, + INDEXSTORE_SYMBOL_PROPERTY_LOCAL = 1 << 7, +} indexstore_symbol_property_t; + +typedef enum { + INDEXSTORE_SYMBOL_LANG_C = 0, + INDEXSTORE_SYMBOL_LANG_OBJC = 1, + INDEXSTORE_SYMBOL_LANG_CXX = 2, + + INDEXSTORE_SYMBOL_LANG_SWIFT = 100, +} indexstore_symbol_language_t; + +typedef enum { + INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, + INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, + INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, + INDEXSTORE_SYMBOL_ROLE_READ = 1 << 3, + INDEXSTORE_SYMBOL_ROLE_WRITE = 1 << 4, + INDEXSTORE_SYMBOL_ROLE_CALL = 1 << 5, + INDEXSTORE_SYMBOL_ROLE_DYNAMIC = 1 << 6, + INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7, + INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8, + + // Relation roles. + INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 9, + INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 10, + INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 11, + INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 12, + INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 13, + INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 14, + INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 15, + INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 16, + INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 17, + INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 18, +} indexstore_symbol_role_t; + +INDEXSTORE_PUBLIC indexstore_symbol_language_t +indexstore_symbol_get_language(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_kind_t +indexstore_symbol_get_kind(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_subkind_t +indexstore_symbol_get_subkind(indexstore_symbol_t); + +INDEXSTORE_PUBLIC uint64_t +indexstore_symbol_get_properties(indexstore_symbol_t); + +INDEXSTORE_PUBLIC uint64_t +indexstore_symbol_get_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC uint64_t +indexstore_symbol_get_related_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_symbol_get_name(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_symbol_get_usr(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_symbol_get_codegen_name(indexstore_symbol_t); + +typedef void *indexstore_symbol_relation_t; + +INDEXSTORE_PUBLIC uint64_t +indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t); + +INDEXSTORE_PUBLIC indexstore_symbol_t +indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t); + +typedef void *indexstore_occurrence_t; + +INDEXSTORE_PUBLIC indexstore_symbol_t +indexstore_occurrence_get_symbol(indexstore_occurrence_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool +indexstore_occurrence_relations_apply(indexstore_occurrence_t, + bool(^applier)(indexstore_symbol_relation_t symbol_rel)); +#endif + +INDEXSTORE_PUBLIC uint64_t +indexstore_occurrence_get_roles(indexstore_occurrence_t); + +INDEXSTORE_PUBLIC void +indexstore_occurrence_get_line_col(indexstore_occurrence_t, + unsigned *line, unsigned *column); + +typedef void *indexstore_record_reader_t; + +INDEXSTORE_PUBLIC indexstore_record_reader_t +indexstore_record_reader_create(indexstore_t store, const char *record_name, + indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_record_reader_dispose(indexstore_record_reader_t); + +#if INDEXSTORE_HAS_BLOCKS +/// Goes through the symbol data and passes symbols to \c receiver, for the +/// symbol data that \c filter returns true on. +/// +/// This allows allocating memory only for the record symbols that the caller is +/// interested in. +INDEXSTORE_PUBLIC bool +indexstore_record_reader_search_symbols(indexstore_record_reader_t, + bool(^filter)(indexstore_symbol_t symbol, bool *stop), + void(^receiver)(indexstore_symbol_t symbol)); + +/// \param nocache if true, avoids allocating memory for the symbols. +/// Useful when the caller does not intend to keep \c indexstore_record_reader_t +/// for more queries. +INDEXSTORE_PUBLIC bool +indexstore_record_reader_symbols_apply(indexstore_record_reader_t, + bool nocache, + bool(^applier)(indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_apply(indexstore_record_reader_t, + bool(^applier)(indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_in_line_range_apply(indexstore_record_reader_t, + unsigned line_start, + unsigned line_count, + bool(^applier)(indexstore_occurrence_t occur)); + +/// \param symbols if non-zero \c symbols_count, indicates the list of symbols +/// that we want to get occurrences for. An empty array indicates that we want +/// occurrences for all symbols. +/// \param related_symbols Same as \c symbols but for related symbols. +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t, + indexstore_symbol_t *symbols, size_t symbols_count, + indexstore_symbol_t *related_symbols, size_t related_symbols_count, + bool(^applier)(indexstore_occurrence_t occur)); +#endif + + +typedef void *indexstore_unit_reader_t; + +INDEXSTORE_PUBLIC indexstore_unit_reader_t +indexstore_unit_reader_create(indexstore_t store, const char *unit_name, + indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_unit_reader_dispose(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC void +indexstore_unit_reader_get_modification_time(indexstore_unit_reader_t, + int64_t *seconds, + int64_t *nanoseconds); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_has_main_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_main_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_module_name(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_output_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_target(indexstore_unit_reader_t); + +typedef void *indexstore_unit_dependency_t; +typedef void *indexstore_unit_include_t; + +typedef enum { + INDEXSTORE_UNIT_DEPENDENCY_UNIT = 1, + INDEXSTORE_UNIT_DEPENDENCY_RECORD = 2, + INDEXSTORE_UNIT_DEPENDENCY_FILE = 3, +} indexstore_unit_dependency_kind_t; + +INDEXSTORE_PUBLIC indexstore_unit_dependency_kind_t +indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_dependency_is_system(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_dependency_get_name(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC time_t +indexstore_unit_dependency_get_modification_time(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC size_t +indexstore_unit_dependency_get_file_size(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_include_get_source_path(indexstore_unit_include_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_include_get_target_path(indexstore_unit_include_t); + +INDEXSTORE_PUBLIC unsigned +indexstore_unit_include_get_source_line(indexstore_unit_include_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t, + bool(^applier)(indexstore_unit_dependency_t)); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_includes_apply(indexstore_unit_reader_t, + bool(^applier)(indexstore_unit_include_t)); + +#endif + +INDEXSTORE_END_DECLS + +#endif diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index 574c511747f40..5e291e52c52c9 100644 --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(Frontend) add_subdirectory(FrontendTool) add_subdirectory(Tooling) add_subdirectory(Index) +add_subdirectory(DirectoryWatcher) if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) endif() diff --git a/clang/lib/DirectoryWatcher/CMakeLists.txt b/clang/lib/DirectoryWatcher/CMakeLists.txt new file mode 100644 index 0000000000000..425a40ff4509e --- /dev/null +++ b/clang/lib/DirectoryWatcher/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangDirectoryWatcher + DirectoryWatcher.cpp + + LINK_LIBS + clangBasic + ) diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp new file mode 100644 index 0000000000000..3a90526aac756 --- /dev/null +++ b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp @@ -0,0 +1,275 @@ +//===- DirectoryWatcher.cpp - Listens for directory file changes ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// \brief Utility class for listening for file system changes in a directory. +//===----------------------------------------------------------------------===// + +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" + +#define HAVE_CORESERVICES 0 + +#if defined(__has_include) +#if __has_include(<CoreServices/CoreServices.h>) + +#include <CoreServices/CoreServices.h> +#undef HAVE_CORESERVICES +#define HAVE_CORESERVICES 1 + +#endif +#endif + +using namespace clang; +using namespace llvm; + +static timespec toTimeSpec(sys::TimePoint<> tp) { + std::chrono::seconds sec = std::chrono::time_point_cast<std::chrono::seconds>( + tp).time_since_epoch(); + std::chrono::nanoseconds nsec = + std::chrono::time_point_cast<std::chrono::nanoseconds>(tp - sec) + .time_since_epoch(); + timespec ts; + ts.tv_sec = sec.count(); + ts.tv_nsec = nsec.count(); + return ts; +} + +static Optional<timespec> getModTime(StringRef path) { + sys::fs::file_status Status; + std::error_code EC = status(path, Status); + if (EC) + return None; + return toTimeSpec(Status.getLastModificationTime()); +} + +struct DirectoryWatcher::Implementation { +#if HAVE_CORESERVICES + FSEventStreamRef EventStream = nullptr; + + bool setupFSEventStream(StringRef path, EventReceiver receiver, + dispatch_queue_t queue); + void stopFSEventStream(); + + ~Implementation() { + stopFSEventStream(); + }; +#endif +}; + +#if HAVE_CORESERVICES +namespace { +struct EventStreamContextData { + std::string WatchedPath; + DirectoryWatcher::EventReceiver Receiver; + + EventStreamContextData(std::string watchedPath, DirectoryWatcher::EventReceiver receiver) + : WatchedPath(std::move(watchedPath)), Receiver(std::move(receiver)) { + } + + static void dispose(const void *ctx) { + delete static_cast<const EventStreamContextData*>(ctx); + } +}; +} + +static void eventStreamCallback( + ConstFSEventStreamRef stream, + void *clientCallBackInfo, + size_t numEvents, + void *eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) { + auto *ctx = static_cast<EventStreamContextData*>(clientCallBackInfo); + + std::vector<DirectoryWatcher::Event> Events; + for (size_t i = 0; i < numEvents; ++i) { + StringRef path = ((const char **)eventPaths)[i]; + const FSEventStreamEventFlags flags = eventFlags[i]; + if (!(flags & kFSEventStreamEventFlagItemIsFile)) { + if ((flags & kFSEventStreamEventFlagItemRemoved) && path == ctx->WatchedPath) { + DirectoryWatcher::Event Evt{DirectoryWatcher::EventKind::DirectoryDeleted, path, timespec{}}; + Events.push_back(Evt); + break; + } + continue; + } + DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Modified; + if ((flags & kFSEventStreamEventFlagItemCreated) || + (flags & kFSEventStreamEventFlagItemRenamed)) + K = DirectoryWatcher::EventKind::Added; + if (flags & kFSEventStreamEventFlagItemRemoved) + K = DirectoryWatcher::EventKind::Removed; + timespec modTime{}; + if (K != DirectoryWatcher::EventKind::Removed) { + auto modTimeOpt = getModTime(path); + if (!modTimeOpt.hasValue()) + continue; + modTime = modTimeOpt.getValue(); + } + DirectoryWatcher::Event Evt{K, path, modTime}; + Events.push_back(Evt); + } + + ctx->Receiver(Events, /*isInitial=*/false); +} + +bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path, + EventReceiver receiver, + dispatch_queue_t queue) { + if (path.empty()) + return true; + + CFMutableArrayRef pathsToWatch = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); + CFStringRef cfPathStr = CFStringCreateWithBytes(nullptr, (const UInt8 *)path.data(), path.size(), kCFStringEncodingUTF8, false); + CFArrayAppendValue(pathsToWatch, cfPathStr); + CFRelease(cfPathStr); + CFAbsoluteTime latency = 0.2; // Latency in seconds. + + std::string realPath; + { + SmallString<128> Storage; + StringRef P = llvm::Twine(path).toNullTerminatedStringRef(Storage); + char Buffer[PATH_MAX]; + // Use ::realpath to get the real path name + if (::realpath(P.begin(), Buffer) != nullptr) + realPath = Buffer; + else + realPath = path; + } + + EventStreamContextData *ctxData = new EventStreamContextData(std::move(realPath), std::move(receiver)); + FSEventStreamContext context; + context.version = 0; + context.info = ctxData; + context.retain = nullptr; + context.release = EventStreamContextData::dispose; + context.copyDescription = nullptr; + + EventStream = FSEventStreamCreate(nullptr, + eventStreamCallback, + &context, + pathsToWatch, + kFSEventStreamEventIdSinceNow, + latency, + kFSEventStreamCreateFlagFileEvents | + kFSEventStreamCreateFlagNoDefer); + CFRelease(pathsToWatch); + if (!EventStream) { + return true; + } + FSEventStreamSetDispatchQueue(EventStream, queue); + FSEventStreamStart(EventStream); + return false; +} + +void DirectoryWatcher::Implementation::stopFSEventStream() { + if (!EventStream) + return; + FSEventStreamStop(EventStream); + FSEventStreamInvalidate(EventStream); + FSEventStreamRelease(EventStream); + EventStream = nullptr; +} +#endif + +DirectoryWatcher::DirectoryWatcher() + : Impl(*new Implementation()) {} + +DirectoryWatcher::~DirectoryWatcher() { + delete &Impl; +} + +#if HAVE_CORESERVICES +static std::vector<DirectoryWatcher::Event> scanDirectory(StringRef Path) { + using namespace llvm::sys; + + std::vector<DirectoryWatcher::Event> Events; + std::error_code EC; + for (auto It = fs::directory_iterator(Path, EC), End = fs::directory_iterator(); + !EC && It != End; It.increment(EC)) { + auto modTime = getModTime(It->path()); + if (!modTime.hasValue()) + continue; + DirectoryWatcher::Event Event{DirectoryWatcher::EventKind::Added, It->path(), modTime.getValue()}; + Events.push_back(std::move(Event)); + } + return Events; +} +#endif + +std::unique_ptr<DirectoryWatcher> DirectoryWatcher::create(StringRef Path, + EventReceiver Receiver, bool waitInitialSync, std::string &Error) { +#if HAVE_CORESERVICES + + using namespace llvm::sys; + + if (!fs::exists(Path)) { + std::error_code EC = fs::create_directories(Path); + if (EC) { + Error = EC.message(); + return nullptr; + } + } + + bool IsDir; + std::error_code EC = fs::is_directory(Path, IsDir); + if (EC) { + Error = EC.message(); + return nullptr; + } + if (!IsDir) { + Error = "path is not a directory: "; + Error += Path; + return nullptr; + } + + std::unique_ptr<DirectoryWatcher> DirWatch; + DirWatch.reset(new DirectoryWatcher()); + auto &Impl = DirWatch->Impl; + + dispatch_queue_t queue = dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL); + dispatch_semaphore_t initScanSema = dispatch_semaphore_create(0); + dispatch_semaphore_t setupFSEventsSema = dispatch_semaphore_create(0); + + std::string copiedPath = Path; + dispatch_retain(initScanSema); + dispatch_retain(setupFSEventsSema); + dispatch_async(queue, ^{ + // Wait for the event stream to be setup before doing the initial scan, + // to make sure we won't miss any events. + dispatch_semaphore_wait(setupFSEventsSema, DISPATCH_TIME_FOREVER); + auto events = scanDirectory(copiedPath); + Receiver(events, /*isInitial=*/true); + dispatch_semaphore_signal(initScanSema); + dispatch_release(setupFSEventsSema); + dispatch_release(initScanSema); + }); + bool fsErr = Impl.setupFSEventStream(Path, Receiver, queue); + dispatch_semaphore_signal(setupFSEventsSema); + + if (waitInitialSync) { + dispatch_semaphore_wait(initScanSema, DISPATCH_TIME_FOREVER); + } + dispatch_release(setupFSEventsSema); + dispatch_release(initScanSema); + dispatch_release(queue); + + if (fsErr) { + raw_string_ostream(Error) << "failed to setup FSEvents stream for path: " << Path; + return nullptr; + } + + return DirWatch; +#else + return nullptr; +#endif +} diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index f23975829eaf3..98e806ec66305 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -966,7 +966,9 @@ void Driver::generateCompilationDiagnostics(Compilation &C, } // Assume associated files are based off of the first temporary file. - CrashReportInfo CrashInfo(TempFiles[0], VFS); + CrashReportInfo CrashInfo( + TempFiles[0], VFS, + C.getArgs().getLastArgValue(options::OPT_index_store_path)); std::string Script = CrashInfo.Filename.rsplit('.').first.str() + ".sh"; std::error_code EC; diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 789f107bbe610..355b9e0556a6c 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -67,6 +67,8 @@ static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, .Default(false); if (IsInclude) return HaveCrashVFS ? false : true; + if (StringRef(Flag).startswith("-index-store-path")) + return true; // The remaining flags are treated as a single argument. @@ -221,6 +223,7 @@ void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, } bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty(); + bool HaveIndexStorePath = CrashInfo && !CrashInfo->IndexStorePath.empty(); for (size_t i = 0, e = Args.size(); i < e; ++i) { const char *const Arg = Args[i]; @@ -284,6 +287,24 @@ void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, printArg(OS, ModCachePath, Quote); } + if (CrashInfo && HaveIndexStorePath) { + SmallString<128> IndexStoreDir; + + if (HaveCrashVFS) { + IndexStoreDir = llvm::sys::path::parent_path( + llvm::sys::path::parent_path(CrashInfo->VFSPath)); + llvm::sys::path::append(IndexStoreDir, "index-store"); + } else { + IndexStoreDir = "index-store"; + } + + OS << ' '; + printArg(OS, "-index-store-path", Quote); + OS << ' '; + printArg(OS, IndexStoreDir.c_str(), Quote); + } + + if (ResponseFile != nullptr) { OS << "\n Arguments passed via response file:\n"; writeResponseFile(OS); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index f45b2df204798..8c50695ffd559 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -2957,6 +2957,26 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_objcmt_whitelist_dir_path); } + if (Args.hasArg(options::OPT_index_store_path)) { + Args.AddLastArg(CmdArgs, options::OPT_index_store_path); + Args.AddLastArg(CmdArgs, options::OPT_index_ignore_system_symbols); + Args.AddLastArg(CmdArgs, options::OPT_index_record_codegen_name); + + // If '-o' is passed along with '-fsyntax-only' pass it along the cc1 + // invocation so that the index action knows what the out file is. + if (isa<CompileJobAction>(JA) && JA.getType() == types::TY_Nothing) { + Args.AddLastArg(CmdArgs, options::OPT_o); + } + } + + if (const char *IdxStorePath = ::getenv("CLANG_PROJECT_INDEX_PATH")) { + CmdArgs.push_back("-index-store-path"); + CmdArgs.push_back(IdxStorePath); + CmdArgs.push_back("-index-ignore-system-symbols"); + CmdArgs.push_back("-index-record-codegen-name"); + } + + // Add preprocessing options like -I, -D, etc. if we are using the // preprocessor. // diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index e41b50c40b289..29704b4291cb8 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -435,6 +435,10 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, // more information. ArgStringList CmdArgs; + Args.ClaimAllArgs(options::OPT_index_store_path); + Args.ClaimAllArgs(options::OPT_index_ignore_system_symbols); + Args.ClaimAllArgs(options::OPT_index_record_codegen_name); + /// Hack(tm) to ignore linking errors when we are doing ARC migration. if (Args.hasArg(options::OPT_ccc_arcmt_check, options::OPT_ccc_arcmt_migrate)) { diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index ba3bd7d28c703..9f4f7d316cee5 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -57,6 +57,7 @@ add_clang_library(clangFrontend clangBasic clangDriver clangEdit + clangIndex clangLex clangParse clangSema diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 849d6651fb796..de594a0ce4393 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -29,6 +29,7 @@ #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Index/IndexingAction.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PTHManager.h" #include "clang/Lex/Preprocessor.h" @@ -1166,8 +1167,18 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, llvm::CrashRecoveryContext CRC; CRC.RunSafelyOnThread( [&]() { - GenerateModuleFromModuleMapAction Action; - Instance.ExecuteAction(Action); + // FIXME: I have no idea what the best way to do this is, but it's + // probably not this. Interfaces changed upstream. + std::unique_ptr<FrontendAction> Action( + new GenerateModuleFromModuleMapAction); + + if (!FrontendOpts.IndexStorePath.empty()) { +#if defined(__APPLE__) + Action = index::createIndexDataRecordingAction(FrontendOpts, + std::move(Action)); +#endif + } + Instance.ExecuteAction(*Action); }, ThreadStackSize); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index e7603ce522214..adb970c0ea5dc 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1382,6 +1382,10 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, << "ARC migration" << "ObjC migration"; } + Opts.IndexStorePath = Args.getLastArgValue(OPT_index_store_path); + Opts.IndexIgnoreSystemSymbols = Args.hasArg(OPT_index_ignore_system_symbols); + Opts.IndexRecordCodegenName = Args.hasArg(OPT_index_record_codegen_name); + InputKind DashX(InputKind::Unknown); if (const Arg *A = Args.getLastArg(OPT_x)) { StringRef XValue = A->getValue(); diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index a7c140188b35b..816a1e7a817d9 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -22,6 +22,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/Utils.h" +#include "clang/Index/IndexingAction.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "llvm/Option/OptTable.h" @@ -163,6 +164,12 @@ CreateFrontendAction(CompilerInstance &CI) { } #endif + if (!FEOpts.IndexStorePath.empty()) { +#if defined(__APPLE__) + Act = index::createIndexDataRecordingAction(FEOpts, std::move(Act)); +#endif + } + // If there are any AST files to merge, create a frontend action // adaptor to perform the merge. if (!FEOpts.ASTMergeFiles.empty()) diff --git a/clang/lib/Index/BitstreamVisitor.h b/clang/lib/Index/BitstreamVisitor.h new file mode 100644 index 0000000000000..d324f1a4fdbac --- /dev/null +++ b/clang/lib/Index/BitstreamVisitor.h @@ -0,0 +1,163 @@ +//===--- BitstreamVisitor.h - Helper for reading a bitstream --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H +#define LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H + +#include "llvm/Bitcode/BitstreamReader.h" +#include "clang/Basic/LLVM.h" +#include "clang/Serialization/ASTReader.h" +#include <string> + +namespace clang { +namespace index { +namespace store { + +/// Helper class that saves the current stream position and +/// then restores it when destroyed. +struct SavedStreamPosition { + explicit SavedStreamPosition(llvm::BitstreamCursor &Cursor) + : Cursor(Cursor), Offset(Cursor.GetCurrentBitNo()) { } + + ~SavedStreamPosition() { + Cursor.JumpToBit(Offset); + } + +private: + llvm::BitstreamCursor &Cursor; + uint64_t Offset; +}; + +enum class StreamVisit { + Continue, + Skip, + Abort +}; + +template <typename ImplClass> +class BitstreamVisitor { + SmallVector<unsigned, 4> BlockStack; + +protected: + llvm::BitstreamCursor &Stream; + Optional<llvm::BitstreamBlockInfo> BlockInfo; + std::string *Error; + +public: + BitstreamVisitor(llvm::BitstreamCursor &Stream) + : Stream(Stream) {} + + StreamVisit visitBlock(unsigned ID) { + return StreamVisit::Continue; + } + + bool visit(std::string &Error) { + this->Error = &Error; + + ASTReader::RecordData Record; + while (1) { + llvm::BitstreamEntry Entry = Stream.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + + switch (Entry.Kind) { + case llvm::BitstreamEntry::Error: + Error = "malformed serialization"; + return false; + + case llvm::BitstreamEntry::EndBlock: + if (BlockStack.empty()) + return true; + BlockStack.pop_back(); + if (Stream.ReadBlockEnd()) { + Error = "malformed serialization"; + return false; + } + if (Stream.AtEndOfStream()) + return true; + break; + + case llvm::BitstreamEntry::SubBlock: { + if (Entry.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) { + BlockInfo = Stream.ReadBlockInfoBlock(); + if (!BlockInfo) { + Error = "malformed BlockInfoBlock"; + return false; + } + Stream.setBlockInfo(&*BlockInfo); + break; + } + + StreamVisit Ret = static_cast<ImplClass*>(this)->visitBlock(Entry.ID); + switch (Ret) { + case StreamVisit::Continue: + if (Stream.EnterSubBlock(Entry.ID)) { + Error = "malformed block record"; + return false; + } + readBlockAbbrevs(Stream); + BlockStack.push_back(Entry.ID); + break; + + case StreamVisit::Skip: + if (Stream.SkipBlock()) { + Error = "malformed serialization"; + return false; + } + if (Stream.AtEndOfStream()) + return true; + break; + + case StreamVisit::Abort: + return false; + } + break; + } + + case llvm::BitstreamEntry::Record: { + Record.clear(); + StringRef Blob; + unsigned RecID = Stream.readRecord(Entry.ID, Record, &Blob); + unsigned BlockID = BlockStack.empty() ? 0 : BlockStack.back(); + StreamVisit Ret = static_cast<ImplClass*>(this)->visitRecord(BlockID, RecID, Record, Blob); + switch (Ret) { + case StreamVisit::Continue: + break; + + case StreamVisit::Skip: + Stream.skipRecord(Entry.ID); + break; + + case StreamVisit::Abort: + return false; + } + break; + } + } + } + } + + static void readBlockAbbrevs(llvm::BitstreamCursor &Cursor) { + while (true) { + uint64_t Offset = Cursor.GetCurrentBitNo(); + unsigned Code = Cursor.ReadCode(); + + // We expect all abbrevs to be at the start of the block. + if (Code != llvm::bitc::DEFINE_ABBREV) { + Cursor.JumpToBit(Offset); + return; + } + Cursor.ReadAbbrevRecord(); + } + } +}; + +} // end namespace store +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/CMakeLists.txt b/clang/lib/Index/CMakeLists.txt index c9fbfafcf9460..b5dea4cd05c50 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -4,14 +4,23 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangIndex + ClangIndexRecordWriter.cpp CodegenNameGenerator.cpp CommentToXML.cpp + FileIndexRecord.cpp IndexBody.cpp + IndexDataStore.cpp + IndexDataStoreUtils.cpp IndexDecl.cpp IndexingAction.cpp IndexingContext.cpp + IndexRecordHasher.cpp + IndexRecordReader.cpp + IndexRecordWriter.cpp IndexSymbol.cpp IndexTypeSourceInfo.cpp + IndexUnitReader.cpp + IndexUnitWriter.cpp USRGeneration.cpp ADDITIONAL_HEADERS diff --git a/clang/lib/Index/ClangIndexRecordWriter.cpp b/clang/lib/Index/ClangIndexRecordWriter.cpp new file mode 100644 index 0000000000000..612ef1b5b41a0 --- /dev/null +++ b/clang/lib/Index/ClangIndexRecordWriter.cpp @@ -0,0 +1,128 @@ +//===--- ClangIndexRecordWriter.cpp - Index record serialization ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangIndexRecordWriter.h" +#include "FileIndexRecord.h" +#include "clang/Index/IndexSymbol.h" +#include "clang/Index/IndexRecordReader.h" +#include "clang/Index/USRGeneration.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" + +using namespace clang; +using namespace clang::index; + +StringRef ClangIndexRecordWriter::getUSR(const Decl *D) { + assert(D->isCanonicalDecl()); + auto Insert = USRByDecl.insert(std::make_pair(D, StringRef())); + if (Insert.second) { + Insert.first->second = getUSRNonCached(D); + } + return Insert.first->second; +} + +StringRef ClangIndexRecordWriter::getUSRNonCached(const Decl *D) { + SmallString<256> Buf; + bool Ignore = generateUSRForDecl(D, Buf); + if (Ignore) + return StringRef(); + StringRef USR = Buf.str(); + char *Ptr = Allocator.Allocate<char>(USR.size()); + std::copy(USR.begin(), USR.end(), Ptr); + return StringRef(Ptr, USR.size()); +} + +ClangIndexRecordWriter::ClangIndexRecordWriter(ASTContext &Ctx, + RecordingOptions Opts) + : Impl(Opts.DataDirPath), Ctx(Ctx), RecordOpts(std::move(Opts)), + Hasher(Ctx) { + if (Opts.RecordSymbolCodeGenName) + CGNameGen.reset(new CodegenNameGenerator(Ctx)); +} + +ClangIndexRecordWriter::~ClangIndexRecordWriter() {} + +bool ClangIndexRecordWriter::writeRecord(StringRef Filename, + const FileIndexRecord &IdxRecord, + std::string &Error, + std::string *OutRecordFile) { + + auto RecordHash = Hasher.hashRecord(IdxRecord); + + switch (Impl.beginRecord(Filename, RecordHash, Error, OutRecordFile)) { + case IndexRecordWriter::Result::Success: + break; // Continue writing. + case IndexRecordWriter::Result::Failure: + return true; + case IndexRecordWriter::Result::AlreadyExists: + return false; + } + + ASTContext &Ctx = getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + FileID FID = IdxRecord.getFileID(); + auto getLineCol = [&](unsigned Offset) -> std::pair<unsigned, unsigned> { + unsigned LineNo = SM.getLineNumber(FID, Offset); + unsigned ColNo = SM.getColumnNumber(FID, Offset); + return std::make_pair(LineNo, ColNo); + }; + + for (auto &Occur : IdxRecord.getDeclOccurrences()) { + unsigned Line, Col; + std::tie(Line, Col) = getLineCol(Occur.Offset); + SmallVector<writer::SymbolRelation, 3> Related; + Related.reserve(Occur.Relations.size()); + for (auto &Rel : Occur.Relations) + Related.push_back(writer::SymbolRelation{Rel.RelatedSymbol, Rel.Roles}); + + Impl.addOccurrence(Occur.Dcl, Occur.Roles, Line, Col, Related); + } + + PrintingPolicy Policy(Ctx.getLangOpts()); + Policy.SuppressTemplateArgsInCXXConstructors = true; + + auto Result = Impl.endRecord(Error, + [&](writer::OpaqueDecl OD, SmallVectorImpl<char> &Scratch) { + const Decl *D = static_cast<const Decl *>(OD); + auto Info = getSymbolInfo(D); + + writer::Symbol Sym; + Sym.SymInfo = Info; + + auto *ND = dyn_cast<NamedDecl>(D); + if (ND) { + llvm::raw_svector_ostream OS(Scratch); + DeclarationName DeclName = ND->getDeclName(); + if (!DeclName.isEmpty()) + DeclName.print(OS, Policy); + } + unsigned NameLen = Scratch.size(); + Sym.Name = StringRef(Scratch.data(), NameLen); + + Sym.USR = getUSR(D); + assert(!Sym.USR.empty() && "Recorded decl without USR!"); + + if (CGNameGen && ND) { + llvm::raw_svector_ostream OS(Scratch); + CGNameGen->writeName(ND, OS); + } + unsigned CGNameLen = Scratch.size() - NameLen; + Sym.CodeGenName = StringRef(Scratch.data() + NameLen, CGNameLen); + return Sym; + }); + + switch (Result) { + case IndexRecordWriter::Result::Success: + case IndexRecordWriter::Result::AlreadyExists: + return false; + case IndexRecordWriter::Result::Failure: + return true; + } +} diff --git a/clang/lib/Index/ClangIndexRecordWriter.h b/clang/lib/Index/ClangIndexRecordWriter.h new file mode 100644 index 0000000000000..b68f9875fb34b --- /dev/null +++ b/clang/lib/Index/ClangIndexRecordWriter.h @@ -0,0 +1,55 @@ +//===--- ClangIndexRecordWriter.h - Index record serialization ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_CLANGINDEXRECORDWRITER_H +#define LLVM_CLANG_LIB_INDEX_CLANGINDEXRECORDWRITER_H + +#include "IndexRecordHasher.h" +#include "clang/Index/IndexRecordWriter.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Index/CodegenNameGenerator.h" +#include "llvm/ADT/SmallString.h" + +namespace clang { + class ASTContext; + class Decl; + +namespace index { + class FileIndexRecord; + +class ClangIndexRecordWriter { + IndexRecordWriter Impl; + + ASTContext &Ctx; + RecordingOptions RecordOpts; + + std::unique_ptr<CodegenNameGenerator> CGNameGen; + llvm::BumpPtrAllocator Allocator; + llvm::DenseMap<const Decl *, StringRef> USRByDecl; + IndexRecordHasher Hasher; + +public: + ClangIndexRecordWriter(ASTContext &Ctx, RecordingOptions Opts); + ~ClangIndexRecordWriter(); + + ASTContext &getASTContext() { return Ctx; } + CodegenNameGenerator *getCGNameGen() { return CGNameGen.get(); } + + bool writeRecord(StringRef Filename, const FileIndexRecord &Record, + std::string &Error, std::string *RecordFile = nullptr); + StringRef getUSR(const Decl *D); + +private: + StringRef getUSRNonCached(const Decl *D); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/FileIndexRecord.cpp b/clang/lib/Index/FileIndexRecord.cpp new file mode 100644 index 0000000000000..98d66b67c0780 --- /dev/null +++ b/clang/lib/Index/FileIndexRecord.cpp @@ -0,0 +1,59 @@ +//===--- FileIndexRecord.cpp - Index data per file ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FileIndexRecord.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace clang::index; + +void FileIndexRecord::addDeclOccurence(SymbolRoleSet Roles, + unsigned Offset, + const Decl *D, + ArrayRef<SymbolRelation> Relations) { + assert(D->isCanonicalDecl()); + + auto IsNextOccurence = [&]()->bool { + if (Decls.empty()) + return true; + auto &Last = Decls.back(); + return Last.Offset < Offset; + }; + + if (IsNextOccurence()) { + Decls.emplace_back(Roles, Offset, D, Relations); + return; + } + + DeclOccurrence NewInfo(Roles, Offset, D, Relations); + auto It = std::upper_bound(Decls.begin(), Decls.end(), NewInfo); + Decls.insert(It, std::move(NewInfo)); +} + +void FileIndexRecord::print(llvm::raw_ostream &OS) { + OS << "DECLS BEGIN ---\n"; + for (auto &DclInfo : Decls) { + auto D = DclInfo.Dcl; + SourceManager &SM = D->getASTContext().getSourceManager(); + SourceLocation Loc = SM.getFileLoc(D->getLocation()); + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + OS << llvm::sys::path::filename(PLoc.getFilename()) << ':' << PLoc.getLine() + << ':' << PLoc.getColumn(); + + if (auto ND = dyn_cast<NamedDecl>(D)) { + OS << ' ' << ND->getNameAsString(); + } + + OS << '\n'; + } + OS << "DECLS END ---\n"; +} diff --git a/clang/lib/Index/FileIndexRecord.h b/clang/lib/Index/FileIndexRecord.h new file mode 100644 index 0000000000000..a663173c7d12d --- /dev/null +++ b/clang/lib/Index/FileIndexRecord.h @@ -0,0 +1,69 @@ +//===--- FileIndexRecord.h - Index data per file --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H +#define LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H + +#include "clang/Index/IndexSymbol.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include <vector> + +namespace clang { + class IdentifierInfo; + +namespace index { + +class FileIndexRecord { +public: + struct DeclOccurrence { + SymbolRoleSet Roles; + unsigned Offset; + const Decl *Dcl; + SmallVector<SymbolRelation, 3> Relations; + + DeclOccurrence(SymbolRoleSet R, + unsigned Offset, + const Decl *D, + ArrayRef<SymbolRelation> Relations) + : Roles(R), + Offset(Offset), + Dcl(D), + Relations(Relations.begin(), Relations.end()) {} + + friend bool operator <(const DeclOccurrence &LHS, const DeclOccurrence &RHS) { + return LHS.Offset < RHS.Offset; + } + }; + +private: + FileID FID; + bool IsSystem; + std::vector<DeclOccurrence> Decls; + +public: + FileIndexRecord(FileID FID, bool isSystem) : FID(FID), IsSystem(isSystem) {} + + ArrayRef<DeclOccurrence> getDeclOccurrences() const { return Decls; } + + FileID getFileID() const { return FID; } + bool isSystem() const { return IsSystem; } + + void addDeclOccurence(SymbolRoleSet Roles, + unsigned Offset, + const Decl *D, + ArrayRef<SymbolRelation> Relations); + void print(llvm::raw_ostream &OS); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/IndexDataStore.cpp b/clang/lib/Index/IndexDataStore.cpp new file mode 100644 index 0000000000000..4140392dfc565 --- /dev/null +++ b/clang/lib/Index/IndexDataStore.cpp @@ -0,0 +1,259 @@ +//===--- IndexDataStore.cpp - Index data store info -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexDataStore.h" +#include "IndexDataStoreUtils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +static void appendSubDir(StringRef subdir, SmallVectorImpl<char> &StorePathBuf) { + SmallString<10> VersionPath; + raw_svector_ostream(VersionPath) << 'v' << STORE_FORMAT_VERSION; + + sys::path::append(StorePathBuf, VersionPath); + sys::path::append(StorePathBuf, subdir); +} + +void store::appendInteriorUnitPath(StringRef UnitName, + SmallVectorImpl<char> &PathBuf) { + sys::path::append(PathBuf, UnitName); +} + +void store::appendUnitSubDir(SmallVectorImpl<char> &StorePathBuf) { + return appendSubDir("units", StorePathBuf); +} + +void store::appendRecordSubDir(SmallVectorImpl<char> &StorePathBuf) { + return appendSubDir("records", StorePathBuf); +} + +void store::appendInteriorRecordPath(StringRef RecordName, + SmallVectorImpl<char> &PathBuf) { + // To avoid putting a huge number of files into the records directory, create + // subdirectories based on the last 2 characters from the hash. + StringRef hash2chars = RecordName.substr(RecordName.size()-2); + sys::path::append(PathBuf, hash2chars); + sys::path::append(PathBuf, RecordName); +} + +//===----------------------------------------------------------------------===// +// IndexDataStore +//===----------------------------------------------------------------------===// + +namespace { + +class UnitEventHandlerData { + mutable sys::Mutex Mtx; + IndexDataStore::UnitEventHandler Handler; + +public: + void setHandler(IndexDataStore::UnitEventHandler handler) { + sys::ScopedLock L(Mtx); + Handler = std::move(handler); + } + IndexDataStore::UnitEventHandler getHandler() const { + sys::ScopedLock L(Mtx); + return Handler; + } +}; + +class IndexDataStoreImpl { + std::string FilePath; + std::shared_ptr<UnitEventHandlerData> TheUnitEventHandlerData; + std::unique_ptr<AbstractDirectoryWatcher> DirWatcher; + +public: + explicit IndexDataStoreImpl(StringRef indexStorePath) + : FilePath(indexStorePath) { + TheUnitEventHandlerData = std::make_shared<UnitEventHandlerData>(); + } + + StringRef getFilePath() const { return FilePath; } + bool foreachUnitName(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver); + void setUnitEventHandler(IndexDataStore::UnitEventHandler Handler); + bool startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, + bool waitInitialSync, std::string &Error); + void stopEventListening(); + void discardUnit(StringRef UnitName); + void discardRecord(StringRef RecordName); + void purgeStaleData(); +}; + +} // anonymous namespace + +bool IndexDataStoreImpl::foreachUnitName(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + std::vector<std::string> filenames; + + std::error_code EC; + for (auto It = sys::fs::directory_iterator(UnitPath, EC), + End = sys::fs::directory_iterator(); + !EC && It != End; It.increment(EC)) { + StringRef unitName = sys::path::filename(It->path()); + if (!sorted) { + if (!receiver(unitName)) + return false; + } else { + filenames.push_back(unitName); + } + } + + if (sorted) { + llvm::array_pod_sort(filenames.begin(), filenames.end()); + for (auto &fname : filenames) + if (!receiver(fname)) + return false; + } + return true; +} + +void IndexDataStoreImpl::setUnitEventHandler(IndexDataStore::UnitEventHandler handler) { + TheUnitEventHandlerData->setHandler(std::move(handler)); +} + +bool IndexDataStoreImpl::startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, + bool waitInitialSync, std::string &Error) { + if (DirWatcher) { + Error = "event listener already active"; + return true; + } + + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + auto localUnitEventHandlerData = TheUnitEventHandlerData; + auto OnUnitsChange = [localUnitEventHandlerData](ArrayRef<AbstractDirectoryWatcher::Event> Events, bool isInitial) { + SmallVector<IndexDataStore::UnitEvent, 16> UnitEvents; + UnitEvents.reserve(Events.size()); + for (const AbstractDirectoryWatcher::Event &evt : Events) { + IndexDataStore::UnitEventKind K; + StringRef UnitName = sys::path::filename(evt.Filename); + switch (evt.Kind) { + case AbstractDirectoryWatcher::EventKind::Added: + K = IndexDataStore::UnitEventKind::Added; break; + case AbstractDirectoryWatcher::EventKind::Removed: + K = IndexDataStore::UnitEventKind::Removed; break; + case AbstractDirectoryWatcher::EventKind::Modified: + K = IndexDataStore::UnitEventKind::Modified; break; + case AbstractDirectoryWatcher::EventKind::DirectoryDeleted: + K = IndexDataStore::UnitEventKind::DirectoryDeleted; + UnitName = StringRef(); + break; + } + UnitEvents.push_back(IndexDataStore::UnitEvent{K, UnitName, evt.ModTime}); + } + + if (auto handler = localUnitEventHandlerData->getHandler()) { + IndexDataStore::UnitEventNotification EventNote{isInitial, UnitEvents}; + handler(EventNote); + } + }; + + DirWatcher = createFn(UnitPath.str(), OnUnitsChange, waitInitialSync, Error); + if (!DirWatcher) + return true; + + return false; +} + +void IndexDataStoreImpl::stopEventListening() { + DirWatcher.reset(); +} + +void IndexDataStoreImpl::discardUnit(StringRef UnitName) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + appendInteriorUnitPath(UnitName, UnitPath); + sys::fs::remove(UnitPath); +} + +void IndexDataStoreImpl::discardRecord(StringRef RecordName) { + SmallString<128> RecordPath; + RecordPath = FilePath; + appendRecordSubDir(RecordPath); + appendInteriorRecordPath(RecordName, RecordPath); + sys::fs::remove(RecordPath); +} + +void IndexDataStoreImpl::purgeStaleData() { + // FIXME: Implement. +} + + +std::unique_ptr<IndexDataStore> +IndexDataStore::create(StringRef IndexStorePath, std::string &Error) { + if (!sys::fs::exists(IndexStorePath)) { + raw_string_ostream OS(Error); + OS << "index store path does not exist: " << IndexStorePath; + return nullptr; + } + + return std::unique_ptr<IndexDataStore>( + new IndexDataStore(new IndexDataStoreImpl(IndexStorePath))); +} + +#define IMPL static_cast<IndexDataStoreImpl*>(Impl) + +IndexDataStore::~IndexDataStore() { + delete IMPL; +} + +StringRef IndexDataStore::getFilePath() const { + return IMPL->getFilePath(); +} + +bool IndexDataStore::foreachUnitName(bool sorted, + llvm::function_ref<bool(StringRef unitName)> receiver) { + return IMPL->foreachUnitName(sorted, std::move(receiver)); +} + +unsigned IndexDataStore::getFormatVersion() { + return STORE_FORMAT_VERSION; +} + +void IndexDataStore::setUnitEventHandler(UnitEventHandler Handler) { + return IMPL->setUnitEventHandler(std::move(Handler)); +} + +bool IndexDataStore::startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, + bool waitInitialSync, std::string &Error) { + return IMPL->startEventListening(std::move(createFn), waitInitialSync, Error); +} + +void IndexDataStore::stopEventListening() { + return IMPL->stopEventListening(); +} + +void IndexDataStore::discardUnit(StringRef UnitName) { + IMPL->discardUnit(UnitName); +} + +void IndexDataStore::discardRecord(StringRef RecordName) { + IMPL->discardRecord(RecordName); +} + +void IndexDataStore::purgeStaleData() { + IMPL->purgeStaleData(); +} diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp new file mode 100644 index 0000000000000..0d9e754cf95fa --- /dev/null +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -0,0 +1,398 @@ +//===--- IndexDataStoreUtils.cpp - Functions/constants for the data store -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "IndexDataStoreUtils.h" +#include "llvm/Bitcode/BitstreamWriter.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +void store::emitBlockID(unsigned ID, const char *Name, + BitstreamWriter &Stream, RecordDataImpl &Record) { + Record.clear(); + Record.push_back(ID); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, Record); + + // Emit the block name if present. + if (!Name || Name[0] == 0) + return; + Record.clear(); + while (*Name) + Record.push_back(*Name++); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +void store::emitRecordID(unsigned ID, const char *Name, + BitstreamWriter &Stream, + RecordDataImpl &Record) { + Record.clear(); + Record.push_back(ID); + while (*Name) + Record.push_back(*Name++); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); +} + +/// Map an indexstore_symbol_kind_t to a SymbolKind, handling unknown values. +SymbolKind index::getSymbolKind(indexstore_symbol_kind_t K) { + switch ((uint64_t)K) { + default: + case INDEXSTORE_SYMBOL_KIND_UNKNOWN: + return SymbolKind::Unknown; + case INDEXSTORE_SYMBOL_KIND_MODULE: + return SymbolKind::Module; + case INDEXSTORE_SYMBOL_KIND_NAMESPACE: + return SymbolKind::Namespace; + case INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS: + return SymbolKind::NamespaceAlias; + case INDEXSTORE_SYMBOL_KIND_MACRO: + return SymbolKind::Macro; + case INDEXSTORE_SYMBOL_KIND_ENUM: + return SymbolKind::Enum; + case INDEXSTORE_SYMBOL_KIND_STRUCT: + return SymbolKind::Struct; + case INDEXSTORE_SYMBOL_KIND_CLASS: + return SymbolKind::Class; + case INDEXSTORE_SYMBOL_KIND_PROTOCOL: + return SymbolKind::Protocol; + case INDEXSTORE_SYMBOL_KIND_EXTENSION: + return SymbolKind::Extension; + case INDEXSTORE_SYMBOL_KIND_UNION: + return SymbolKind::Union; + case INDEXSTORE_SYMBOL_KIND_TYPEALIAS: + return SymbolKind::TypeAlias; + case INDEXSTORE_SYMBOL_KIND_FUNCTION: + return SymbolKind::Function; + case INDEXSTORE_SYMBOL_KIND_VARIABLE: + return SymbolKind::Variable; + case INDEXSTORE_SYMBOL_KIND_FIELD: + return SymbolKind::Field; + case INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT: + return SymbolKind::EnumConstant; + case INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD: + return SymbolKind::InstanceMethod; + case INDEXSTORE_SYMBOL_KIND_CLASSMETHOD: + return SymbolKind::ClassMethod; + case INDEXSTORE_SYMBOL_KIND_STATICMETHOD: + return SymbolKind::StaticMethod; + case INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY: + return SymbolKind::InstanceProperty; + case INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY: + return SymbolKind::ClassProperty; + case INDEXSTORE_SYMBOL_KIND_STATICPROPERTY: + return SymbolKind::StaticProperty; + case INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR: + return SymbolKind::Constructor; + case INDEXSTORE_SYMBOL_KIND_DESTRUCTOR: + return SymbolKind::Destructor; + case INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION: + return SymbolKind::ConversionFunction; + case INDEXSTORE_SYMBOL_KIND_PARAMETER: + return SymbolKind::Parameter; + case INDEXSTORE_SYMBOL_KIND_COMMENTTAG: + return SymbolKind::CommentTag; + } +} + +SymbolSubKind index::getSymbolSubKind(indexstore_symbol_subkind_t K) { + switch ((uint64_t)K) { + default: + case INDEXSTORE_SYMBOL_SUBKIND_NONE: + return SymbolSubKind::None; + case INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR: + return SymbolSubKind::CXXCopyConstructor; + case INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR: + return SymbolSubKind::CXXMoveConstructor; + case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER: + return SymbolSubKind::AccessorGetter; + case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER: + return SymbolSubKind::AccessorSetter; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET: + return SymbolSubKind::SwiftAccessorWillSet; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET: + return SymbolSubKind::SwiftAccessorDidSet; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR: + return SymbolSubKind::SwiftAccessorAddressor; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR: + return SymbolSubKind::SwiftAccessorMutableAddressor; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT: + return SymbolSubKind::SwiftExtensionOfStruct; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS: + return SymbolSubKind::SwiftExtensionOfClass; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM: + return SymbolSubKind::SwiftExtensionOfEnum; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL: + return SymbolSubKind::SwiftExtensionOfProtocol; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR: + return SymbolSubKind::SwiftPrefixOperator; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR: + return SymbolSubKind::SwiftPostfixOperator; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR: + return SymbolSubKind::SwiftInfixOperator; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT: + return SymbolSubKind::SwiftSubscript; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE: + return SymbolSubKind::SwiftAssociatedType; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM: + return SymbolSubKind::SwiftGenericTypeParam; + } +} + +/// Map an indexstore_symbol_language_t to a SymbolLanguage, handling unknown +/// values. +SymbolLanguage index::getSymbolLanguage(indexstore_symbol_language_t L) { + switch ((uint64_t)L) { + default: // FIXME: add an unknown language? + case INDEXSTORE_SYMBOL_LANG_C: + return SymbolLanguage::C; + case INDEXSTORE_SYMBOL_LANG_OBJC: + return SymbolLanguage::ObjC; + case INDEXSTORE_SYMBOL_LANG_CXX: + return SymbolLanguage::CXX; + case INDEXSTORE_SYMBOL_LANG_SWIFT: + return SymbolLanguage::Swift; + } +} + +/// Map an indexstore representation to a SymbolPropertySet, handling +/// unknown values. +SymbolPropertySet index::getSymbolProperties(uint64_t Props) { + // FIXME: currently these enums must be kept in sync. + return (uint64_t)Props; +} + +/// Map an indexstore representation to a SymbolRoleSet, handling unknown +/// values. +SymbolRoleSet index::getSymbolRoles(uint64_t Roles) { + // FIXME: currently these enums must be kept in sync. + return (uint64_t)Roles; +} + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_kind_t index::getIndexStoreKind(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: + return INDEXSTORE_SYMBOL_KIND_UNKNOWN; + case SymbolKind::Module: + return INDEXSTORE_SYMBOL_KIND_MODULE; + case SymbolKind::Namespace: + return INDEXSTORE_SYMBOL_KIND_NAMESPACE; + case SymbolKind::NamespaceAlias: + return INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS; + case SymbolKind::Macro: + return INDEXSTORE_SYMBOL_KIND_MACRO; + case SymbolKind::Enum: + return INDEXSTORE_SYMBOL_KIND_ENUM; + case SymbolKind::Struct: + return INDEXSTORE_SYMBOL_KIND_STRUCT; + case SymbolKind::Class: + return INDEXSTORE_SYMBOL_KIND_CLASS; + case SymbolKind::Protocol: + return INDEXSTORE_SYMBOL_KIND_PROTOCOL; + case SymbolKind::Extension: + return INDEXSTORE_SYMBOL_KIND_EXTENSION; + case SymbolKind::Union: + return INDEXSTORE_SYMBOL_KIND_UNION; + case SymbolKind::TypeAlias: + return INDEXSTORE_SYMBOL_KIND_TYPEALIAS; + case SymbolKind::Function: + return INDEXSTORE_SYMBOL_KIND_FUNCTION; + case SymbolKind::Variable: + return INDEXSTORE_SYMBOL_KIND_VARIABLE; + case SymbolKind::Field: + return INDEXSTORE_SYMBOL_KIND_FIELD; + case SymbolKind::EnumConstant: + return INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT; + case SymbolKind::InstanceMethod: + return INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD; + case SymbolKind::ClassMethod: + return INDEXSTORE_SYMBOL_KIND_CLASSMETHOD; + case SymbolKind::StaticMethod: + return INDEXSTORE_SYMBOL_KIND_STATICMETHOD; + case SymbolKind::InstanceProperty: + return INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY; + case SymbolKind::ClassProperty: + return INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY; + case SymbolKind::StaticProperty: + return INDEXSTORE_SYMBOL_KIND_STATICPROPERTY; + case SymbolKind::Constructor: + return INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR; + case SymbolKind::Destructor: + return INDEXSTORE_SYMBOL_KIND_DESTRUCTOR; + case SymbolKind::ConversionFunction: + return INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION; + case SymbolKind::Parameter: + return INDEXSTORE_SYMBOL_KIND_PARAMETER; + case SymbolKind::CommentTag: + return INDEXSTORE_SYMBOL_KIND_COMMENTTAG; + } + llvm_unreachable("unexpected symbol kind"); +} + +indexstore_symbol_subkind_t index::getIndexStoreSubKind(SymbolSubKind K) { + switch (K) { + case SymbolSubKind::None: + return INDEXSTORE_SYMBOL_SUBKIND_NONE; + case SymbolSubKind::CXXCopyConstructor: + return INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR; + case SymbolSubKind::CXXMoveConstructor: + return INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR; + case SymbolSubKind::AccessorGetter: + return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER; + case SymbolSubKind::AccessorSetter: + return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER; + case SymbolSubKind::SwiftAccessorWillSet: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET; + case SymbolSubKind::SwiftAccessorDidSet: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET; + case SymbolSubKind::SwiftAccessorAddressor: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR; + case SymbolSubKind::SwiftAccessorMutableAddressor: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR; + case SymbolSubKind::SwiftExtensionOfStruct: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT; + case SymbolSubKind::SwiftExtensionOfClass: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS; + case SymbolSubKind::SwiftExtensionOfEnum: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM; + case SymbolSubKind::SwiftExtensionOfProtocol: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL; + case SymbolSubKind::SwiftPrefixOperator: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR; + case SymbolSubKind::SwiftPostfixOperator: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR; + case SymbolSubKind::SwiftInfixOperator: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR; + case SymbolSubKind::SwiftSubscript: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT; + case SymbolSubKind::SwiftAssociatedType: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE; + case SymbolSubKind::SwiftGenericTypeParam: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM; + } + llvm_unreachable("unexpected symbol subkind"); +} + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_language_t index::getIndexStoreLang(SymbolLanguage L) { + switch (L) { + case SymbolLanguage::C: + return INDEXSTORE_SYMBOL_LANG_C; + case SymbolLanguage::ObjC: + return INDEXSTORE_SYMBOL_LANG_OBJC; + case SymbolLanguage::CXX: + return INDEXSTORE_SYMBOL_LANG_CXX; + case SymbolLanguage::Swift: + return INDEXSTORE_SYMBOL_LANG_SWIFT; + } + llvm_unreachable("unexpected symbol language"); +} + +/// Map a SymbolPropertySet to its indexstore representation. +uint64_t index::getIndexStoreProperties(SymbolPropertySet Props) { + uint64_t storeProp = 0; + applyForEachSymbolProperty(Props, [&](SymbolProperty prop) { + switch (prop) { + case SymbolProperty::Generic: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GENERIC; + break; + case SymbolProperty::TemplatePartialSpecialization: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION; + break; + case SymbolProperty::TemplateSpecialization: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION; + break; + case SymbolProperty::UnitTest: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_UNITTEST; + break; + case SymbolProperty::IBAnnotated: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED; + break; + case SymbolProperty::IBOutletCollection: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION; + break; + case SymbolProperty::GKInspectable: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE; + break; + case SymbolProperty::Local: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_LOCAL; + break; + } + }); + return storeProp; +} + +/// Map a SymbolRoleSet to its indexstore representation. +uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) { + uint64_t storeRoles = 0; + applyForEachSymbolRole(Roles, [&](SymbolRole role) { + switch (role) { + case SymbolRole::Declaration: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DECLARATION; + break; + case SymbolRole::Definition: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DEFINITION; + break; + case SymbolRole::Reference: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REFERENCE; + break; + case SymbolRole::Read: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_READ; + break; + case SymbolRole::Write: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_WRITE; + break; + case SymbolRole::Call: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_CALL; + break; + case SymbolRole::Dynamic: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DYNAMIC; + break; + case SymbolRole::AddressOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_ADDRESSOF; + break; + case SymbolRole::Implicit: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_IMPLICIT; + break; + case SymbolRole::RelationChildOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF; + break; + case SymbolRole::RelationBaseOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_BASEOF; + break; + case SymbolRole::RelationOverrideOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF; + break; + case SymbolRole::RelationReceivedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY; + break; + case SymbolRole::RelationCalledBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY; + break; + case SymbolRole::RelationExtendedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY; + break; + case SymbolRole::RelationAccessorOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF; + break; + case SymbolRole::RelationContainedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY; + break; + case SymbolRole::RelationIBTypeOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF; + break; + case SymbolRole::RelationSpecializationOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF; + break; + } + }); + return storeRoles; +} diff --git a/clang/lib/Index/IndexDataStoreUtils.h b/clang/lib/Index/IndexDataStoreUtils.h new file mode 100644 index 0000000000000..ad310e191605e --- /dev/null +++ b/clang/lib/Index/IndexDataStoreUtils.h @@ -0,0 +1,116 @@ +//===--- IndexDataStoreUtils.h - Functions/constants for the data store ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H +#define LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H + +#include "llvm/Bitcode/BitCodes.h" +#include "clang/Basic/LLVM.h" + +namespace llvm { + class BitstreamWriter; +} + +namespace clang { +namespace index { +namespace store { + +static const unsigned STORE_FORMAT_VERSION = 5; + +void appendUnitSubDir(SmallVectorImpl<char> &StorePathBuf); +void appendInteriorUnitPath(StringRef UnitName, + SmallVectorImpl<char> &PathBuf); +void appendRecordSubDir(SmallVectorImpl<char> &StorePathBuf); +void appendInteriorRecordPath(StringRef RecordName, + SmallVectorImpl<char> &PathBuf); + +enum RecordBitRecord { + REC_VERSION = 0, + REC_DECLINFO = 1, + REC_DECLOFFSETS = 2, + REC_DECLOCCURRENCE = 3, +}; + +enum RecordBitBlock { + REC_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + REC_DECLS_BLOCK_ID, + REC_DECLOFFSETS_BLOCK_ID, + REC_DECLOCCURRENCES_BLOCK_ID, +}; + +enum UnitBitRecord { + UNIT_VERSION = 0, + UNIT_INFO = 1, + UNIT_DEPENDENCY = 2, + UNIT_INCLUDE = 3, + UNIT_PATH = 4, + UNIT_PATH_BUFFER = 5, + UNIT_MODULE = 6, + UNIT_MODULE_BUFFER = 7, +}; + +enum UnitBitBlock { + UNIT_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + UNIT_INFO_BLOCK_ID, + UNIT_DEPENDENCIES_BLOCK_ID, + UNIT_INCLUDES_BLOCK_ID, + UNIT_PATHS_BLOCK_ID, + UNIT_MODULES_BLOCK_ID, +}; + +enum UnitDependencyKind { + UNIT_DEPEND_KIND_FILE = 0, + UNIT_DEPEND_KIND_RECORD = 1, + UNIT_DEPEND_KIND_UNIT = 2, +}; +static const unsigned UnitDependencyKindBitNum = 2; + +enum UnitFilePathPrefixKind { + UNIT_PATH_PREFIX_NONE = 0, + UNIT_PATH_PREFIX_WORKDIR = 1, + UNIT_PATH_PREFIX_SYSROOT = 2, +}; +static const unsigned UnitFilePathPrefixKindBitNum = 2; + +typedef SmallVector<uint64_t, 64> RecordData; +typedef SmallVectorImpl<uint64_t> RecordDataImpl; + +struct BitPathComponent { + size_t Offset = 0; + size_t Size = 0; + BitPathComponent(size_t Offset, size_t Size) : Offset(Offset), Size(Size) {} + BitPathComponent() = default; +}; + +struct DirBitPath { + UnitFilePathPrefixKind PrefixKind = UNIT_PATH_PREFIX_NONE; + BitPathComponent Dir; + DirBitPath(UnitFilePathPrefixKind Kind, + BitPathComponent Dir) : PrefixKind(Kind), Dir(Dir) {} + DirBitPath() = default; +}; + +struct FileBitPath : DirBitPath { + BitPathComponent Filename; + FileBitPath(UnitFilePathPrefixKind Kind, BitPathComponent Dir, + BitPathComponent Filename) : DirBitPath(Kind, Dir), Filename(Filename) {} + FileBitPath() = default; +}; + +void emitBlockID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, RecordDataImpl &Record); + +void emitRecordID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, RecordDataImpl &Record); + +} // end namespace store +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp new file mode 100644 index 0000000000000..dd3c11ddfc797 --- /dev/null +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -0,0 +1,468 @@ +//===--- IndexRecordHasher.cpp - Index record hashing ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexRecordHasher.h" +#include "FileIndexRecord.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclVisitor.h" +#include "llvm/Support/Path.h" + +#define INITIAL_HASH 5381 +#define COMBINE_HASH(...) (Hash = hash_combine(Hash, __VA_ARGS__)) + +using namespace clang; +using namespace clang::index; +using namespace llvm; + +static hash_code computeHash(const TemplateArgument &Arg, + IndexRecordHasher &Hasher); + +namespace { +class DeclHashVisitor : public ConstDeclVisitor<DeclHashVisitor, hash_code> { + IndexRecordHasher &Hasher; + +public: + DeclHashVisitor(IndexRecordHasher &Hasher) : Hasher(Hasher) {} + + hash_code VisitDecl(const Decl *D) { + return VisitDeclContext(D->getDeclContext()); + } + + hash_code VisitNamedDecl(const NamedDecl *D) { + hash_code Hash = VisitDecl(D); + if (auto *attr = D->getExternalSourceSymbolAttr()) { + COMBINE_HASH(hash_value(attr->getDefinedIn())); + } + return COMBINE_HASH(Hasher.hash(D->getDeclName())); + } + + hash_code VisitTagDecl(const TagDecl *D) { + if (D->getDeclName().isEmpty()) { + if (const TypedefNameDecl *TD = D->getTypedefNameForAnonDecl()) + return Visit(TD); + + hash_code Hash = VisitDeclContext(D->getDeclContext()); + if (D->isEmbeddedInDeclarator() && !D->isFreeStanding()) { + COMBINE_HASH(hashLoc(D->getLocation(), /*IncludeOffset=*/true)); + } else + COMBINE_HASH('a'); + return Hash; + } + + hash_code Hash = VisitTypeDecl(D); + return COMBINE_HASH('T'); + } + + hash_code VisitClassTemplateSpecializationDecl(const ClassTemplateSpecializationDecl *D) { + hash_code Hash = VisitCXXRecordDecl(D); + const TemplateArgumentList &Args = D->getTemplateArgs(); + COMBINE_HASH('>'); + for (unsigned I = 0, N = Args.size(); I != N; ++I) { + COMBINE_HASH(computeHash(Args.get(I), Hasher)); + } + return Hash; + } + + hash_code VisitObjCContainerDecl(const ObjCContainerDecl *D) { + hash_code Hash = VisitNamedDecl(D); + return COMBINE_HASH('I'); + } + + hash_code VisitObjCImplDecl(const ObjCImplDecl *D) { + if (auto *ID = D->getClassInterface()) + return VisitObjCInterfaceDecl(ID); + else + return 0; + } + + hash_code VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { + // FIXME: Differentiate between category and the interface ? + if (auto *ID = D->getClassInterface()) + return VisitObjCInterfaceDecl(ID); + else + return 0; + } + + hash_code VisitFunctionDecl(const FunctionDecl *D) { + hash_code Hash = VisitNamedDecl(D); + ASTContext &Ctx = Hasher.getASTContext(); + if ((!Ctx.getLangOpts().CPlusPlus && !D->hasAttr<OverloadableAttr>()) + || D->isExternC()) + return Hash; + + for (auto param : D->parameters()) { + COMBINE_HASH(Hasher.hash(param->getType())); + } + return Hash; + } + + hash_code VisitDeclContext(const DeclContext *DC) { + // FIXME: Add location if this is anonymous namespace ? + DC = DC->getRedeclContext(); + const Decl *D = cast<Decl>(DC)->getCanonicalDecl(); + if (auto *ND = dyn_cast<NamedDecl>(D)) + return Hasher.hash(ND); + else + return 0; + } + + hash_code hashLoc(SourceLocation Loc, bool IncludeOffset) { + if (Loc.isInvalid()) { + return 0; + } + hash_code Hash = INITIAL_HASH; + const SourceManager &SM = Hasher.getASTContext().getSourceManager(); + Loc = SM.getFileLoc(Loc); + const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(Loc); + const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); + if (FE) { + COMBINE_HASH(llvm::sys::path::filename(FE->getName())); + } else { + // This case really isn't interesting. + return 0; + } + if (IncludeOffset) { + // Use the offest into the FileID to represent the location. Using + // a line/column can cause us to look back at the original source file, + // which is expensive. + COMBINE_HASH(Decomposed.second); + } + return Hash; + } +}; +} + +hash_code IndexRecordHasher::hashRecord(const FileIndexRecord &Record) { + hash_code Hash = INITIAL_HASH; + for (auto &Info : Record.getDeclOccurrences()) { + COMBINE_HASH(Info.Roles, Info.Offset, hash(Info.Dcl)); + for (auto &Rel : Info.Relations) { + COMBINE_HASH(hash(Rel.RelatedSymbol)); + } + } + return Hash; +} + +hash_code IndexRecordHasher::hash(const Decl *D) { + assert(D->isCanonicalDecl()); + + if (isa<TagDecl>(D) || isa<ObjCContainerDecl>(D)) { + return tryCache(D, D); + } else if (auto *NS = dyn_cast<NamespaceDecl>(D)) { + if (NS->isAnonymousNamespace()) + return hash_value(StringRef("@aN")); + return tryCache(D, D); + } else { + // There's a balance between caching results and not growing the cache too + // much. Measurements showed that avoiding caching all decls is beneficial + // particularly when including all of Cocoa. + return hashImpl(D); + } +} + +hash_code IndexRecordHasher::hash(QualType NonCanTy) { + CanQualType CanTy = Ctx.getCanonicalType(NonCanTy); + return hash(CanTy); +} + +hash_code IndexRecordHasher::hash(CanQualType CT) { + // Do some hashing without going to the cache, for example we can avoid + // storing the hash for both the type and its const-qualified version. + hash_code Hash = INITIAL_HASH; + + auto asCanon = [](QualType Ty) -> CanQualType { + return CanQualType::CreateUnsafe(Ty); + }; + + while (true) { + Qualifiers Q = CT.getQualifiers(); + CT = CT.getUnqualifiedType(); + const Type *T = CT.getTypePtr(); + unsigned qVal = 0; + if (Q.hasConst()) + qVal |= 0x1; + if (Q.hasVolatile()) + qVal |= 0x2; + if (Q.hasRestrict()) + qVal |= 0x4; + if(qVal) + COMBINE_HASH(qVal); + + // Hash in ObjC GC qualifiers? + + if (const BuiltinType *BT = dyn_cast<BuiltinType>(T)) { + return COMBINE_HASH(BT->getKind()); + } + if (const PointerType *PT = dyn_cast<PointerType>(T)) { + COMBINE_HASH('*'); + CT = asCanon(PT->getPointeeType()); + continue; + } + if (const ReferenceType *RT = dyn_cast<ReferenceType>(T)) { + COMBINE_HASH('&'); + CT = asCanon(RT->getPointeeType()); + continue; + } + if (const BlockPointerType *BT = dyn_cast<BlockPointerType>(T)) { + COMBINE_HASH('B'); + CT = asCanon(BT->getPointeeType()); + continue; + } + if (const ObjCObjectPointerType *OPT = dyn_cast<ObjCObjectPointerType>(T)) { + COMBINE_HASH('*'); + CT = asCanon(OPT->getPointeeType()); + continue; + } + if (const TagType *TT = dyn_cast<TagType>(T)) { + return COMBINE_HASH('$', hash(TT->getDecl()->getCanonicalDecl())); + } + if (const ObjCInterfaceType *OIT = dyn_cast<ObjCInterfaceType>(T)) { + return COMBINE_HASH('$', hash(OIT->getDecl()->getCanonicalDecl())); + } + if (const ObjCObjectType *OIT = dyn_cast<ObjCObjectType>(T)) { + for (auto *Prot : OIT->getProtocols()) + COMBINE_HASH(hash(Prot)); + CT = asCanon(OIT->getBaseType()); + continue; + } + if (const TemplateTypeParmType *TTP = dyn_cast<TemplateTypeParmType>(T)) { + return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex()); + } + if (const InjectedClassNameType *InjT = dyn_cast<InjectedClassNameType>(T)) { + CT = asCanon(InjT->getInjectedSpecializationType().getCanonicalType()); + continue; + } + + break; + } + + return COMBINE_HASH(tryCache(CT.getAsOpaquePtr(), CT)); +} + +hash_code IndexRecordHasher::hash(DeclarationName Name) { + assert(!Name.isEmpty()); + // Measurements for using cache or not here, showed significant slowdown when + // using the cache for all DeclarationNames when parsing Cocoa, and minor + // improvement or no difference for a couple of C++ single translation unit + // files. So we avoid caching DeclarationNames. + return hashImpl(Name); +} + +hash_code IndexRecordHasher::hash(const NestedNameSpecifier *NNS) { + assert(NNS); + // Measurements for the C++ single translation unit files did not show much + // difference here; choosing to cache them currently. + return tryCache(NNS, NNS); +} + +template <typename T> +hash_code IndexRecordHasher::tryCache(const void *Ptr, T Obj) { + auto It = HashByPtr.find(Ptr); + if (It != HashByPtr.end()) + return It->second; + + hash_code Hash = hashImpl(Obj); + // hashImpl() may call into tryCache recursively and mutate + // HashByPtr, so we use find() earlier and insert the hash with another + // lookup here instead of calling insert() earlier and utilizing the iterator + // that insert() returns. + HashByPtr[Ptr] = Hash; + return Hash; +} + +hash_code IndexRecordHasher::hashImpl(const Decl *D) { + return DeclHashVisitor(*this).Visit(D); +} + +static hash_code computeHash(const IdentifierInfo *II) { + return hash_value(II->getName()); +} + +static hash_code computeHash(Selector Sel) { + unsigned N = Sel.getNumArgs(); + if (N == 0) + ++N; + hash_code Hash = INITIAL_HASH; + for (unsigned I = 0; I != N; ++I) + if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I)) + COMBINE_HASH(computeHash(II)); + return Hash; +} + +static hash_code computeHash(TemplateName Name, IndexRecordHasher &Hasher) { + hash_code Hash = INITIAL_HASH; + if (TemplateDecl *Template = Name.getAsTemplateDecl()) { + if (TemplateTemplateParmDecl *TTP + = dyn_cast<TemplateTemplateParmDecl>(Template)) { + return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex()); + } + + return COMBINE_HASH(Hasher.hash(Template)); + } + + // FIXME: Hash dependent template names. + return Hash; +} + +static hash_code computeHash(const TemplateArgument &Arg, + IndexRecordHasher &Hasher) { + hash_code Hash = INITIAL_HASH; + + switch (Arg.getKind()) { + case TemplateArgument::Null: + break; + + case TemplateArgument::Declaration: + COMBINE_HASH(Hasher.hash(Arg.getAsDecl())); + break; + + case TemplateArgument::NullPtr: + break; + + case TemplateArgument::TemplateExpansion: + COMBINE_HASH('P'); // pack expansion of... + // Fall through + case TemplateArgument::Template: + COMBINE_HASH(computeHash(Arg.getAsTemplateOrTemplatePattern(), Hasher)); + break; + + case TemplateArgument::Expression: + // FIXME: Hash expressions. + break; + + case TemplateArgument::Pack: + COMBINE_HASH('p'); + for (const auto &P : Arg.pack_elements()) + COMBINE_HASH(computeHash(P, Hasher)); + break; + + case TemplateArgument::Type: + COMBINE_HASH(Hasher.hash(Arg.getAsType())); + break; + + case TemplateArgument::Integral: + COMBINE_HASH('V', Hasher.hash(Arg.getIntegralType()), Arg.getAsIntegral()); + break; + } + + return Hash; +} + +hash_code IndexRecordHasher::hashImpl(CanQualType CQT) { + hash_code Hash = INITIAL_HASH; + + auto asCanon = [](QualType Ty) -> CanQualType { + return CanQualType::CreateUnsafe(Ty); + }; + + const Type *T = CQT.getTypePtr(); + + if (const PackExpansionType *Expansion = dyn_cast<PackExpansionType>(T)) { + return COMBINE_HASH('P', hash(asCanon(Expansion->getPattern()))); + } + if (const RValueReferenceType *RT = dyn_cast<RValueReferenceType>(T)) { + return COMBINE_HASH('%', hash(asCanon(RT->getPointeeType()))); + } + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(T)) { + COMBINE_HASH('F', hash(asCanon(FT->getReturnType()))); + for (const auto &I : FT->param_types()) + COMBINE_HASH(hash(asCanon(I))); + return COMBINE_HASH(FT->isVariadic()); + } + if (const ComplexType *CT = dyn_cast<ComplexType>(T)) { + return COMBINE_HASH('<', hash(asCanon(CT->getElementType()))); + } + if (const TemplateSpecializationType *Spec + = dyn_cast<TemplateSpecializationType>(T)) { + COMBINE_HASH('>', computeHash(Spec->getTemplateName(), *this)); + for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I) + COMBINE_HASH(computeHash(Spec->getArg(I), *this)); + return Hash; + } + if (const DependentNameType *DNT = dyn_cast<DependentNameType>(T)) { + COMBINE_HASH('^'); + if (const NestedNameSpecifier *NNS = DNT->getQualifier()) + COMBINE_HASH(hash(NNS)); + return COMBINE_HASH(computeHash(DNT->getIdentifier())); + } + + // Unhandled type. + return Hash; +} + +hash_code IndexRecordHasher::hashImpl(DeclarationName Name) { + hash_code Hash = INITIAL_HASH; + COMBINE_HASH(Name.getNameKind()); + + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + COMBINE_HASH(computeHash(Name.getAsIdentifierInfo())); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + COMBINE_HASH(computeHash(Name.getObjCSelector())); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + break; + case DeclarationName::CXXOperatorName: + COMBINE_HASH(Name.getCXXOverloadedOperator()); + break; + case DeclarationName::CXXLiteralOperatorName: + COMBINE_HASH(computeHash(Name.getCXXLiteralIdentifier())); + case DeclarationName::CXXUsingDirective: + break; + case DeclarationName::CXXDeductionGuideName: + COMBINE_HASH(computeHash(Name.getCXXDeductionGuideTemplate() + ->getDeclName().getAsIdentifierInfo())); + break; + } + + return Hash; +} + +hash_code IndexRecordHasher::hashImpl(const NestedNameSpecifier *NNS) { + hash_code Hash = INITIAL_HASH; + if (auto *Pre = NNS->getPrefix()) + COMBINE_HASH(hash(Pre)); + + COMBINE_HASH(NNS->getKind()); + + switch (NNS->getKind()) { + case NestedNameSpecifier::Identifier: + COMBINE_HASH(computeHash(NNS->getAsIdentifier())); + break; + + case NestedNameSpecifier::Namespace: + COMBINE_HASH(hash(NNS->getAsNamespace()->getCanonicalDecl())); + break; + + case NestedNameSpecifier::NamespaceAlias: + COMBINE_HASH(hash(NNS->getAsNamespaceAlias()->getCanonicalDecl())); + break; + + case NestedNameSpecifier::Global: + break; + + case NestedNameSpecifier::Super: + break; + + case NestedNameSpecifier::TypeSpecWithTemplate: + // Fall through to hash the type. + + case NestedNameSpecifier::TypeSpec: + COMBINE_HASH(hash(QualType(NNS->getAsType(), 0))); + break; + } + + return Hash; +} diff --git a/clang/lib/Index/IndexRecordHasher.h b/clang/lib/Index/IndexRecordHasher.h new file mode 100644 index 0000000000000..af3acccff5296 --- /dev/null +++ b/clang/lib/Index/IndexRecordHasher.h @@ -0,0 +1,58 @@ +//===--- IndexRecordHasher.h - Index record hashing -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H +#define LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Hashing.h" + +namespace clang { + class ASTContext; + class Decl; + class DeclarationName; + class NestedNameSpecifier; + class QualType; + class Type; + template <typename> class CanQual; + typedef CanQual<Type> CanQualType; + +namespace index { + class FileIndexRecord; + +class IndexRecordHasher { + ASTContext &Ctx; + llvm::DenseMap<const void *, llvm::hash_code> HashByPtr; + +public: + explicit IndexRecordHasher(ASTContext &Ctx) : Ctx(Ctx) {} + ASTContext &getASTContext() { return Ctx; } + + llvm::hash_code hashRecord(const FileIndexRecord &Record); + llvm::hash_code hash(const Decl *D); + llvm::hash_code hash(QualType Ty); + llvm::hash_code hash(CanQualType Ty); + llvm::hash_code hash(DeclarationName Name); + llvm::hash_code hash(const NestedNameSpecifier *NNS); + +private: + template <typename T> + llvm::hash_code tryCache(const void *Ptr, T Obj); + + llvm::hash_code hashImpl(const Decl *D); + llvm::hash_code hashImpl(CanQualType Ty); + llvm::hash_code hashImpl(DeclarationName Name); + llvm::hash_code hashImpl(const NestedNameSpecifier *NNS); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/IndexRecordReader.cpp b/clang/lib/Index/IndexRecordReader.cpp new file mode 100644 index 0000000000000..bd2ec1f571dba --- /dev/null +++ b/clang/lib/Index/IndexRecordReader.cpp @@ -0,0 +1,407 @@ +//===--- IndexRecordReader.cpp - Index record deserialization -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexRecordReader.h" +#include "IndexDataStoreUtils.h" +#include "BitstreamVisitor.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +struct IndexRecordReader::Implementation { + BumpPtrAllocator Allocator; + std::unique_ptr<MemoryBuffer> Buffer; + llvm::BitstreamCursor DeclCursor; + llvm::BitstreamCursor OccurCursor; + ArrayRef<uint32_t> DeclOffsets; + const IndexRecordDecl **Decls; + + void setDeclOffsets(ArrayRef<uint32_t> Offs) { + DeclOffsets = Offs; + Decls = Allocator.Allocate<const IndexRecordDecl*>(Offs.size()); + memset(Decls, 0, sizeof(IndexRecordDecl*)*Offs.size()); + } + + unsigned getNumDecls() const { return DeclOffsets.size(); } + + const IndexRecordDecl *getDeclByID(unsigned DeclID) { + if (DeclID == 0) + return nullptr; + return getDecl(DeclID-1); + } + + const IndexRecordDecl *getDecl(unsigned Index) { + assert(Index < getNumDecls()); + if (const IndexRecordDecl *D = Decls[Index]) + return D; + + IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>(); + readDecl(Index, *D); + Decls[Index] = D; + return D; + } + + /// Goes through the decls and populates a vector of record decls, based on + /// what the given function returns. + /// + /// The advantage of this function is to allocate memory only for the record + /// decls that the caller is interested in. + bool searchDecls(llvm::function_ref<DeclSearchCheck> Checker, + llvm::function_ref<void(const IndexRecordDecl *)> Receiver) { + for (unsigned I = 0, E = getNumDecls(); I != E; ++I) { + if (const IndexRecordDecl *D = Decls[I]) { + DeclSearchReturn Ret = Checker(*D); + if (Ret.AcceptDecl) + Receiver(D); + if (!Ret.ContinueSearch) + return false; + continue; + } + + IndexRecordDecl LocalD; + readDecl(I, LocalD); + DeclSearchReturn Ret = Checker(LocalD); + if (Ret.AcceptDecl) { + IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>(); + *D = LocalD; + Decls[I] = D; + Receiver(D); + } + if (!Ret.ContinueSearch) + return false; + } + return true; + } + + void readDecl(unsigned Index, IndexRecordDecl &RecD) { + RecordData Record; + StringRef Blob; + DeclCursor.JumpToBit(DeclOffsets[Index]); + unsigned Code = DeclCursor.ReadCode(); + unsigned RecID = DeclCursor.readRecord(Code, Record, &Blob); + assert(RecID == REC_DECLINFO); + (void)RecID; + + unsigned I = 0; + RecD.DeclID = Index+1; + RecD.SymInfo.Kind = getSymbolKind((indexstore_symbol_kind_t)read(Record, I)); + RecD.SymInfo.SubKind = getSymbolSubKind((indexstore_symbol_subkind_t)read(Record, I)); + RecD.SymInfo.Lang = + getSymbolLanguage((indexstore_symbol_language_t)read(Record, I)); + RecD.SymInfo.Properties = getSymbolProperties(read(Record, I)); + RecD.Roles = getSymbolRoles(read(Record, I)); + RecD.RelatedRoles = getSymbolRoles(read(Record, I)); + size_t NameLen = read(Record, I); + size_t USRLen = read(Record, I); + RecD.Name = Blob.substr(0, NameLen); + RecD.USR = Blob.substr(NameLen, USRLen); + RecD.CodeGenName = Blob.substr(NameLen+USRLen); + } + + /// Reads occurrence data. + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. If empty then indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + /// \returns true if the occurrence info was filled out, false if occurrence + /// was ignored. + bool readOccurrence(RecordDataImpl &Record, StringRef Blob, + ArrayRef<const IndexRecordDecl *> DeclsFilter, + ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter, + IndexRecordOccurrence &RecOccur) { + + auto isDeclIDContained = [](unsigned DeclID, + ArrayRef<const IndexRecordDecl *> Ds) -> bool { + if (Ds.empty()) + return true; // empty means accept all. + auto pred = [DeclID](const IndexRecordDecl *D) { return D->DeclID == DeclID; }; + return std::find_if(Ds.begin(), Ds.end(), pred) != Ds.end(); + }; + + unsigned I = 0; + unsigned DeclID = read(Record, I); + if (!isDeclIDContained(DeclID, DeclsFilter)) + return false; + + if (!RelatedDeclsFilter.empty()) { + unsigned RelI = I+3; + unsigned NumRelated = read(Record, RelI); + bool FoundRelated = false; + while (NumRelated--) { + ++RelI; // roles; + unsigned RelDID = read(Record, RelI); + if (isDeclIDContained(RelDID, RelatedDeclsFilter)) { + FoundRelated = true; + break; + } + } + if (!FoundRelated) + return false; + } + + RecOccur.Dcl = getDeclByID(DeclID); + RecOccur.Roles = getSymbolRoles(read(Record, I)); + RecOccur.Line = read(Record, I); + RecOccur.Column = read(Record, I); + + unsigned NumRelated = read(Record, I); + while (NumRelated--) { + SymbolRoleSet RelRoles = getSymbolRoles(read(Record, I)); + const IndexRecordDecl *RelD = getDeclByID(read(Record, I)); + RecOccur.Relations.emplace_back(RelRoles, RelD); + } + + return true; + } + + bool foreachDecl(bool NoCache, + function_ref<bool(const IndexRecordDecl *)> Receiver) { + for (unsigned I = 0, E = getNumDecls(); I != E; ++I) { + if (const IndexRecordDecl *D = Decls[I]) { + if (!Receiver(D)) + return false; + continue; + } + + if (NoCache) { + IndexRecordDecl LocalD; + readDecl(I, LocalD); + if (!Receiver(&LocalD)) + return false; + } else { + if (!Receiver(getDecl(I))) + return false; + } + } + return true; + } + + bool foreachOccurrence(ArrayRef<const IndexRecordDecl *> DeclsFilter, + ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter, + function_ref<bool(const IndexRecordOccurrence &)> Receiver) { + class OccurBitVisitor : public BitstreamVisitor<OccurBitVisitor> { + IndexRecordReader::Implementation &Reader; + ArrayRef<const IndexRecordDecl *> DeclsFilter; + ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter; + function_ref<bool(const IndexRecordOccurrence &)> Receiver; + + public: + OccurBitVisitor(llvm::BitstreamCursor &Stream, + IndexRecordReader::Implementation &Reader, + ArrayRef<const IndexRecordDecl *> DeclsFilter, + ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter, + function_ref<bool(const IndexRecordOccurrence &)> Receiver) + : BitstreamVisitor(Stream), + Reader(Reader), + DeclsFilter(DeclsFilter), + RelatedDeclsFilter(RelatedDeclsFilter), + Receiver(std::move(Receiver)) {} + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + assert(RecID == REC_DECLOCCURRENCE); + IndexRecordOccurrence RecOccur; + if (Reader.readOccurrence(Record, Blob, DeclsFilter, RelatedDeclsFilter, + RecOccur)) + if (!Receiver(RecOccur)) + return StreamVisit::Abort; + return StreamVisit::Continue; + } + }; + + SavedStreamPosition SavedPosition(OccurCursor); + OccurBitVisitor Visitor(OccurCursor, *this, DeclsFilter, RelatedDeclsFilter, + Receiver); + std::string Error; + return Visitor.visit(Error); + } + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount, + llvm::function_ref<bool(const IndexRecordOccurrence &)> receiver) { + // FIXME: Use binary search and make this more efficient. + unsigned lineEnd = lineStart+lineCount; + return foreachOccurrence(None, None, [&](const IndexRecordOccurrence &occur) -> bool { + if (occur.Line > lineEnd) + return false; // we're done. + if (occur.Line >= lineStart) { + if (!receiver(occur)) + return false; + } + return true; + }); + } + + static uint64_t read(RecordDataImpl &Record, unsigned &I) { + return Record[I++]; + } +}; + +namespace { + +class IndexBitstreamVisitor : public BitstreamVisitor<IndexBitstreamVisitor> { + IndexRecordReader::Implementation &Reader; + +public: + IndexBitstreamVisitor(llvm::BitstreamCursor &Stream, + IndexRecordReader::Implementation &Reader) + : BitstreamVisitor(Stream), Reader(Reader) {} + + StreamVisit visitBlock(unsigned ID) { + switch ((RecordBitBlock)ID) { + case REC_VERSION_BLOCK_ID: + case REC_DECLOFFSETS_BLOCK_ID: + return StreamVisit::Continue; + + case REC_DECLS_BLOCK_ID: + Reader.DeclCursor = Stream; + if (Reader.DeclCursor.EnterSubBlock(ID)) { + *Error = "malformed block record"; + return StreamVisit::Abort; + } + readBlockAbbrevs(Reader.DeclCursor); + return StreamVisit::Skip; + + case REC_DECLOCCURRENCES_BLOCK_ID: + Reader.OccurCursor = Stream; + if (Reader.OccurCursor.EnterSubBlock(ID)) { + *Error = "malformed block record"; + return StreamVisit::Abort; + } + readBlockAbbrevs(Reader.OccurCursor); + return StreamVisit::Skip; + } + + // Some newly introduced block in a minor version update that we cannot + // handle. + return StreamVisit::Skip; + } + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + switch (BlockID) { + case REC_VERSION_BLOCK_ID: { + unsigned StoreFormatVersion = Record[0]; + if (StoreFormatVersion != STORE_FORMAT_VERSION) { + llvm::raw_string_ostream OS(*Error); + OS << "Store format version mismatch: " << StoreFormatVersion; + OS << " , expected: " << STORE_FORMAT_VERSION; + return StreamVisit::Abort; + } + break; + } + case REC_DECLOFFSETS_BLOCK_ID: + assert(RecID == REC_DECLOFFSETS); + Reader.setDeclOffsets(makeArrayRef((uint32_t*)Blob.data(), Record[0])); + break; + + case REC_DECLS_BLOCK_ID: + case REC_DECLOCCURRENCES_BLOCK_ID: + llvm_unreachable("shouldn't visit this block'"); + } + return StreamVisit::Continue; + } +}; + +} // anonymous namespace + +std::unique_ptr<IndexRecordReader> +IndexRecordReader::createWithRecordFilename(StringRef RecordFilename, + StringRef StorePath, + std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendRecordSubDir(PathBuf); + appendInteriorRecordPath(RecordFilename, PathBuf); + return createWithFilePath(PathBuf.str(), Error); +} + +std::unique_ptr<IndexRecordReader> +IndexRecordReader::createWithFilePath(StringRef FilePath, std::string &Error) { + auto ErrOrBuf = MemoryBuffer::getFile(FilePath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrOrBuf) { + raw_string_ostream(Error) << "failed opening index record '" + << FilePath << "': " << ErrOrBuf.getError().message(); + return nullptr; + } + return createWithBuffer(std::move(*ErrOrBuf), Error); +} + +std::unique_ptr<IndexRecordReader> +IndexRecordReader::createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer, + std::string &Error) { + + std::unique_ptr<IndexRecordReader> Reader; + Reader.reset(new IndexRecordReader()); + auto &Impl = Reader->Impl; + Impl.Buffer = std::move(Buffer); + llvm::BitstreamCursor Stream(*Impl.Buffer); + + // Sniff for the signature. + if (Stream.Read(8) != 'I' || + Stream.Read(8) != 'D' || + Stream.Read(8) != 'X' || + Stream.Read(8) != 'R') { + Error = "not a serialized index record file"; + return nullptr; + } + + IndexBitstreamVisitor BitVisitor(Stream, Impl); + if (!BitVisitor.visit(Error)) + return nullptr; + + return Reader; +} + +IndexRecordReader::IndexRecordReader() + : Impl(*new Implementation()) { + +} + +IndexRecordReader::~IndexRecordReader() { + delete &Impl; +} + +bool IndexRecordReader::searchDecls( + llvm::function_ref<DeclSearchCheck> Checker, + llvm::function_ref<void(const IndexRecordDecl *)> Receiver) { + return Impl.searchDecls(std::move(Checker), std::move(Receiver)); +} + +bool IndexRecordReader::foreachDecl(bool NoCache, + function_ref<bool(const IndexRecordDecl *)> Receiver) { + return Impl.foreachDecl(NoCache, std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrence( + ArrayRef<const IndexRecordDecl *> DeclsFilter, + ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter, + function_ref<bool(const IndexRecordOccurrence &)> Receiver) { + return Impl.foreachOccurrence(DeclsFilter, RelatedDeclsFilter, + std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrence( + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver) { + return foreachOccurrence(None, None, std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrenceInLineRange(unsigned lineStart, + unsigned lineCount, + llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver) { + return Impl.foreachOccurrenceInLineRange(lineStart, lineCount, Receiver); +} diff --git a/clang/lib/Index/IndexRecordWriter.cpp b/clang/lib/Index/IndexRecordWriter.cpp new file mode 100644 index 0000000000000..c4e6d50716a96 --- /dev/null +++ b/clang/lib/Index/IndexRecordWriter.cpp @@ -0,0 +1,366 @@ +//===--- IndexRecordWriter.cpp - Index record serialization ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexRecordWriter.h" +#include "IndexDataStoreUtils.h" +#include "indexstore/indexstore.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +using writer::OpaqueDecl; + +namespace { +struct DeclInfo { + OpaqueDecl D; + SymbolRoleSet Roles; + SymbolRoleSet RelatedRoles; +}; + +struct OccurrenceInfo { + unsigned DeclID; + OpaqueDecl D; + SymbolRoleSet Roles; + unsigned Line; + unsigned Column; + SmallVector<std::pair<writer::SymbolRelation, unsigned>, 4> Related; +}; + +struct RecordState { + std::string RecordPath; + SmallString<512> Buffer; + BitstreamWriter Stream; + + DenseMap<OpaqueDecl, unsigned> IndexForDecl; + std::vector<DeclInfo> Decls; + std::vector<OccurrenceInfo> Occurrences; + + RecordState(std::string &&RecordPath) + : RecordPath(std::move(RecordPath)), Stream(Buffer) {} +}; +} // end anonymous namespace + +static void writeBlockInfo(BitstreamWriter &Stream) { + RecordData Record; + + Stream.EnterBlockInfoBlock(); +#define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record) +#define RECORD(X) emitRecordID(X, #X, Stream, Record) + + BLOCK(REC_VERSION_BLOCK); + RECORD(REC_VERSION); + + BLOCK(REC_DECLS_BLOCK); + RECORD(REC_DECLINFO); + + BLOCK(REC_DECLOFFSETS_BLOCK); + RECORD(REC_DECLOFFSETS); + + BLOCK(REC_DECLOCCURRENCES_BLOCK); + RECORD(REC_DECLOCCURRENCE); + +#undef RECORD +#undef BLOCK + Stream.ExitBlock(); +} + +static void writeVersionInfo(BitstreamWriter &Stream) { + using namespace llvm::sys; + + Stream.EnterSubblock(REC_VERSION_BLOCK_ID, 3); + + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(REC_VERSION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Store format version + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + Record.push_back(REC_VERSION); + Record.push_back(STORE_FORMAT_VERSION); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + + Stream.ExitBlock(); +} + +template <typename T, typename Allocator> +static StringRef data(const std::vector<T, Allocator> &v) { + if (v.empty()) + return StringRef(); + return StringRef(reinterpret_cast<const char *>(&v[0]), sizeof(T) * v.size()); +} + +template <typename T> static StringRef data(const SmallVectorImpl<T> &v) { + return StringRef(reinterpret_cast<const char *>(v.data()), + sizeof(T) * v.size()); +} + +static void writeDecls(BitstreamWriter &Stream, ArrayRef<DeclInfo> Decls, + ArrayRef<OccurrenceInfo> Occurrences, + writer::SymbolWriterCallback GetSymbolForDecl) { + SmallVector<uint32_t, 32> DeclOffsets; + DeclOffsets.reserve(Decls.size()); + + //===--------------------------------------------------------------------===// + // DECLS_BLOCK_ID + //===--------------------------------------------------------------------===// + + Stream.EnterSubblock(REC_DECLS_BLOCK_ID, 3); + + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(REC_DECLINFO)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Kind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // SubKind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Language + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolPropertyBitNum)); // Properties + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Roles + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Related Roles + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Length of name in block + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Length of USR in block + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + USR + CodeGen symbol name + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + +#ifndef NDEBUG + StringSet<> USRSet; +#endif + + RecordData Record; + llvm::SmallString<256> Blob; + llvm::SmallString<256> Scratch; + for (auto &Info : Decls) { + DeclOffsets.push_back(Stream.GetCurrentBitNo()); + Blob.clear(); + Scratch.clear(); + + writer::Symbol SymInfo = GetSymbolForDecl(Info.D, Scratch); + assert(SymInfo.SymInfo.Kind != SymbolKind::Unknown); + assert(!SymInfo.USR.empty() && "Recorded decl without USR!"); + + Blob += SymInfo.Name; + Blob += SymInfo.USR; + Blob += SymInfo.CodeGenName; + +#ifndef NDEBUG + bool IsNew = USRSet.insert(SymInfo.USR).second; + if (!IsNew) { + llvm::errs() << "Index: Duplicate USR! " << SymInfo.USR << "\n"; + // FIXME: print more information so it's easier to find the declaration. + } +#endif + + Record.clear(); + Record.push_back(REC_DECLINFO); + Record.push_back(getIndexStoreKind(SymInfo.SymInfo.Kind)); + Record.push_back(getIndexStoreSubKind(SymInfo.SymInfo.SubKind)); + Record.push_back(getIndexStoreLang(SymInfo.SymInfo.Lang)); + Record.push_back(getIndexStoreProperties(SymInfo.SymInfo.Properties)); + Record.push_back(getIndexStoreRoles(Info.Roles)); + Record.push_back(getIndexStoreRoles(Info.RelatedRoles)); + Record.push_back(SymInfo.Name.size()); + Record.push_back(SymInfo.USR.size()); + Stream.EmitRecordWithBlob(AbbrevCode, Record, Blob); + } + + Stream.ExitBlock(); + + //===--------------------------------------------------------------------===// + // DECLOFFSETS_BLOCK_ID + //===--------------------------------------------------------------------===// + + Stream.EnterSubblock(REC_DECLOFFSETS_BLOCK_ID, 3); + + Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(REC_DECLOFFSETS)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Number of Decls + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Offsets array + AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + Record.clear(); + Record.push_back(REC_DECLOFFSETS); + Record.push_back(DeclOffsets.size()); + Stream.EmitRecordWithBlob(AbbrevCode, Record, data(DeclOffsets)); + + Stream.ExitBlock(); + + //===--------------------------------------------------------------------===// + // DECLOCCURRENCES_BLOCK_ID + //===--------------------------------------------------------------------===// + + Stream.EnterSubblock(REC_DECLOCCURRENCES_BLOCK_ID, 3); + + Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(REC_DECLOCCURRENCE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Decl ID + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Roles + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // Line + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Column + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // Num related + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); // Related Roles/IDs + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // Roles or ID + AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + for (auto &Occur : Occurrences) { + Record.clear(); + Record.push_back(REC_DECLOCCURRENCE); + Record.push_back(Occur.DeclID); + Record.push_back(getIndexStoreRoles(Occur.Roles)); + Record.push_back(Occur.Line); + Record.push_back(Occur.Column); + Record.push_back(Occur.Related.size()); + for (auto &Rel : Occur.Related) { + Record.push_back(getIndexStoreRoles(Rel.first.Roles)); + Record.push_back(Rel.second); + } + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + } + Stream.ExitBlock(); +} + +IndexRecordWriter::IndexRecordWriter(StringRef IndexPath) + : RecordsPath(IndexPath) { + store::appendRecordSubDir(RecordsPath); +} + +IndexRecordWriter::Result +IndexRecordWriter::beginRecord(StringRef Filename, hash_code RecordHash, + std::string &Error, std::string *OutRecordFile) { + using namespace llvm::sys; + assert(!Record && "called beginRecord before calling endRecord on previous"); + + std::string RecordName; + { + llvm::raw_string_ostream RN(RecordName); + RN << path::filename(Filename); + RN << "-" << APInt(64, RecordHash).toString(36, /*Signed=*/false); + } + SmallString<256> RecordPath = RecordsPath.str(); + appendInteriorRecordPath(RecordName, RecordPath); + + if (OutRecordFile) + *OutRecordFile = RecordName; + + if (std::error_code EC = + fs::access(RecordPath.c_str(), fs::AccessMode::Exist)) { + if (EC != errc::no_such_file_or_directory) { + llvm::raw_string_ostream Err(Error); + Err << "could not access record '" << RecordPath + << "': " << EC.message(); + return Result::Failure; + } + } else { + return Result::AlreadyExists; + } + + // Write the record header. + auto *State = new RecordState(RecordPath.str()); + Record = State; + llvm::BitstreamWriter &Stream = State->Stream; + Stream.Emit('I', 8); + Stream.Emit('D', 8); + Stream.Emit('X', 8); + Stream.Emit('R', 8); + + writeBlockInfo(Stream); + writeVersionInfo(Stream); + + return Result::Success; +} + +IndexRecordWriter::Result +IndexRecordWriter::endRecord(std::string &Error, + writer::SymbolWriterCallback GetSymbolForDecl) { + assert(Record && "called endRecord without calling beginRecord"); + auto &State = *static_cast<RecordState *>(Record); + Record = nullptr; + struct ScopedDelete { + RecordState *S; + ScopedDelete(RecordState *S) : S(S) {} + ~ScopedDelete() { delete S; } + } Deleter(&State); + + if (!State.Decls.empty()) { + writeDecls(State.Stream, State.Decls, State.Occurrences, GetSymbolForDecl); + } + + if (std::error_code EC = sys::fs::create_directory(sys::path::parent_path(State.RecordPath))) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create directory '" << sys::path::parent_path(State.RecordPath) << "': " << EC.message(); + return Result::Failure; + } + + // Create a unique file to write to so that we can move the result into place + // atomically. If this process crashes we don't want to interfere with any + // other concurrent processes. + SmallString<128> TempPath(State.RecordPath); + TempPath += "-temp-%%%%%%%%"; + int TempFD; + if (sys::fs::createUniqueFile(TempPath.str(), TempFD, TempPath)) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create temporary file: " << TempPath; + return Result::Failure; + } + + raw_fd_ostream OS(TempFD, /*shouldClose=*/true); + OS.write(State.Buffer.data(), State.Buffer.size()); + OS.close(); + + // Atomically move the unique file into place. + if (std::error_code EC = + sys::fs::rename(TempPath.c_str(), State.RecordPath.c_str())) { + llvm::raw_string_ostream Err(Error); + Err << "failed to rename '" << TempPath << "' to '" << State.RecordPath << "': " << EC.message(); + return Result::Failure; + } + + return Result::Success; +} + +void IndexRecordWriter::addOccurrence( + OpaqueDecl D, SymbolRoleSet Roles, unsigned Line, unsigned Column, + ArrayRef<writer::SymbolRelation> Related) { + assert(Record && "called addOccurrence without calling beginRecord"); + auto &State = *static_cast<RecordState *>(Record); + + auto insertDecl = [&](OpaqueDecl D, SymbolRoleSet Roles, + SymbolRoleSet RelatedRoles) -> unsigned { + auto Insert = + State.IndexForDecl.insert(std::make_pair(D, State.Decls.size())); + unsigned Index = Insert.first->second; + + if (Insert.second) { + State.Decls.push_back(DeclInfo{D, Roles, RelatedRoles}); + } else { + State.Decls[Index].Roles |= Roles; + State.Decls[Index].RelatedRoles |= RelatedRoles; + } + return Index + 1; + }; + + unsigned DeclID = insertDecl(D, Roles, SymbolRoleSet()); + + decltype(OccurrenceInfo::Related) RelatedDecls; + RelatedDecls.reserve(Related.size()); + for (auto &Rel : Related) { + unsigned ID = insertDecl(Rel.RelatedSymbol, SymbolRoleSet(), Rel.Roles); + RelatedDecls.emplace_back(Rel, ID); + } + + State.Occurrences.push_back( + OccurrenceInfo{DeclID, D, Roles, Line, Column, std::move(RelatedDecls)}); +} diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp new file mode 100644 index 0000000000000..12a905616f16b --- /dev/null +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -0,0 +1,516 @@ +//===--- IndexUnitReader.cpp - Index unit deserialization -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexUnitReader.h" +#include "IndexDataStoreUtils.h" +#include "BitstreamVisitor.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +#include <unistd.h> + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +namespace { + +typedef function_ref<bool(const IndexUnitReader::DependencyInfo &)> DependencyReceiver; +typedef function_ref<bool(const IndexUnitReader::IncludeInfo &)> IncludeReceiver; + +class IndexUnitReaderImpl { + sys::TimePoint<> ModTime; + std::unique_ptr<MemoryBuffer> MemBuf; + +public: + StringRef ProviderIdentifier; + StringRef ProviderVersion; + llvm::BitstreamCursor DependCursor; + llvm::BitstreamCursor IncludeCursor; + bool IsSystemUnit; + bool IsModuleUnit; + bool IsDebugCompilation; + StringRef WorkingDir; + StringRef OutputFile; + StringRef SysrootPath; + StringRef ModuleName; + SmallString<128> MainFilePath; + StringRef Target; + std::vector<FileBitPath> Paths; + StringRef PathsBuffer; + + struct ModuleInfo { + unsigned NameOffset; + unsigned NameSize; + }; + std::vector<ModuleInfo> Modules; + StringRef ModuleNamesBuffer; + + bool init(std::unique_ptr<MemoryBuffer> Buf, sys::TimePoint<> ModTime, + std::string &Error); + + StringRef getProviderIdentifier() const { return ProviderIdentifier; } + StringRef getProviderVersion() const { return ProviderVersion; } + + sys::TimePoint<> getModificationTime() const { return ModTime; } + StringRef getWorkingDirectory() const { return WorkingDir; } + StringRef getOutputFile() const { return OutputFile; } + StringRef getSysrootPath() const { return SysrootPath; } + StringRef getTarget() const { return Target; } + + StringRef getModuleName() const { return ModuleName; } + StringRef getMainFilePath() const { return MainFilePath.str(); } + bool hasMainFile() const { return !MainFilePath.empty(); } + bool isSystemUnit() const { return IsSystemUnit; } + bool isModuleUnit() const { return IsModuleUnit; } + bool isDebugCompilation() const { return IsDebugCompilation; } + + /// Unit dependencies are provided ahead of record ones, record ones + /// ahead of the file ones. + bool foreachDependency(DependencyReceiver Receiver); + + bool foreachInclude(IncludeReceiver Receiver); + + StringRef getPathFromBuffer(size_t Offset, size_t Size) { + return PathsBuffer.substr(Offset, Size); + } + + void constructFilePath(SmallVectorImpl<char> &Path, int PathIndex); + + StringRef getModuleName(int ModuleIndex); +}; + +class IndexUnitBitstreamVisitor : public BitstreamVisitor<IndexUnitBitstreamVisitor> { + IndexUnitReaderImpl &Reader; + size_t WorkDirOffset; + size_t WorkDirSize; + size_t OutputFileOffset; + size_t OutputFileSize; + size_t SysrootOffset; + size_t SysrootSize; + int MainPathIndex; + +public: + IndexUnitBitstreamVisitor(llvm::BitstreamCursor &Stream, + IndexUnitReaderImpl &Reader) + : BitstreamVisitor(Stream), Reader(Reader) {} + + StreamVisit visitBlock(unsigned ID) { + switch ((UnitBitBlock)ID) { + case UNIT_VERSION_BLOCK_ID: + case UNIT_INFO_BLOCK_ID: + case UNIT_PATHS_BLOCK_ID: + case UNIT_MODULES_BLOCK_ID: + return StreamVisit::Continue; + + case UNIT_DEPENDENCIES_BLOCK_ID: + Reader.DependCursor = Stream; + if (Reader.DependCursor.EnterSubBlock(ID)) { + *Error = "malformed unit dependencies block record"; + return StreamVisit::Abort; + } + readBlockAbbrevs(Reader.DependCursor); + return StreamVisit::Skip; + case UNIT_INCLUDES_BLOCK_ID: + Reader.IncludeCursor = Stream; + if (Reader.IncludeCursor.EnterSubBlock(ID)) { + *Error = "malformed unit includes block record"; + return StreamVisit::Abort; + } + readBlockAbbrevs(Reader.IncludeCursor); + return StreamVisit::Skip; + } + + // Some newly introduced block in a minor version update that we cannot + // handle. + return StreamVisit::Skip; + } + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + switch (BlockID) { + case UNIT_VERSION_BLOCK_ID: { + unsigned StoreFormatVersion = Record[0]; + if (StoreFormatVersion != STORE_FORMAT_VERSION) { + llvm::raw_string_ostream OS(*Error); + OS << "Store format version mismatch: " << StoreFormatVersion; + OS << " , expected: " << STORE_FORMAT_VERSION; + return StreamVisit::Abort; + } + break; + } + + case UNIT_INFO_BLOCK_ID: { + assert(RecID == UNIT_INFO); + unsigned I = 0; + Reader.IsSystemUnit = Record[I++]; + + // Save these to lookup them up after we get the paths buffer. + WorkDirOffset = Record[I++]; + WorkDirSize = Record[I++]; + OutputFileOffset = Record[I++]; + OutputFileSize = Record[I++]; + SysrootOffset = Record[I++]; + SysrootSize = Record[I++]; + MainPathIndex = (int)Record[I++] - 1; + Reader.IsDebugCompilation = Record[I++]; + Reader.IsModuleUnit = Record[I++]; + + size_t moduleNameSize = Record[I++]; + size_t providerIdentifierSize = Record[I++]; + size_t providerVersionSize = Record[I++]; + I++; // Reserved for ProviderDataVersion. + Reader.ModuleName = Blob.substr(0, moduleNameSize); + Blob = Blob.drop_front(moduleNameSize); + Reader.ProviderIdentifier = Blob.substr(0, providerIdentifierSize); + Blob = Blob.drop_front(providerIdentifierSize); + Reader.ProviderVersion = Blob.substr(0, providerVersionSize); + Reader.Target = Blob.drop_front(providerVersionSize); + break; + } + + case UNIT_PATHS_BLOCK_ID: + switch (RecID) { + case UNIT_PATH: + { + unsigned I = 0; + UnitFilePathPrefixKind Kind = (UnitFilePathPrefixKind)Record[I++]; + size_t DirOffset = Record[I++]; + size_t DirSize = Record[I++]; + size_t FilenameOffset = Record[I++]; + size_t FilenameSize = Record[I++]; + + Reader.Paths.emplace_back(Kind, BitPathComponent(DirOffset, DirSize), + BitPathComponent(FilenameOffset, FilenameSize)); + } + break; + case UNIT_PATH_BUFFER: + Reader.PathsBuffer = Blob; + Reader.WorkingDir = Reader.getPathFromBuffer(WorkDirOffset, WorkDirSize); + Reader.OutputFile = Reader.getPathFromBuffer(OutputFileOffset, OutputFileSize); + Reader.SysrootPath = Reader.getPathFromBuffer(SysrootOffset, SysrootSize); + + // now we can populate the main file's path + Reader.constructFilePath(Reader.MainFilePath, MainPathIndex); + break; + default: + llvm_unreachable("shouldn't visit this record"); + } + break; + + case UNIT_MODULES_BLOCK_ID: + switch (RecID) { + case UNIT_MODULE: + { + unsigned I = 0; + unsigned NameOffset = Record[I++]; + unsigned NameSize = Record[I++]; + Reader.Modules.push_back({NameOffset, NameSize}); + } + break; + case UNIT_MODULE_BUFFER: + Reader.ModuleNamesBuffer = Blob; + break; + default: + llvm_unreachable("shouldn't visit this record"); + } + break; + + case UNIT_DEPENDENCIES_BLOCK_ID: + case UNIT_INCLUDES_BLOCK_ID: + llvm_unreachable("shouldn't visit this block'"); + } + return StreamVisit::Continue; + } +}; + +typedef std::function<bool(RecordDataImpl& Record, StringRef Blob)> + BlockVisitorCallback; + +class IndexUnitBlockBitstreamVisitor : public BitstreamVisitor<IndexUnitBlockBitstreamVisitor> { + unsigned RecID; + BlockVisitorCallback Visit; + +public: + IndexUnitBlockBitstreamVisitor(unsigned RecID, + llvm::BitstreamCursor &BlockStream, + BlockVisitorCallback Visit) + : BitstreamVisitor(BlockStream), RecID(RecID), Visit(std::move(Visit)) {} + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + if (RecID != this->RecID) + llvm_unreachable("shouldn't be called with this RecID"); + + if (Visit(Record, Blob)) + return StreamVisit::Continue; + return StreamVisit::Abort; + } +}; + +} // anonymous namespace + +bool IndexUnitReaderImpl::init(std::unique_ptr<MemoryBuffer> Buf, + sys::TimePoint<> ModTime, std::string &Error) { + this->ModTime = ModTime; + this->MemBuf = std::move(Buf); + llvm::BitstreamCursor Stream(*MemBuf); + + // Sniff for the signature. + if (Stream.Read(8) != 'I' || + Stream.Read(8) != 'D' || + Stream.Read(8) != 'X' || + Stream.Read(8) != 'U') { + Error = "not a serialized index unit file"; + return true; + } + + IndexUnitBitstreamVisitor BitVisitor(Stream, *this); + return !BitVisitor.visit(Error); +} + +/// Unit dependencies are provided ahead of record ones, record ones +/// ahead of the file ones. +bool IndexUnitReaderImpl::foreachDependency(DependencyReceiver Receiver) { + store::SavedStreamPosition SavedDepPosition(DependCursor); + IndexUnitBlockBitstreamVisitor Visitor(UNIT_DEPENDENCY, DependCursor, + [&](RecordDataImpl& Record, StringRef Blob) { + unsigned I = 0; + UnitDependencyKind UnitDepKind = (UnitDependencyKind)Record[I++]; + bool IsSystem = Record[I++]; + int PathIndex = (int)Record[I++] - 1; + int ModuleIndex = (int)Record[I++] - 1; + time_t ModTime = (time_t)Record[I++]; + size_t FileSize = Record[I++]; + StringRef Name = Blob; + + IndexUnitReader::DependencyKind DepKind; + switch (UnitDepKind) { + case UNIT_DEPEND_KIND_UNIT: + DepKind = IndexUnitReader::DependencyKind::Unit; break; + case UNIT_DEPEND_KIND_RECORD: + DepKind = IndexUnitReader::DependencyKind::Record; break; + case UNIT_DEPEND_KIND_FILE: + DepKind = IndexUnitReader::DependencyKind::File; break; + } + + SmallString<512> PathBuf; + this->constructFilePath(PathBuf, PathIndex); + StringRef ModuleName = this->getModuleName(ModuleIndex); + + return Receiver(IndexUnitReader::DependencyInfo{DepKind, IsSystem, Name, + PathBuf.str(), ModuleName, FileSize, ModTime}); + }); + + std::string Error; + return Visitor.visit(Error); +} + +bool IndexUnitReaderImpl::foreachInclude(IncludeReceiver Receiver) { + store::SavedStreamPosition SavedIncPosition(IncludeCursor); + IndexUnitBlockBitstreamVisitor Visitor(UNIT_INCLUDE, IncludeCursor, + [&](RecordDataImpl& Record, StringRef Blob) { + unsigned I = 0; + int SourcePathIndex = (int)Record[I++] - 1; + unsigned Line = Record[I++]; + int TargetPathIndex = (int)Record[I++] - 1; + + SmallString<512> SourceBuf, TargetBuf; + this->constructFilePath(SourceBuf, SourcePathIndex); + this->constructFilePath(TargetBuf, TargetPathIndex); + return Receiver(IndexUnitReader::IncludeInfo{SourceBuf.str(), Line, TargetBuf.str()}); + }); + + std::string Error; + return Visitor.visit(Error); +} + + +void IndexUnitReaderImpl::constructFilePath(SmallVectorImpl<char> &PathBuf, + int PathIndex) { + + if (PathIndex < 0) return; + FileBitPath &Path = Paths[PathIndex]; + StringRef Prefix; + switch (Path.PrefixKind) { + case UNIT_PATH_PREFIX_NONE: + break; + case UNIT_PATH_PREFIX_WORKDIR: + Prefix = getWorkingDirectory(); + break; + case UNIT_PATH_PREFIX_SYSROOT: + Prefix = getSysrootPath(); + break; + } + PathBuf.append(Prefix.begin(), Prefix.end()); + sys::path::append(PathBuf, + getPathFromBuffer(Path.Dir.Offset, Path.Dir.Size), + getPathFromBuffer(Path.Filename.Offset, Path.Filename.Size)); +} + +StringRef IndexUnitReaderImpl::getModuleName(int ModuleIndex) { + if (ModuleIndex < 0) + return StringRef(); + auto &ModInfo = Modules[ModuleIndex]; + return StringRef(ModuleNamesBuffer.data()+ModInfo.NameOffset, ModInfo.NameSize); +} + + +//===----------------------------------------------------------------------===// +// IndexUnitReader +//===----------------------------------------------------------------------===// + +std::unique_ptr<IndexUnitReader> +IndexUnitReader::createWithUnitFilename(StringRef UnitFilename, + StringRef StorePath, + std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendUnitSubDir(PathBuf); + sys::path::append(PathBuf, UnitFilename); + return createWithFilePath(PathBuf.str(), Error); +} + +std::unique_ptr<IndexUnitReader> +IndexUnitReader::createWithFilePath(StringRef FilePath, std::string &Error) { + int FD; + std::error_code EC = sys::fs::openFileForRead(FilePath, FD); + if (EC) { + raw_string_ostream(Error) << "Failed opening '" << FilePath << "': " + << EC.message(); + return nullptr; + } + + assert(FD != -1); + struct AutoFDClose { + int FD; + AutoFDClose(int FD) : FD(FD) {} + ~AutoFDClose() { + ::close(FD); + } + } AutoFDClose(FD); + + sys::fs::file_status FileStat; + EC = sys::fs::status(FD, FileStat); + if (EC) { + Error = EC.message(); + return nullptr; + } + + auto ErrOrBuf = MemoryBuffer::getOpenFile(FD, FilePath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrOrBuf) { + raw_string_ostream(Error) << "Failed opening '" << FilePath << "': " + << ErrOrBuf.getError().message(); + return nullptr; + } + + std::unique_ptr<IndexUnitReaderImpl> Impl(new IndexUnitReaderImpl()); + bool Err = Impl->init(std::move(*ErrOrBuf), FileStat.getLastModificationTime(), + Error); + if (Err) + return nullptr; + + std::unique_ptr<IndexUnitReader> Reader; + Reader.reset(new IndexUnitReader(Impl.release())); + return Reader; +} + +Optional<sys::TimePoint<>> +IndexUnitReader::getModificationTimeForUnit(StringRef UnitFilename, + StringRef StorePath, + std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendUnitSubDir(PathBuf); + sys::path::append(PathBuf, UnitFilename); + + sys::fs::file_status FileStat; + std::error_code EC = sys::fs::status(PathBuf.str(), FileStat); + if (EC) { + Error = EC.message(); + return None; + } + return FileStat.getLastModificationTime(); +} + +#define IMPL static_cast<IndexUnitReaderImpl*>(Impl) + +IndexUnitReader::~IndexUnitReader() { + delete IMPL; +} + +StringRef IndexUnitReader::getProviderIdentifier() const { + return IMPL->getProviderIdentifier(); +} + +StringRef IndexUnitReader::getProviderVersion() const { + return IMPL->getProviderVersion(); +} + +llvm::sys::TimePoint<> IndexUnitReader::getModificationTime() const { + return IMPL->getModificationTime(); +} + +StringRef IndexUnitReader::getWorkingDirectory() const { + return IMPL->getWorkingDirectory(); +} + +StringRef IndexUnitReader::getOutputFile() const { + return IMPL->getOutputFile(); +} + +StringRef IndexUnitReader::getSysrootPath() const { + return IMPL->getSysrootPath(); +} + +StringRef IndexUnitReader::getMainFilePath() const { + return IMPL->getMainFilePath(); +} + +StringRef IndexUnitReader::getModuleName() const { + return IMPL->getModuleName(); +} + +StringRef IndexUnitReader::getTarget() const { + return IMPL->getTarget(); +} + +bool IndexUnitReader::hasMainFile() const { + return IMPL->hasMainFile(); +} + +bool IndexUnitReader::isSystemUnit() const { + return IMPL->isSystemUnit(); +} + +bool IndexUnitReader::isModuleUnit() const { + return IMPL->isModuleUnit(); +} + +bool IndexUnitReader::isDebugCompilation() const { + return IMPL->isDebugCompilation(); +} + +/// \c Index is the index in the \c getDependencies array. +/// Unit dependencies are provided ahead of record ones. +bool IndexUnitReader::foreachDependency(DependencyReceiver Receiver) { + return IMPL->foreachDependency(std::move(Receiver)); +} + +bool IndexUnitReader::foreachInclude(IncludeReceiver Receiver) { + return IMPL->foreachInclude(std::move(Receiver)); +} diff --git a/clang/lib/Index/IndexUnitWriter.cpp b/clang/lib/Index/IndexUnitWriter.cpp new file mode 100644 index 0000000000000..7c981ae9750d8 --- /dev/null +++ b/clang/lib/Index/IndexUnitWriter.cpp @@ -0,0 +1,628 @@ +//===--- IndexUnitWriter.cpp - Index unit serialization -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexUnitWriter.h" +#include "IndexDataStoreUtils.h" +#include "clang/Basic/FileManager.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + + +class IndexUnitWriter::PathStorage { + std::string WorkDir; + std::string SysrootPath; + SmallString<512> PathsBuf; + StringMap<DirBitPath, BumpPtrAllocator> Dirs; + std::vector<FileBitPath> FileBitPaths; + DenseMap<const FileEntry *, size_t> FileToIndex; + +public: + PathStorage(StringRef workDir, StringRef sysrootPath) { + WorkDir = workDir; + if (sysrootPath == "/") + sysrootPath = StringRef(); + SysrootPath = sysrootPath; + } + + StringRef getPathsBuffer() const { return PathsBuf.str(); } + + ArrayRef<FileBitPath> getBitPaths() const { return FileBitPaths; } + + int getPathIndex(const FileEntry *FE) { + if (!FE) + return -1; + auto Pair = FileToIndex.insert(std::make_pair(FE, FileBitPaths.size())); + bool IsNew = Pair.second; + size_t Index = Pair.first->getSecond(); + + if (IsNew) { + StringRef Filename = sys::path::filename(FE->getName()); + DirBitPath Dir = getDirBitPath(sys::path::parent_path(FE->getName())); + FileBitPaths.emplace_back(Dir.PrefixKind, Dir.Dir, + BitPathComponent(getPathOffset(Filename), + Filename.size())); + } + return Index; + } + + size_t getPathOffset(StringRef Path) { + if (Path.empty()) + return 0; + size_t offset = PathsBuf.size(); + PathsBuf += Path; + return offset; + } + +private: + DirBitPath getDirBitPath(StringRef dirStr) { + auto pair = Dirs.insert(std::make_pair(dirStr, DirBitPath())); + bool isNew = pair.second; + auto &dirPath = pair.first->second; + + if (isNew) { + if (isPathInDir(SysrootPath, dirStr)) { + dirPath.PrefixKind = UNIT_PATH_PREFIX_SYSROOT; + dirStr = dirStr.drop_front(SysrootPath.size()); + while (!dirStr.empty() && dirStr[0] == '/') + dirStr = dirStr.drop_front(); + } else if (isPathInDir(WorkDir, dirStr)) { + dirPath.PrefixKind = UNIT_PATH_PREFIX_WORKDIR; + dirStr = dirStr.drop_front(WorkDir.size()); + while (!dirStr.empty() && dirStr[0] == '/') + dirStr = dirStr.drop_front(); + } + dirPath.Dir.Offset = getPathOffset(dirStr); + dirPath.Dir.Size = dirStr.size(); + } + return dirPath; + } + + static bool isPathInDir(StringRef dir, StringRef path) { + if (dir.empty() || !path.startswith(dir)) + return false; + StringRef rest = path.drop_front(dir.size()); + return !rest.empty() && sys::path::is_separator(rest.front()); + } +}; + +IndexUnitWriter::IndexUnitWriter(FileManager &FileMgr, + StringRef StorePath, + StringRef ProviderIdentifier, + StringRef ProviderVersion, + StringRef OutputFile, + StringRef ModuleName, + const FileEntry *MainFile, + bool IsSystem, + bool IsModuleUnit, + bool IsDebugCompilation, + StringRef TargetTriple, + StringRef SysrootPath, + writer::ModuleInfoWriterCallback GetInfoForModule) +: FileMgr(FileMgr) { + this->UnitsPath = StorePath; + store::appendUnitSubDir(this->UnitsPath); + this->ProviderIdentifier = ProviderIdentifier; + this->ProviderVersion = ProviderVersion; + this->OutputFile = OutputFile; + this->ModuleName = ModuleName; + this->MainFile = MainFile; + this->IsSystemUnit = IsSystem; + this->IsModuleUnit = IsModuleUnit; + this->IsDebugCompilation = IsDebugCompilation; + this->TargetTriple = TargetTriple; + this->SysrootPath = SysrootPath; + this->GetInfoForModuleFn = GetInfoForModule; +} + +IndexUnitWriter::~IndexUnitWriter() {} + +int IndexUnitWriter::addModule(writer::OpaqueModule Mod) { + if (!Mod) + return -1; + + auto Pair = IndexByModule.insert(std::make_pair(Mod, Modules.size())); + bool WasInserted = Pair.second; + if (WasInserted) { + Modules.push_back(Mod); + } + return Pair.first->second; +} + +int IndexUnitWriter::addFileDependency(const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod) { + assert(File); + auto Pair = IndexByFile.insert(std::make_pair(File, Files.size())); + bool WasInserted = Pair.second; + if (WasInserted) { + Files.push_back(FileEntryData{File, IsSystem, addModule(Mod), {}}); + } + return Pair.first->second; +} + +void IndexUnitWriter::addRecordFile(StringRef RecordFile, const FileEntry *File, + bool IsSystem, writer::OpaqueModule Mod) { + int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1; + Records.push_back(RecordOrUnitData{RecordFile, Dep, addModule(Mod), IsSystem}); +} + +void IndexUnitWriter::addASTFileDependency(const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod, + bool withoutUnitName) { + assert(File); + if (!SeenASTFiles.insert(File).second) + return; + + SmallString<64> UnitName; + if (!withoutUnitName) + getUnitNameForOutputFile(File->getName(), UnitName); + addUnitDependency(UnitName.str(), File, IsSystem, Mod); +} + +void IndexUnitWriter::addUnitDependency(StringRef UnitFile, + const FileEntry *File, bool IsSystem, + writer::OpaqueModule Mod) { + int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1; + ASTFileUnits.emplace_back(RecordOrUnitData{UnitFile, Dep, addModule(Mod), IsSystem}); +} + +bool IndexUnitWriter::addInclude(const FileEntry *Source, unsigned Line, + const FileEntry *Target) { + // FIXME: This will ignore includes of headers that resolve to module imports + // because the 'target' header has not been added as a file dependency earlier + // so it is missing from \c IndexByFile. + + auto It = IndexByFile.find(Source); + if (It == IndexByFile.end()) + return false; + int SourceIndex = It->getSecond(); + It = IndexByFile.find(Target); + if (It == IndexByFile.end()) + return false; + int TargetIndex = It->getSecond(); + Files[SourceIndex].Includes.emplace_back(FileInclude{TargetIndex, Line}); + return true; +}; + +void IndexUnitWriter::getUnitNameForOutputFile(StringRef FilePath, + SmallVectorImpl<char> &Str) { + SmallString<256> AbsPath(FilePath); + FileMgr.makeAbsolutePath(AbsPath); + return getUnitNameForAbsoluteOutputFile(AbsPath, Str); +} + +void IndexUnitWriter::getUnitPathForOutputFile(StringRef FilePath, + SmallVectorImpl<char> &Str) { + Str.append(UnitsPath.begin(), UnitsPath.end()); + Str.push_back('/'); + return getUnitNameForOutputFile(FilePath, Str); +} + +Optional<bool> IndexUnitWriter::isUnitUpToDateForOutputFile(StringRef FilePath, + Optional<StringRef> TimeCompareFilePath, + std::string &Error) { + SmallString<256> UnitPath; + getUnitPathForOutputFile(FilePath, UnitPath); + + llvm::sys::fs::file_status UnitStat; + if (std::error_code EC = llvm::sys::fs::status(UnitPath.c_str(), UnitStat)) { + if (EC != llvm::errc::no_such_file_or_directory) { + llvm::raw_string_ostream Err(Error); + Err << "could not access path '" << UnitPath + << "': " << EC.message(); + return None; + } + return false; + } + + if (!TimeCompareFilePath.hasValue()) + return true; + + llvm::sys::fs::file_status CompareStat; + if (std::error_code EC = llvm::sys::fs::status(*TimeCompareFilePath, CompareStat)) { + if (EC != llvm::errc::no_such_file_or_directory) { + llvm::raw_string_ostream Err(Error); + Err << "could not access path '" << *TimeCompareFilePath + << "': " << EC.message(); + return None; + } + return true; + } + + // Return true (unit is up-to-date) if the file to compare is older than the + // unit file. + return CompareStat.getLastModificationTime() <= UnitStat.getLastModificationTime(); +} + +void IndexUnitWriter::getUnitNameForAbsoluteOutputFile(StringRef FilePath, + SmallVectorImpl<char> &Str) { + StringRef Fname = sys::path::filename(FilePath); + Str.append(Fname.begin(), Fname.end()); + Str.push_back('-'); + llvm::hash_code PathHashVal = llvm::hash_value(FilePath); + llvm::APInt(64, PathHashVal).toString(Str, 36, /*Signed=*/false); +} + +static void writeBlockInfo(BitstreamWriter &Stream) { + RecordData Record; + + Stream.EnterBlockInfoBlock(); +#define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record) +#define RECORD(X) emitRecordID(X, #X, Stream, Record) + + BLOCK(UNIT_VERSION_BLOCK); + RECORD(UNIT_VERSION); + + BLOCK(UNIT_INFO_BLOCK); + RECORD(UNIT_INFO); + + BLOCK(UNIT_DEPENDENCIES_BLOCK); + RECORD(UNIT_DEPENDENCY); + + BLOCK(UNIT_INCLUDES_BLOCK); + RECORD(UNIT_INCLUDE); + + BLOCK(UNIT_PATHS_BLOCK); + RECORD(UNIT_PATH); + RECORD(UNIT_PATH_BUFFER); + + BLOCK(UNIT_MODULES_BLOCK); + RECORD(UNIT_MODULE); + RECORD(UNIT_MODULE_BUFFER); + +#undef RECORD +#undef BLOCK + Stream.ExitBlock(); +} + +static void writeVersionInfo(BitstreamWriter &Stream) { + using namespace llvm::sys; + + Stream.EnterSubblock(UNIT_VERSION_BLOCK_ID, 3); + + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_VERSION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Store format version + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + Record.push_back(UNIT_VERSION); + Record.push_back(STORE_FORMAT_VERSION); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + + Stream.ExitBlock(); +} + +bool IndexUnitWriter::write(std::string &Error) { + using namespace llvm::sys; + + // Determine the working directory. + SmallString<128> CWDPath; + if (!FileMgr.getFileSystemOpts().WorkingDir.empty()) { + CWDPath = FileMgr.getFileSystemOpts().WorkingDir; + if (!path::is_absolute(CWDPath)) { + fs::make_absolute(CWDPath); + } + } else { + std::error_code EC = sys::fs::current_path(CWDPath); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to determine current working directory: " << EC.message(); + return true; + } + } + WorkDir = CWDPath.str(); + + SmallString<512> Buffer; + BitstreamWriter Stream(Buffer); + Stream.Emit('I', 8); + Stream.Emit('D', 8); + Stream.Emit('X', 8); + Stream.Emit('U', 8); + + PathStorage PathStore(WorkDir, SysrootPath); + + writeBlockInfo(Stream); + writeVersionInfo(Stream); + writeUnitInfo(Stream, PathStore); + writeDependencies(Stream, PathStore); + writeIncludes(Stream, PathStore); + writePaths(Stream, PathStore); + writeModules(Stream); + + SmallString<256> UnitPath; + getUnitPathForOutputFile(OutputFile, UnitPath); + + SmallString<128> TempPath; + TempPath = path::parent_path(UnitsPath); + TempPath += '/'; + TempPath += path::filename(UnitPath); + TempPath += "-%%%%%%%%"; + int TempFD; + if (llvm::sys::fs::createUniqueFile(TempPath.str(), TempFD, TempPath)) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create temporary file: " << TempPath; + return true; + } + + raw_fd_ostream OS(TempFD, /*shouldClose=*/true); + OS.write(Buffer.data(), Buffer.size()); + OS.close(); + + std::error_code EC = fs::rename(/*from=*/TempPath.c_str(), /*to=*/UnitPath.c_str()); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to rename '" << TempPath << "' to '" << UnitPath << "': " << EC.message(); + return true; + } + + return false; +} + +void IndexUnitWriter::writeUnitInfo(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + Stream.EnterSubblock(UNIT_INFO_BLOCK_ID, 3); + + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_INFO)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystemUnit + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // WorkDir offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // WorkDir size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // OutputFile offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // OutputFile size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Sysroot offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Sysroot size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Main path id + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsDebugCompilation + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsModuleUnit + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Module name size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderIdentifier size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderVersion size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderDataVersion + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module name + ProviderIdentifier + ProviderVersion + target triple + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + Record.push_back(UNIT_INFO); + Record.push_back(IsSystemUnit); + Record.push_back(PathStore.getPathOffset(WorkDir)); + Record.push_back(WorkDir.size()); + Record.push_back(PathStore.getPathOffset(OutputFile)); + Record.push_back(OutputFile.size()); + Record.push_back(PathStore.getPathOffset(SysrootPath)); + Record.push_back(SysrootPath.size()); + Record.push_back(PathStore.getPathIndex(MainFile) + 1); // Make 1-based with 0=invalid + Record.push_back(IsDebugCompilation); + Record.push_back(IsModuleUnit); + Record.push_back(ModuleName.size()); + Record.push_back(ProviderIdentifier.size()); + Record.push_back(ProviderVersion.size()); + // ProviderDataVersion is reserved. Not sure it is a good to idea to have + // clients consider the specifics of a 'provider data version', but reserving + // to avoid store format version change in case there is a use case in the + // future. + Record.push_back(0); // ProviderDataVersion + SmallString<128> InfoStrings; + InfoStrings += ModuleName; + InfoStrings += ProviderIdentifier; + InfoStrings += ProviderVersion; + InfoStrings += TargetTriple; + Stream.EmitRecordWithBlob(AbbrevCode, Record, InfoStrings); + + Stream.ExitBlock(); +} + +void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + std::vector<bool> FileUsedForRecordOrUnit; + FileUsedForRecordOrUnit.resize(Files.size()); + + Stream.EnterSubblock(UNIT_DEPENDENCIES_BLOCK_ID, 3); + + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_DEPENDENCY)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitDependencyKindBitNum)); // Dependency kind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // PathIndex (1-based, 0 = none) + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // ModuleIndex (1-based, 0 = none) + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 32)); // time_t + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // file size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + + auto addRecordOrUnitData = [&](UnitDependencyKind K, const RecordOrUnitData &Data) { + Record.push_back(UNIT_DEPENDENCY); + Record.push_back(K); + Record.push_back(Data.IsSystem); + if (Data.FileIndex != -1) { + Record.push_back(PathStore.getPathIndex(Files[Data.FileIndex].File) + 1); + FileUsedForRecordOrUnit[Data.FileIndex] = true; + } else { + Record.push_back(0); + } + if (Data.ModuleIndex != -1) { + Record.push_back(Data.ModuleIndex + 1); + } else { + Record.push_back(0); + } + if (Data.FileIndex != -1) { + Record.push_back(Files[Data.FileIndex].File->getModificationTime()); + Record.push_back(Files[Data.FileIndex].File->getSize()); + } else { + Record.push_back(0); + Record.push_back(0); + } + Stream.EmitRecordWithBlob(AbbrevCode, Record, Data.Name); + }; + + for (auto &ASTData : ASTFileUnits) { + Record.clear(); + addRecordOrUnitData(UNIT_DEPEND_KIND_UNIT, ASTData); + } + for (auto &recordData : Records) { + Record.clear(); + addRecordOrUnitData(UNIT_DEPEND_KIND_RECORD, recordData); + } + size_t FileIndex = 0; + for (auto &File : Files) { + if (FileUsedForRecordOrUnit[FileIndex++]) + continue; + Record.clear(); + Record.push_back(UNIT_DEPENDENCY); + Record.push_back(UNIT_DEPEND_KIND_FILE); + Record.push_back(File.IsSystem); + Record.push_back(PathStore.getPathIndex(File.File) + 1); + if (File.ModuleIndex != -1) { + Record.push_back(File.ModuleIndex + 1); + } else { + Record.push_back(0); + } + Record.push_back(File.File->getModificationTime()); + Record.push_back(File.File->getSize()); + Stream.EmitRecordWithBlob(AbbrevCode, Record, StringRef()); + } + + Stream.ExitBlock(); +} + +void IndexUnitWriter::writeIncludes(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + Stream.EnterSubblock(UNIT_INCLUDES_BLOCK_ID, 3); + + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_INCLUDE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // source path index (1-based, 0 = no path) + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // source include line + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // target path index (1-based, 0 = no path) + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + + for (auto &Including : Files) { + for(auto &Included: Including.Includes) { + Record.clear(); + Record.push_back(UNIT_INCLUDE); + Record.push_back(PathStore.getPathIndex(Including.File) + 1); + Record.push_back(Included.Line); + Record.push_back(PathStore.getPathIndex(Files[Included.Index].File) + 1); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + } + } + Stream.ExitBlock(); +} + +void IndexUnitWriter::writePaths(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + Stream.EnterSubblock(UNIT_PATHS_BLOCK_ID, 3); + + auto PathAbbrev = std::make_shared<BitCodeAbbrev>(); + PathAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH)); + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitFilePathPrefixKindBitNum)); // Path prefix kind + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // DirPath offset + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // DirPath size + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Filename offset + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Filename size + unsigned PathAbbrevCode = Stream.EmitAbbrev(std::move(PathAbbrev)); + + auto PathBufferAbbrev = std::make_shared<BitCodeAbbrev>(); + PathBufferAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH_BUFFER)); + PathBufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Paths buffer + unsigned PathBufferAbbrevCode = Stream.EmitAbbrev(PathBufferAbbrev); + + RecordData Record; + for(auto &BitPath: PathStore.getBitPaths()) { + Record.push_back(UNIT_PATH); + Record.push_back(BitPath.PrefixKind); + Record.push_back(BitPath.Dir.Offset); + Record.push_back(BitPath.Dir.Size); + Record.push_back(BitPath.Filename.Offset); + Record.push_back(BitPath.Filename.Size); + Stream.EmitRecordWithAbbrev(PathAbbrevCode, Record); + Record.clear(); + } + + Record.push_back(UNIT_PATH_BUFFER); + Stream.EmitRecordWithBlob(PathBufferAbbrevCode, Record, PathStore.getPathsBuffer()); + + Stream.ExitBlock(); +} + +void IndexUnitWriter::writeModules(llvm::BitstreamWriter &Stream) { + Stream.EnterSubblock(UNIT_MODULES_BLOCK_ID, 3); + + auto Abbrev = std::make_shared<BitCodeAbbrev>(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_MODULE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 9)); // Module name offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Module name size + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + auto BufferAbbrev = std::make_shared<BitCodeAbbrev>(); + BufferAbbrev->Add(BitCodeAbbrevOp(UNIT_MODULE_BUFFER)); + BufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module names buffer + unsigned BufferAbbrevCode = Stream.EmitAbbrev(BufferAbbrev); + + SmallString<512> ModuleNamesBuf; + + RecordData Record; + for (auto &Mod : Modules) { + SmallString<64> ModuleName; + StringRef name = GetInfoForModuleFn(Mod, ModuleName).Name; + size_t offset = ModuleNamesBuf.size(); + ModuleNamesBuf += name; + + Record.push_back(UNIT_MODULE); + Record.push_back(offset); + Record.push_back(name.size()); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + Record.clear(); + } + + Record.push_back(UNIT_MODULE_BUFFER); + Stream.EmitRecordWithBlob(BufferAbbrevCode, Record, ModuleNamesBuf.str()); + + Stream.ExitBlock(); +} + +bool IndexUnitWriter::initIndexDirectory(StringRef StorePath, + std::string &Error) { + using namespace llvm::sys; + SmallString<128> SubPath = StorePath; + store::appendRecordSubDir(SubPath); + std::error_code EC = fs::create_directories(SubPath); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create directory '" << SubPath << "': " << EC.message(); + return true; + } + + SubPath = StorePath; + store::appendUnitSubDir(SubPath); + EC = fs::create_directory(SubPath); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create directory '" << SubPath << "': " << EC.message(); + return true; + } + + return false; +} diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index cac24d4b9c4c1..32da3624f3a94 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -9,9 +9,16 @@ #include "clang/Index/IndexingAction.h" #include "clang/Index/IndexDataConsumer.h" +#include "FileIndexRecord.h" #include "IndexingContext.h" +#include "ClangIndexRecordWriter.h" +#include "IndexDataStoreUtils.h" +#include "clang/Index/IndexUnitWriter.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/Utils.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" @@ -80,7 +87,8 @@ class IndexActionBase { : DataConsumer(std::move(dataConsumer)), IndexCtx(Opts, *DataConsumer) {} - std::unique_ptr<IndexASTConsumer> createIndexASTConsumer() { + std::unique_ptr<IndexASTConsumer> createIndexASTConsumer(CompilerInstance &CI) { + IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot); return llvm::make_unique<IndexASTConsumer>(IndexCtx); } @@ -98,7 +106,7 @@ class IndexAction : public ASTFrontendAction, IndexActionBase { protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { - return createIndexASTConsumer(); + return createIndexASTConsumer(CI); } void EndSourceFileAction() override { @@ -108,7 +116,7 @@ class IndexAction : public ASTFrontendAction, IndexActionBase { }; class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { - bool IndexActionFailed = false; + bool CreatedASTConsumer = false; public: WrappingIndexAction(std::unique_ptr<FrontendAction> WrappedAction, @@ -128,21 +136,20 @@ class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { void WrappingIndexAction::EndSourceFileAction() { // Invoke wrapped action's method. WrapperFrontendAction::EndSourceFileAction(); - if (!IndexActionFailed) + if (CreatedASTConsumer) finish(); } std::unique_ptr<ASTConsumer> WrappingIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); - if (!OtherConsumer) { - IndexActionFailed = true; + if (!OtherConsumer) return nullptr; - } + CreatedASTConsumer = true; std::vector<std::unique_ptr<ASTConsumer>> Consumers; Consumers.push_back(std::move(OtherConsumer)); - Consumers.push_back(createIndexASTConsumer()); + Consumers.push_back(createIndexASTConsumer(CI)); return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); } @@ -191,3 +198,628 @@ void index::indexModuleFile(serialization::ModuleFile &Mod, } DataConsumer->finish(); } + +//===----------------------------------------------------------------------===// +// Index Data Recording +//===----------------------------------------------------------------------===// + +namespace { + +class IndexDataRecorder : public IndexDataConsumer { + IndexingContext *IndexCtx = nullptr; + const Preprocessor *PP = nullptr; + typedef llvm::DenseMap<FileID, std::unique_ptr<FileIndexRecord>> RecordByFileTy; + RecordByFileTy RecordByFile; + +public: + void init(IndexingContext *idxCtx, const CompilerInstance &CI) { + IndexCtx = idxCtx; + PP = &CI.getPreprocessor(); + initialize(CI.getASTContext()); + } + + RecordByFileTy::const_iterator record_begin() const { return RecordByFile.begin(); } + RecordByFileTy::const_iterator record_end() const { return RecordByFile.end(); } + bool record_empty() const { return RecordByFile.empty(); } + +private: + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) override { + // Ignore the predefines buffer. + if (FID == PP->getPredefinesFileID()) + return true; + + FileIndexRecord &Rec = getFileIndexRecord(FID); + Rec.addDeclOccurence(Roles, Offset, D, Relations); + return true; + } + + FileIndexRecord &getFileIndexRecord(FileID FID) { + auto &Entry = RecordByFile[FID]; + if (!Entry) { + Entry.reset(new FileIndexRecord(FID, IndexCtx->isSystemFile(FID))); + } + return *Entry; + } +}; + +struct IncludeLocation { + const FileEntry *Source; + const FileEntry *Target; + unsigned Line; +}; + +class IncludePPCallbacks : public PPCallbacks { + IndexingContext &IndexCtx; + RecordingOptions RecordOpts; + std::vector<IncludeLocation> &Includes; + SourceManager &SourceMgr; + +public: + IncludePPCallbacks(IndexingContext &indexCtx, RecordingOptions recordOpts, + std::vector<IncludeLocation> &IncludesForFile, + SourceManager &SourceMgr) : + IndexCtx(indexCtx), RecordOpts(recordOpts), + Includes(IncludesForFile), SourceMgr(SourceMgr) {} + +private: + void addInclude(SourceLocation From, const FileEntry *To) { + assert(To); + if (RecordOpts.RecordIncludes == RecordingOptions::IncludesRecordingKind::None) + return; + + std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedExpansionLoc(From); + switch (RecordOpts.RecordIncludes) { + case RecordingOptions::IncludesRecordingKind::None: + llvm_unreachable("should have already checked in the beginning"); + case RecordingOptions::IncludesRecordingKind::UserOnly: + if (IndexCtx.isSystemFile(LocInfo.first)) + return; // Ignore includes of system headers. + break; + case RecordingOptions::IncludesRecordingKind::All: + break; + } + auto *FE = SourceMgr.getFileEntryForID(LocInfo.first); + if (!FE) + return; + auto lineNo = SourceMgr.getLineNumber(LocInfo.first, LocInfo.second); + Includes.push_back({FE, To, lineNo}); + } + + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) override { + if (HashLoc.isFileID() && File && File->isValid()) + addInclude(HashLoc, File); + } +}; + +class IndexDependencyProvider { +public: + virtual ~IndexDependencyProvider() {} + + virtual void visitFileDependencies(const CompilerInstance &CI, + llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) = 0; + virtual void visitIncludes( + llvm::function_ref<void(const FileEntry *Source, unsigned Line, + const FileEntry *Target)> visitor) = 0; + virtual void visitModuleImports(const CompilerInstance &CI, + llvm::function_ref<void(serialization::ModuleFile &Mod, + bool isSystem)> visitor) = 0; +}; + +class SourceFilesIndexDependencyCollector : public DependencyCollector, public IndexDependencyProvider { + IndexingContext &IndexCtx; + RecordingOptions RecordOpts; + llvm::SetVector<const FileEntry *> Entries; + llvm::BitVector IsSystemByUID; + std::vector<IncludeLocation> Includes; + SourceManager *SourceMgr = nullptr; + std::string SysrootPath; + +public: + SourceFilesIndexDependencyCollector(IndexingContext &indexCtx, RecordingOptions recordOpts) + : IndexCtx(indexCtx), RecordOpts(recordOpts) {} + + virtual void attachToPreprocessor(Preprocessor &PP) override { + DependencyCollector::attachToPreprocessor(PP); + PP.addPPCallbacks(llvm::make_unique<IncludePPCallbacks>(IndexCtx, + RecordOpts, + Includes, + PP.getSourceManager())); + } + + void setSourceManager(SourceManager *SourceMgr) { + this->SourceMgr = SourceMgr; + } + void setSysrootPath(StringRef sysroot) { SysrootPath = sysroot; } + + void visitFileDependencies(const CompilerInstance &CI, + llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) override { + for (auto *FE : getEntries()) { + visitor(FE, isSystemFile(FE)); + } + } + + void visitIncludes( + llvm::function_ref<void(const FileEntry *Source, unsigned Line, + const FileEntry *Target)> visitor) override { + for (auto &Include : Includes) { + visitor(Include.Source, Include.Line, Include.Target); + } + } + + void visitModuleImports(const CompilerInstance &CI, + llvm::function_ref<void(serialization::ModuleFile &Mod, + bool isSystem)> visitor) override { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + + if (auto Reader = CI.getModuleManager()) { + Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool { + bool isSystemMod = false; + if (Mod.isModule()) { + if (auto *M = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false)) + isSystemMod = M->IsSystem; + } + if (!isSystemMod || needSystemDependencies()) + visitor(Mod, isSystemMod); + return true; // skip module dependencies. + }); + } + } + +private: + bool isSystemFile(const FileEntry *FE) { + auto UID = FE->getUID(); + return IsSystemByUID.size() > UID && IsSystemByUID[UID]; + } + + ArrayRef<const FileEntry *> getEntries() const { + return Entries.getArrayRef(); + } + + bool needSystemDependencies() override { + return RecordOpts.RecordSystemDependencies; + } + + bool sawDependency(StringRef Filename, bool FromModule, + bool IsSystem, bool IsModuleFile, bool IsMissing) override { + bool sawIt = DependencyCollector::sawDependency(Filename, FromModule, + IsSystem, IsModuleFile, + IsMissing); + if (auto *FE = SourceMgr->getFileManager().getFile(Filename)) { + if (sawIt) + Entries.insert(FE); + // Record system-ness for all files that we pass through. + if (IsSystemByUID.size() < FE->getUID()+1) + IsSystemByUID.resize(FE->getUID()+1); + IsSystemByUID[FE->getUID()] = IsSystem || isInSysroot(Filename); + } + return sawIt; + } + + bool isInSysroot(StringRef Filename) { + return !SysrootPath.empty() && Filename.startswith(SysrootPath); + } +}; + +class IndexRecordActionBase { +protected: + RecordingOptions RecordOpts; + IndexDataRecorder Recorder; + IndexingContext IndexCtx; + SourceFilesIndexDependencyCollector DepCollector; + + IndexRecordActionBase(IndexingOptions IndexOpts, RecordingOptions recordOpts) + : RecordOpts(std::move(recordOpts)), + IndexCtx(IndexOpts, Recorder), + DepCollector(IndexCtx, RecordOpts) { + } + + std::unique_ptr<IndexASTConsumer> + createIndexASTConsumer(CompilerInstance &CI) { + IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot); + Recorder.init(&IndexCtx, CI); + + Preprocessor &PP = CI.getPreprocessor(); + DepCollector.setSourceManager(&CI.getSourceManager()); + DepCollector.setSysrootPath(IndexCtx.getSysrootPath()); + DepCollector.attachToPreprocessor(PP); + + return llvm::make_unique<IndexASTConsumer>(IndexCtx); + } + + void finish(CompilerInstance &CI); +}; + +class IndexRecordAction : public ASTFrontendAction, IndexRecordActionBase { +public: + IndexRecordAction(IndexingOptions IndexOpts, RecordingOptions RecordOpts) + : IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} + +protected: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return createIndexASTConsumer(CI); + } + + void EndSourceFileAction() override { + FrontendAction::EndSourceFileAction(); + finish(getCompilerInstance()); + } +}; + +class WrappingIndexRecordAction : public WrapperFrontendAction, IndexRecordActionBase { + bool CreatedASTConsumer = false; + +public: + WrappingIndexRecordAction(std::unique_ptr<FrontendAction> WrappedAction, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts) + : WrapperFrontendAction(std::move(WrappedAction)), + IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} + +protected: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (!OtherConsumer) + return nullptr; + + CreatedASTConsumer = true; + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + Consumers.push_back(std::move(OtherConsumer)); + Consumers.push_back(createIndexASTConsumer(CI)); + return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); + } + + void EndSourceFileAction() override { + // Invoke wrapped action's method. + WrapperFrontendAction::EndSourceFileAction(); + if (CreatedASTConsumer) + finish(getCompilerInstance()); + } +}; + +} // anonymous namespace + +static std::string getClangVersion() { + // Try picking the version from an Apple Clang tag. + std::string RepositoryPath = getClangRepositoryPath(); + StringRef BuildNumber = StringRef(RepositoryPath); + size_t DashOffset = BuildNumber.find('-'); + if (BuildNumber.startswith("clang") && DashOffset != StringRef::npos) { + BuildNumber = BuildNumber.substr(DashOffset + 1); + return BuildNumber; + } + // Fallback to the generic version. + return CLANG_VERSION_STRING; +} + +static void writeUnitData(const CompilerInstance &CI, + IndexDataRecorder &Recorder, + IndexDependencyProvider &DepProvider, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + StringRef OutputFile, + const FileEntry *RootFile, + Module *UnitModule, + StringRef SysrootPath); + +void IndexRecordActionBase::finish(CompilerInstance &CI) { + // We may emit more diagnostics so do the begin/end source file invocations + // on the diagnostic client. + // FIXME: FrontendAction::EndSourceFile() should probably not call + // CI.getDiagnosticClient().EndSourceFile()' until after it has called + // 'EndSourceFileAction()', so that code executing during EndSourceFileAction() + // can emit diagnostics. If this is fixed, DiagClientBeginEndRAII can go away. + struct DiagClientBeginEndRAII { + CompilerInstance &CI; + DiagClientBeginEndRAII(CompilerInstance &CI) : CI(CI) { + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts()); + } + ~DiagClientBeginEndRAII() { + CI.getDiagnosticClient().EndSourceFile(); + } + } diagClientBeginEndRAII(CI); + + SourceManager &SM = CI.getSourceManager(); + DiagnosticsEngine &Diag = CI.getDiagnostics(); + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + StringRef DataPath = RecordOpts.DataDirPath; + + std::string Error; + if (IndexUnitWriter::initIndexDirectory(DataPath, Error)) { + unsigned DiagID = Diag.getCustomDiagID( + DiagnosticsEngine::Error, "failed creating index directory %0"); + Diag.Report(DiagID) << Error; + return; + } + + std::string OutputFile = CI.getFrontendOpts().OutputFile; + if (OutputFile.empty()) { + OutputFile = CI.getFrontendOpts().Inputs[0].getFile(); + OutputFile += ".o"; + } + + const FileEntry *RootFile = nullptr; + Module *UnitMod = nullptr; + bool isModuleGeneration = CI.getLangOpts().isCompilingModule(); + if (!isModuleGeneration && + CI.getFrontendOpts().ProgramAction != frontend::GeneratePCH) { + RootFile = SM.getFileEntryForID(SM.getMainFileID()); + } + if (isModuleGeneration) { + UnitMod = HS.lookupModule(CI.getLangOpts().CurrentModule, + /*AllowSearch=*/false); + } + + writeUnitData(CI, Recorder, DepCollector, IndexCtx.getIndexOpts(), RecordOpts, + OutputFile, RootFile, UnitMod, + IndexCtx.getSysrootPath()); +} + +/// Checks if the unit file exists for module file, if it doesn't it generates +/// index data for it. +static bool produceIndexDataForModuleFile( + serialization::ModuleFile &Mod, + const CompilerInstance &CI, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + IndexUnitWriter &ParentUnitWriter); + +static void writeUnitData(const CompilerInstance &CI, + IndexDataRecorder &Recorder, + IndexDependencyProvider &DepProvider, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + StringRef OutputFile, + const FileEntry *RootFile, + Module *UnitModule, + StringRef SysrootPath) { + + SourceManager &SM = CI.getSourceManager(); + DiagnosticsEngine &Diag = CI.getDiagnostics(); + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + StringRef DataPath = RecordOpts.DataDirPath; + bool IsSystemUnit = UnitModule ? UnitModule->IsSystem : false; + bool IsModuleUnit = UnitModule != nullptr; + bool IsDebugCompilation = CI.getCodeGenOpts().OptimizationLevel == 0; + std::string ModuleName = UnitModule ? UnitModule->getFullModuleName() : std::string(); + + auto getModuleInfo = [](writer::OpaqueModule mod, SmallVectorImpl<char> &Scratch) -> writer::ModuleInfo { + assert(mod); + writer::ModuleInfo info; + std::string fullName = static_cast<const Module*>(mod)->getFullModuleName(); + unsigned offset = Scratch.size(); + Scratch.append(fullName.begin(), fullName.end()); + info.Name = StringRef(Scratch.data()+offset, fullName.size()); + return info; + }; + + auto findModuleForHeader = [&](const FileEntry *FE) -> Module * { + if (!UnitModule) + return nullptr; + if (auto Mod = HS.findModuleForHeader(FE).getModule()) + if (Mod->isSubModuleOf(UnitModule)) + return Mod; + return nullptr; + }; + + IndexUnitWriter UnitWriter(CI.getFileManager(), + DataPath, + "clang", getClangVersion(), + OutputFile, + ModuleName, + RootFile, + IsSystemUnit, + IsModuleUnit, + IsDebugCompilation, + CI.getTargetOpts().Triple, + SysrootPath, + getModuleInfo); + + DepProvider.visitFileDependencies(CI, [&](const FileEntry *FE, bool isSystemFile) { + UnitWriter.addFileDependency(FE, isSystemFile, findModuleForHeader(FE)); + }); + DepProvider.visitIncludes([&](const FileEntry *Source, unsigned Line, const FileEntry *Target) { + UnitWriter.addInclude(Source, Line, Target); + }); + DepProvider.visitModuleImports(CI, [&](serialization::ModuleFile &Mod, bool isSystemMod) { + Module *UnitMod = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false); + UnitWriter.addASTFileDependency(Mod.File, isSystemMod, UnitMod); + if (Mod.isModule()) { + produceIndexDataForModuleFile(Mod, CI, IndexOpts, RecordOpts, UnitWriter); + } + }); + + ClangIndexRecordWriter RecordWriter(CI.getASTContext(), RecordOpts); + for (auto I = Recorder.record_begin(), E = Recorder.record_end(); I != E; ++I) { + FileID FID = I->first; + const FileIndexRecord &Rec = *I->second; + const FileEntry *FE = SM.getFileEntryForID(FID); + std::string RecordFile; + std::string Error; + + if (RecordWriter.writeRecord(FE->getName(), Rec, Error, &RecordFile)) { + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, + "failed writing record '%0': %1"); + Diag.Report(DiagID) << RecordFile << Error; + return; + } + UnitWriter.addRecordFile(RecordFile, FE, Rec.isSystem(), + findModuleForHeader(FE)); + } + + std::string Error; + if (UnitWriter.write(Error)) { + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, + "failed writing unit data: %0"); + Diag.Report(DiagID) << Error; + return; + } +} + +namespace { +class ModuleFileIndexDependencyCollector : public IndexDependencyProvider { + serialization::ModuleFile &ModFile; + RecordingOptions RecordOpts; + +public: + ModuleFileIndexDependencyCollector(serialization::ModuleFile &Mod, + RecordingOptions recordOpts) + : ModFile(Mod), RecordOpts(recordOpts) {} + + void visitFileDependencies(const CompilerInstance &CI, + llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) override { + auto Reader = CI.getModuleManager(); + Reader->visitInputFiles(ModFile, RecordOpts.RecordSystemDependencies, + /*Complain=*/false, + [&](const serialization::InputFile &IF, bool isSystem) { + auto *FE = IF.getFile(); + if (!FE) + return; + // Ignore module map files, they are not as important to track as source + // files and they may be auto-generated which would create an undesirable + // dependency on an intermediate build byproduct. + if (FE->getName().endswith("module.modulemap")) + return; + + visitor(FE, isSystem); + }); + } + + void visitIncludes( + llvm::function_ref<void(const FileEntry *Source, unsigned Line, + const FileEntry *Target)> visitor) override { + // FIXME: Module files without a preprocessing record do not have info about + // include locations. Serialize enough data to be able to retrieve such info. + } + + void visitModuleImports(const CompilerInstance &CI, + llvm::function_ref<void(serialization::ModuleFile &Mod, + bool isSystem)> visitor) override { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + for (auto *Mod : ModFile.Imports) { + bool isSystemMod = false; + if (auto *M = HS.lookupModule(Mod->ModuleName, /*AllowSearch=*/false)) + isSystemMod = M->IsSystem; + if (!isSystemMod || RecordOpts.RecordSystemDependencies) + visitor(*Mod, isSystemMod); + } + } +}; +} // anonymous namespace. + +static void indexModule(serialization::ModuleFile &Mod, + const CompilerInstance &CI, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts) { + DiagnosticsEngine &Diag = CI.getDiagnostics(); + Diag.Report(Mod.ImportLoc, diag::remark_index_producing_module_file_data) + << Mod.FileName; + + StringRef SysrootPath = CI.getHeaderSearchOpts().Sysroot; + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + Module *UnitMod = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false); + + IndexDataRecorder Recorder; + IndexingContext IndexCtx(IndexOpts, Recorder); + + IndexCtx.setASTContext(CI.getASTContext()); + IndexCtx.setSysrootPath(SysrootPath); + Recorder.init(&IndexCtx, CI); + + for (const Decl *D : CI.getModuleManager()->getModuleFileLevelDecls(Mod)) { + IndexCtx.indexTopLevelDecl(D); + } + Recorder.finish(); + + ModuleFileIndexDependencyCollector DepCollector(Mod, RecordOpts); + writeUnitData(CI, Recorder, DepCollector, IndexOpts, RecordOpts, + Mod.FileName, /*RootFile=*/nullptr, UnitMod, SysrootPath); + +} + +static bool produceIndexDataForModuleFile( + serialization::ModuleFile &Mod, + const CompilerInstance &CI, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + IndexUnitWriter &ParentUnitWriter) { + DiagnosticsEngine &Diag = CI.getDiagnostics(); + std::string Error; + // We don't do timestamp check with the PCM file, on purpose. The PCM may get + // touched for various reasons which would cause unnecessary work to emit + // index data. User modules normally will get rebuilt and their index data + // re-emitted, and system modules are generally stable (and they can also can + // get rebuilt along with their index data). + auto IsUptodateOpt = ParentUnitWriter.isUnitUpToDateForOutputFile(Mod.FileName, None, Error); + if (!IsUptodateOpt.hasValue()) { + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, + "failed file status check: %0"); + Diag.Report(DiagID) << Error; + return false; + } + if (*IsUptodateOpt) + return false; + + indexModule(Mod, CI, IndexOpts, RecordOpts); + return true; +} + +static std::unique_ptr<FrontendAction> +createIndexDataRecordingAction(IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + std::unique_ptr<FrontendAction> WrappedAction) { + if (WrappedAction) + return llvm::make_unique<WrappingIndexRecordAction>(std::move(WrappedAction), + std::move(IndexOpts), + std::move(RecordOpts)); + return llvm::make_unique<IndexRecordAction>(std::move(IndexOpts), + std::move(RecordOpts)); +} + +static std::pair<IndexingOptions, RecordingOptions> +getIndexOptionsFromFrontendOptions(const FrontendOptions &FEOpts) { + index::IndexingOptions IndexOpts; + index::RecordingOptions RecordOpts; + RecordOpts.DataDirPath = FEOpts.IndexStorePath; + if (FEOpts.IndexIgnoreSystemSymbols) { + IndexOpts.SystemSymbolFilter = + index::IndexingOptions::SystemSymbolFilterKind::None; + } + RecordOpts.RecordSymbolCodeGenName = FEOpts.IndexRecordCodegenName; + return { IndexOpts, RecordOpts }; +} + +std::unique_ptr<FrontendAction> +index::createIndexDataRecordingAction(const FrontendOptions &FEOpts, + std::unique_ptr<FrontendAction> WrappedAction) { + index::IndexingOptions IndexOpts; + index::RecordingOptions RecordOpts; + std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(FEOpts); + return ::createIndexDataRecordingAction(IndexOpts, RecordOpts, + std::move(WrappedAction)); +} + +bool index::emitIndexDataForModuleFile(const Module *Mod, + const CompilerInstance &CI, + IndexUnitWriter &ParentUnitWriter) { + index::IndexingOptions IndexOpts; + index::RecordingOptions RecordOpts; + std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(CI.getFrontendOpts()); + + auto astReader = CI.getModuleManager(); + serialization::ModuleFile *ModFile = astReader->getModuleManager().lookup(Mod->getASTFile()); + assert(ModFile && "no module file loaded for module ?"); + return produceIndexDataForModuleFile(*ModFile, CI, IndexOpts, RecordOpts, ParentUnitWriter); +} diff --git a/clang/lib/Index/IndexingContext.cpp b/clang/lib/Index/IndexingContext.cpp index 754bc84ff4b2a..d2d2f95a46853 100644 --- a/clang/lib/Index/IndexingContext.cpp +++ b/clang/lib/Index/IndexingContext.cpp @@ -93,12 +93,7 @@ bool IndexingContext::importedModule(const ImportDecl *ImportD) { if (FID.isInvalid()) return true; - bool Invalid = false; - const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); - if (Invalid || !SEntry.isFile()) - return true; - - if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + if (isSystemFile(FID)) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; @@ -161,6 +156,56 @@ bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { return true; } +void IndexingContext::setSysrootPath(StringRef path) { + // Ignore sysroot path if it points to root, otherwise every header will be + // treated as system one. + if (path == "/") + path = StringRef(); + SysrootPath = path; +} + +bool IndexingContext::isSystemFile(FileID FID) { + if (LastFileCheck.first == FID) + return LastFileCheck.second; + + auto result = [&](bool res) -> bool { + LastFileCheck = { FID, res }; + return res; + }; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = + Ctx->getSourceManager().getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return result(false); + + const SrcMgr::FileInfo &FI = SEntry.getFile(); + if (FI.getFileCharacteristic() != SrcMgr::C_User) + return result(true); + + auto *CC = FI.getContentCache(); + if (!CC) + return result(false); + auto *FE = CC->OrigEntry; + if (!FE) + return result(false); + + if (SysrootPath.empty()) + return result(false); + + // Check if directory is in sysroot so that we can consider system headers + // even the headers found via a user framework search path, pointing inside + // sysroot. + auto dirEntry = FE->getDir(); + auto pair = DirEntries.insert(std::make_pair(dirEntry, false)); + bool &isSystemDir = pair.first->second; + bool wasInserted = pair.second; + if (wasInserted) { + isSystemDir = StringRef(dirEntry->getName()).startswith(SysrootPath); + } + return result(isSystemDir); +} + static const CXXRecordDecl * getDeclContextForTemplateInstationPattern(const Decl *D) { if (const auto *CTSD = @@ -313,7 +358,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, const Expr *OrigE, const Decl *OrigD, const DeclContext *ContainerDC) { - if (D->isImplicit() && !isa<ObjCMethodDecl>(D)) + if (D->isImplicit() && !(isa<ObjCMethodDecl>(D) || isa<ObjCIvarDecl>(D))) return true; if (!isa<NamedDecl>(D) || (cast<NamedDecl>(D)->getDeclName().isEmpty() && @@ -331,12 +376,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, if (FID.isInvalid()) return true; - bool Invalid = false; - const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); - if (Invalid || !SEntry.isFile()) - return true; - - if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + if (isSystemFile(FID)) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; diff --git a/clang/lib/Index/IndexingContext.h b/clang/lib/Index/IndexingContext.h index 566651c83a75f..70e72e1d28cc7 100644 --- a/clang/lib/Index/IndexingContext.h +++ b/clang/lib/Index/IndexingContext.h @@ -11,9 +11,11 @@ #define LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H #include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Index/IndexSymbol.h" #include "clang/Index/IndexingAction.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" namespace clang { class ASTContext; @@ -29,7 +31,7 @@ namespace clang { class Stmt; class Expr; class TypeLoc; - class SourceLocation; + class DirectoryEntry; namespace index { class IndexDataConsumer; @@ -38,6 +40,13 @@ class IndexingContext { IndexingOptions IndexOpts; IndexDataConsumer &DataConsumer; ASTContext *Ctx = nullptr; + std::string SysrootPath; + + // Records whether a directory entry is system or not. + llvm::DenseMap<const DirectoryEntry *, bool> DirEntries; + // Keeps track of the last check for whether a FileID is system or + // not. This is used to speed up isSystemFile() call. + std::pair<FileID, bool> LastFileCheck; public: IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer) @@ -47,6 +56,10 @@ class IndexingContext { IndexDataConsumer &getDataConsumer() { return DataConsumer; } void setASTContext(ASTContext &ctx) { Ctx = &ctx; } + void setSysrootPath(StringRef path); + StringRef getSysrootPath() const { return SysrootPath; } + + bool isSystemFile(FileID FID); bool shouldIndex(const Decl *D); diff --git a/clang/test/Index/Store/Inputs/head.h b/clang/test/Index/Store/Inputs/head.h new file mode 100644 index 0000000000000..6ac174dd19e6c --- /dev/null +++ b/clang/test/Index/Store/Inputs/head.h @@ -0,0 +1,3 @@ + +extern void test1_func(void); +extern void test2_func(void); diff --git a/clang/test/Index/Store/Inputs/json.c.json b/clang/test/Index/Store/Inputs/json.c.json new file mode 100644 index 0000000000000..498022d230855 --- /dev/null +++ b/clang/test/Index/Store/Inputs/json.c.json @@ -0,0 +1,151 @@ +{ + "files": [ + "/test1.o", + "/Inputs/test1.c", + "/Inputs/head.h", + "/test2.o", + "/Inputs/test2.c", + "/test3.o", + "/Inputs/test3.cpp" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test1_func", + "name": "test1_func", + "codegen": "_test1_func", + "roles": "Decl,Def" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test2_func", + "name": "test2_func", + "codegen": "_test2_func", + "roles": "Decl,Def" + }, + { + "kind": "class", + "lang": "C++", + "usr": "c:@S@Base", + "name": "Base", + "roles": "Def,Ref,RelBase,RelCont" + }, + { + "kind": "class", + "lang": "C++", + "usr": "c:@S@Sub", + "name": "Sub", + "roles": "Def", + "rel-roles": "RelBase,RelCont" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 3, + "col": 6, + "roles": "Def" + } + ] + }, + { + "occurrences": [ + { + "symbol": 0, + "line": 2, + "col": 13, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 13, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 1, + "line": 3, + "col": 6, + "roles": "Def" + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 1, + "col": 7, + "roles": "Def" + }, + { + "symbol": 3, + "line": 2, + "col": 7, + "roles": "Def" + }, + { + "symbol": 2, + "line": 2, + "col": 20, + "roles": "Ref,RelBase,RelCont", + "relations": [ + { + "symbol": 3, + "rel-roles": "RelBase,RelCont" + } + ] + + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "sources": [ + { + "file": 1, + "records": [0] + }, + { + "file": 2, + "records": [1] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 3, + "sources": [ + { + "file": 4, + "records": [2] + }, + { + "file": 2, + "records": [1] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 5, + "sources": [ + { + "file": 6, + "records": [3] + } + ] + } + ] +} diff --git a/clang/test/Index/Store/Inputs/module/ModDep.h b/clang/test/Index/Store/Inputs/module/ModDep.h new file mode 100644 index 0000000000000..e96ef5440f43a --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModDep.h @@ -0,0 +1,3 @@ +#include "ModTop.h" + +void ModDep_func(ModTopStruct s); diff --git a/clang/test/Index/Store/Inputs/module/ModSystem.h b/clang/test/Index/Store/Inputs/module/ModSystem.h new file mode 100644 index 0000000000000..0419f97804b5d --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModSystem.h @@ -0,0 +1,4 @@ + +typedef struct {} ModSystemStruct; + +void ModSystem_func(void); diff --git a/clang/test/Index/Store/Inputs/module/ModTop.h b/clang/test/Index/Store/Inputs/module/ModTop.h new file mode 100644 index 0000000000000..60c56868bbffe --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModTop.h @@ -0,0 +1,4 @@ + +typedef struct {} ModTopStruct; + +void ModTop_func(void); diff --git a/clang/test/Index/Store/Inputs/module/ModTopSub1.h b/clang/test/Index/Store/Inputs/module/ModTopSub1.h new file mode 100644 index 0000000000000..e1e3cf3ec54bc --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModTopSub1.h @@ -0,0 +1 @@ +void ModTopSub1_func(void); diff --git a/clang/test/Index/Store/Inputs/module/ModTopSub2.h b/clang/test/Index/Store/Inputs/module/ModTopSub2.h new file mode 100644 index 0000000000000..39d37f12f0e1b --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModTopSub2.h @@ -0,0 +1 @@ +// This header has no symbols, intended to show up as file dependency. diff --git a/clang/test/Index/Store/Inputs/module/module.modulemap b/clang/test/Index/Store/Inputs/module/module.modulemap new file mode 100644 index 0000000000000..ada2f38ef76e9 --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/module.modulemap @@ -0,0 +1,12 @@ +module ModTop { + header "ModTop.h" + export * + module Sub1 { + header "ModTopSub1.h" + } + module Sub2 { + header "ModTopSub2.h" + } +} +module ModDep { header "ModDep.h" export * } +module ModSystem [system] { header "ModSystem.h" export * } diff --git a/clang/test/Index/Store/Inputs/overlay.yaml b/clang/test/Index/Store/Inputs/overlay.yaml new file mode 100644 index 0000000000000..7b55b30f4bedd --- /dev/null +++ b/clang/test/Index/Store/Inputs/overlay.yaml @@ -0,0 +1,6 @@ +{ + 'version': 0, + 'roots': [{ 'type': 'file', 'name': 'OUT_DIR/using-overlay.h', + 'external-contents': 'INPUT_DIR/using-overlay.h' + }] +} diff --git a/clang/test/Index/Store/Inputs/print-unit.h b/clang/test/Index/Store/Inputs/print-unit.h new file mode 100644 index 0000000000000..62039c47219b5 --- /dev/null +++ b/clang/test/Index/Store/Inputs/print-unit.h @@ -0,0 +1,2 @@ +#include "head.h" +#include "using-overlay.h" diff --git a/clang/test/Index/Store/Inputs/sys/another.h b/clang/test/Index/Store/Inputs/sys/another.h new file mode 100644 index 0000000000000..555b99b0ce36f --- /dev/null +++ b/clang/test/Index/Store/Inputs/sys/another.h @@ -0,0 +1,2 @@ + +extern void sys_another_func(void); diff --git a/clang/test/Index/Store/Inputs/sys/syshead.h b/clang/test/Index/Store/Inputs/sys/syshead.h new file mode 100644 index 0000000000000..8941fd6997af7 --- /dev/null +++ b/clang/test/Index/Store/Inputs/sys/syshead.h @@ -0,0 +1,4 @@ + +#include "another.h" + +extern void sys_test1_func(void); diff --git a/clang/test/Index/Store/Inputs/test1.c b/clang/test/Index/Store/Inputs/test1.c new file mode 100644 index 0000000000000..505711d181d34 --- /dev/null +++ b/clang/test/Index/Store/Inputs/test1.c @@ -0,0 +1,3 @@ +#include "head.h" + +void test1_func(void) {} diff --git a/clang/test/Index/Store/Inputs/test2.c b/clang/test/Index/Store/Inputs/test2.c new file mode 100644 index 0000000000000..333b8aef67d52 --- /dev/null +++ b/clang/test/Index/Store/Inputs/test2.c @@ -0,0 +1,3 @@ +#include "head.h" + +void test2_func(void) {} diff --git a/clang/test/Index/Store/Inputs/test3.cpp b/clang/test/Index/Store/Inputs/test3.cpp new file mode 100644 index 0000000000000..06334a1706b41 --- /dev/null +++ b/clang/test/Index/Store/Inputs/test3.cpp @@ -0,0 +1,2 @@ +class Base {}; +class Sub : public Base {}; diff --git a/clang/test/Index/Store/Inputs/using-overlay.h b/clang/test/Index/Store/Inputs/using-overlay.h new file mode 100644 index 0000000000000..bb361c3d58285 --- /dev/null +++ b/clang/test/Index/Store/Inputs/using-overlay.h @@ -0,0 +1 @@ +void using_overlay(void); diff --git a/clang/test/Index/Store/assembly-invocation.c b/clang/test/Index/Store/assembly-invocation.c new file mode 100644 index 0000000000000..ab9c197a5391b --- /dev/null +++ b/clang/test/Index/Store/assembly-invocation.c @@ -0,0 +1,3 @@ +// Make sure it doesn't crash. +// RUN: %clang -target x86_64-apple-macosx10.7 -S %s -o %t.s +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -c %t.s -o %t.o diff --git a/clang/test/Index/Store/external-source-symbol-hash.m b/clang/test/Index/Store/external-source-symbol-hash.m new file mode 100644 index 0000000000000..243616d94854f --- /dev/null +++ b/clang/test/Index/Store/external-source-symbol-hash.m @@ -0,0 +1,47 @@ +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx -D USE_EXTERNAL +// RUN: c-index-test core -print-record %t.idx | FileCheck %s +// RUN: %clang_cc1 %s -index-store-path %t.idx +// RUN: find %t.idx/*/records -name "external-source-symbol-hash*" | count 2 + +#ifdef USE_EXTERNAL +# define EXT_DECL(mod_name) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name))) +#else +# define EXT_DECL(mod_name) +#endif + +#define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type + +// Forward declarations should pick up the attribute from later decls +@protocol P1; +// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref | rel: 0 +@class I2; +// CHECK: [[@LINE-1]]:8 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref | rel: 0 +enum E3: int; +// CHECK: [[@LINE-1]]:6 | enum/Swift | c:@M@third_module@E@E3 | Ref | rel: 0 + +void test(id<P1> first, I2 *second, enum E3 third) {} +// CHECK: [[@LINE-1]]:14 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref,RelCont | rel: 1 +// CHECK: [[@LINE-2]]:25 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref,RelCont | rel: 1 +// CHECK: [[@LINE-3]]:42 | enum/Swift | c:@M@third_module@E@E3 | Ref,RelCont | rel: 1 + +EXT_DECL("some_module") +@protocol P1 +// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Decl | rel: 0 +-(void)method; +// CHECK: [[@LINE-1]]:8 | instance-method/Swift | c:@M@some_module@objc(pl)P1(im)method | Decl,Dyn,RelChild | rel: 1 +@end + +EXT_DECL("other_module") +@interface I2 +// CHECK: [[@LINE-1]]:12 | class/Swift | c:@M@other_module@objc(cs)I2 | Decl | rel: 0 +-(void)method; +// CHECK: [[@LINE-1]]:8 | instance-method/Swift | c:@M@other_module@objc(cs)I2(im)method | Decl,Dyn,RelChild | rel: 1 +@end + + +typedef NS_ENUM(E3, int) { +// CHECK: [[@LINE-1]]:17 | enum/Swift | c:@M@third_module@E@E3 | Def | rel: 0 + firstCase = 1, + // CHECK: [[@LINE-1]]:3 | enumerator/Swift | c:@M@third_module@E@E3@firstCase | Def,RelChild | rel: 1 +} EXT_DECL("third_module"); diff --git a/clang/test/Index/Store/handle-prebuilt-module.m b/clang/test/Index/Store/handle-prebuilt-module.m new file mode 100644 index 0000000000000..f6a0c42a8e8be --- /dev/null +++ b/clang/test/Index/Store/handle-prebuilt-module.m @@ -0,0 +1,25 @@ +// XFAIL: linux + +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx1 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err1 +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err2 +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err3 +// RUN: FileCheck -input-file=%t.err1 -check-prefix=CREATING_MODULES %s -allow-empty +// RUN: FileCheck -input-file=%t.err2 -check-prefix=CREATING_INDEX_DATA_FROM_MODULE_FILES %s +// RUN: FileCheck -input-file=%t.err3 -check-prefix=EXISTING_INDEX_DATA_FROM_MODULE_FILES %s -allow-empty +// RUN: c-index-test core -print-unit %t/idx1 > %t/all-units1.txt +// RUN: c-index-test core -print-unit %t/idx2 > %t/all-units2.txt +// RUN: c-index-test core -print-record %t/idx1 > %t/all-records1.txt +// RUN: c-index-test core -print-record %t/idx2 > %t/all-records2.txt +// RUN: diff -u %t/all-units1.txt %t/all-units2.txt +// RUN: diff -u %t/all-records1.txt %t/all-records2.txt + +@import ModDep; + +// CREATING_MODULES-NOT: remark: + +// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModDep{{.*}}.pcm +// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModTop{{.*}}.pcm + +// EXISTING_INDEX_DATA_FROM_MODULE_FILES-NOT: remark: diff --git a/clang/test/Index/Store/json-with-module.m b/clang/test/Index/Store/json-with-module.m new file mode 100644 index 0000000000000..1ef69697749a5 --- /dev/null +++ b/clang/test/Index/Store/json-with-module.m @@ -0,0 +1,9 @@ +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json +// RUN: diff -u %s.json %t.final.json + +// XFAIL: linux + +@import ModDep; diff --git a/clang/test/Index/Store/json-with-module.m.json b/clang/test/Index/Store/json-with-module.m.json new file mode 100644 index 0000000000000..bb020cc2ccd2f --- /dev/null +++ b/clang/test/Index/Store/json-with-module.m.json @@ -0,0 +1,151 @@ +{ + "files": [ + "/json-with-module.m.tmp.mcp/ModDep.pcm", + "/json-with-module.m.tmp.mcp/ModTop.pcm", + "/Inputs/module/ModDep.h", + "/Inputs/module/ModTop.h", + "/Inputs/module/ModTopSub1.h", + "/Inputs/module/ModTopSub2.h", + "/json-with-module.m.tmp.o", + "/json-with-module.m", + "/Inputs/module/module.modulemap" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModDep_func", + "name": "ModDep_func", + "roles": "Decl", + "rel-roles": "RelCont" + }, + { + "kind": "type-alias", + "lang": "C", + "usr": "c:ModTop.h@T@ModTopStruct", + "name": "ModTopStruct", + "roles": "Def,Ref,RelCont" + }, + { + "kind": "struct", + "lang": "C", + "usr": "c:@SA@ModTopStruct", + "name": "", + "roles": "Def" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModTop_func", + "name": "ModTop_func", + "roles": "Decl" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModTopSub1_func", + "name": "ModTopSub1_func", + "roles": "Decl" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 3, + "col": 6, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 18, + "roles": "Ref,RelCont", + "relations": [ + { + "symbol": 0, + "rel-roles": "RelCont" + } + ] + + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 2, + "col": 9, + "roles": "Def" + }, + { + "symbol": 1, + "line": 2, + "col": 19, + "roles": "Def" + }, + { + "symbol": 3, + "line": 4, + "col": 6, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 4, + "line": 1, + "col": 6, + "roles": "Decl" + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "unit-dependencies": [1], + "sources": [ + { + "file": 2, + "records": [0] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 1, + "sources": [ + { + "file": 3, + "records": [1] + }, + { + "file": 4, + "records": [2] + }, + { + "file": 5 + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 6, + "unit-dependencies": [0], + "sources": [ + { + "file": 7 + }, + { + "file": 8 + } + ] + } + ] +} diff --git a/clang/test/Index/Store/json-with-pch.c b/clang/test/Index/Store/json-with-pch.c new file mode 100644 index 0000000000000..9ffe80f02c340 --- /dev/null +++ b/clang/test/Index/Store/json-with-pch.c @@ -0,0 +1,10 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json +// RUN: diff -u %s.json %t.final.json +// XFAIL: linux +int main() { + test1_func(); +} diff --git a/clang/test/Index/Store/json-with-pch.c.json b/clang/test/Index/Store/json-with-pch.c.json new file mode 100644 index 0000000000000..605f33efd9573 --- /dev/null +++ b/clang/test/Index/Store/json-with-pch.c.json @@ -0,0 +1,96 @@ +{ + "files": [ + "/json-with-pch.c.tmp.h.pch", + "/Inputs/head.h", + "/json-with-pch.c.tmp.o", + "/json-with-pch.c" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test1_func", + "name": "test1_func", + "roles": "Decl,Ref,Call,RelCall,RelCont" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test2_func", + "name": "test2_func", + "roles": "Decl" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@main", + "name": "main", + "roles": "Def", + "rel-roles": "RelCall,RelCont" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 2, + "col": 13, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 13, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 8, + "col": 5, + "roles": "Def" + }, + { + "symbol": 0, + "line": 9, + "col": 3, + "roles": "Ref,Call,RelCall,RelCont", + "relations": [ + { + "symbol": 2, + "rel-roles": "RelCall,RelCont" + } + ] + + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "sources": [ + { + "file": 1, + "records": [0] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 2, + "unit-dependencies": [0], + "sources": [ + { + "file": 3, + "records": [1] + } + ] + } + ] +} diff --git a/clang/test/Index/Store/json.c b/clang/test/Index/Store/json.c new file mode 100644 index 0000000000000..c4ea965d36b1c --- /dev/null +++ b/clang/test/Index/Store/json.c @@ -0,0 +1,10 @@ +// RUN: rm -rf %t.idx +// RUN: mkdir -p %t.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test1.c -o %t.o/test1.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test2.c -o %t.o/test2.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test3.cpp -o %t.o/test3.o +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%t.o::g" %t.json > %t.final.json +// RUN: diff -u %S/Inputs/json.c.json %t.final.json + +// XFAIL: linux diff --git a/clang/test/Index/Store/print-record.mm b/clang/test/Index/Store/print-record.mm new file mode 100644 index 0000000000000..ce24983503a6d --- /dev/null +++ b/clang/test/Index/Store/print-record.mm @@ -0,0 +1,28 @@ +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx +// RUN: c-index-test core -print-record %t.idx | FileCheck %s + +// XFAIL: linux + +@class MyCls; + +@interface MyCls +@end + +// CHECK: [[@LINE+2]]:6 | function/C | c:@F@foo#*$objc(cs)MyCls# | Decl | rel: 0 +// CHECK: [[@LINE+1]]:10 | class/ObjC | c:objc(cs)MyCls | Ref,RelCont | rel: 1 +void foo(MyCls *p); + + +// RANGE-NOT: before_range +void before_range(); + +// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range1# | Decl +void in_range1(); +// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range2# | Decl +void in_range2(); + +// RANGE-NOT: after_range +void after_range(); + +// RUN: c-index-test core -print-record %t.idx -filepath %s:21:23 | FileCheck -check-prefix=RANGE %s diff --git a/clang/test/Index/Store/print-unit.c b/clang/test/Index/Store/print-unit.c new file mode 100644 index 0000000000000..19254a1665d4e --- /dev/null +++ b/clang/test/Index/Store/print-unit.c @@ -0,0 +1,39 @@ +// XFAIL: linux + +#include "print-unit.h" +#include "syshead.h" + +void foo(int i); + +// RUN: rm -rf %t +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx %s -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx | FileCheck %s +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt1 %s -triple x86_64-apple-macosx10.8 -O2 +// RUN: c-index-test core -print-unit %t/idx_opt1 | FileCheck %s -check-prefix=OPT +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt2 %s -triple x86_64-apple-macosx10.8 -Os +// RUN: c-index-test core -print-unit %t/idx_opt2 | FileCheck %s -check-prefix=OPT + +// CHECK: print-unit.c.o +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}/print-unit.c +// CHECK: out-file: {{.*}}/print-unit.c.o +// CHECK: target: x86_64-apple-macosx10.8 +// CHECK: is-debug: 1 +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}/print-unit.c | print-unit.c- +// CHECK: Record | user | {{.*}}/Inputs/head.h | head.h- +// CHECK: Record | user | {{.*}}/Inputs/using-overlay.h | using-overlay.h- +// CHECK: Record | system | {{.*}}/Inputs/sys/syshead.h | syshead.h- +// CHECK: Record | system | {{.*}}/Inputs/sys/another.h | another.h- +// CHECK: File | user | {{.*}}/Inputs/print-unit.h | | {{[0-9]*$}} +// CHECK: DEPEND END (6) +// CHECK: INCLUDE START +// CHECK: {{.*}}/print-unit.c:3 | {{.*}}/Inputs/print-unit.h +// CHECK: {{.*}}/print-unit.c:4 | {{.*}}/Inputs/sys/syshead.h +// CHECK: {{.*}}/Inputs/print-unit.h:1 | {{.*}}/Inputs/head.h +// CHECK: {{.*}}/Inputs/print-unit.h:2 | {{.*}}/Inputs/using-overlay.h +// CHECK: INCLUDE END (4) + +// OPT: is-debug: 0 diff --git a/clang/test/Index/Store/print-units-with-modules.m b/clang/test/Index/Store/print-units-with-modules.m new file mode 100644 index 0000000000000..cedfe2ceb41e5 --- /dev/null +++ b/clang/test/Index/Store/print-units-with-modules.m @@ -0,0 +1,59 @@ +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +// XFAIL: linux + +@import ModDep; +@import ModSystem; + +// CHECK: ModDep.pcm +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: is-module: 1 +// CHECK: module-name: ModDep +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/ModDep.pcm +// CHECK: DEPEND START +// CHECK: Unit | user | ModTop | {{.*}}/ModTop.pcm | ModTop.pcm +// CHECK: Record | user | ModDep | {{.*}}/Inputs/module/ModDep.h | ModDep.h +// CHECK: DEPEND END (2) + +// CHECK: ModSystem.pcm +// CHECK: is-system: 1 +// CHECK: is-module: 1 +// CHECK: module-name: ModSystem +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/ModSystem.pcm +// CHECK: DEPEND START +// CHECK: Record | system | ModSystem | {{.*}}/Inputs/module/ModSystem.h | ModSystem.h +// CHECK: DEPEND END (1) + +// CHECK: ModTop.pcm +// CHECK: is-system: 0 +// CHECK: is-module: 1 +// CHECK: module-name: ModTop +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/ModTop.pcm +// CHECK: DEPEND START +// CHECK: Record | user | ModTop | {{.*}}/Inputs/module/ModTop.h | ModTop.h +// CHECK: Record | user | ModTop.Sub1 | {{.*}}/Inputs/module/ModTopSub1.h | ModTopSub1.h +// CHECK: File | user | ModTop.Sub2 | {{.*}}/Inputs/module/ModTopSub2.h | | {{[0-9]*$}} +// CHECK: DEPEND END (3) + +// CHECK: print-units-with-modules.m.tmp.o +// CHECK: is-system: 0 +// CHECK: is-module: 0 +// CHECK: module-name: <none> +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}/print-units-with-modules.m +// CHECK: out-file: {{.*}}/print-units-with-modules.m.tmp.o +// CHECK: DEPEND START +// CHECK: Unit | user | ModDep | {{.*}}/ModDep.pcm | ModDep.pcm +// CHECK: Unit | system | ModSystem | {{.*}}/ModSystem.pcm | ModSystem.pcm +// CHECK: File | user | {{.*}}/print-units-with-modules.m | | {{[0-9]*$}} +// CHECK: File | user | {{.*}}/Inputs/module/module.modulemap | | {{[0-9]*$}} +// CHECK: DEPEND END (4) diff --git a/clang/test/Index/Store/print-units-with-pch.c b/clang/test/Index/Store/print-units-with-pch.c new file mode 100644 index 0000000000000..1e533a2eab7b1 --- /dev/null +++ b/clang/test/Index/Store/print-units-with-pch.c @@ -0,0 +1,29 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +// XFAIL: linux + +int main() { + test1_func(); +} + +// CHECK: print-units-with-pch.c.tmp.h.pch +// CHECK: is-system: 0 +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}/print-units-with-pch.c.tmp.h.pch +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}/Inputs/head.h | head.h +// CHECK: DEPEND END (1) + +// CHECK: print-units-with-pch.c.tmp.o +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}/print-units-with-pch.c +// CHECK: out-file: {{.*}}/print-units-with-pch.c.tmp.o +// CHECK: DEPEND START +// CHECK: Unit | user | {{.*}}/print-units-with-pch.c.tmp.h.pch | print-units-with-pch.c.tmp.h.pch +// CHECK: Record | user | {{.*}}/print-units-with-pch.c | print-units-with-pch.c +// CHECK: DEPEND END (2) diff --git a/clang/test/Index/Store/record-hash-crash.cpp b/clang/test/Index/Store/record-hash-crash.cpp new file mode 100644 index 0000000000000..8239901669c04 --- /dev/null +++ b/clang/test/Index/Store/record-hash-crash.cpp @@ -0,0 +1,12 @@ +// Makes sure it doesn't crash. + +// XFAIL: linux + +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -index-store-path %t/idx -std=c++14 +// RUN: c-index-test core -print-record %t/idx | FileCheck %s + +namespace crash1 { +// CHECK: [[@LINE+1]]:6 | function/C +auto getit() { return []() {}; } +} diff --git a/clang/test/Index/Store/record-hash.cpp b/clang/test/Index/Store/record-hash.cpp new file mode 100644 index 0000000000000..21a4dc4d974ad --- /dev/null +++ b/clang/test/Index/Store/record-hash.cpp @@ -0,0 +1,12 @@ +// XFAIL: linux + +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=long +// RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=char +// RUN: find %t/idx/*/records -name "record-hash*" | count 2 + +template<typename T> +class TC {}; + +// This should result in different records, due to the different template parameter type. +void some_func(TC<THE_TYPE>); diff --git a/clang/test/Index/Store/relative-out-path.c b/clang/test/Index/Store/relative-out-path.c new file mode 100644 index 0000000000000..1d47ea039c046 --- /dev/null +++ b/clang/test/Index/Store/relative-out-path.c @@ -0,0 +1,19 @@ +// Needs 'find'. +// REQUIRES: shell + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang %s -index-store-path %t/idx1 -c -o %t/outfile.o +// RUN: cd %t +// RUN: %clang %s -index-store-path %t/idx2 -c -o outfile.o +// RUN: cd .. +// RUN: %clang %s -index-store-path %t/idx3 -fsyntax-only -o outfile.o -working-directory=%t +// RUN: diff -r -u %t/idx2 %t/idx3 + +// RUN: find %t/idx1 -name '*outfile.o*' > %t/hashes.txt +// RUN: find %t/idx3 -name '*outfile.o*' >> %t/hashes.txt +// RUN: FileCheck %s --input-file=%t/hashes.txt +// CHECK: outfile.o[[OUT_HASH:.*$]] +// CHECK-NEXT: outfile.o[[OUT_HASH]] + +void foo(); diff --git a/clang/test/Index/Store/syntax-only.c b/clang/test/Index/Store/syntax-only.c new file mode 100644 index 0000000000000..53d22bc142464 --- /dev/null +++ b/clang/test/Index/Store/syntax-only.c @@ -0,0 +1,11 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -fsyntax-only %s -index-store-path %t.idx -o %T/syntax-only.c.myoutfile +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s -check-prefix=CHECK-UNIT +// RUN: c-index-test core -print-record %t.idx | FileCheck %s -check-prefix=CHECK-RECORD + +// XFAIL: linux + +// CHECK-UNIT: out-file: {{.*}}/syntax-only.c.myoutfile +// CHECK-RECORD: function/C | foo | c:@F@foo + +void foo(); diff --git a/clang/test/Index/Store/unit-with-vfs.c b/clang/test/Index/Store/unit-with-vfs.c new file mode 100644 index 0000000000000..cbed6261246e0 --- /dev/null +++ b/clang/test/Index/Store/unit-with-vfs.c @@ -0,0 +1,12 @@ +// RUN: sed -e "s:INPUT_DIR:%S/Inputs:g" -e "s:OUT_DIR:%t:g" %S/Inputs/overlay.yaml > %t.yaml +// REQUIRES: shell + +#include "using-overlay.h" + +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx -I %t -ivfsoverlay %t.yaml +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +// XFAIL: linux + +// CHECK: Record | user | {{.*}}test/Index/Store/Inputs/using-overlay.h diff --git a/clang/test/Index/Store/unit-workdir-prefix.c b/clang/test/Index/Store/unit-workdir-prefix.c new file mode 100644 index 0000000000000..e7a3a7102f33b --- /dev/null +++ b/clang/test/Index/Store/unit-workdir-prefix.c @@ -0,0 +1,30 @@ +// XFAIL: linux + +#include "header.h" + +void foo(void) { + bar(); +} + +// RUN: rm -rf %t +// RUN: mkdir -p %t/Directory +// RUN: mkdir -p %t/Directory.surprise +// RUN: mkdir -p %t/sdk +// RUN: mkdir -p %t/sdk_other +// RUN: echo "void bar(void);" > %t/sdk_other/header.h +// RUN: cp %s %t/Directory.surprise/main.c +// +// RUN: %clang_cc1 -isystem %t/sdk_other -isysroot %t/sdk -index-store-path %t/idx %t/Directory.surprise/main.c -triple x86_64-apple-macosx10.8 -working-directory %t/Directory +// RUN: c-index-test core -print-unit %t/idx | FileCheck %s + +// CHECK: main.c.o +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}Directory.surprise{{/|\\}}main.c +// CHECK: out-file: {{.*}}Directory.surprise{{/|\\}}main.c.o +// CHECK: target: x86_64-apple-macosx10.8 +// CHECK: is-debug: 1 +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}Directory.surprise{{/|\\}}main.c | main.c- +// CHECK: Record | system | {{.*}}sdk_other{{/|\\}}header.h | header.h- diff --git a/clang/test/Index/Store/using-libstdcpp-arc.mm b/clang/test/Index/Store/using-libstdcpp-arc.mm new file mode 100644 index 0000000000000..9738c869838e5 --- /dev/null +++ b/clang/test/Index/Store/using-libstdcpp-arc.mm @@ -0,0 +1,10 @@ +// Test to make sure we don't crash, rdar://30816887. + +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx -fobjc-arc -fobjc-arc-cxxlib=libstdc++ +// RUN: c-index-test core -print-record %t.idx | FileCheck %s + +// XFAIL: linux + +// CHECK: [[@LINE+1]]:6 | function/C +void test1(void); diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt index ec3307b1415ca..b4d5f759ec3ed 100644 --- a/clang/tools/CMakeLists.txt +++ b/clang/tools/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_subdirectory(clang-import-test) add_clang_subdirectory(clang-offload-bundler) add_clang_subdirectory(c-index-test) +add_clang_subdirectory(IndexStore) if(CLANG_ENABLE_ARCMT) add_clang_subdirectory(arcmt-test) diff --git a/clang/tools/IndexStore/CMakeLists.txt b/clang/tools/IndexStore/CMakeLists.txt new file mode 100644 index 0000000000000..8ad6499117616 --- /dev/null +++ b/clang/tools/IndexStore/CMakeLists.txt @@ -0,0 +1,94 @@ +include(CheckIncludeFiles) + +set(SOURCES + IndexStore.cpp + + ADDITIONAL_HEADERS + ../../include/indexstore/indexstore.h + ../../include/indexstore/IndexStoreCXX.h + ) + +set(LIBS + clangDirectoryWatcher + clangIndex +) + +set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/IndexStore.exports) + +set(ENABLE_SHARED SHARED) + +if(WIN32) + set(output_name "libIndexStore") +else() + set(output_name "IndexStore") +endif() + +# FIXME: needs to be ported to non-Apple platforms. +if(APPLE) + +add_clang_library(IndexStore ${ENABLE_SHARED} ${ENABLE_STATIC} + OUTPUT_NAME ${output_name} + ${SOURCES} + + LINK_LIBS + ${LIBS} + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Core + Support + ) + +set(INDEXSTORE_LIBRARY_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + +if(ENABLE_SHARED) + if(WIN32) + set_target_properties(IndexStore + PROPERTIES + VERSION ${INDEXSTORE_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) + elseif(APPLE) + set(INDEXSTORE_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -Wl,-current_version -Wl,${INDEXSTORE_LIBRARY_VERSION}") + + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -framework CoreServices") + endif() + + set_property(TARGET IndexStore APPEND_STRING PROPERTY + LINK_FLAGS ${INDEXSTORE_LINK_FLAGS}) + else() + set_target_properties(IndexStore + PROPERTIES + VERSION ${INDEXSTORE_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) + endif() +endif() + +if (LLVM_INSTALL_TOOLCHAIN_ONLY) + install(TARGETS IndexStore + COMPONENT IndexStore + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} + RUNTIME DESTINATION bin) + + if (NOT CMAKE_CONFIGURATION_TYPES) + add_custom_target(install-IndexStore + DEPENDS IndexStore + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=IndexStore + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + endif() +endif() + +set(INDEXSTORE_HEADERS_INSTALL_DESTINATION "local/include") + +install(DIRECTORY ../../include/indexstore + COMPONENT IndexStore + DESTINATION "${INDEXSTORE_HEADERS_INSTALL_DESTINATION}" + FILES_MATCHING + PATTERN "*.h" + PATTERN ".svn" EXCLUDE + ) +endif() diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp new file mode 100644 index 0000000000000..422652499c994 --- /dev/null +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -0,0 +1,647 @@ +//===- IndexStore.cpp - Index store API -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the API for the index store. +// +//===----------------------------------------------------------------------===// + +#include "indexstore/indexstore.h" +#include "clang/Index/IndexDataStore.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "clang/Index/IndexRecordReader.h" +#include "clang/Index/IndexUnitReader.h" +#include "clang/Index/IndexUnitWriter.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Chrono.h" +#include <Block.h> + +using namespace clang; +using namespace clang::index; +using namespace llvm; + +static indexstore_string_ref_t toIndexStoreString(StringRef str) { + return indexstore_string_ref_t{ str.data(), str.size() }; +} + +static timespec toTimeSpec(sys::TimePoint<> tp) { + std::chrono::seconds sec = std::chrono::time_point_cast<std::chrono::seconds>( + tp).time_since_epoch(); + std::chrono::nanoseconds nsec = + std::chrono::time_point_cast<std::chrono::nanoseconds>(tp - sec) + .time_since_epoch(); + timespec ts; + ts.tv_sec = sec.count(); + ts.tv_nsec = nsec.count(); + return ts; +} + +namespace { + +struct IndexStoreError { + std::string Error; +}; + +} // anonymous namespace + +const char * +indexstore_error_get_description(indexstore_error_t err) { + return static_cast<IndexStoreError*>(err)->Error.c_str(); +} + +void +indexstore_error_dispose(indexstore_error_t err) { + delete static_cast<IndexStoreError*>(err); +} + +unsigned +indexstore_format_version(void) { + return IndexDataStore::getFormatVersion(); +} + +indexstore_t +indexstore_store_create(const char *store_path, indexstore_error_t *c_error) { + std::unique_ptr<IndexDataStore> store; + std::string error; + store = IndexDataStore::create(store_path, error); + if (!store) { + if (c_error) + *c_error = new IndexStoreError{ error }; + return nullptr; + } + return store.release(); +} + +void +indexstore_store_dispose(indexstore_t store) { + delete static_cast<IndexDataStore*>(store); +} + +#if INDEXSTORE_HAS_BLOCKS +bool +indexstore_store_units_apply(indexstore_t c_store, unsigned sorted, + bool(^applier)(indexstore_string_ref_t unit_name)) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool { + return applier(toIndexStoreString(unitName)); + }); +} + +size_t +indexstore_unit_event_notification_get_events_count(indexstore_unit_event_notification_t c_evtnote) { + auto *evtnote = static_cast<IndexDataStore::UnitEventNotification*>(c_evtnote); + return evtnote->Events.size(); +} + +indexstore_unit_event_t +indexstore_unit_event_notification_get_event(indexstore_unit_event_notification_t c_evtnote, size_t index) { + auto *evtnote = static_cast<IndexDataStore::UnitEventNotification*>(c_evtnote); + return (indexstore_unit_event_t)&evtnote->Events[index]; +} + +bool +indexstore_unit_event_notification_is_initial(indexstore_unit_event_notification_t c_evtnote) { + auto *evtnote = static_cast<IndexDataStore::UnitEventNotification*>(c_evtnote); + return evtnote->IsInitial; +} + +indexstore_unit_event_kind_t +indexstore_unit_event_get_kind(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt); + indexstore_unit_event_kind_t k; + switch (evt->Kind) { + case IndexDataStore::UnitEventKind::Added: + k = INDEXSTORE_UNIT_EVENT_ADDED; break; + case IndexDataStore::UnitEventKind::Removed: + k = INDEXSTORE_UNIT_EVENT_REMOVED; break; + case IndexDataStore::UnitEventKind::Modified: + k = INDEXSTORE_UNIT_EVENT_MODIFIED; break; + case IndexDataStore::UnitEventKind::DirectoryDeleted: + k = INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED; break; + } + return k; +} + +indexstore_string_ref_t +indexstore_unit_event_get_unit_name(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt); + return toIndexStoreString(evt->UnitName); +} + +timespec +indexstore_unit_event_get_modification_time(indexstore_unit_event_t c_evt) { + auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt); + return evt->ModTime; +} + +void +indexstore_store_set_unit_event_handler(indexstore_t c_store, + indexstore_unit_event_handler_t blk_handler) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + if (!blk_handler) { + store->setUnitEventHandler(nullptr); + return; + } + + class BlockWrapper { + indexstore_unit_event_handler_t blk_handler; + public: + BlockWrapper(indexstore_unit_event_handler_t handler) { + blk_handler = Block_copy(handler); + } + BlockWrapper(const BlockWrapper &other) { + blk_handler = Block_copy(other.blk_handler); + } + ~BlockWrapper() { + Block_release(blk_handler); + } + + void operator()(indexstore_unit_event_notification_t evt_note) const { + blk_handler(evt_note); + } + }; + + BlockWrapper handler(blk_handler); + + store->setUnitEventHandler([handler](IndexDataStore::UnitEventNotification evtNote) { + handler(&evtNote); + }); +} +#endif + +bool +indexstore_store_start_unit_event_listening(indexstore_t c_store, + indexstore_unit_event_listen_options_t *client_opts, + size_t listen_options_struct_size, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + indexstore_unit_event_listen_options_t listen_opts; + memset(&listen_opts, 0, sizeof(listen_opts)); + unsigned clientOptSize = listen_options_struct_size < sizeof(listen_opts) + ? listen_options_struct_size : sizeof(listen_opts); + memcpy(&listen_opts, client_opts, clientOptSize); + + std::string error; + auto createFn = [](StringRef Path, AbstractDirectoryWatcher::EventReceiver Receiver, bool waitInitialSync, std::string &Error) + -> std::unique_ptr<AbstractDirectoryWatcher> { + return DirectoryWatcher::create(Path, std::move(Receiver), waitInitialSync, Error); + }; + bool err = store->startEventListening(createFn, listen_opts.wait_initial_sync, error); + if (err && c_error) + *c_error = new IndexStoreError{ error }; + return err; +} + +void +indexstore_store_stop_unit_event_listening(indexstore_t c_store) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + store->stopEventListening(); +} + +void +indexstore_store_discard_unit(indexstore_t c_store, const char *unit_name) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + store->discardUnit(unit_name); +} + +void +indexstore_store_discard_record(indexstore_t c_store, const char *record_name) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + store->discardRecord(record_name); +} + +void +indexstore_store_purge_stale_data(indexstore_t c_store) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + store->purgeStaleData(); +} + +indexstore_symbol_kind_t +indexstore_symbol_get_kind(indexstore_symbol_t sym) { + return getIndexStoreKind(static_cast<IndexRecordDecl *>(sym)->SymInfo.Kind); +} + +indexstore_symbol_subkind_t +indexstore_symbol_get_subkind(indexstore_symbol_t sym) { + return getIndexStoreSubKind(static_cast<IndexRecordDecl *>(sym)->SymInfo.SubKind); +} + +indexstore_symbol_language_t +indexstore_symbol_get_language(indexstore_symbol_t sym) { + return getIndexStoreLang(static_cast<IndexRecordDecl *>(sym)->SymInfo.Lang); +} + +uint64_t +indexstore_symbol_get_properties(indexstore_symbol_t sym) { + return getIndexStoreProperties(static_cast<IndexRecordDecl *>(sym)->SymInfo.Properties); +} + +uint64_t +indexstore_symbol_get_roles(indexstore_symbol_t sym) { + return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->Roles); +} + +uint64_t +indexstore_symbol_get_related_roles(indexstore_symbol_t sym) { + return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->RelatedRoles); +} + +indexstore_string_ref_t +indexstore_symbol_get_name(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl*>(sym); + return toIndexStoreString(D->Name); +} + +indexstore_string_ref_t +indexstore_symbol_get_usr(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl*>(sym); + return toIndexStoreString(D->USR); +} + +indexstore_string_ref_t +indexstore_symbol_get_codegen_name(indexstore_symbol_t sym) { + auto *D = static_cast<IndexRecordDecl*>(sym); + return toIndexStoreString(D->CodeGenName); +} + +uint64_t +indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t sym_rel) { + return getIndexStoreRoles(static_cast<IndexRecordRelation *>(sym_rel)->Roles); +} + +indexstore_symbol_t +indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t sym_rel) { + return (indexstore_symbol_t)static_cast<IndexRecordRelation*>(sym_rel)->Dcl; +} + +indexstore_symbol_t +indexstore_occurrence_get_symbol(indexstore_occurrence_t occur) { + return (indexstore_symbol_t)static_cast<IndexRecordOccurrence*>(occur)->Dcl; +} + +#if INDEXSTORE_HAS_BLOCKS +bool +indexstore_occurrence_relations_apply(indexstore_occurrence_t occur, + bool(^applier)(indexstore_symbol_relation_t symbol_rel)) { + auto *recOccur = static_cast<IndexRecordOccurrence*>(occur); + for (auto &rel : recOccur->Relations) { + if (!applier(&rel)) + return false; + } + return true; +} +#endif + +uint64_t +indexstore_occurrence_get_roles(indexstore_occurrence_t occur) { + return static_cast<IndexRecordOccurrence*>(occur)->Roles; +} + +void +indexstore_occurrence_get_line_col(indexstore_occurrence_t occur, + unsigned *line, unsigned *column) { + auto *recOccur = static_cast<IndexRecordOccurrence*>(occur); + if (line) + *line = recOccur->Line; + if (column) + *column = recOccur->Column; +} + +typedef void *indexstore_record_reader_t; + +indexstore_record_reader_t +indexstore_record_reader_create(indexstore_t c_store, const char *record_name, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + std::unique_ptr<IndexRecordReader> reader; + std::string error; + reader = IndexRecordReader::createWithRecordFilename(record_name, + store->getFilePath(), + error); + if (!reader) { + if (c_error) + *c_error = new IndexStoreError{ error }; + return nullptr; + } + return reader.release(); +} + +void +indexstore_record_reader_dispose(indexstore_record_reader_t rdr) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + delete reader; +} + +#if INDEXSTORE_HAS_BLOCKS +/// Goes through the symbol data and passes symbols to \c receiver, for the +/// symbol data that \c filter returns true on. +/// +/// This allows allocating memory only for the record symbols that the caller is +/// interested in. +bool +indexstore_record_reader_search_symbols(indexstore_record_reader_t rdr, + bool(^filter)(indexstore_symbol_t symbol, bool *stop), + void(^receiver)(indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + + auto filterFn = [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn { + bool stop = false; + bool accept = filter((indexstore_symbol_t)&D, &stop); + return { accept, !stop }; + }; + auto receiverFn = [&](const IndexRecordDecl *D) { + receiver((indexstore_symbol_t)D); + }; + + return reader->searchDecls(filterFn, receiverFn); +} + +bool +indexstore_record_reader_symbols_apply(indexstore_record_reader_t rdr, + bool nocache, + bool(^applier)(indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordDecl *D) -> bool { + return applier((indexstore_symbol_t)D); + }; + return reader->foreachDecl(nocache, receiverFn); +} + +bool +indexstore_record_reader_occurrences_apply(indexstore_record_reader_t rdr, + bool(^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence(receiverFn); +} + +bool +indexstore_record_reader_occurrences_in_line_range_apply(indexstore_record_reader_t rdr, + unsigned line_start, + unsigned line_count, + bool(^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrenceInLineRange(line_start, line_count, receiverFn); +} + +/// \param symbols if non-zero \c symbols_count, indicates the list of symbols +/// that we want to get occurrences for. An empty array indicates that we want +/// occurrences for all symbols. +/// \param related_symbols Same as \c symbols but for related symbols. +bool +indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t rdr, + indexstore_symbol_t *symbols, size_t symbols_count, + indexstore_symbol_t *related_symbols, size_t related_symbols_count, + bool(^applier)(indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier((indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence({(IndexRecordDecl**)symbols, symbols_count}, + {(IndexRecordDecl**)related_symbols, related_symbols_count}, + receiverFn); +} +#endif + +size_t +indexstore_store_get_unit_name_from_output_path(indexstore_t store, + const char *output_path, + char *name_buf, + size_t buf_size) { + SmallString<256> unitName; + IndexUnitWriter::getUnitNameForAbsoluteOutputFile(output_path, unitName); + size_t nameLen = unitName.size(); + strlcpy(name_buf, unitName.c_str(), buf_size); + return nameLen; +} + +bool +indexstore_store_get_unit_modification_time(indexstore_t c_store, + const char *unit_name, + int64_t *seconds, + int64_t *nanoseconds, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + std::string error; + // FIXME: This provides mod time with second-only accuracy. + auto optModTime = IndexUnitReader::getModificationTimeForUnit(unit_name, + store->getFilePath(), error); + if (!optModTime) { + if (c_error) + *c_error = new IndexStoreError{ error }; + return true; + } + + timespec ts = toTimeSpec(*optModTime); + if (seconds) + *seconds = ts.tv_sec; + if (nanoseconds) + *nanoseconds = ts.tv_nsec; + + return false; +} + +indexstore_unit_reader_t +indexstore_unit_reader_create(indexstore_t c_store, const char *unit_name, + indexstore_error_t *c_error) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + std::unique_ptr<IndexUnitReader> reader; + std::string error; + reader = IndexUnitReader::createWithUnitFilename(unit_name, + store->getFilePath(), error); + if (!reader) { + if (c_error) + *c_error = new IndexStoreError{ error }; + return nullptr; + } + return reader.release(); +} + +void +indexstore_unit_reader_dispose(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + delete reader; +} + +indexstore_string_ref_t +indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getProviderIdentifier()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getProviderVersion()); +} + +void +indexstore_unit_reader_get_modification_time(indexstore_unit_reader_t rdr, + int64_t *seconds, + int64_t *nanoseconds) { + auto reader = static_cast<IndexUnitReader*>(rdr); + // FIXME: This provides mod time with second-only accuracy. + sys::TimePoint<> timeVal = reader->getModificationTime(); + timespec ts = toTimeSpec(timeVal); + if (seconds) + *seconds = ts.tv_sec; + if (nanoseconds) + *nanoseconds = ts.tv_nsec; +} + +bool +indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->isSystemUnit(); +} + +bool +indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->isModuleUnit(); +} + +bool +indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->isDebugCompilation(); +} + +bool +indexstore_unit_reader_has_main_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->hasMainFile(); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_main_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getMainFilePath()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_module_name(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getModuleName()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getWorkingDirectory()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_output_file(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getOutputFile()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getSysrootPath()); +} + +indexstore_string_ref_t +indexstore_unit_reader_get_target(indexstore_unit_reader_t rdr) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return toIndexStoreString(reader->getTarget()); +} + +indexstore_unit_dependency_kind_t +indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); + switch (dep->Kind) { + case IndexUnitReader::DependencyKind::Unit: return INDEXSTORE_UNIT_DEPENDENCY_UNIT; + case IndexUnitReader::DependencyKind::Record: return INDEXSTORE_UNIT_DEPENDENCY_RECORD; + case IndexUnitReader::DependencyKind::File: return INDEXSTORE_UNIT_DEPENDENCY_FILE; + } +} + +bool +indexstore_unit_dependency_is_system(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); + return dep->IsSystem; +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); + return toIndexStoreString(dep->FilePath); +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); + return toIndexStoreString(dep->ModuleName); +} + +indexstore_string_ref_t +indexstore_unit_dependency_get_name(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); + return toIndexStoreString(dep->UnitOrRecordName); +} + +time_t +indexstore_unit_dependency_get_modification_time(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); + return dep->ModTime; +} + +size_t +indexstore_unit_dependency_get_file_size(indexstore_unit_dependency_t c_dep) { + auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); + return dep->FileSize; +} + +indexstore_string_ref_t +indexstore_unit_include_get_source_path(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo*>(c_inc); + return toIndexStoreString(inc->SourcePath); +} + +indexstore_string_ref_t +indexstore_unit_include_get_target_path(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo*>(c_inc); + return toIndexStoreString(inc->TargetPath); +} + +unsigned +indexstore_unit_include_get_source_line(indexstore_unit_include_t c_inc) { + auto inc = static_cast<const IndexUnitReader::IncludeInfo*>(c_inc); + return inc->SourceLine; +} + +#if INDEXSTORE_HAS_BLOCKS +bool +indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t rdr, + bool(^applier)(indexstore_unit_dependency_t)) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &depInfo) -> bool { + return applier((void*)&depInfo); + }); +} + +bool +indexstore_unit_reader_includes_apply(indexstore_unit_reader_t rdr, + bool(^applier)(indexstore_unit_include_t)) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &incInfo) -> bool { + return applier((void*)&incInfo); + }); +} +#endif diff --git a/clang/tools/IndexStore/IndexStore.exports b/clang/tools/IndexStore/IndexStore.exports new file mode 100644 index 0000000000000..70c174f360315 --- /dev/null +++ b/clang/tools/IndexStore/IndexStore.exports @@ -0,0 +1,69 @@ +indexstore_error_get_description +indexstore_error_dispose +indexstore_format_version +indexstore_store_create +indexstore_store_dispose +indexstore_store_get_unit_modification_time +indexstore_store_get_unit_name_from_output_path +indexstore_store_units_apply +indexstore_store_set_unit_event_handler +indexstore_store_start_unit_event_listening +indexstore_store_stop_unit_event_listening +indexstore_store_discard_unit +indexstore_store_discard_record +indexstore_store_purge_stale_data +indexstore_symbol_get_kind +indexstore_symbol_get_language +indexstore_symbol_get_properties +indexstore_symbol_get_roles +indexstore_symbol_get_related_roles +indexstore_symbol_get_subkind +indexstore_symbol_get_name +indexstore_symbol_get_usr +indexstore_symbol_get_codegen_name +indexstore_symbol_relation_get_roles +indexstore_symbol_relation_get_symbol +indexstore_occurrence_get_symbol +indexstore_occurrence_get_roles +indexstore_occurrence_get_line_col +indexstore_occurrence_relations_apply +indexstore_record_reader_create +indexstore_record_reader_dispose +indexstore_record_reader_search_symbols +indexstore_record_reader_symbols_apply +indexstore_record_reader_occurrences_apply +indexstore_record_reader_occurrences_in_line_range_apply +indexstore_record_reader_occurrences_of_symbols_apply +indexstore_unit_dependency_get_kind +indexstore_unit_dependency_get_filepath +indexstore_unit_dependency_get_file_size +indexstore_unit_dependency_get_modification_time +indexstore_unit_dependency_get_modulename +indexstore_unit_dependency_get_name +indexstore_unit_dependency_is_system +indexstore_unit_event_get_kind +indexstore_unit_event_get_modification_time +indexstore_unit_event_get_unit_name +indexstore_unit_event_notification_get_event +indexstore_unit_event_notification_get_events_count +indexstore_unit_event_notification_is_initial +indexstore_unit_reader_create +indexstore_unit_reader_dispose +indexstore_unit_reader_get_main_file +indexstore_unit_reader_get_modification_time +indexstore_unit_reader_get_module_name +indexstore_unit_reader_get_provider_identifier +indexstore_unit_reader_get_provider_version +indexstore_unit_reader_get_working_dir +indexstore_unit_reader_get_output_file +indexstore_unit_reader_get_sysroot_path +indexstore_unit_reader_get_target +indexstore_unit_reader_dependencies_apply +indexstore_unit_reader_includes_apply +indexstore_unit_reader_has_main_file +indexstore_unit_reader_is_debug_compilation +indexstore_unit_reader_is_module_unit +indexstore_unit_reader_is_system_unit +indexstore_unit_include_get_source_path +indexstore_unit_include_get_target_path +indexstore_unit_include_get_source_line diff --git a/clang/tools/c-index-test/CMakeLists.txt b/clang/tools/c-index-test/CMakeLists.txt index ad990e010eeff..c8d33b9e36f16 100644 --- a/clang/tools/c-index-test/CMakeLists.txt +++ b/clang/tools/c-index-test/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CheckIncludeFiles) + set(LLVM_LINK_COMPONENTS support ) @@ -5,8 +7,15 @@ set(LLVM_LINK_COMPONENTS add_clang_executable(c-index-test c-index-test.c core_main.cpp + JSONAggregation.cpp ) +set(INDEXSTORE_LIB) +set(CINDEXTEST_LIBS) +if(APPLE) + set(INDEXSTORE_LIB IndexStore) +endif() + if(NOT MSVC) set_property( SOURCE c-index-test.c @@ -19,16 +28,20 @@ if (LLVM_BUILD_STATIC) libclang_static clangCodeGen clangIndex + ${CINDEXTEST_LIBS} ) else() target_link_libraries(c-index-test libclang + ${INDEXSTORE_LIB} clangAST clangBasic clangCodeGen + clangDirectoryWatcher clangFrontend clangIndex clangSerialization + ${CINDEXTEST_LIBS} ) endif() @@ -42,6 +55,13 @@ if (CLANG_HAVE_LIBXML) target_link_libraries(c-index-test ${LIBXML2_LIBRARIES}) endif() +if(APPLE) + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + target_link_libraries(c-index-test "-framework CoreServices") + endif() +endif() + if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) if(INTERNAL_INSTALL_PREFIX) set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") diff --git a/clang/tools/c-index-test/JSONAggregation.cpp b/clang/tools/c-index-test/JSONAggregation.cpp new file mode 100644 index 0000000000000..c7f4136bde7e6 --- /dev/null +++ b/clang/tools/c-index-test/JSONAggregation.cpp @@ -0,0 +1,409 @@ +//===--- JSONAggregation.cpp - Index data aggregation in JSON format ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "JSONAggregation.h" +#include "indexstore/IndexStoreCXX.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace indexstore; +using namespace llvm; + +#if INDEXSTORE_HAS_BLOCKS + +namespace { + +typedef size_t FilePathIndex; +typedef size_t RecordIndex; +typedef size_t SymbolIndex; + +struct UnitSourceInfo { + FilePathIndex FilePath; + SmallVector<RecordIndex, 2> AssociatedRecords; +}; + +struct UnitInfo { + std::string Name; + SmallVector<UnitSourceInfo, 8> Sources; + SmallVector<std::string, 3> UnitDepends; + FilePathIndex OutFile; + StringRef Triple; +}; + +struct SymbolInfo { + SymbolKind Kind; + SymbolLanguage Lang; + StringRef USR; + StringRef Name; + StringRef CodegenName; + SymbolRoleSet Roles = 0; + SymbolRoleSet RelatedRoles = 0; +}; + +struct SymbolRelationInfo { + SymbolIndex RelatedSymbol; + SymbolRoleSet Roles; + SymbolRelationInfo(SymbolIndex relSymbol, SymbolRoleSet roles) + : RelatedSymbol(relSymbol), Roles(roles) {} +}; + +struct SymbolOccurrenceInfo { + SymbolIndex Symbol; + SymbolRoleSet Roles = 0; + std::vector<SymbolRelationInfo> Relations; + unsigned Line; + unsigned Column; +}; + +struct RecordInfo { + SmallVector<SymbolOccurrenceInfo, 8> Occurrences; +}; + +class Aggregator { + IndexStore Store; + + BumpPtrAllocator Allocator; + + StringMap<FilePathIndex, BumpPtrAllocator &> FilePathIndices; + std::vector<StringRef> FilePaths; + StringMap<char, BumpPtrAllocator &> Triples; + + std::vector<std::unique_ptr<UnitInfo>> Units; + + StringMap<RecordIndex, BumpPtrAllocator &> RecordIndices; + std::vector<std::unique_ptr<RecordInfo>> Records; + + StringMap<SymbolIndex, BumpPtrAllocator &> SymbolIndices; + std::vector<SymbolInfo> Symbols; + +public: + explicit Aggregator(IndexStore store) + : Store(std::move(store)), + FilePathIndices(Allocator), + Triples(Allocator), + RecordIndices(Allocator), + SymbolIndices(Allocator) {} + + bool process(); + void processUnit(StringRef name, IndexUnitReader &UnitReader); + void dumpJSON(raw_ostream &OS); + +private: + StringRef copyStr(StringRef str) { + if (str.empty()) + return StringRef(); + char *buf = Allocator.Allocate<char>(str.size()); + std::copy(str.begin(), str.end(), buf); + return StringRef(buf, str.size()); + } + + StringRef getTripleString(StringRef inputTriple) { + return Triples.insert(std::make_pair(inputTriple, 0)).first->first(); + } + + FilePathIndex getFilePathIndex(StringRef path, StringRef workingDir); + RecordIndex getRecordIndex(StringRef recordFile); + SymbolIndex getSymbolIndex(IndexRecordSymbol sym); + std::unique_ptr<RecordInfo> processRecord(StringRef recordFile); +}; + +} // anonymous namespace + +bool Aggregator::process() { + bool succ = Store.foreachUnit(/*sorted=*/true, [&](StringRef unitName) -> bool { + std::string error; + auto unitReader = IndexUnitReader(Store, unitName, error); + if (!unitReader) { + errs() << "error opening unit file '" << unitName << "': " << error << '\n'; + return false; + } + + processUnit(unitName, unitReader); + return true; + }); + + return !succ; +} + +void Aggregator::processUnit(StringRef name, IndexUnitReader &UnitReader) { + auto workDir = UnitReader.getWorkingDirectory(); + auto unit = llvm::make_unique<UnitInfo>(); + unit->Name = name; + unit->Triple = getTripleString(UnitReader.getTarget()); + unit->OutFile = getFilePathIndex(UnitReader.getOutputFile(), workDir); + + struct DepInfo { + UnitSourceInfo source; + std::string unitName; + }; + SmallVector<DepInfo, 32> Deps; + UnitReader.foreachDependency([&](IndexUnitDependency dep) -> bool { + Deps.resize(Deps.size()+1); + auto &depInfo = Deps.back(); + switch (dep.getKind()) { + case IndexUnitDependency::DependencyKind::Unit: { + depInfo.unitName = dep.getName(); + StringRef filePath = dep.getFilePath(); + if (!filePath.empty()) + depInfo.source.FilePath = getFilePathIndex(filePath, workDir); + break; + } + case IndexUnitDependency::DependencyKind::Record: { + depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); + RecordIndex recIndex = getRecordIndex(dep.getName()); + depInfo.source.AssociatedRecords.push_back(recIndex); + break; + } + case IndexUnitDependency::DependencyKind::File: + depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); + } + return true; + }); + + unit->Sources.reserve(Deps.size()); + for (auto &dep : Deps) { + if (!dep.unitName.empty()) { + unit->UnitDepends.emplace_back(std::move(dep.unitName)); + } else { + unit->Sources.push_back(std::move(dep.source)); + } + } + + Units.push_back(std::move(unit)); +} + +FilePathIndex Aggregator::getFilePathIndex(StringRef path, StringRef workingDir) { + StringRef absPath; + SmallString<128> absPathBuf; + if (sys::path::is_absolute(path) || workingDir.empty()) { + absPath = path; + } else { + absPathBuf = workingDir; + sys::path::append(absPathBuf, path); + absPath = absPathBuf.str(); + } + + auto pair = FilePathIndices.insert(std::make_pair(absPath, FilePaths.size())); + bool wasInserted = pair.second; + if (wasInserted) { + FilePaths.push_back(pair.first->first()); + } + return pair.first->second; +} + +RecordIndex Aggregator::getRecordIndex(StringRef recordFile) { + auto pair = RecordIndices.insert(std::make_pair(recordFile, Records.size())); + bool wasInserted = pair.second; + if (wasInserted) { + Records.push_back(processRecord(recordFile)); + } + return pair.first->second; +} + +std::unique_ptr<RecordInfo> Aggregator::processRecord(StringRef recordFile) { + std::string error; + auto recordReader = IndexRecordReader(Store, recordFile, error); + if (!recordReader) { + errs() << "failed reading record file: " << recordFile << '\n'; + ::exit(1); + } + auto record = llvm::make_unique<RecordInfo>(); + recordReader.foreachOccurrence([&](IndexRecordOccurrence idxOccur) -> bool { + SymbolIndex symIdx = getSymbolIndex(idxOccur.getSymbol()); + SymbolInfo &symInfo = Symbols[symIdx]; + symInfo.Roles |= idxOccur.getRoles(); + SymbolOccurrenceInfo occurInfo; + occurInfo.Symbol = symIdx; + idxOccur.foreachRelation([&](IndexSymbolRelation rel) -> bool { + SymbolIndex relsymIdx = getSymbolIndex(rel.getSymbol()); + SymbolInfo &relsymInfo = Symbols[relsymIdx]; + relsymInfo.RelatedRoles |= rel.getRoles(); + occurInfo.Relations.emplace_back(relsymIdx, rel.getRoles()); + return true; + }); + occurInfo.Roles = idxOccur.getRoles(); + std::tie(occurInfo.Line, occurInfo.Column) = idxOccur.getLineCol(); + record->Occurrences.push_back(std::move(occurInfo)); + return true; + }); + return record; +} + +SymbolIndex Aggregator::getSymbolIndex(IndexRecordSymbol sym) { + auto pair = SymbolIndices.insert(std::make_pair(sym.getUSR(), Symbols.size())); + bool wasInserted = pair.second; + if (wasInserted) { + SymbolInfo symInfo; + symInfo.Kind = getSymbolKind(sym.getKind()); + symInfo.Lang = getSymbolLanguage(sym.getLanguage()); + symInfo.USR = pair.first->first(); + symInfo.Name = copyStr(sym.getName()); + symInfo.CodegenName = copyStr(sym.getCodegenName()); + Symbols.push_back(std::move(symInfo)); + } + return pair.first->second; +} + + +void Aggregator::dumpJSON(raw_ostream &OS) { + OS << "{\n"; + OS.indent(2) << "\"files\": [\n"; + for (unsigned i = 0, e = FilePaths.size(); i != e; ++i) { + OS.indent(4) << '\"' << FilePaths[i] << '\"'; + if (i < e-1) OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + OS.indent(2) << "\"symbols\": [\n"; + for (unsigned i = 0, e = Symbols.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + SymbolInfo &symInfo = Symbols[i]; + OS.indent(6) << "\"kind\": \"" << getSymbolKindString(symInfo.Kind) << "\",\n"; + OS.indent(6) << "\"lang\": \"" << getSymbolLanguageString(symInfo.Lang) << "\",\n"; + OS.indent(6) << "\"usr\": \"" << symInfo.USR << "\",\n"; + OS.indent(6) << "\"name\": \"" << symInfo.Name << "\",\n"; + if (!symInfo.CodegenName.empty()) + OS.indent(6) << "\"codegen\": \"" << symInfo.CodegenName << "\",\n"; + OS.indent(6) << "\"roles\": \""; + printSymbolRoles(symInfo.Roles, OS); + OS << '\"'; + if (symInfo.RelatedRoles != 0) { + OS << ",\n"; + OS.indent(6) << "\"rel-roles\": \""; + printSymbolRoles(symInfo.RelatedRoles, OS); + OS << '\"'; + } + OS << '\n'; + OS.indent(4) << "}"; + if (i < e-1) OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + OS.indent(2) << "\"records\": [\n"; + for (unsigned i = 0, e = Records.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + RecordInfo &recInfo = *Records[i]; + OS.indent(6) << "\"occurrences\": [\n"; + for (unsigned oi = 0, oe = recInfo.Occurrences.size(); oi != oe; ++oi) { + OS.indent(8) << "{\n"; + SymbolOccurrenceInfo &occurInfo = recInfo.Occurrences[oi]; + OS.indent(10) << "\"symbol\": " << occurInfo.Symbol << ",\n"; + OS.indent(10) << "\"line\": " << occurInfo.Line << ",\n"; + OS.indent(10) << "\"col\": " << occurInfo.Column << ",\n"; + OS.indent(10) << "\"roles\": \""; + printSymbolRoles(occurInfo.Roles, OS); + OS << '\"'; + if (!occurInfo.Relations.empty()) { + OS << ",\n"; + OS.indent(10) << "\"relations\": [\n"; + for (unsigned ri = 0, re = occurInfo.Relations.size(); ri != re; ++ri) { + OS.indent(12) << "{\n"; + SymbolRelationInfo &relInfo = occurInfo.Relations[ri]; + OS.indent(14) << "\"symbol\": " << relInfo.RelatedSymbol << ",\n"; + OS.indent(14) << "\"rel-roles\": \""; + printSymbolRoles(relInfo.Roles, OS); + OS << "\"\n"; + OS.indent(12) << "}"; + if (ri < re-1) OS << ','; + OS << '\n'; + } + OS.indent(10) << "]\n"; + } + OS << '\n'; + OS.indent(8) << "}"; + if (oi < oe-1) OS << ','; + OS << '\n'; + } + OS.indent(6) << "]\n"; + OS.indent(4) << "}"; + if (i < e-1) OS << ','; + OS << '\n'; + } + OS.indent(2) << "],\n"; + + StringMap<size_t> UnitIndicesByName; + for (unsigned i = 0, e = Units.size(); i != e; ++i) { + UnitInfo &unit = *Units[i]; + UnitIndicesByName[unit.Name] = i; + } + + OS.indent(2) << "\"units\": [\n"; + for (unsigned i = 0, e = Units.size(); i != e; ++i) { + OS.indent(4) << "{\n"; + UnitInfo &unit = *Units[i]; + OS.indent(6) << "\"triple\": \"" << unit.Triple << "\",\n"; + OS.indent(6) << "\"out-file\": " << unit.OutFile << ",\n"; + if (!unit.UnitDepends.empty()) { + OS.indent(6) << "\"unit-dependencies\": ["; + for (unsigned ui = 0, ue = unit.UnitDepends.size(); ui != ue; ++ui) { + OS << UnitIndicesByName[unit.UnitDepends[ui]]; + if (ui < ue-1) OS << ", "; + } + OS << "],\n"; + } + OS.indent(6) << "\"sources\": [\n"; + for (unsigned si = 0, se = unit.Sources.size(); si != se; ++si) { + OS.indent(8) << "{\n"; + UnitSourceInfo &source = unit.Sources[si]; + OS.indent(10) << "\"file\": " << source.FilePath; + if (!source.AssociatedRecords.empty()) { + OS << ",\n"; + OS.indent(10) << "\"records\": ["; + for (unsigned ri = 0, re = source.AssociatedRecords.size(); ri != re; ++ri) { + OS << source.AssociatedRecords[ri]; + if (ri < re-1) OS << ", "; + } + OS << ']'; + } + OS << '\n'; + OS.indent(8) << "}"; + if (si < se-1) OS << ','; + OS << '\n'; + } + OS.indent(6) << "]\n"; + OS.indent(4) << "}"; + if (i < e-1) OS << ','; + OS << '\n'; + } + OS.indent(2) << "]\n"; + OS << "}\n"; +} + + +bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { + std::string error; + auto dataStore = IndexStore(StorePath, error); + if (!dataStore) { + errs() << "error opening store path '" << StorePath << "': " << error << '\n'; + return true; + } + + // Explicitely avoid doing any memory cleanup for aggregator since the process + // is going to exit when we are done. + Aggregator *aggregator = new Aggregator(std::move(dataStore)); + bool err = aggregator->process(); + if (err) + return true; + aggregator->dumpJSON(OS); + return false; +} + +#else + +bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { + return true; +} +#endif diff --git a/clang/tools/c-index-test/JSONAggregation.h b/clang/tools/c-index-test/JSONAggregation.h new file mode 100644 index 0000000000000..5224ce8e8769b --- /dev/null +++ b/clang/tools/c-index-test/JSONAggregation.h @@ -0,0 +1,24 @@ +//===--- JSONAggregation.h - Index data aggregation in JSON format --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H +#define LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H + +#include "clang/Basic/LLVM.h" + +namespace clang { +namespace index { + +/// Returns true if an error occurred, false otherwise. +bool aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS); + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index 4f2c3cb34a9ba..1335b05075cfb 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +#include "JSONAggregation.h" +#include "indexstore/IndexStoreCXX.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" @@ -14,14 +17,31 @@ #include "clang/Frontend/FrontendAction.h" #include "clang/Index/IndexingAction.h" #include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "clang/Index/IndexRecordReader.h" +#include "clang/Index/IndexUnitReader.h" #include "clang/Index/USRGeneration.h" #include "clang/Index/CodegenNameGenerator.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/PrettyStackTrace.h" +#define HAVE_CORESERVICES 0 + +#if defined(__has_include) +#if __has_include(<CoreServices/CoreServices.h>) + +#include <CoreServices/CoreServices.h> +#undef HAVE_CORESERVICES +#define HAVE_CORESERVICES 1 + +#endif +#endif + using namespace clang; using namespace clang::index; using namespace llvm; @@ -33,6 +53,11 @@ namespace { enum class ActionType { None, PrintSourceSymbols, + PrintRecord, + PrintUnit, + PrintStoreFormatVersion, + AggregateAsJSON, + WatchDir, }; namespace options { @@ -43,9 +68,26 @@ static cl::opt<ActionType> Action(cl::desc("Action:"), cl::init(ActionType::None), cl::values( clEnumValN(ActionType::PrintSourceSymbols, - "print-source-symbols", "Print symbols from source")), + "print-source-symbols", "Print symbols from source"), + clEnumValN(ActionType::PrintRecord, + "print-record", "Print record info"), + clEnumValN(ActionType::PrintUnit, + "print-unit", "Print unit info"), + clEnumValN(ActionType::PrintStoreFormatVersion, + "print-store-format-version", "Print store format version"), + clEnumValN(ActionType::AggregateAsJSON, + "aggregate-json", "Aggregate index data in JSON format"), + clEnumValN(ActionType::WatchDir, + "watch-dir", "Watch directory for file events")), cl::cat(IndexTestCoreCategory)); +static cl::opt<std::string> +OutputFile("o", cl::desc("output file"), + cl::cat(IndexTestCoreCategory)); + +static cl::list<std::string> +InputFiles(cl::Positional, cl::desc("<filename>...")); + static cl::extrahelp MoreHelp( "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler " "invocation\n" @@ -65,6 +107,10 @@ static cl::opt<std::string> ModuleFormat("fmodule-format", cl::init("raw"), cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'")); +static cl::opt<std::string> +FilePathAndRange("filepath", + cl::desc("File path that can optionally include a line range")); + } } // anonymous namespace @@ -235,6 +281,299 @@ static bool printSourceSymbolsFromModule(StringRef modulePath, return false; } +#if INDEXSTORE_HAS_BLOCKS + +//===----------------------------------------------------------------------===// +// Print Record +//===----------------------------------------------------------------------===// + +static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS); +static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS); + +static int printRecord(StringRef Filename, raw_ostream &OS) { + std::string Error; + auto Reader = IndexRecordReader::createWithFilePath(Filename, Error); + if (!Reader) { + errs() << Error << '\n'; + return true; + } + + Reader->foreachDecl(/*noCache=*/true, [&](const IndexRecordDecl *Rec)->bool { + printSymbol(*Rec, OS); + return true; + }); + OS << "------------\n"; + Reader->foreachOccurrence([&](const IndexRecordOccurrence &Rec)->bool { + printSymbol(Rec, OS); + return true; + }); + + return false; +}; + +//===----------------------------------------------------------------------===// +// Print Store Records +//===----------------------------------------------------------------------===// + +static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS); +static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS); + +static bool printStoreRecord(indexstore::IndexStore &Store, StringRef RecName, + StringRef FilePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexRecordReader Reader(Store, RecName, Error); + if (!Reader) { + errs() << "error loading record: " << Error << "\n"; + return true; + } + + StringRef Filename = sys::path::filename(FilePath); + OS << Filename << '\n'; + OS << "------------\n"; + Reader.foreachSymbol(/*noCache=*/true, [&](indexstore::IndexRecordSymbol Sym) -> bool { + printSymbol(Sym, OS); + return true; + }); + OS << "------------\n"; + Reader.foreachOccurrence([&](indexstore::IndexRecordOccurrence Occur)->bool { + printSymbol(Occur, OS); + return true; + }); + + return false; +} + +static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexStore Store(StorePath, Error); + if (!Store) { + errs() << "error loading store: " << Error << "\n"; + return 1; + } + + bool Success = Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { + indexstore::IndexUnitReader Reader(Store, UnitName, Error); + if (!Reader) { + errs() << "error loading unit: " << Error << "\n"; + return false; + } + return Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { + if (Dep.getKind() == indexstore::IndexUnitDependency::DependencyKind::Record) { + bool Err = printStoreRecord(Store, Dep.getName(), Dep.getFilePath(), OS); + OS << '\n'; + return !Err; + } + return true; + }); + }); + + return !Success; +} + +static std::string findRecordNameForFile(indexstore::IndexStore &store, + StringRef filePath) { + std::string recName; + store.foreachUnit(/*sorted=*/false, [&](StringRef unitName) -> bool { + std::string error; + indexstore::IndexUnitReader Reader(store, unitName, error); + if (!Reader) { + errs() << "error loading unit: " << error << "\n"; + return false; + } + Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { + if (Dep.getKind() == indexstore::IndexUnitDependency::DependencyKind::Record) { + if (Dep.getFilePath() == filePath) { + recName = Dep.getName(); + return false; + } + return true; + } + return true; + }); + return true; + }); + return recName; +} + +static int printStoreFileRecord(StringRef storePath, StringRef filePath, + Optional<unsigned> lineStart, unsigned lineCount, + raw_ostream &OS) { + std::string error; + indexstore::IndexStore store(storePath, error); + if (!store) { + errs() << "error loading store: " << error << "\n"; + return 1; + } + + std::string recName = findRecordNameForFile(store, filePath); + if (recName.empty()) { + errs() << "could not find record for '" << filePath << "'\n"; + return 1; + } + + if (!lineStart.hasValue()) + return printStoreRecord(store, recName, filePath, OS); + + indexstore::IndexRecordReader Reader(store, recName, error); + if (!Reader) { + errs() << "error loading record: " << error << "\n"; + return 1; + } + + Reader.foreachOccurrenceInLineRange(*lineStart, lineCount, [&](indexstore::IndexRecordOccurrence Occur)->bool { + printSymbol(Occur, OS); + return true; + }); + + return 0; +} + + +//===----------------------------------------------------------------------===// +// Print Unit +//===----------------------------------------------------------------------===// + +static int printUnit(StringRef Filename, raw_ostream &OS) { + std::string Error; + auto Reader = IndexUnitReader::createWithFilePath(Filename, Error); + if (!Reader) { + errs() << Error << '\n'; + return true; + } + + OS << "provider: " << Reader->getProviderIdentifier() << '-' << Reader->getProviderVersion() << '\n'; + OS << "is-system: " << Reader->isSystemUnit() << '\n'; + OS << "is-module: " << Reader->isModuleUnit() << '\n'; + OS << "module-name: " << (Reader->getModuleName().empty() ? "<none>" : Reader->getModuleName()) << '\n'; + OS << "has-main: " << Reader->hasMainFile() << '\n'; + OS << "main-path: " << Reader->getMainFilePath() << '\n'; + OS << "work-dir: " << Reader->getWorkingDirectory() << '\n'; + OS << "out-file: " << Reader->getOutputFile() << '\n'; + OS << "target: " << Reader->getTarget() << '\n'; + OS << "is-debug: " << Reader->isDebugCompilation() << '\n'; + OS << "DEPEND START\n"; + unsigned NumDepends = 0; + Reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &Dep) -> bool { + switch (Dep.Kind) { + case IndexUnitReader::DependencyKind::Unit: + OS << "Unit | "; break; + case IndexUnitReader::DependencyKind::Record: + OS << "Record | "; break; + case IndexUnitReader::DependencyKind::File: + OS << "File | "; break; + } + OS << (Dep.IsSystem ? "system" : "user"); + OS << " | "; + if (!Dep.ModuleName.empty()) + OS << Dep.ModuleName << " | "; + OS << Dep.FilePath << " | " << Dep.UnitOrRecordName << " | "; + OS << Dep.ModTime << " | " << Dep.FileSize << '\n'; + ++NumDepends; + return true; + }); + OS << "DEPEND END (" << NumDepends << ")\n"; + OS << "INCLUDE START\n"; + unsigned NumIncludes = 0; + Reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &Inc) -> bool { + OS << Inc.SourcePath << ":" << Inc.SourceLine << " | "; + OS << Inc.TargetPath << '\n'; + ++NumIncludes; + return true; + }); + OS << "INCLUDE END (" << NumIncludes << ")\n"; + + return false; +}; + +//===----------------------------------------------------------------------===// +// Print Store Units +//===----------------------------------------------------------------------===// + +static bool printStoreUnit(indexstore::IndexStore &Store, StringRef UnitName, + raw_ostream &OS) { + std::string Error; + indexstore::IndexUnitReader Reader(Store, UnitName, Error); + if (!Reader) { + errs() << "error loading unit: " << Error << "\n"; + return true; + } + + OS << "provider: " << Reader.getProviderIdentifier() << '-' << Reader.getProviderVersion() << '\n'; + OS << "is-system: " << Reader.isSystemUnit() << '\n'; + OS << "is-module: " << Reader.isModuleUnit() << '\n'; + OS << "module-name: " << (Reader.getModuleName().empty() ? "<none>" : Reader.getModuleName()) << '\n'; + OS << "has-main: " << Reader.hasMainFile() << '\n'; + OS << "main-path: " << Reader.getMainFilePath() << '\n'; + OS << "work-dir: " << Reader.getWorkingDirectory() << '\n'; + OS << "out-file: " << Reader.getOutputFile() << '\n'; + OS << "target: " << Reader.getTarget() << '\n'; + OS << "is-debug: " << Reader.isDebugCompilation() << '\n'; + OS << "DEPEND START\n"; + unsigned NumDepends = 0; + Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool { + switch (Dep.getKind()) { + case indexstore::IndexUnitDependency::DependencyKind::Unit: + OS << "Unit | "; break; + case indexstore::IndexUnitDependency::DependencyKind::Record: + OS << "Record | "; break; + case indexstore::IndexUnitDependency::DependencyKind::File: + OS << "File | "; break; + } + OS << (Dep.isSystem() ? "system" : "user"); + OS << " | "; + if (!Dep.getModuleName().empty()) + OS << Dep.getModuleName() << " | "; + OS << Dep.getFilePath() << " | " << Dep.getName() << " | "; + OS << Dep.getModificationTime() << '\n'; + ++NumDepends; + return true; + }); + OS << "DEPEND END (" << NumDepends << ")\n"; + OS << "INCLUDE START\n"; + unsigned NumIncludes = 0; + Reader.foreachInclude([&](indexstore::IndexUnitInclude Inc) -> bool { + OS << Inc.getSourcePath() << ":" << Inc.getSourceLine() << " | "; + OS << Inc.getTargetPath() << '\n'; + ++NumIncludes; + return true; + }); + OS << "INCLUDE END (" << NumIncludes << ")\n"; + + return false; +} + +static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { + std::string Error; + indexstore::IndexStore Store(StorePath, Error); + if (!Store) { + errs() << "error loading store: " << Error << "\n"; + return 1; + } + + bool Success = Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool { + OS << UnitName << '\n'; + OS << "--------\n"; + bool err = printStoreUnit(Store, UnitName, OS); + OS << '\n'; + return !err; + }); + + return !Success; +} + + +#else + +static int printUnit(StringRef Filename, raw_ostream &OS) { + return 1; +} + +static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { + return 1; +} + +#endif + //===----------------------------------------------------------------------===// // Helper Utils //===----------------------------------------------------------------------===// @@ -266,10 +605,208 @@ static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, } } +#if INDEXSTORE_HAS_BLOCKS + +static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS) { + printSymbolInfo(Rec.SymInfo, OS); + OS << " | "; + + if (Rec.Name.empty()) + OS << "<no-name>"; + else + OS << Rec.Name; + OS << " | "; + + if (Rec.USR.empty()) + OS << "<no-usr>"; + else + OS << Rec.USR; + OS << " | "; + + if (Rec.CodeGenName.empty()) + OS << "<no-cgname>"; + else + OS << Rec.CodeGenName; + OS << " | "; + + printSymbolRoles(Rec.Roles, OS); + OS << " - "; + printSymbolRoles(Rec.RelatedRoles, OS); + OS << '\n'; +} + +static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS) { + OS << Rec.Line << ':' << Rec.Column << " | "; + printSymbolInfo(Rec.Dcl->SymInfo, OS); + OS << " | "; + + if (Rec.Dcl->USR.empty()) + OS << "<no-usr>"; + else + OS << Rec.Dcl->USR; + OS << " | "; + + printSymbolRoles(Rec.Roles, OS); + OS << " | "; + OS << "rel: " << Rec.Relations.size() << '\n'; + for (auto &Rel : Rec.Relations) { + OS << '\t'; + printSymbolRoles(Rel.Roles, OS); + OS << " | "; + if (Rel.Dcl->USR.empty()) + OS << "<no-usr>"; + else + OS << Rel.Dcl->USR; + OS << '\n'; + } +} + +static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS) { + SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), + getSymbolSubKind(Sym.getSubKind()), + SymbolPropertySet(Sym.getProperties()), + getSymbolLanguage(Sym.getLanguage())}; + + printSymbolInfo(SymInfo, OS); + OS << " | "; + + if (Sym.getName().empty()) + OS << "<no-name>"; + else + OS << Sym.getName(); + OS << " | "; + + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << " | "; + + if (Sym.getCodegenName().empty()) + OS << "<no-cgname>"; + else + OS << Sym.getCodegenName(); + OS << " | "; + + printSymbolRoles(Sym.getRoles(), OS); + OS << " - "; + printSymbolRoles(Sym.getRelatedRoles(), OS); + OS << '\n'; +} + +static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS) { + OS << Occur.getLineCol().first << ':' << Occur.getLineCol().second << " | "; + auto Sym = Occur.getSymbol(); + SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), + getSymbolSubKind(Sym.getSubKind()), + SymbolPropertySet(Sym.getProperties()), + getSymbolLanguage(Sym.getLanguage())}; + + printSymbolInfo(SymInfo, OS); + OS << " | "; + + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << " | "; + + unsigned NumRelations = 0; + Occur.foreachRelation([&](indexstore::IndexSymbolRelation) { + ++NumRelations; + return true; + }); + + printSymbolRoles(Occur.getRoles(), OS); + OS << " | "; + OS << "rel: " << NumRelations << '\n'; + Occur.foreachRelation([&](indexstore::IndexSymbolRelation Rel) { + OS << '\t'; + printSymbolRoles(Rel.getRoles(), OS); + OS << " | "; + auto Sym = Rel.getSymbol(); + if (Sym.getUSR().empty()) + OS << "<no-usr>"; + else + OS << Sym.getUSR(); + OS << '\n'; + return true; + }); +} + +#else + +static int printRecord(StringRef Filename, raw_ostream &OS) { + return 1; +} +static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { + return 1; +} + +#endif + +static int watchDirectory(StringRef dirPath) { + raw_ostream &OS = outs(); + auto receiver = [&](ArrayRef<DirectoryWatcher::Event> Events, bool isInitial) { + for (auto evt : Events) { + switch (evt.Kind) { + case DirectoryWatcher::EventKind::Added: + OS << "added: "; break; + case DirectoryWatcher::EventKind::Modified: + OS << "modified: "; break; + case DirectoryWatcher::EventKind::Removed: + OS << "removed: "; break; + case DirectoryWatcher::EventKind::DirectoryDeleted: + OS << "dir deleted: "; break; + + } + OS << evt.Filename << '\n'; + } + }; + std::string Error; + auto watcher = DirectoryWatcher::create(dirPath, receiver, + /*waitInitialSync=*/true, Error); + if (!watcher) { + errs() << "failed creating directory watcher: " << Error << '\n'; + return 1; + } +#if HAVE_CORESERVICES + dispatch_main(); +#endif +} + //===----------------------------------------------------------------------===// // Command line processing. //===----------------------------------------------------------------------===// +bool deconstructPathAndRange(StringRef input, + std::string &filepath, + Optional<unsigned> &lineStart, + unsigned &lineCount) { + StringRef path, range; + std::tie(path, range) = input.split(':'); + StringRef start, end; + std::tie(start, end) = range.split(':'); + filepath = path; + lineCount = 0; + if (start.empty()) + return false; + unsigned num; + if (start.getAsInteger(10, num)) { + errs() << "couldn't convert to integer: " << start << '\n'; + return true; + } + lineStart = num; + if (end.empty()) + return false; + if (end.getAsInteger(10, num)) { + errs() << "couldn't convert to integer: " << end << '\n'; + return true; + } + lineCount = num-lineStart.getValue(); + return false; +} + int indextest_core_main(int argc, const char **argv) { sys::PrintStackTraceOnErrorSignal(argv[0]); PrettyStackTraceProgram X(argc, argv); @@ -305,5 +842,73 @@ int indextest_core_main(int argc, const char **argv) { return printSourceSymbols(CompArgs, options::DumpModuleImports, options::IncludeLocals); } + if (options::Action == ActionType::PrintRecord) { + if (!options::FilePathAndRange.empty()) { + std::string filepath; + Optional<unsigned> lineStart; + unsigned lineCount; + if (deconstructPathAndRange(options::FilePathAndRange, + filepath, lineStart, lineCount)) + return 1; + + if (options::InputFiles.empty()) { + errs() << "error: missing index store path\n"; + return 1; + } + return printStoreFileRecord(options::InputFiles[0], filepath, lineStart, lineCount, outs()); + } + + if (options::InputFiles.empty()) { + errs() << "error: missing input file or directory\n"; + return 1; + } + + if (sys::fs::is_directory(options::InputFiles[0])) + return printStoreRecords(options::InputFiles[0], outs()); + else + return printRecord(options::InputFiles[0], outs()); + } + + if (options::Action == ActionType::PrintUnit) { + if (options::InputFiles.empty()) { + errs() << "error: missing input file or directory\n"; + return 1; + } + + if (sys::fs::is_directory(options::InputFiles[0])) + return printStoreUnits(options::InputFiles[0], outs()); + else + return printUnit(options::InputFiles[0], outs()); + } + + if (options::Action == ActionType::PrintStoreFormatVersion) { + outs() << indexstore::IndexStore::formatVersion() << '\n'; + } + + if (options::Action == ActionType::AggregateAsJSON) { + if (options::InputFiles.empty()) { + errs() << "error: missing input data store directory\n"; + return 1; + } + StringRef storePath = options::InputFiles[0]; + if (options::OutputFile.empty()) + return aggregateDataAsJSON(storePath, outs()); + std::error_code EC; + raw_fd_ostream OS(options::OutputFile, EC, llvm::sys::fs::F_None); + if (EC) { + errs() << "failed to open output file: " << EC.message() << '\n'; + return 1; + } + return aggregateDataAsJSON(storePath, OS); + } + + if (options::Action == ActionType::WatchDir) { + if (options::InputFiles.empty()) { + errs() << "error: missing directory path\n"; + return 1; + } + return watchDirectory(options::InputFiles[0]); + } + return 0; } From 8289650bc1334a77f1a87427d389e2adf8e8e42f Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 26 Jun 2017 11:34:44 +0100 Subject: [PATCH 173/582] "Generate Missing Function Definitions" should insert method stubs into the class @implementation when there's no appropriate category implementation This will require a slight SK update as well. rdar://32875896 apple-llvm-split-commit: bd2ec9d3e33564fa2b1d33bcea293bfce6160ddf apple-llvm-split-dir: clang/ --- .../Refactor/ASTStateSerialization.cpp | 2 ++ .../Refactor/ImplementDeclaredMethods.cpp | 28 ++++++++++++++----- .../Refactor/RefactoringContinuations.h | 9 ++++-- .../implement-declared-methods.m | 11 ++++++++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp index 3fa2e606e1f79..2d10fbc31ad33 100644 --- a/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp +++ b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp @@ -40,6 +40,8 @@ class USRToDeclConverter } // end anonymous namespace const Decl *PersistentToASTSpecificStateConverter::lookupDecl(StringRef USR) { + if (USR.empty()) + return nullptr; auto It = ConvertedDeclRefs.find(USR); if (It != ConvertedDeclRefs.end()) return It->second; diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp index ba4fb903c392c..72649a8f42f21 100644 --- a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -86,11 +86,18 @@ class ImplementDeclaredObjCMethodsOperation : public ImplementDeclaredMethodsOperation< ObjCContainerDecl, ObjCMethodDecl, ImplementDeclaredObjCMethodsOperation> { + const ObjCInterfaceDecl *Interface; + public: ImplementDeclaredObjCMethodsOperation( const ObjCContainerDecl *Container, ArrayRef<const ObjCMethodDecl *> SelectedMethods) - : ImplementDeclaredMethodsOperation(Container, SelectedMethods) {} + : ImplementDeclaredMethodsOperation(Container, SelectedMethods) { + if (const auto *CD = dyn_cast<ObjCCategoryDecl>(Container)) + Interface = CD->getClassInterface(); + else + Interface = nullptr; + } llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor, @@ -100,6 +107,7 @@ class ImplementDeclaredObjCMethodsOperation static llvm::Expected<RefactoringResult> runInImplementationAST(ASTContext &Context, const FileID &File, const ObjCContainerDecl *Container, + const ObjCInterfaceDecl *Interface, ArrayRef<const ObjCMethodDecl *> SelectedMethods); }; @@ -363,27 +371,33 @@ ImplementDeclaredObjCMethodsOperation::perform( using namespace indexer; return continueInExternalASTUnit( fileThatShouldContainImplementationOf(Container), runInImplementationAST, - Container, filter(llvm::makeArrayRef(SelectedMethods), - [](const DeclEntity &D) { return !D.isDefined(); })); + Container, Interface, + filter(llvm::makeArrayRef(SelectedMethods), + [](const DeclEntity &D) { return !D.isDefined(); })); } static const ObjCImplDecl * -getImplementationContainer(const ObjCContainerDecl *Container) { +getImplementationContainer(const ObjCContainerDecl *Container, + const ObjCInterfaceDecl *Interface) { if (!Container) return nullptr; if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(Container)) return ID->getImplementation(); - if (const auto *CD = dyn_cast<ObjCCategoryDecl>(Container)) - return CD->getImplementation(); + if (const auto *CD = dyn_cast<ObjCCategoryDecl>(Container)) { + if (const auto *Impl = CD->getImplementation()) + return Impl; + return getImplementationContainer(Interface, /*Interface=*/nullptr); + } return nullptr; } llvm::Expected<RefactoringResult> ImplementDeclaredObjCMethodsOperation::runInImplementationAST( ASTContext &Context, const FileID &File, const ObjCContainerDecl *Container, + const ObjCInterfaceDecl *Interface, ArrayRef<const ObjCMethodDecl *> SelectedMethods) { const ObjCImplDecl *ImplementationContainer = - getImplementationContainer(Container); + getImplementationContainer(Container, Interface); if (!ImplementationContainer) return llvm::make_error<RefactoringOperationError>( "the target @interface is not implemented in the continuation AST " diff --git a/clang/lib/Tooling/Refactor/RefactoringContinuations.h b/clang/lib/Tooling/Refactor/RefactoringContinuations.h index 25c3a39f89428..87b65351c74cc 100644 --- a/clang/lib/Tooling/Refactor/RefactoringContinuations.h +++ b/clang/lib/Tooling/Refactor/RefactoringContinuations.h @@ -115,7 +115,8 @@ class PersistentToASTSpecificStateConverter { const PersistentDeclRef<T> &Ref, typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = nullptr) { - ConvertedDeclRefs[Ref.USR] = nullptr; + if (!Ref.USR.empty()) + ConvertedDeclRefs[Ref.USR] = nullptr; return true; } @@ -132,8 +133,10 @@ class PersistentToASTSpecificStateConverter { const std::vector<PersistentDeclRef<T>> &Refs, typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = nullptr) { - for (const auto &Ref : Refs) - ConvertedDeclRefs[Ref.USR] = nullptr; + for (const auto &Ref : Refs) { + if (!Ref.USR.empty()) + ConvertedDeclRefs[Ref.USR] = nullptr; + } return true; } diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m index 1c330392d2066..5e56fd0ac8f9d 100644 --- a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m @@ -34,6 +34,7 @@ - (void)someOtherMethod { } // CHECK1: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n+ (void)classMethod { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n- (void)method:(int)x with:(int)y { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 // CHECK2: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n" [[@LINE-2]]:1 // CHECK-EXT: "{{.*}}implement-declared-methods.m" "- (void)anExtensionMethod { \n <#code#>;\n}\n\n" [[@LINE-3]]:1 +// CHECK-CAT-NO-IMPL: "{{.*}}implement-declared-methods.m" "- (void)thisCategoryMethodShouldBeInTheClassImplementation { \n <#code#>;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1 #endif // RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s // RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK2 %s @@ -66,3 +67,13 @@ - (void)anotherMethod { @end // CHECK3: "{{.*}}implement-declared-methods.m" "- (void)categoryMethod { \n <#code#>;\n}\n\n+ (MyClass *)classCategoryMethod { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 // RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-category-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK3 %s + +@interface MyClass (NoCategoryImplementation) + +// category-no-impl-begin: +1:1 +- (void)thisCategoryMethodShouldBeInTheClassImplementation; +// category-no-impl-end: +0:1 + +@end + +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=category-no-impl -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-CAT-NO-IMPL %s From a987013fcefe6f440d5780c2a57ee2725dce0705 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Thu, 29 Jun 2017 17:25:37 -0700 Subject: [PATCH 174/582] [c-index-test] Fix compilation for linux apple-llvm-split-commit: 82f1bbe40a106c5e710c049dd0f60cf3312edcee apple-llvm-split-dir: clang/ --- clang/tools/c-index-test/core_main.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index 1335b05075cfb..0bafcda1f359f 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -572,6 +572,12 @@ static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { return 1; } +static int printStoreFileRecord(StringRef storePath, StringRef filePath, + Optional<unsigned> lineStart, unsigned lineCount, + raw_ostream &OS) { + return 1; +} + #endif //===----------------------------------------------------------------------===// @@ -772,6 +778,8 @@ static int watchDirectory(StringRef dirPath) { } #if HAVE_CORESERVICES dispatch_main(); +#else + return 1; #endif } @@ -881,9 +889,11 @@ int indextest_core_main(int argc, const char **argv) { return printUnit(options::InputFiles[0], outs()); } +#if INDEXSTORE_HAS_BLOCKS if (options::Action == ActionType::PrintStoreFormatVersion) { outs() << indexstore::IndexStore::formatVersion() << '\n'; } +#endif if (options::Action == ActionType::AggregateAsJSON) { if (options::InputFiles.empty()) { From c8dce8a2e5ca19f50b740344dade7100734366ef Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 30 Jun 2017 10:58:00 +0100 Subject: [PATCH 175/582] Look through declarations that start in a macro but end up in a source file when searching for a declaration at some location rdar://32934347 apple-llvm-split-commit: 6ea0e95dbd98a66942c6aaf9d872a70813c87b3f apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/USRFinder.cpp | 14 +++++++++----- .../test/Refactor/Rename/Inputs/TransparentEnum.h | 1 + clang/test/Refactor/Rename/TransparentTypedef.m | 13 +++++++++++-- 3 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 clang/test/Refactor/Rename/Inputs/TransparentEnum.h diff --git a/clang/lib/Tooling/Refactor/USRFinder.cpp b/clang/lib/Tooling/Refactor/USRFinder.cpp index d4e78bd28a6b3..95e5244639daa 100644 --- a/clang/lib/Tooling/Refactor/USRFinder.cpp +++ b/clang/lib/Tooling/Refactor/USRFinder.cpp @@ -556,6 +556,7 @@ const NamedDecl *getNamedDeclAt(const ASTContext &Context, SourceLocation Point) { if (Point.isMacroID()) Point = Context.getSourceManager().getSpellingLoc(Point); + // FIXME: If point is in a system header, return early here. OccurrenceCheckerType PointChecker = [Point, &Context]( const NamedDecl *Decl, SourceLocation Start, SourceLocation End) -> bool { @@ -566,13 +567,16 @@ const NamedDecl *getNamedDeclAt(const ASTContext &Context, NamedDeclFindingASTVisitor Visitor(PointChecker, Context); // We only want to search the decls that exist in the same file as the point. - StringRef SearchFile = Context.getSourceManager().getFilename(Point); + FileID InitiationFile = Context.getSourceManager().getFileID(Point); for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { - const SourceLocation FileLoc = CurrDecl->getLocStart(); - StringRef FileName = Context.getSourceManager().getFilename(FileLoc); - // FIME: Skip system headers. + const SourceRange DeclRange = CurrDecl->getSourceRange(); + SourceLocation FileLoc; + if (DeclRange.getBegin().isMacroID() && !DeclRange.getEnd().isMacroID()) + FileLoc = DeclRange.getEnd(); + else + FileLoc = Context.getSourceManager().getSpellingLoc(DeclRange.getBegin()); // FIXME: Add test. - if (FileName == SearchFile) + if (Context.getSourceManager().getFileID(FileLoc) == InitiationFile) Visitor.TraverseDecl(CurrDecl); if (Visitor.isDone()) break; diff --git a/clang/test/Refactor/Rename/Inputs/TransparentEnum.h b/clang/test/Refactor/Rename/Inputs/TransparentEnum.h new file mode 100644 index 0000000000000..b21adae6ae786 --- /dev/null +++ b/clang/test/Refactor/Rename/Inputs/TransparentEnum.h @@ -0,0 +1 @@ +#define TRANSPARENT_ENUM(_name, _type) enum _name:_type _name; enum _name : _type diff --git a/clang/test/Refactor/Rename/TransparentTypedef.m b/clang/test/Refactor/Rename/TransparentTypedef.m index 5717bb8636cd7..1a2dcb1e5afea 100644 --- a/clang/test/Refactor/Rename/TransparentTypedef.m +++ b/clang/test/Refactor/Rename/TransparentTypedef.m @@ -36,5 +36,14 @@ typedef OPAQUE(Separate) { // CHECK3: rename [[@LINE]]:16 -> [[@LINE]]:24 Separate separateT; // CHECK3: rename [[@LINE]]:1 -> [[@LINE]]:9 struct Separate separateE; // CHECK4: rename [[@LINE]]:8 -> [[@LINE]]:16 -// RUN: clang-refactor-test rename-initiate -at=%s:31:16 -at=%s:36:1 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK3 %s -// RUN: clang-refactor-test rename-initiate -at=%s:37:8 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:36:1 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:31:16 -at=%s:37:8 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK4 %s + +#include "Inputs/TransparentEnum.h" + +// CHECK5: 'c:@E@AnotherEnum2' +typedef TRANSPARENT_ENUM(AnotherEnum2, int) { // CHECK5: rename [[@LINE]]:26 -> [[@LINE]]:38 + EnumThing = 0, // CHECK6: [[@LINE]]:3 -> [[@LINE]]:12 +}; +// RUN: clang-refactor-test rename-initiate -at=%s:45:26 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:46:3 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s From 5ecdb772817b8493fe313aeca51d46249404b563 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 26 Jun 2017 15:31:37 +0100 Subject: [PATCH 176/582] [refactor] Indexed file rename should match 'prop' when renaming 'setProp' rdar://32375673 apple-llvm-split-commit: edeb489d3049373634384b580bb1b925dae10481 apple-llvm-split-dir: clang/ --- .../clang/Tooling/Refactor/RenamedSymbol.h | 32 ++++++++-- .../Tooling/Refactor/RenameIndexedFile.cpp | 64 ++++++++++++++----- .../Refactor/Rename/IndexedObjCProperty.m | 15 +++++ clang/tools/libclang/CRefactor.cpp | 18 +++--- 4 files changed, 96 insertions(+), 33 deletions(-) diff --git a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h index 5c5a9061eac72..d54c49799e821 100644 --- a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h +++ b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h @@ -54,6 +54,9 @@ class Symbol { /// account for things like Objective-C selectors. // TODO: Rename class SymbolOccurrence { + /// The source locations that correspond to the occurence of the symbol. + SmallVector<SourceLocation, 4> Locations; + public: enum OccurrenceKind { /// \brief This occurrence is an exact match and can be renamed @@ -87,29 +90,44 @@ class SymbolOccurrence { /// The index of the symbol stored in a \c SymbolOperation which matches this /// occurrence. unsigned SymbolIndex; - /// The source locations that correspond to the occurence of the symbol. - SmallVector<SourceLocation, 4> Locations; SymbolOccurrence() : Kind(MatchingSymbol), IsMacroExpansion(false), SymbolIndex(0) {} SymbolOccurrence(OccurrenceKind Kind, bool IsMacroExpansion, unsigned SymbolIndex, ArrayRef<SourceLocation> Locations) - : Kind(Kind), IsMacroExpansion(IsMacroExpansion), - SymbolIndex(SymbolIndex), - Locations(Locations.begin(), Locations.end()) { + : Locations(Locations.begin(), Locations.end()), Kind(Kind), + IsMacroExpansion(IsMacroExpansion), SymbolIndex(SymbolIndex) { assert(!Locations.empty() && "Renamed occurence without locations!"); } SymbolOccurrence(SymbolOccurrence &&) = default; SymbolOccurrence &operator=(SymbolOccurrence &&) = default; + ArrayRef<SourceLocation> locations() const { + if (Kind == MatchingImplicitProperty && Locations.size() == 2) + return llvm::makeArrayRef(Locations).drop_back(); + return Locations; + } + /// Return the source range that corresponds to an individual source location /// in this occurrence. SourceRange getLocationRange(SourceLocation Loc, size_t OldNameSize) const { - return SourceRange( - Loc, IsMacroExpansion ? Loc : Loc.getLocWithOffset(OldNameSize)); + SourceLocation EndLoc; + // Implicit property references might store the end as the second location + // to take into account the match for 'prop' when the old name is 'setProp'. + if (Kind == MatchingImplicitProperty && Locations.size() == 2) { + assert(Loc == Locations[0] && "invalid loc"); + EndLoc = Locations[1]; + } else + EndLoc = IsMacroExpansion ? Loc : Loc.getLocWithOffset(OldNameSize); + return SourceRange(Loc, EndLoc); } + + friend bool operator<(const SymbolOccurrence &LHS, + const SymbolOccurrence &RHS); + friend bool operator==(const SymbolOccurrence &LHS, + const SymbolOccurrence &RHS); }; /// \brief Less-than operator between the two renamed symbol occurrences. diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index 5ec491c557b6b..55238bc0788ba 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -48,22 +48,36 @@ IndexedFileOccurrenceProducer::IndexedFileOccurrenceProducer( namespace { -enum class MatchKind { SourceMatch, MacroExpansion, None }; +enum class MatchKind { + SourceMatch, + SourcePropSetterMatch, + MacroExpansion, + None +}; } // end anonymous namespace +static bool isSetterNameEqualToPropName(StringRef SetterName, + StringRef PropertyName) { + assert(SetterName.startswith("set") && "invalid setter name"); + SetterName = SetterName.drop_front(3); + return SetterName[0] == toUppercase(PropertyName[0]) && + SetterName.drop_front() == PropertyName.drop_front(); +} + static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence, const IndexedSymbol &Symbol, const SourceManager &SM, const LangOptions &LangOpts, - SourceLocation &BeginLoc) { + SourceRange &SymbolRange, + bool AllowObjCSetterProp = false) { if (!Occurrence.Line || !Occurrence.Column) return MatchKind::None; // Ignore any invalid indexed locations. // Ensure that the first string in the name is present at the given // location. - BeginLoc = SM.translateLineCol(SM.getMainFileID(), Occurrence.Line, - Occurrence.Column); + SourceLocation BeginLoc = SM.translateLineCol( + SM.getMainFileID(), Occurrence.Line, Occurrence.Column); if (BeginLoc.isInvalid()) return MatchKind::None; StringRef SymbolNameStart = Symbol.Name[0]; @@ -83,8 +97,17 @@ static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence, RawLex.LexFromRawLexer(Tok); if (Tok.isNot(tok::raw_identifier) || Tok.getLocation() != BeginLoc) return MatchKind::None; - return Tok.getRawIdentifier() == SymbolNameStart ? MatchKind::SourceMatch - : MatchKind::MacroExpansion; + SymbolRange = SourceRange(BeginLoc, Tok.getEndLoc()); + if (Tok.getRawIdentifier() == SymbolNameStart) + return MatchKind::SourceMatch; + // Match 'prop' when looking for 'setProp'. + // FIXME: Verify that the previous token is a '.' to be sure. + if (AllowObjCSetterProp && + Occurrence.Kind == IndexedOccurrence::IndexedObjCMessageSend && + SymbolNameStart.startswith("set") && + isSetterNameEqualToPropName(SymbolNameStart, Tok.getRawIdentifier())) + return MatchKind::SourcePropSetterMatch; + return MatchKind::MacroExpansion; } static void @@ -366,16 +389,22 @@ void IndexedFileOccurrenceProducer::ExecuteAction() { Consumer); continue; } - SourceLocation BeginLoc; - MatchKind Match = - checkOccurrence(Occurrence, Symbol.value(), SM, LangOpts, BeginLoc); + SourceRange SymbolRange; + MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM, + LangOpts, SymbolRange, + /*AllowObjCSetterProp=*/true); if (Match == MatchKind::None) continue; - - SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol, - /*IsMacroExpansion=*/Match == - MatchKind::MacroExpansion, - Symbol.index(), BeginLoc); + llvm::SmallVector<SourceLocation, 2> Locs; + Locs.push_back(SymbolRange.getBegin()); + bool IsImpProp = Match == MatchKind::SourcePropSetterMatch; + if (IsImpProp) + Locs.push_back(SymbolRange.getEnd()); + SymbolOccurrence Result( + IsImpProp ? SymbolOccurrence::MatchingImplicitProperty + : SymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/Match == MatchKind::MacroExpansion, + Symbol.index(), Locs); Consumer.handleOccurrence(Result, SM, LangOpts); } } @@ -492,11 +521,12 @@ findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, // Selectors and names in #includes shouldn't really mix. if (Occurrence.Kind == IndexedOccurrence::InclusionDirective) continue; - SourceLocation Loc; - MatchKind Match = - checkOccurrence(Occurrence, Symbol.value(), SM, LangOpts, Loc); + SourceRange SymbolRange; + MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM, + LangOpts, SymbolRange); if (Match == MatchKind::None) continue; + SourceLocation Loc = SymbolRange.getBegin(); if (Match == MatchKind::MacroExpansion) { SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol, /*IsMacroExpansion=*/true, Symbol.index(), Loc); diff --git a/clang/test/Refactor/Rename/IndexedObjCProperty.m b/clang/test/Refactor/Rename/IndexedObjCProperty.m index d8725754932ea..003186dab99dc 100644 --- a/clang/test/Refactor/Rename/IndexedObjCProperty.m +++ b/clang/test/Refactor/Rename/IndexedObjCProperty.m @@ -28,3 +28,18 @@ - (void)foo { // CHECK: selector [[@LINE-3]]:11 // CHECK: selector "setFoo" [[@LINE-3]]:11 // CHECK-NOT: selector + +@interface ImplicitProperty + +- (int)implicit; // IMPL_GET: rename [[@LINE]]:8 -> [[@LINE]]:16 +- (void)setImplicit:(int)x; // IMPL_SET: rename [[@LINE]]:9 -> [[@LINE]]:20 + +@end + +void useImplicitProperty(ImplicitProperty *x) { + x.implicit; // IMPL_GET: rename [[@LINE]]:5 -> [[@LINE]]:13 + x.implicit = 0; // IMPL_SET: implicit-property [[@LINE]]:5 -> [[@LINE]]:13 +} + +// RUN: clang-refactor-test rename-indexed-file -name=implicit -new-name=foo -indexed-file=%s -indexed-at=objc-im:34:8 -indexed-at=objc-message:40:5 %s | FileCheck --check-prefix=IMPL_GET %s +// RUN: clang-refactor-test rename-indexed-file -name=setImplicit -new-name=setFoo -indexed-file=%s -indexed-at=objc-im:35:9 -indexed-at=objc-message:41:5 %s | FileCheck --check-prefix=IMPL_SET %s diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index 098e685f880e3..506f8d28f4280 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -101,15 +101,15 @@ class RenamingResult { void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, const SourceManager &SM, const LangOptions &LangOpts) { CXRefactoringReplacement_Old *OccurrenceReplacements = - Replacements.Allocate(RenamedOccurrence.Locations.size()); + Replacements.Allocate(RenamedOccurrence.locations().size()); unsigned I = 0; const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; if (!RenamedOccurrence.IsMacroExpansion && RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) - assert(RenamedOccurrence.Locations.size() == SymbolNameInfo.size()); - for (const auto &Location : RenamedOccurrence.Locations) { + assert(RenamedOccurrence.locations().size() == SymbolNameInfo.size()); + for (const auto &Location : RenamedOccurrence.locations()) { CXSourceRange Range = cxloc::translateSourceRange( SM, LangOpts, CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange( @@ -188,7 +188,7 @@ class RenamingResult { llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; for (auto &Occurrence : Results) { const std::pair<FileID, unsigned> DecomposedLocation = - SM.getDecomposedLoc(Occurrence.Locations[0]); + SM.getDecomposedLoc(Occurrence.locations()[0]); const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); assert(Entry && "Invalid file entry"); auto &FileOccurrences = @@ -259,16 +259,16 @@ class SymbolOccurrencesResult { void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, const SourceManager &SM, const LangOptions &LangOpts) { - CXFileRange *OccurrenceRanges = - Ranges.Allocate(RenamedOccurrence.Locations.size()); + ArrayRef<SourceLocation> Locations = RenamedOccurrence.locations(); + CXFileRange *OccurrenceRanges = Ranges.Allocate(Locations.size()); unsigned I = 0; const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; if (!RenamedOccurrence.IsMacroExpansion && RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) - assert(RenamedOccurrence.Locations.size() == SymbolNameInfo.size()); - for (const auto &Location : RenamedOccurrence.Locations) { + assert(Locations.size() == SymbolNameInfo.size()); + for (const auto &Location : Locations) { CXSourceRange Range = cxloc::translateSourceRange( SM, LangOpts, CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange( @@ -327,7 +327,7 @@ class SymbolOccurrencesResult { llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; for (auto &Occurrence : Results) { const std::pair<FileID, unsigned> DecomposedLocation = - SM.getDecomposedLoc(Occurrence.Locations[0]); + SM.getDecomposedLoc(Occurrence.locations()[0]); const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); assert(Entry && "Invalid file entry"); auto &FileOccurrences = From d965fa06180845991960ec28884955016f776fa9 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Sun, 2 Jul 2017 14:58:30 -0700 Subject: [PATCH 177/582] Update IndexerQueries.cpp according to changes made in r306878. Patch by Michael Zolotukhin. apple-llvm-split-commit: 9fb6d709f324c0e5580ce5f087e0014db227581b apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/IndexerQueries.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/IndexerQueries.cpp b/clang/lib/Tooling/Refactor/IndexerQueries.cpp index 063a27be3b83a..bf4fb6539cfba 100644 --- a/clang/lib/Tooling/Refactor/IndexerQueries.cpp +++ b/clang/lib/Tooling/Refactor/IndexerQueries.cpp @@ -59,7 +59,6 @@ struct QueryYAMLNode { } // end anonymous namespace -LLVM_YAML_IS_SEQUENCE_VECTOR(int) LLVM_YAML_IS_SEQUENCE_VECTOR(QueryPredicateNode) LLVM_YAML_IS_SEQUENCE_VECTOR(QueryYAMLNode) From ddddfa0063fdd36ffe07d77026f794276911ad20 Mon Sep 17 00:00:00 2001 From: Ben Langmuir <blangmuir@apple.com> Date: Wed, 12 Jul 2017 13:27:02 -0700 Subject: [PATCH 178/582] [index] Add a test for a crash with unnamed NamedDecls Previously this would crash during record hashing. rdar://problem/32474406 apple-llvm-split-commit: f127690cd0cafdd53c70f839dddaab98eca7406f apple-llvm-split-dir: clang/ --- .../Store/record-hash-crash-invalid-name.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 clang/test/Index/Store/record-hash-crash-invalid-name.cpp diff --git a/clang/test/Index/Store/record-hash-crash-invalid-name.cpp b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp new file mode 100644 index 0000000000000..4479789e9a236 --- /dev/null +++ b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp @@ -0,0 +1,17 @@ +// Makes sure it doesn't crash. + +// XFAIL: linux + +// RUN: rm -rf %t +// RUN: not %clang_cc1 %s -index-store-path %t/idx -std=c++14 +// RUN: c-index-test core -print-record %t/idx | FileCheck %s + +namespace rdar32474406 { +void foo(); +typedef void (*Func_t)(); +// CHECK: [[@LINE+4]]:1 | type-alias/C | c:record-hash-crash-invalid-name.cpp@N@rdar32474406@T@Func_t | Ref,RelCont | rel: 1 +// CHECK-NEXT: RelCont | c:@N@rdar32474406 +// CHECK: [[@LINE+2]]:14 | function/C | c:@N@rdar32474406@F@foo# | Ref,RelCont | rel: 1 +// CHECK-NEXT: RelCont | c:@N@rdar32474406 +Func_t[] = { foo }; // invalid decomposition +} From 2d8e1c4f761f21623715802e26050e6ccb979aa2 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 10 Jul 2017 17:21:06 +0100 Subject: [PATCH 179/582] [refactor][rename] Indexed ObjC method renames should take empty selector name pieces into account rdar://33188656 apple-llvm-split-commit: 454f38d28bbbe1312e7e8388b45a0ebcc0613e0f apple-llvm-split-dir: clang/ --- .../clang/Tooling/Refactor/SymbolName.h | 8 +++ .../Tooling/Refactor/RenameIndexedFile.cpp | 72 ++++++++++++++----- .../Rename/IndexedObjCMethodEmptySelector.mm | 49 +++++++++++++ 3 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 clang/test/Refactor/Rename/IndexedObjCMethodEmptySelector.mm diff --git a/clang/include/clang/Tooling/Refactor/SymbolName.h b/clang/include/clang/Tooling/Refactor/SymbolName.h index b2d18720987ae..f348a02a813e0 100644 --- a/clang/include/clang/Tooling/Refactor/SymbolName.h +++ b/clang/include/clang/Tooling/Refactor/SymbolName.h @@ -52,6 +52,14 @@ class SymbolName { ArrayRef<std::string> strings() const { return Strings; } + bool containsEmptyPiece() const { + for (const auto &String : Strings) { + if (String.empty()) + return true; + } + return false; + } + void print(raw_ostream &OS) const; private: diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index 55238bc0788ba..82e9c917107e2 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -81,7 +81,9 @@ static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence, if (BeginLoc.isInvalid()) return MatchKind::None; StringRef SymbolNameStart = Symbol.Name[0]; - SourceLocation EndLoc = BeginLoc.getLocWithOffset(SymbolNameStart.size()); + SourceLocation EndLoc = BeginLoc.getLocWithOffset(std::max( + SymbolNameStart.size(), + size_t(1))); // Take empty Objective-C selector pieces into account. if (!SM.isBeforeInTranslationUnit(BeginLoc, EndLoc)) { // Ignore any invalid source ranges. This can occur if the indexed // location is invalid. @@ -95,8 +97,16 @@ static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence, File->getBufferStart() + DecomposedLoc.second, File->getBufferEnd()); Token Tok; RawLex.LexFromRawLexer(Tok); - if (Tok.isNot(tok::raw_identifier) || Tok.getLocation() != BeginLoc) + if (Tok.isNot(tok::raw_identifier) || Tok.getLocation() != BeginLoc) { + if (SymbolNameStart.empty() && Tok.is(tok::colon) && + Tok.getLocation() == BeginLoc) { + // Must be the location of an empty Objective-C selector piece. + SymbolRange = SourceRange(BeginLoc, BeginLoc); + return MatchKind::SourceMatch; + } + // FIXME: Handle empty selector piece in a macro? return MatchKind::None; + } SymbolRange = SourceRange(BeginLoc, Tok.getEndLoc()); if (Tok.getRawIdentifier() == SymbolNameStart) return MatchKind::SourceMatch; @@ -176,18 +186,22 @@ SelectorParser::ParseState SelectorParser::stateForToken(const Token &RawTok) { break; SelectorLocations.clear(); return ExpectingSelectorPiece; - case ExpectingSelectorPiece: + case ExpectingSelectorPiece: { assert(SelectorLocations.size() < Name.size() && "Expecting invalid selector piece"); - if (RawTok.isNot(tok::raw_identifier) || - RawTok.getRawIdentifier() != Name[SelectorLocations.size()]) + StringRef NamePiece = Name[SelectorLocations.size()]; + if ((RawTok.isNot(tok::raw_identifier) || + RawTok.getRawIdentifier() != NamePiece) && + !(NamePiece.empty() && RawTok.is(tok::colon))) { break; + } SelectorLocations.push_back(RawTok.getLocation()); if (SelectorLocations.size() == Name.size()) { // We found the selector that we were looking for, now check for ')'. - return ExpectingRParenOrColon; + return NamePiece.empty() ? ExpectingRParen : ExpectingRParenOrColon; } - return ExpectingColon; + return NamePiece.empty() ? ExpectingSelectorPiece : ExpectingColon; + } case ExpectingColon: if (RawTok.is(tok::colon)) return ExpectingSelectorPiece; @@ -210,6 +224,15 @@ SelectorParser::ParseState SelectorParser::stateForToken(const Token &RawTok) { } bool SelectorParser::handleToken(const Token &RawTok) { + if (RawTok.is(tok::coloncolon)) { + // Split the '::' into two ':'. + Token T(RawTok); + T.setKind(tok::colon); + T.setLength(1); + handleToken(T); + T.setLocation(T.getLocation().getLocWithOffset(1)); + return handleToken(T); + } State = stateForToken(RawTok); if (State != Success) return false; @@ -221,14 +244,18 @@ static void collectTextualMatchesInComment( ArrayRef<IndexedSymbol> Symbols, SourceLocation CommentLoc, StringRef Comment, llvm::SmallVectorImpl<TextualMatchOccurrence> &Result) { for (const auto &Symbol : llvm::enumerate(Symbols)) { + const SymbolName &Name = Symbol.value().Name; + if (Name.containsEmptyPiece()) // Ignore Objective-C selectors with empty + // pieces. + continue; size_t Offset = 0; while (true) { - Offset = Comment.find(Symbol.value().Name[0], /*From=*/Offset); + Offset = Comment.find(Name[0], /*From=*/Offset); if (Offset == StringRef::npos) break; Result.push_back( {CommentLoc.getLocWithOffset(Offset), (unsigned)Symbol.index()}); - Offset += Symbol.value().Name[0].size(); + Offset += Name[0].size(); } } } @@ -432,12 +459,20 @@ enum class ObjCSymbolSelectorKind { MessageSend, MethodDecl }; } // end anonymous namespace +static bool isMatchingSelectorName(const Token &Tok, const Token &Next, + StringRef NamePiece) { + if (NamePiece.empty()) + return Tok.is(tok::colon); + return Tok.is(tok::raw_identifier) && Next.is(tok::colon) && + Tok.getRawIdentifier() == NamePiece; +} + static bool findObjCSymbolSelectorPieces(ArrayRef<Token> Tokens, const SymbolName &Name, SmallVectorImpl<SourceLocation> &Pieces, ObjCSymbolSelectorKind Kind) { assert(!Tokens.empty() && "no tokens"); - assert(Tokens[0].getRawIdentifier() == Name[0]); + assert(Name[0].empty() || Tokens[0].getRawIdentifier() == Name[0]); assert(Name.size() > 1); assert(Pieces.empty()); @@ -453,13 +488,17 @@ findObjCSymbolSelectorPieces(ArrayRef<Token> Tokens, const SymbolName &Name, // Start looking for the next selector piece. unsigned Last = Tokens.size() - 1; // Skip the ':' or any other token after the first selector piece token. - for (unsigned Index = 2; Index < Last; ++Index) { + for (unsigned Index = Name[0].empty() ? 1 : 2; Index < Last; ++Index) { const auto &Tok = Tokens[Index]; bool NoScoping = SquareCount == 0 && BraceCount == 0 && ParenCount == 0; - if (NoScoping && Tok.is(tok::raw_identifier) && - Tokens[Index + 1].is(tok::colon) && - Tok.getRawIdentifier() == Name[Pieces.size()]) { + if (NoScoping && + isMatchingSelectorName(Tok, Tokens[Index + 1], Name[Pieces.size()])) { + if (!Name[Pieces.size()].empty()) { + // Skip the ':' after the name. This ensures that it won't match a + // follow-up selector piece with an empty name. + ++Index; + } Pieces.push_back(Tok.getLocation()); // All the selector pieces have been found. if (Pieces.size() == Name.size()) @@ -565,10 +604,11 @@ findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, auto It = MappedIndexedOccurrences.find(Tok.getLocation().getRawEncoding()); if (It == MappedIndexedOccurrences.end()) continue; - if (Tok.getKind() != tok::raw_identifier) + unsigned SymbolIndex = It->second.second; + if (Tok.getKind() != tok::raw_identifier && + !(Symbols[SymbolIndex].Name[0].empty() && Tok.is(tok::colon))) continue; const IndexedOccurrence &Occurrence = It->second.first; - unsigned SymbolIndex = It->second.second; // Scan the source for the remaining selector pieces. SmallVector<SourceLocation, 4> SelectorPieces; diff --git a/clang/test/Refactor/Rename/IndexedObjCMethodEmptySelector.mm b/clang/test/Refactor/Rename/IndexedObjCMethodEmptySelector.mm new file mode 100644 index 0000000000000..f533331a3f859 --- /dev/null +++ b/clang/test/Refactor/Rename/IndexedObjCMethodEmptySelector.mm @@ -0,0 +1,49 @@ + +@interface EmptySelectorsRule_Psych + +- (int):(int)_; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:8 +- (void)test: (int)x :(int)y; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:22 -> [[@LINE]]:22 +- (void):(int)_ :(int) m:(int)z; // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:9, [[@LINE]]:17 -> [[@LINE]]:17, [[@LINE]]:25 -> [[@LINE]]:25 + +@end + +namespace g { + int x; +} + +@implementation EmptySelectorsRule_Psych + +- (int):(int)_ { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:8 + [self :2]; // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:11 + SEL s0 = @selector(:); // CHECK1: selector [[@LINE]]:24 -> [[@LINE]]:24 + // CHECK1-NOT: comment + // CHECK1-NOT: rename + // CHECK1-NOT: selector +// RUN: clang-refactor-test rename-indexed-file -name=: -new-name=foo -indexed-file=%s -indexed-symbol-kind=objc-im -indexed-at=4:8 -indexed-at=16:8 -indexed-at=objc-message:17:11 %s | FileCheck --check-prefix=CHECK1 %s + return 0; +} +- (void)test: (int)x :(int)y { } // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:22 -> [[@LINE]]:22 +- (void) :(int)_ :(int)m :(int)z { // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:18 -> [[@LINE]]:18, [[@LINE]]:26 -> [[@LINE]]:26 + [self test:0:1]; // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:15, [[@LINE]]:17 -> [[@LINE]]:17 + SEL s1 = @selector(test::); // CHECK2: selector [[@LINE]]:24 -> [[@LINE]]:28, [[@LINE]]:29 -> [[@LINE]]:29 + @selector(test: :); // CHECK2: selector [[@LINE]]:15 -> [[@LINE]]:19, [[@LINE]]:21 -> [[@LINE]]:21 + // CHECK2-NOT: comment + // CHECK2-NOT: rename + // CHECK2-NOT: selector +// RUN: clang-refactor-test rename-indexed-file -name=test:: -new-name=foo:bar: -indexed-file=%s -indexed-symbol-kind=objc-im -indexed-at=5:9 -indexed-at=25:9 -indexed-at=objc-message:27:11 %s | FileCheck --check-prefix=CHECK2 %s + + [self: ::g::x + ([self: 0]):~0 :3]; // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:32 -> [[@LINE]]:32, [[@LINE]]:36 -> [[@LINE]]:36 + SEL s2 = @selector(:::); // CHECK3: selector [[@LINE]]:24 -> [[@LINE]]:24, [[@LINE]]:25 -> [[@LINE]]:25, [[@LINE]]:26 -> [[@LINE]]:26 + @selector(: ::); // CHECK3: selector [[@LINE]]:15 -> [[@LINE]]:15, [[@LINE]]:17 -> [[@LINE]]:17, [[@LINE]]:18 -> [[@LINE]]:18 + @selector(::::); // not matching. + // CHECK3-NOT: comment + // CHECK3-NOT: rename + // CHECK3-NOT: selector +// RUN: clang-refactor-test rename-indexed-file -name=::: -new-name=do:stuff:: -indexed-file=%s -indexed-symbol-kind=objc-im -indexed-at=6:9 -indexed-at=26:10 -indexed-at=objc-message:35:10 %s | FileCheck --check-prefix=CHECK3 %s + + // NO Textual matches: text: + // : + // ::: +} + +@end From 145a7725af64ca55c168aa82513b2c4abfdae226 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 10 Jul 2017 17:40:07 +0100 Subject: [PATCH 180/582] [refactor][rename] Initiate rename even for blank selectors! rdar://33188656 apple-llvm-split-commit: 7088654281909cd409d2c29ec1cab26589038148 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/USRFinder.cpp | 12 ++++++++---- clang/test/Refactor/Rename/ObjCMethod.m | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/clang/lib/Tooling/Refactor/USRFinder.cpp b/clang/lib/Tooling/Refactor/USRFinder.cpp index 95e5244639daa..6959490f014e5 100644 --- a/clang/lib/Tooling/Refactor/USRFinder.cpp +++ b/clang/lib/Tooling/Refactor/USRFinder.cpp @@ -127,8 +127,10 @@ class NamedDeclFindingASTVisitor bool VisitObjCMethodDecl(const ObjCMethodDecl *Decl) { // Check all of the selector source ranges. for (unsigned I = 0, E = Decl->getNumSelectorLocs(); I != E; ++I) { - if (!checkOccurrence(Decl, Decl->getSelectorLoc(I), - Decl->getSelector().getNameForSlot(I).size())) + SourceLocation Loc = Decl->getSelectorLoc(I); + if (!checkOccurrence(Decl, Loc, + Loc.getLocWithOffset( + Decl->getSelector().getNameForSlot(I).size()))) return false; } return true; @@ -247,8 +249,10 @@ class NamedDeclFindingASTVisitor // Check all of the selector source ranges. for (unsigned I = 0, E = Expr->getNumSelectorLocs(); I != E; ++I) { - if (!checkOccurrence(Decl, Expr->getSelectorLoc(I), - Decl->getSelector().getNameForSlot(I).size())) + SourceLocation Loc = Expr->getSelectorLoc(I); + if (!checkOccurrence(Decl, Loc, + Loc.getLocWithOffset( + Decl->getSelector().getNameForSlot(I).size()))) return false; } return true; diff --git a/clang/test/Refactor/Rename/ObjCMethod.m b/clang/test/Refactor/Rename/ObjCMethod.m index 7f3655d8979e3..9a255a6c74590 100644 --- a/clang/test/Refactor/Rename/ObjCMethod.m +++ b/clang/test/Refactor/Rename/ObjCMethod.m @@ -133,3 +133,18 @@ - (void)foo { @end // RUN: not clang-refactor-test rename-initiate -at=%s:130:3 -new-name=foo %s -Wno-objc-root-class 2>&1 | FileCheck --check-prefix=CHECK-NORENAME %s // CHECK-NORENAME: could not rename symbol at the given location + +@interface EmptySelectorsRule_Psych + +- (void):(int)_ :(int) m:(int)z; // EMPTY-SELECTOR: rename [[@LINE]]:9 -> [[@LINE]]:9, [[@LINE]]:17 -> [[@LINE]]:17, [[@LINE]]:25 -> [[@LINE]]:25 + +@end + +@implementation EmptySelectorsRule_Psych + +- (void) :(int)_ :(int)m :(int)z { // EMPTY-SELECTOR: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:18 -> [[@LINE]]:18, [[@LINE]]:26 -> [[@LINE]]:26 + [self: 15:0 :3]; // EMPTY-SELECTOR: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:14 -> [[@LINE]]:14, [[@LINE]]:17 -> [[@LINE]]:17 +} +// RUN: clang-refactor-test rename-initiate -at=%s:139:9 -new-name=test:a: %s -Wno-objc-root-class | FileCheck --check-prefix=EMPTY-SELECTOR %s + +@end From f666256c8f40b6e2e4b9c34bf87035fd68e20b8f Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 12 Jul 2017 14:18:57 +0100 Subject: [PATCH 181/582] rename indexed file: drop redundant beginloc < endloc check Thanks to Ben who pointed this out! rdar://33188656 apple-llvm-split-commit: 13bfdc79c24fb64bd0d6ed86bee47527957c2450 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/RenameIndexedFile.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index 82e9c917107e2..ef88e1fac6b44 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -81,14 +81,6 @@ static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence, if (BeginLoc.isInvalid()) return MatchKind::None; StringRef SymbolNameStart = Symbol.Name[0]; - SourceLocation EndLoc = BeginLoc.getLocWithOffset(std::max( - SymbolNameStart.size(), - size_t(1))); // Take empty Objective-C selector pieces into account. - if (!SM.isBeforeInTranslationUnit(BeginLoc, EndLoc)) { - // Ignore any invalid source ranges. This can occur if the indexed - // location is invalid. - return MatchKind::None; - } // Extract the token at the location. auto DecomposedLoc = SM.getDecomposedLoc(BeginLoc); const llvm::MemoryBuffer *File = SM.getBuffer(DecomposedLoc.first); From a3839bfc32520606e3a2cd82c13e94f7c8ca369a Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 14 Jul 2017 12:07:28 +0100 Subject: [PATCH 182/582] [refactor] Fix a test by adding a missing selector piece apple-llvm-split-commit: 1d5cc186fcc8564ee27a2c95fe4e494fd58806c3 apple-llvm-split-dir: clang/ --- clang/test/Refactor/Rename/ObjCMethod.m | 2 +- clang/tools/clang-refactor-test/ClangRefactorTest.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/test/Refactor/Rename/ObjCMethod.m b/clang/test/Refactor/Rename/ObjCMethod.m index 9a255a6c74590..d2b1ad354883c 100644 --- a/clang/test/Refactor/Rename/ObjCMethod.m +++ b/clang/test/Refactor/Rename/ObjCMethod.m @@ -145,6 +145,6 @@ @implementation EmptySelectorsRule_Psych - (void) :(int)_ :(int)m :(int)z { // EMPTY-SELECTOR: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:18 -> [[@LINE]]:18, [[@LINE]]:26 -> [[@LINE]]:26 [self: 15:0 :3]; // EMPTY-SELECTOR: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:14 -> [[@LINE]]:14, [[@LINE]]:17 -> [[@LINE]]:17 } -// RUN: clang-refactor-test rename-initiate -at=%s:139:9 -new-name=test:a: %s -Wno-objc-root-class | FileCheck --check-prefix=EMPTY-SELECTOR %s +// RUN: clang-refactor-test rename-initiate -at=%s:139:9 -new-name=test:a:: %s -Wno-objc-root-class | FileCheck --check-prefix=EMPTY-SELECTOR %s @end diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index 5f220e40adc2d..524a5f01842bf 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -372,6 +372,8 @@ occurrenceToString(const CXSymbolOccurrence &Occurrence, bool IsLocal, OS << '"' << Filename << "\" "; bool FirstRange = true; + assert(NewName.size() == Occurrence.NumNamePieces && + "new name doesn't match the number of pieces"); for (unsigned J = 0; J != Occurrence.NumNamePieces; ++J) { if (!FirstRange) // TODO OS << ", "; From d73f9d38f952ecf5ed684ac30cf990f85bf3abfe Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 14 Jul 2017 12:30:06 +0100 Subject: [PATCH 183/582] [refactor] The assertion from previous commit should use >= Some occurrences might have fewer pieces than new name. apple-llvm-split-commit: d2e5cf7dd61bd3404ffad4c509da4d287c9435f5 apple-llvm-split-dir: clang/ --- clang/tools/clang-refactor-test/ClangRefactorTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index 524a5f01842bf..a5fd3e487b0eb 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -372,7 +372,7 @@ occurrenceToString(const CXSymbolOccurrence &Occurrence, bool IsLocal, OS << '"' << Filename << "\" "; bool FirstRange = true; - assert(NewName.size() == Occurrence.NumNamePieces && + assert(NewName.size() >= Occurrence.NumNamePieces && "new name doesn't match the number of pieces"); for (unsigned J = 0; J != Occurrence.NumNamePieces; ++J) { if (!FirstRange) // TODO From d1188b9588329155b181bfc3131d1bf7376b9cc9 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 12 Jul 2017 17:22:09 +0100 Subject: [PATCH 184/582] [refactor] "Generate Missing Function Definitions" should insert category methods into class implementation declarations even when the category is not available in the TU in which the class is implemented rdar://32875896 apple-llvm-split-commit: c790745221aa5c187a9819d60645b4ee6e8d13a5 apple-llvm-split-dir: clang/ --- .../clang/Tooling/Refactor/IndexerQuery.h | 38 +++++++++-- .../Refactor/ImplementDeclaredMethods.cpp | 68 +++++++++++-------- clang/lib/Tooling/Refactor/IndexerQueries.cpp | 8 ++- .../Refactor/RefactoringContinuations.h | 50 +++++++++++++- .../Inputs/objcClass.m | 2 + .../implement-declared-methods.m | 2 + clang/tools/libclang/CRefactor.cpp | 12 ++-- 7 files changed, 137 insertions(+), 43 deletions(-) diff --git a/clang/include/clang/Tooling/Refactor/IndexerQuery.h b/clang/include/clang/Tooling/Refactor/IndexerQuery.h index a3b3caa723c44..b14e46a8d75a1 100644 --- a/clang/include/clang/Tooling/Refactor/IndexerQuery.h +++ b/clang/include/clang/Tooling/Refactor/IndexerQuery.h @@ -181,6 +181,33 @@ class DeclPredicateNotPredicate : public DeclPredicateNode { } // end namespace detail +enum class QueryBoolResult { + Unknown, + Yes, + No, +}; + +// FIXME: Check that 'T' is either a PersistentDeclRef<> or a Decl *. +template <typename T> struct Indexed { + T Decl; + // FIXME: Generalize better in the new refactoring engine. + QueryBoolResult IsNotDefined; + + Indexed(T Decl, QueryBoolResult IsNotDefined = QueryBoolResult::Unknown) + : Decl(Decl), IsNotDefined(IsNotDefined) {} + + Indexed(Indexed<T> &&Other) = default; + Indexed &operator=(Indexed<T> &&Other) = default; + Indexed(const Indexed<T> &Other) = default; + Indexed &operator=(const Indexed<T> &Other) = default; + + /// True iff the declaration is not defined in the entire project. + bool isNotDefined() const { + // FIXME: This is hack. Need a better system in the new engine. + return IsNotDefined == QueryBoolResult::Yes; + } +}; + /// Transforms one set of declarations into another using some predicate. class DeclarationsQuery : public IndexerQuery { static const char *BaseUIDString; @@ -189,7 +216,7 @@ class DeclarationsQuery : public IndexerQuery { std::unique_ptr<detail::DeclPredicateNode> Predicate; protected: - std::vector<PersistentDeclRef<Decl>> Output; + std::vector<Indexed<PersistentDeclRef<Decl>>> Output; public: DeclarationsQuery(std::vector<const Decl *> Input, @@ -203,7 +230,7 @@ class DeclarationsQuery : public IndexerQuery { void invalidateTUSpecificState() override { Input.clear(); } - void setOutput(std::vector<PersistentDeclRef<Decl>> Output) { + void setOutput(std::vector<Indexed<PersistentDeclRef<Decl>>> Output) { this->Output = Output; } @@ -238,10 +265,11 @@ class ManyToManyDeclarationsQuery final : DeclarationsQuery(std::vector<const Decl *>(Input.begin(), Input.end()), std::move(Predicate)) {} - std::vector<PersistentDeclRef<T>> getOutput() const { - std::vector<PersistentDeclRef<T>> Results; + std::vector<Indexed<PersistentDeclRef<T>>> getOutput() const { + std::vector<Indexed<PersistentDeclRef<T>>> Results; for (const auto &Ref : DeclarationsQuery::Output) - Results.push_back(PersistentDeclRef<T>(Ref.USR)); + Results.push_back(Indexed<PersistentDeclRef<T>>( + PersistentDeclRef<T>(Ref.Decl.USR), Ref.IsNotDefined)); return Results; } }; diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp index 72649a8f42f21..c5865e468a20b 100644 --- a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -76,10 +76,9 @@ class ImplementDeclaredCXXMethodsOperation static void addInlineBody(const CXXMethodDecl *MD, const ASTContext &Context, std::vector<RefactoringReplacement> &Replacements); - static llvm::Expected<RefactoringResult> - runInImplementationAST(ASTContext &Context, const FileID &File, - const CXXRecordDecl *Class, - ArrayRef<const CXXMethodDecl *> SelectedMethods); + static llvm::Expected<RefactoringResult> runInImplementationAST( + ASTContext &Context, const FileID &File, const CXXRecordDecl *Class, + ArrayRef<indexer::Indexed<const CXXMethodDecl *>> SelectedMethods); }; class ImplementDeclaredObjCMethodsOperation @@ -104,11 +103,11 @@ class ImplementDeclaredObjCMethodsOperation const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) override; - static llvm::Expected<RefactoringResult> - runInImplementationAST(ASTContext &Context, const FileID &File, - const ObjCContainerDecl *Container, - const ObjCInterfaceDecl *Interface, - ArrayRef<const ObjCMethodDecl *> SelectedMethods); + static llvm::Expected<RefactoringResult> runInImplementationAST( + ASTContext &Context, const FileID &File, + const ObjCContainerDecl *Container, const ObjCInterfaceDecl *Interface, + ArrayRef<std::string> MethodDeclarations, + ArrayRef<indexer::Indexed<const ObjCMethodDecl *>> SelectedMethods); }; /// Returns true if the given Objective-C method has an implementation. @@ -224,7 +223,7 @@ static bool containsUsingOf(const NamespaceDecl *ND, llvm::Expected<RefactoringResult> ImplementDeclaredCXXMethodsOperation::runInImplementationAST( ASTContext &Context, const FileID &File, const CXXRecordDecl *Class, - ArrayRef<const CXXMethodDecl *> SelectedMethods) { + ArrayRef<indexer::Indexed<const CXXMethodDecl *>> SelectedMethods) { if (!Class) return llvm::make_error<RefactoringOperationError>( "the target class is not defined in the continuation AST unit"); @@ -316,7 +315,8 @@ ImplementDeclaredCXXMethodsOperation::runInImplementationAST( PP.SuppressLifetimeQualifiers = true; PP.SuppressUnwrittenScope = true; OS << "\n"; - for (const CXXMethodDecl *MD : SelectedMethods) { + for (const auto &I : SelectedMethods) { + const CXXMethodDecl *MD = I.Decl; // Check if the method is already defined. if (!MD) continue; @@ -369,24 +369,41 @@ ImplementDeclaredObjCMethodsOperation::perform( ASTContext &Context, const Preprocessor &ThePreprocessor, const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { using namespace indexer; + + // Print the methods before running the continuation because the continuation + // TU might not have these method declarations (e.g. category implemented in + // the class implementation). + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + std::vector<std::string> MethodDeclarations; + for (const ObjCMethodDecl *MD : SelectedMethods) { + std::string MethodDeclStr; + llvm::raw_string_ostream MethodOS(MethodDeclStr); + MD->print(MethodOS, PP); + MethodDeclarations.push_back(std::move(MethodOS.str())); + } + return continueInExternalASTUnit( fileThatShouldContainImplementationOf(Container), runInImplementationAST, - Container, Interface, + Container, Interface, MethodDeclarations, filter(llvm::makeArrayRef(SelectedMethods), [](const DeclEntity &D) { return !D.isDefined(); })); } static const ObjCImplDecl * getImplementationContainer(const ObjCContainerDecl *Container, - const ObjCInterfaceDecl *Interface) { + const ObjCInterfaceDecl *Interface = nullptr) { if (!Container) - return nullptr; + return Interface ? getImplementationContainer(Interface) : nullptr; if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(Container)) return ID->getImplementation(); if (const auto *CD = dyn_cast<ObjCCategoryDecl>(Container)) { if (const auto *Impl = CD->getImplementation()) return Impl; - return getImplementationContainer(Interface, /*Interface=*/nullptr); + return getImplementationContainer(Interface); } return nullptr; } @@ -395,7 +412,8 @@ llvm::Expected<RefactoringResult> ImplementDeclaredObjCMethodsOperation::runInImplementationAST( ASTContext &Context, const FileID &File, const ObjCContainerDecl *Container, const ObjCInterfaceDecl *Interface, - ArrayRef<const ObjCMethodDecl *> SelectedMethods) { + ArrayRef<std::string> MethodDeclarations, + ArrayRef<indexer::Indexed<const ObjCMethodDecl *>> SelectedMethods) { const ObjCImplDecl *ImplementationContainer = getImplementationContainer(Container, Interface); if (!ImplementationContainer) @@ -408,21 +426,15 @@ ImplementDeclaredObjCMethodsOperation::runInImplementationAST( std::string MethodString; llvm::raw_string_ostream OS(MethodString); - PrintingPolicy PP = Context.getPrintingPolicy(); - PP.PolishForDeclaration = true; - PP.SuppressStrongLifetime = true; - PP.SuppressLifetimeQualifiers = true; - PP.SuppressUnwrittenScope = true; - for (const ObjCMethodDecl *MD : SelectedMethods) { + assert(MethodDeclarations.size() >= SelectedMethods.size() && + "fewer declarations than selected methods?"); + for (const auto &I : llvm::enumerate(SelectedMethods)) { + indexer::Indexed<const ObjCMethodDecl *> Decl = I.value(); // Skip methods that are already defined. - if (!MD) + if (!Decl.isNotDefined()) continue; - std::string MethodDeclStr; - llvm::raw_string_ostream MethodOS(MethodDeclStr); - MD->print(MethodOS, PP); - - OS << StringRef(MethodOS.str()).drop_back(); // Drop the ';' + OS << StringRef(MethodDeclarations[I.index()]).drop_back(); // Drop the ';' OS << " { \n <#code#>;\n}\n\n"; } SourceLocation InsertionLoc = ImplementationContainer->getLocEnd(); diff --git a/clang/lib/Tooling/Refactor/IndexerQueries.cpp b/clang/lib/Tooling/Refactor/IndexerQueries.cpp index bf4fb6539cfba..b96599caecfdf 100644 --- a/clang/lib/Tooling/Refactor/IndexerQueries.cpp +++ b/clang/lib/Tooling/Refactor/IndexerQueries.cpp @@ -116,13 +116,15 @@ IndexerQuery::loadResultsFromYAML(StringRef Source, for (const auto &PredicateResult : Result.PredicateResults) { if (PredicateResult.Name != ActualPredicate.Name) continue; - std::vector<PersistentDeclRef<Decl>> Output; + std::vector<Indexed<PersistentDeclRef<Decl>>> Output; for (const auto &ResultTuple : zip(DQ->getInputs(), PredicateResult.IntegerValues)) { const Decl *D = std::get<0>(ResultTuple); int Result = std::get<1>(ResultTuple); - Output.push_back(PersistentDeclRef<Decl>::create( - (IsNot ? !Result : !!Result) ? D : nullptr)); + bool Value = (IsNot ? !Result : !!Result); + Output.push_back(Indexed<PersistentDeclRef<Decl>>( + PersistentDeclRef<Decl>::create(Value ? D : nullptr), + Value ? QueryBoolResult::Yes : QueryBoolResult::No)); } DQ->setOutput(std::move(Output)); break; diff --git a/clang/lib/Tooling/Refactor/RefactoringContinuations.h b/clang/lib/Tooling/Refactor/RefactoringContinuations.h index 87b65351c74cc..ec2ee1bcf50ca 100644 --- a/clang/lib/Tooling/Refactor/RefactoringContinuations.h +++ b/clang/lib/Tooling/Refactor/RefactoringContinuations.h @@ -61,8 +61,13 @@ struct StateTraits<ArrayRef<const T *>> template <typename T> struct StateTraits<std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>>> : std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type { - using StoredResultType = std::vector<const T *>; - using PersistentType = std::vector<PersistentDeclRef<T>>; + using StoredResultType = std::vector<indexer::Indexed<const T *>>; + using PersistentType = std::vector<indexer::Indexed<PersistentDeclRef<T>>>; +}; + +template <> struct StateTraits<std::vector<std::string>> { + using StoredResultType = std::vector<std::string>; + using PersistentType = std::vector<std::string>; }; /// Conversion functions convert the TU-specific state to a TU independent @@ -88,7 +93,8 @@ std::vector<PersistentDeclRef<T>> convertToPersistentRepresentation( } template <typename T> -std::vector<PersistentDeclRef<T>> convertToPersistentRepresentation( +std::vector<indexer::Indexed<PersistentDeclRef<T>>> +convertToPersistentRepresentation( std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>> &Query, typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = nullptr) { @@ -96,6 +102,11 @@ std::vector<PersistentDeclRef<T>> convertToPersistentRepresentation( return Query->getOutput(); } +inline std::vector<std::string> +convertToPersistentRepresentation(const std::vector<std::string> &Values) { + return Values; +} + /// Converts the TU-independent state to the TU-specific state. class PersistentToASTSpecificStateConverter { ASTContext &Context; @@ -154,6 +165,33 @@ class PersistentToASTSpecificStateConverter { return Results; } + template <typename T> + bool addConvertible( + const std::vector<indexer::Indexed<PersistentDeclRef<T>>> &Refs, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + for (const auto &Ref : Refs) { + if (!Ref.Decl.USR.empty()) + ConvertedDeclRefs[Ref.Decl.USR] = nullptr; + } + return true; + } + + template <typename T> + std::vector<indexer::Indexed<const T *>> + convert(const std::vector<indexer::Indexed<PersistentDeclRef<T>>> &Refs, + typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = + nullptr) { + std::vector<indexer::Indexed<const T *>> Results; + Results.reserve(Refs.size()); + // Allow nulls in the produced array, the continuation will have to deal + // with them by itself. + for (const auto &Ref : Refs) + Results.push_back(indexer::Indexed<const T *>( + dyn_cast_or_null<T>(lookupDecl(Ref.Decl.USR)), Ref.IsNotDefined)); + return Results; + } + bool addConvertible(const PersistentFileID &) { // Do nothing since FileIDs are converted one-by-one. return true; @@ -161,6 +199,12 @@ class PersistentToASTSpecificStateConverter { FileID convert(const PersistentFileID &Ref); + bool addConvertible(const std::vector<std::string> &) { return true; } + + std::vector<std::string> convert(const std::vector<std::string> &Values) { + return Values; + } + /// Converts the added persistent state into TU-specific state using one /// efficient operation. void runCoalescedConversions(); diff --git a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m index 1b31110b051ee..318b7b72832c4 100644 --- a/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m +++ b/clang/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m @@ -11,3 +11,5 @@ - (void)method:(int)x with:(int)y { } @end // CHECK1: "{{.*}}objcClass.m" "- (void)method { \n <#code#>;\n}\n\n+ (void)classMethod { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n- (void)method:(int)x with:(int)y { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1 // CHECK2: "{{.*}}objcClass.m" "- (void)method { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n" [[@LINE-2]]:1 + +// CHECK-CAT-NO-IMPL: "{{.*}}objcClass.m" "- (void)thisCategoryMethodShouldBeInTheClassImplementation { \n <#code#>;\n}\n\n" 11:1 -> 11:1 diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m index 5e56fd0ac8f9d..38b52971d6f8a 100644 --- a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m @@ -77,3 +77,5 @@ - (void)thisCategoryMethodShouldBeInTheClassImplementation; @end // RUN: clang-refactor-test perform -action implement-declared-methods -selected=category-no-impl -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-CAT-NO-IMPL %s +// It should work even in another TU! +// RUN: clang-refactor-test perform -action implement-declared-methods -selected=category-no-impl -continuation-file=%S/Inputs/objcClass.m -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-CAT-NO-IMPL %S/Inputs/objcClass.m diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index 506f8d28f4280..e5b411c153fc6 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -1024,7 +1024,7 @@ class RefactoringContinuationWrapper { struct QueryWrapper { indexer::IndexerQuery *Query; CXTranslationUnit TU; - std::vector<PersistentDeclRef<Decl>> DeclResults; + std::vector<indexer::Indexed<PersistentDeclRef<Decl>>> DeclResults; unsigned ConsumedResults = 0; QueryWrapper(indexer::IndexerQuery *Query, CXTranslationUnit TU) @@ -1884,13 +1884,17 @@ clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex, return CXIndexerQueryAction_None; if (Wrapper->DeclResults.empty()) Wrapper->DeclResults.resize(DQ->getInputs().size(), - PersistentDeclRef<Decl>::create(nullptr)); + indexer::Indexed<PersistentDeclRef<Decl>>( + PersistentDeclRef<Decl>::create(nullptr))); // Filter the declarations! bool IsNot = false; if (isa<indexer::detail::DeclPredicateNotPredicate>(DQ->getPredicateNode())) IsNot = true; - Wrapper->DeclResults[CursorIndex] = PersistentDeclRef<Decl>::create( - (IsNot ? !Value : !!Value) ? DQ->getInputs()[CursorIndex] : nullptr); + bool Result = IsNot ? !Value : !!Value; + Wrapper->DeclResults[CursorIndex] = indexer::Indexed<PersistentDeclRef<Decl>>( + PersistentDeclRef<Decl>::create(Result ? DQ->getInputs()[CursorIndex] + : nullptr), + Result ? indexer::QueryBoolResult::Yes : indexer::QueryBoolResult::No); Wrapper->ConsumedResults++; if (Wrapper->ConsumedResults == Wrapper->DeclResults.size()) { // We've received all the results, pass them back to the query. From 61abc6bc528e67a87c4044a5646e05cc9da955ac Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 14 Jul 2017 15:19:56 +0100 Subject: [PATCH 185/582] The "does not conform to protocol" -Wprotocol warning should take method availability into account This also ensures that the "Add missing requirements" refactoring action doesn't add stubs for methods that are unavailable or not yet introduced. rdar://32549953 apple-llvm-split-commit: 4d7cc27fda0cc3c9ff5eb74fbcb1f6cdfe6c422e apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingProtocolStubs.cpp | 5 +++ clang/lib/Sema/SemaDeclObjC.cpp | 41 +++++++++++-------- .../fixit-fill-in-protocol-requirements.m | 34 ++++++++++++++- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/clang/lib/Edit/FillInMissingProtocolStubs.cpp b/clang/lib/Edit/FillInMissingProtocolStubs.cpp index 3173a345e56fa..64843e508b380 100644 --- a/clang/lib/Edit/FillInMissingProtocolStubs.cpp +++ b/clang/lib/Edit/FillInMissingProtocolStubs.cpp @@ -139,6 +139,11 @@ class MethodSet { for (const ObjCMethodDecl *M : P->methods()) { if (M->isImplicit()) continue; + AvailabilityResult Availability = M->getAvailability(); + // Methods that are unavailable or not yet introduced are not considered + // to be required. + if (Availability == AR_NotYetIntroduced || Availability == AR_Unavailable) + continue; auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods; Map.insert(std::make_pair(M->getSelector(), MethodInfo(M, P, Priority))); } diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 299509bdb78b8..b13238112cbb3 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -2149,23 +2149,28 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, Diag(IVI->getLocation(), diag::err_inconsistent_ivar_count); } -static void WarnUndefinedMethod(Sema &S, SourceLocation ImpLoc, - ObjCMethodDecl *method, - bool &IncompleteImpl, - unsigned DiagID, - NamedDecl *NeededFor = nullptr) { +static bool shouldWarnUndefinedMethod(const ObjCMethodDecl *M) { // No point warning no definition of method which is 'unavailable'. - switch (method->getAvailability()) { + switch (M->getAvailability()) { case AR_Available: case AR_Deprecated: - break; + return true; - // Don't warn about unavailable or not-yet-introduced methods. + // Don't warn about unavailable or not-yet-introduced methods. case AR_NotYetIntroduced: case AR_Unavailable: - return; + return false; } - + llvm_unreachable("Invalid availability"); +} + +static void WarnUndefinedMethod(Sema &S, SourceLocation ImpLoc, + ObjCMethodDecl *method, bool &IncompleteImpl, + unsigned DiagID, + NamedDecl *NeededFor = nullptr) { + if (!shouldWarnUndefinedMethod(method)) + return; + // FIXME: For now ignore 'IncompleteImpl'. // Previously we grouped all unimplemented methods under a single // warning, but some users strongly voiced that they would prefer @@ -2721,11 +2726,13 @@ static void CheckProtocolMethodDefs( continue; unsigned DIAG = diag::warn_unimplemented_protocol_method; if (!S.Diags.isIgnored(DIAG, ImpLoc)) { - if (MissingRequirements) - HasMissingRequirements = true; - else + if (MissingRequirements) { + if (!HasMissingRequirements) + HasMissingRequirements = shouldWarnUndefinedMethod(method); + } else { WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, PDecl); + } } } } @@ -2747,10 +2754,12 @@ static void CheckProtocolMethodDefs( unsigned DIAG = diag::warn_unimplemented_protocol_method; if (!S.Diags.isIgnored(DIAG, ImpLoc)) { - if (MissingRequirements) - HasMissingRequirements = true; - else + if (MissingRequirements) { + if (!HasMissingRequirements) + HasMissingRequirements = shouldWarnUndefinedMethod(method); + } else { WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, PDecl); + } } } } diff --git a/clang/test/FixIt/fixit-fill-in-protocol-requirements.m b/clang/test/FixIt/fixit-fill-in-protocol-requirements.m index c86b8c28672c8..642a70cf34d7e 100644 --- a/clang/test/FixIt/fixit-fill-in-protocol-requirements.m +++ b/clang/test/FixIt/fixit-fill-in-protocol-requirements.m @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -verify -Wno-objc-root-class -serialize-diagnostic-file /dev/null %s -// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -serialize-diagnostic-file /dev/null %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-apple-macos10.10 -verify -Wno-objc-root-class -serialize-diagnostic-file /dev/null %s +// RUN: %clang_cc1 -triple=x86_64-apple-macos10.10 -fdiagnostics-parseable-fixits -serialize-diagnostic-file /dev/null %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-apple-macos10.12 -fdiagnostics-parseable-fixits -serialize-diagnostic-file /dev/null %s 2>&1 | FileCheck --check-prefix=AVAILABLE %s @protocol P1 @@ -52,3 +53,32 @@ @interface FourProtocols <P2, P3, P4> // expected-warning {{class 'ThreeProtocol @end @implementation FourProtocols @end + +// Unavailable methods +@protocol TakeAvailabilityIntoAccount + +- (void)unavailableMethod __attribute__((availability(macos,unavailable))); ++ (void)notYetAvailableMethod __attribute__((availability(macos,introduced=10.11))); +- (void)availableMethod; +- (void)deprecatedMethod __attribute__((availability(macos,introduced=10.0, deprecated=10.6))); + +@end + +@interface ImplementsAllAvailable <TakeAvailabilityIntoAccount> +@end + +@implementation ImplementsAllAvailable // No warning! + +- (void)availableMethod { } +- (void)deprecatedMethod { } + +@end + +@interface FixitJustAvailable <TakeAvailabilityIntoAccount> +@end + +@implementation FixitJustAvailable // expected-warning {{class 'FixitJustAvailable' does not conform to protocol 'TakeAvailabilityIntoAccount'}} expected-note {{add stubs for missing protocol requirements}} + +@end +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"- (void)availableMethod { \n <#code#>\n}\n\n- (void)deprecatedMethod { \n <#code#>\n}\n\n" +// AVAILABLE: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"- (void)availableMethod { \n <#code#>\n}\n\n- (void)deprecatedMethod { \n <#code#>\n}\n\n+ (void)notYetAvailableMethod { \n <#code#>\n}\n\n" From a784fa3dcc77bb2aa00d277c219c7d656da113d0 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 17 Jul 2017 16:58:59 +0100 Subject: [PATCH 186/582] [refactor] Extract: use substitued types for ObjC property expressions rdar://33350790 apple-llvm-split-commit: 29428eeadd217195045dde123b6f540fd8a0363f apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/TypeUtils.cpp | 15 +++++-- .../return-objc-generic-argument-type.m | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 clang/test/Refactor/Extract/return-objc-generic-argument-type.m diff --git a/clang/lib/Tooling/Refactor/TypeUtils.cpp b/clang/lib/Tooling/Refactor/TypeUtils.cpp index 3a36aec1d5ba4..c07136bd709ad 100644 --- a/clang/lib/Tooling/Refactor/TypeUtils.cpp +++ b/clang/lib/Tooling/Refactor/TypeUtils.cpp @@ -162,10 +162,17 @@ QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl, // Get the correct property type. if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { if (PRE->isMessagingGetter()) { - if (PRE->isExplicitProperty()) - return PRE->getExplicitProperty()->getType(); - if (const ObjCMethodDecl *M = PRE->getImplicitPropertyGetter()) - return M->getReturnType(); + if (PRE->isExplicitProperty()) { + QualType ReceiverType = PRE->getReceiverType(Ctx); + return PRE->getExplicitProperty()->getUsageType(ReceiverType); + } + if (const ObjCMethodDecl *M = PRE->getImplicitPropertyGetter()) { + if (!PRE->isObjectReceiver()) + return M->getSendResultType(PRE->getReceiverType(Ctx)); + const Expr *Base = PRE->getBase(); + return M->getSendResultType(findExpressionLexicalType( + FunctionLikeParentDecl, Base, Base->getType(), Policy, Ctx)); + } } } diff --git a/clang/test/Refactor/Extract/return-objc-generic-argument-type.m b/clang/test/Refactor/Extract/return-objc-generic-argument-type.m new file mode 100644 index 0000000000000..42d97705c4b22 --- /dev/null +++ b/clang/test/Refactor/Extract/return-objc-generic-argument-type.m @@ -0,0 +1,40 @@ +// RUN: clang-refactor-test perform -action extract -selected=prop -selected=imp-prop -selected=class-prop -selected=class-prop2 -selected=class-method %s | FileCheck %s + +@interface NSObject +@end + +@interface Array<Element> : NSObject + +@property Element prop; + +- (Element)get; + +@property (class) Array<Element> *classProp; + ++ (Element *)classGet; + +@end + +void foo(Array<NSObject *> *objects) { +// prop-begin: +1:3 + objects.prop; +// prop-end: -1:15 +// CHECK: "static NSObject * extracted(Array<NSObject *> *objects) {\nreturn objects.prop;\n}\n\n" +// imp-prop-begin: +1:3 + objects.get; +// imp-prop-end: -1:14 +// CHECK: "static NSObject * extracted(Array<NSObject *> *objects) {\nreturn objects.get;\n}\n\n" +// class-prop-begin: +1:3 + Array.classProp; +// class-prop-end: -1:30 +// CHECK: "static Array * extracted() {\nreturn Array.classProp;\n}\n\n" + typedef Array<NSObject *> ObjectArray; +// class-prop2-begin: +1:3 + [ObjectArray classProp]; +// class-prop2-end: -1:26 +// CHECK: "static Array<NSObject *> * extracted() {\nreturn [ObjectArray classProp];\n}\n\n" +// class-method-begin: +1:3 + [ObjectArray classGet]; +// class-method-end: -1:25 +// CHECK: "static NSObject ** extracted() {\nreturn [ObjectArray classGet];\n}\n\n" +} From 690870a316c1b7c9c029a9e81a3698c5281750bf Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 18 Jul 2017 18:14:52 +0100 Subject: [PATCH 187/582] [refactor] Extract expression should be prohibited for calls that return void rdar://33378777 apple-llvm-split-commit: a4c1b7aeb931fe4b1a62170ed9ab049713738152 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/StmtUtils.cpp | 2 +- .../test/Refactor/Extract/extract-expression-into-var.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/StmtUtils.cpp b/clang/lib/Tooling/Refactor/StmtUtils.cpp index ed8ae30543a48..d5644b29c1b33 100644 --- a/clang/lib/Tooling/Refactor/StmtUtils.cpp +++ b/clang/lib/Tooling/Refactor/StmtUtils.cpp @@ -68,5 +68,5 @@ bool clang::tooling::isLexicalExpression(const Stmt *S, const Stmt *Parent) { // of an expression. if (isAssignmentOperator(S) && (!Parent || !isa<Expr>(Parent))) return false; - return true; + return !cast<Expr>(S)->getType()->isVoidType(); } diff --git a/clang/test/Refactor/Extract/extract-expression-into-var.cpp b/clang/test/Refactor/Extract/extract-expression-into-var.cpp index f1ae8f5ae41f1..9fd4f7a54f777 100644 --- a/clang/test/Refactor/Extract/extract-expression-into-var.cpp +++ b/clang/test/Refactor/Extract/extract-expression-into-var.cpp @@ -37,3 +37,11 @@ void dontExtractStatement(int x, int y) { // RUN: not clang-refactor-test perform -action extract-expression -selected=stmt1 -selected=stmt2 %s -std=c++11 2>&1 | FileCheck --check-prefix=CHECK-FAIL %s // CHECK-FAIL: Failed to initiate the refactoring action! + +void dontExtractVoidCall() { + // void-call-begin: +1:3 + dontExtractVoidCall(); + // void-call-end: -1:24 +} + +// RUN: not clang-refactor-test perform -action extract-expression -selected=void-call %s -std=c++11 2>&1 | FileCheck --check-prefix=CHECK-FAIL %s From 4ec65cfdc36764c83ca8bf14290ebe2f0634ce21 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 19 Jul 2017 17:26:11 +0100 Subject: [PATCH 188/582] refactor: Add a continuation verification function to report errors for the "Generate Missing Function Definitions" action that can be propagated back to SK rdar://33403708 apple-llvm-split-commit: 414251c9711ba8fa6781f2f899d855a9579b91d5 apple-llvm-split-dir: clang/ --- clang/include/clang-c/Refactor.h | 12 +++++++ .../clang/Basic/DiagnosticRefactoringKinds.td | 12 +++++++ .../clang/Tooling/Refactor/IndexerQuery.h | 8 +++++ clang/lib/Tooling/Refactor/IndexerQueries.cpp | 35 +++++++++++++++++++ .../implement-declared-methods.cpp | 4 +++ .../implement-declared-methods.m | 4 +++ .../clang-refactor-test/ClangRefactorTest.cpp | 13 +++++++ clang/tools/libclang/CRefactor.cpp | 34 +++++++++++++++--- clang/tools/libclang/libclang.exports | 1 + 9 files changed, 119 insertions(+), 4 deletions(-) diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h index 63b01a4175450..834584285ddc8 100644 --- a/clang/include/clang-c/Refactor.h +++ b/clang/include/clang-c/Refactor.h @@ -1169,6 +1169,18 @@ CINDEX_LINKAGE CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery( CXRefactoringContinuation Continuation, unsigned Index); +/** + * \brief Verify that the all of the indexer queries are satisfied by the + * continuation. + * + * \returns Null if all of the queries are satisfied an no errors have been + * reported, or a set of diagnostics that describes why the continuation should + * not be run. + */ +CINDEX_LINKAGE +CXDiagnosticSet clang_RefactoringContinuation_verifyBeforeFinalizing( + CXRefactoringContinuation Continuation); + /** * \brief Terminate the connection between the initiation TU and the refactoring * continuation. diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td index 6dfd29169923a..41113c25516ce 100644 --- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td +++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td @@ -24,4 +24,16 @@ def err_rename_external_source_symbol : Error<"%0 is declared in a %1 file; " "rename can be initiated in a %1 file only">; } +let CategoryName = "Refactoring Continuation Issue" in { + +def err_ref_continuation_missing_implementation : Error< + "no %select{implementation file|@implementation declaration}0 for the " + "selected %select{declaration|@interface}0 %1; please add one and run the " + "refactoring action again">; + +def err_implement_declared_methods_all_implemented : Error< + "the selected methods are already implemented">; + +} + } // end of Refactoring diagnostics diff --git a/clang/include/clang/Tooling/Refactor/IndexerQuery.h b/clang/include/clang/Tooling/Refactor/IndexerQuery.h index b14e46a8d75a1..48728930b182e 100644 --- a/clang/include/clang/Tooling/Refactor/IndexerQuery.h +++ b/clang/include/clang/Tooling/Refactor/IndexerQuery.h @@ -36,6 +36,10 @@ class IndexerQuery { virtual void invalidateTUSpecificState() = 0; + /// Checks if this query was satisfied. Returns true if it wasn't and reports + /// appropriate errors. + virtual bool verify(ASTContext &) { return false; } + // Mainly used for testing. static llvm::Error loadResultsFromYAML(StringRef Source, ArrayRef<IndexerQuery *> Queries); @@ -82,6 +86,8 @@ class ASTUnitForImplementationOfDeclarationQuery final void setResult(PersistentFileID File) { Result = std::move(File); } + bool verify(ASTContext &Context) override; + const PersistentFileID &getResult() const { return Result; } static bool classof(const IndexerQuery *D) { @@ -230,6 +236,8 @@ class DeclarationsQuery : public IndexerQuery { void invalidateTUSpecificState() override { Input.clear(); } + bool verify(ASTContext &Context) override; + void setOutput(std::vector<Indexed<PersistentDeclRef<Decl>>> Output) { this->Output = Output; } diff --git a/clang/lib/Tooling/Refactor/IndexerQueries.cpp b/clang/lib/Tooling/Refactor/IndexerQueries.cpp index b96599caecfdf..f2392e419c8e5 100644 --- a/clang/lib/Tooling/Refactor/IndexerQueries.cpp +++ b/clang/lib/Tooling/Refactor/IndexerQueries.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Tooling/Core/RefactoringDiagnostic.h" #include "clang/Tooling/Refactor/IndexerQuery.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" @@ -44,6 +45,40 @@ clang::tooling::indexer::fileThatShouldContainImplementationOf(const Decl *D) { return llvm::make_unique<ASTUnitForImplementationOfDeclarationQuery>(D); } +bool ASTUnitForImplementationOfDeclarationQuery::verify(ASTContext &Context) { + if (!D) { + assert(false && "Query should be verified before persisting"); + return false; + } + // Check if we've got the filename. + if (!Result.Filename.empty()) + return false; + Context.getDiagnostics().Report( + D->getLocation(), diag::err_ref_continuation_missing_implementation) + << isa<ObjCContainerDecl>(D) << cast<NamedDecl>(D); + return true; +} + +bool DeclarationsQuery::verify(ASTContext &Context) { + if (Input.empty()) { + assert(false && "Query should be verified before persisting"); + return false; + } + if (!Output.empty()) { + // At least one output declaration must be valid. + for (const auto &Ref : Output) { + if (!Ref.Decl.USR.empty()) + return false; + } + } + // FIXME: This is too specific, the new refactoring engine at llvm.org should + // generalize this. + Context.getDiagnostics().Report( + Input[0]->getLocation(), + diag::err_implement_declared_methods_all_implemented); + return true; +} + namespace { struct QueryPredicateNode { diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp index 82ea02187aeb6..3d8164f9eb2fe 100644 --- a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp @@ -85,6 +85,10 @@ struct } // query-mix-impl-header: [ { name: ast.producer.query, filenameResult: "%S/classInHeader.h" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 1, 0, 1] }] }] // RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl-header %s | FileCheck %S/Inputs/classInHeader.h +// query-no-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [1, 1, 1, 1] }] }] +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-no-impl %s 2>&1 | FileCheck --check-prefix=ALL-IMPLEMENTED-ERROR %s +// ALL-IMPLEMENTED-ERROR: error: continuation failed: the selected methods are already implemented + // Ensure that the methods which are placed right after the record are placed // after the outermost record: namespace ns { diff --git a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m index 38b52971d6f8a..834e20f62bb69 100644 --- a/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m +++ b/clang/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m @@ -47,6 +47,10 @@ - (void)someOtherMethod { } // RUN: not clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s -DNO_IMPL 2>&1 | FileCheck --check-prefix=CHECK-EMPTY-ERR %s // CHECK-EMPTY-ERR: failed to perform the refactoring continuation (the target @interface is not implemented in the continuation AST unit)! +// query-no-file-all-impl: [ { name: ast.producer.query, filenameResult: "" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 0, 0, 0] }] }] +// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-no-file-all-impl %s -DNO_IMPL 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLEMENTATION-ERROR %s +// CHECK-NO-IMPLEMENTATION-ERROR: error: continuation failed: no @implementation declaration for the selected @interface 'MyClass'; please add one and run the refactoring action again + // RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/objcClass.m -query-results=query-all-impl %s -DNO_IMPL | FileCheck --check-prefix=CHECK1 %S/Inputs/objcClass.m // RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/objcClass.m -query-results=query-mix-impl %s -DNO_IMPL -DMIX_IMPL | FileCheck --check-prefix=CHECK2 %S/Inputs/objcClass.m diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index a5fd3e487b0eb..5de26f96fcf7c 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -1109,6 +1109,19 @@ bool performOperation(CXRefactoringAction Action, ArrayRef<const char *> Args, /*FileSubstitution=*/opts::initiateAndPerform::ContinuationFile); clang_RefactoringContinuation_loadSerializedIndexerQueryResults( Continuation, /*Source=*/QueryResults.c_str()); + CXDiagnosticSet Diags = + clang_RefactoringContinuation_verifyBeforeFinalizing(Continuation); + if (Diags) { + llvm::errs() << "error: continuation failed: "; + for (unsigned I = 0, E = clang_getNumDiagnosticsInSet(Diags); I != E; ++I) { + CXDiagnostic Diag = clang_getDiagnosticInSet(Diags, I); + CXString Spelling = clang_getDiagnosticSpelling(Diag); + errs() << clang_getCString(Spelling) << "\n"; + clang_disposeString(Spelling); + } + clang_RefactoringContinuation_dispose(Continuation); + return true; + } clang_RefactoringContinuation_finalizeEvaluationInInitationTU(Continuation); // Load the continuation TU. CXTranslationUnit ContinuationTU; diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index e5b411c153fc6..45f21ba134206 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -1051,6 +1051,7 @@ class RefactoringDiagnosticConsumer : public DiagnosticConsumer { DiagnosticConsumer *PreviousClient; std::unique_ptr<DiagnosticConsumer> PreviousClientPtr; llvm::SmallVector<StoredDiagnostic, 2> RenameDiagnostics; + llvm::SmallVector<StoredDiagnostic, 1> ContinuationDiagnostics; public: RefactoringDiagnosticConsumer(ASTContext &Context) : Context(Context) { @@ -1069,17 +1070,24 @@ class RefactoringDiagnosticConsumer : public DiagnosticConsumer { void HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) override { - if (DiagnosticIDs::getCategoryNumberForDiag(Info.getID()) == - diag::DiagCat_Rename_Issue) + unsigned Cat = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); + if (Cat == diag::DiagCat_Rename_Issue) RenameDiagnostics.push_back(StoredDiagnostic(Level, Info)); + else if (Cat == diag::DiagCat_Refactoring_Continuation_Issue) + ContinuationDiagnostics.push_back(StoredDiagnostic(Level, Info)); else assert(false && "Unhandled refactoring category"); } CXDiagnosticSetImpl *createDiags() const { - if (RenameDiagnostics.empty()) + if (RenameDiagnostics.empty() && ContinuationDiagnostics.empty()) return nullptr; - return cxdiag::createStoredDiags(RenameDiagnostics, Context.getLangOpts()); + llvm::SmallVector<StoredDiagnostic, 2> AllDiagnostics; + for (const auto &D : RenameDiagnostics) + AllDiagnostics.push_back(D); + for (const auto &D : ContinuationDiagnostics) + AllDiagnostics.push_back(D); + return cxdiag::createStoredDiags(AllDiagnostics, Context.getLangOpts()); } CXRefactoringActionSetWithDiagnostics createActionSet() const { @@ -1771,6 +1779,24 @@ CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery( return &Wrapper->Queries[Index]; } +CXDiagnosticSet clang_RefactoringContinuation_verifyBeforeFinalizing( + CXRefactoringContinuation Continuation) { + if (!Continuation) + return nullptr; + auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); + CXTranslationUnit TU = Wrapper->Queries[0].TU; + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return nullptr; + ASTContext &Context = CXXUnit->getASTContext(); + RefactoringDiagnosticConsumer DiagConsumer(Context); + for (const auto &Query : Wrapper->Queries) { + if (Query.Query->verify(Context)) + break; + } + return DiagConsumer.createDiags(); +} + void clang_RefactoringContinuation_finalizeEvaluationInInitationTU( CXRefactoringContinuation Continuation) { if (!Continuation) diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports index f8b9f09bc2908..e9a9b580dd50b 100644 --- a/clang/tools/libclang/libclang.exports +++ b/clang/tools/libclang/libclang.exports @@ -398,6 +398,7 @@ clang_RefactoringResult_dispose clang_RefactoringContinuation_loadSerializedIndexerQueryResults clang_RefactoringContinuation_getNumIndexerQueries clang_RefactoringContinuation_getIndexerQuery +clang_RefactoringContinuation_verifyBeforeFinalizing clang_RefactoringContinuation_finalizeEvaluationInInitationTU clang_RefactoringContinuation_continueOperationInTU clang_RefactoringContinuation_dispose From ec75656258cb4f768c6395a48571317000cc17ac Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 19 Jul 2017 18:07:40 +0100 Subject: [PATCH 189/582] [refactor] Record extracted symbol information for the "Extract Repeated Expression" action rdar://33404892 apple-llvm-split-commit: 6ba2ae5e4543ab6a5813d6d2bd890154041e9978 apple-llvm-split-dir: clang/ --- .../ExtractRepeatedExpressionIntoVariable.cpp | 25 ++++++++++++++----- .../extract-repeated-expr-perform.cpp | 10 ++++---- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index dc90d9ea93c1e..43718c37eabf0 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -244,7 +244,8 @@ llvm::Expected<RefactoringResult> ExtractRepeatedExpressionIntoVariableOperation::perform( ASTContext &Context, const Preprocessor &ThePreprocessor, const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { - std::vector<RefactoringReplacement> Replacements; + RefactoringResult Result(std::vector<RefactoringReplacement>{}); + std::vector<RefactoringReplacement> &Replacements = Result.Replacements; const SourceManager &SM = Context.getSourceManager(); SourceLocation InsertionLoc = @@ -255,6 +256,10 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( "no appropriate insertion location found"); StringRef Name = nameForExtractedVariable(E); + Result.AssociatedSymbols.push_back( + llvm::make_unique<RefactoringResultAssociatedSymbol>(SymbolName(Name))); + RefactoringResultAssociatedSymbol *CreatedSymbol = + Result.AssociatedSymbols.back().get(); // Create the variable that will hold the value of the duplicate expression. std::string VariableDeclarationString; @@ -265,18 +270,26 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( PP.SuppressLifetimeQualifiers = true; PP.SuppressUnwrittenScope = true; T.print(OS, PP, /*PlaceHolder*/ Name); + // FIXME: We should hook into the TypePrinter when moving over to llvm.org + // instead and get the offset from it. + unsigned NameOffset = StringRef(OS.str()).find(Name); OS << " = "; E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy()); OS << ";\n"; - Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc), OS.str()); + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), OS.str(), CreatedSymbol, + RefactoringReplacement::AssociatedSymbolLocation( + llvm::makeArrayRef(NameOffset), /*IsDeclaration=*/true))); // Replace the duplicates with a reference to the variable. - for (const Expr *E : DuplicateExpressions) - Replacements.emplace_back( + for (const Expr *E : DuplicateExpressions) { + Replacements.push_back(RefactoringReplacement( SourceRange(SM.getSpellingLoc(E->getLocStart()), getPreciseTokenLocEnd(SM.getSpellingLoc(E->getLocEnd()), SM, Context.getLangOpts())), - Name); + Name, CreatedSymbol, + /*NameOffset=*/llvm::makeArrayRef(unsigned(0)))); + } - return std::move(Replacements); + return std::move(Result); } diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp index 4d8c40099801c..9329502b4c232 100644 --- a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp @@ -45,12 +45,12 @@ void takesClass2(AWrapper &ref) { ref.object(x).constMethod(); ref.object(x).method(); } -// CHECK3: "AClass &object = ref.object(x);\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3 -// CHECK3-NEXT: "object" [[@LINE-4]]:5 -> [[@LINE-4]]:18 -// CHECK3-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:16 +// CHECK3: "AClass &object = ref.object(x);\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3 [Symbol extracted-decl 0 1:9 -> 1:15] +// CHECK3-NEXT: "object" [[@LINE-4]]:5 -> [[@LINE-4]]:18 [Symbol extracted-decl-ref 0 1:1 -> 1:7] +// CHECK3-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:16 [Symbol extracted-decl-ref 0 1:1 -> 1:7] -// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:45:5 %s | FileCheck --check-prefix=CHECK3 %s -// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:46:3 %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:45:5 -emit-associated %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:46:3 -emit-associated %s | FileCheck --check-prefix=CHECK3 %s void takesClass4(AWrapper &ref) { int x = 0; From 5cd0ae5e49ece217150207c0da591447cbc49cbe Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 21 Jul 2017 14:33:18 +0100 Subject: [PATCH 190/582] Dispose of the diagnostics in clang-refactor-test 414251c9711ba8fa6781f2f899d855a9579b91d5 forgot that. apple-llvm-split-commit: 6b59623ebe1dd854c9828b43ad3bb03d03e76bf9 apple-llvm-split-dir: clang/ --- clang/tools/clang-refactor-test/ClangRefactorTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index 5de26f96fcf7c..424d130d14f86 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -1118,8 +1118,10 @@ bool performOperation(CXRefactoringAction Action, ArrayRef<const char *> Args, CXString Spelling = clang_getDiagnosticSpelling(Diag); errs() << clang_getCString(Spelling) << "\n"; clang_disposeString(Spelling); + clang_disposeDiagnostic(Diag); } clang_RefactoringContinuation_dispose(Continuation); + clang_disposeDiagnosticSet(Diags); return true; } clang_RefactoringContinuation_finalizeEvaluationInInitationTU(Continuation); From eb3dbbb7b598e430adb4c8b73dc6f4bd417a35c2 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Mon, 24 Jul 2017 10:03:46 -0700 Subject: [PATCH 191/582] Mark the swift_wrapper attribute as inheritable (#111) (as it should have been all along) This prevents a crash in the case where someone happened to redeclare the typedef later, and then different Swift compilation contexts treated the typedef differently. rdar://problem/31935341 apple-llvm-split-commit: 0ad3d0a63b8524e644874825ff78bb8f054d43d3 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 2 +- clang/test/SemaObjC/attr-swift_newtype.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 55abe0757aa5f..59fc73b0682dc 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1711,7 +1711,7 @@ def SwiftName : InheritableAttr { let Documentation = [Undocumented]; } -def SwiftNewtype : Attr { +def SwiftNewtype : InheritableAttr { let Spellings = [GNU<"swift_newtype">, GNU<"swift_wrapper">]; let Subjects = SubjectList<[TypedefName], ErrorDiag, "ExpectedType">; let Args = [EnumArgument<"NewtypeKind", "NewtypeKind", diff --git a/clang/test/SemaObjC/attr-swift_newtype.c b/clang/test/SemaObjC/attr-swift_newtype.c index 61e4d89642a7c..62a20d1cf27e0 100644 --- a/clang/test/SemaObjC/attr-swift_newtype.c +++ b/clang/test/SemaObjC/attr-swift_newtype.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -verify -fsyntax-only %s +// RUN: not %clang_cc1 -ast-dump %s | FileCheck %s typedef int T1 __attribute__((swift_newtype(struct))); typedef int T2 __attribute__((swift_newtype(enum))); @@ -6,6 +7,17 @@ typedef int T2 __attribute__((swift_newtype(enum))); typedef int T3 __attribute__((swift_wrapper(struct))); typedef int T4 __attribute__((swift_wrapper(enum))); +typedef int T5; +typedef int T5 __attribute__((swift_wrapper(struct))); +typedef int T5; +// CHECK-LABEL: TypedefDecl {{.+}} T5 'int' +// CHECK-NEXT: BuiltinType {{.+}} 'int' +// CHECK-NEXT: TypedefDecl {{.+}} T5 'int' +// CHECK-NEXT: BuiltinType {{.+}} 'int' +// CHECK-NEXT: SwiftNewtypeAttr {{.+}} NK_Struct +// CHECK-NEXT: TypedefDecl {{.+}} T5 'int' +// CHECK-NEXT: BuiltinType {{.+}} 'int' +// CHECK-NEXT: SwiftNewtypeAttr {{.+}} NK_Struct typedef int Bad1 __attribute__((swift_newtype(bad))); // expected-warning{{'swift_newtype' attribute argument not supported: 'bad'}} typedef int Bad2 __attribute__((swift_newtype())); // expected-error{{argument required after attribute}} From 4e8e12043bf971bcd7883580bdcc4bf6570c1456 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 24 Jul 2017 14:55:03 +0100 Subject: [PATCH 192/582] [refactor][extract] Look through PseudoObjectExprs when looking for the selected statement in a CompoundStatement rdar://33348439 apple-llvm-split-commit: d5107e32179107ca8c669478520d17f047e50bb5 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/Extract.cpp | 21 +++++++++++++++++-- .../Refactor/Extract/extract-objc-property.m | 13 ++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index 624b18dc772a3..57a8775f4b095 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -161,6 +161,22 @@ static bool isMultipleCandidateBinOp(BinaryOperatorKind Op) { return Op == BO_Add || Op == BO_Sub; } +/// Searches for the selected statement in the given CompoundStatement, looking +/// through things like PseudoObjectExpressions. +static CompoundStmt::const_body_iterator +findSelectedStmt(CompoundStmt::body_const_range Statements, + const Stmt *Target) { + return llvm::find_if(Statements, [=](const Stmt *S) { + if (S == Target) + return true; + if (const auto *POE = dyn_cast<PseudoObjectExpr>(S)) { + if (POE->getSyntacticForm() == Target) + return true; + } + return false; + }); +} + /// Returns the first and the last statements that should be extracted from a /// compound statement. Optional<CompoundStatementRange> getExtractedStatements(const CompoundStmt *CS, @@ -170,9 +186,10 @@ Optional<CompoundStatementRange> getExtractedStatements(const CompoundStmt *CS, return None; assert(Begin && End); CompoundStatementRange Result; - Result.First = std::find(CS->body_begin(), CS->body_end(), Begin); + Result.First = findSelectedStmt(CS->body(), Begin); assert(Result.First != CS->body_end()); - Result.Last = std::find(Result.First, CS->body_end(), End); + Result.Last = findSelectedStmt( + CompoundStmt::body_const_range(Result.First, CS->body_end()), End); assert(Result.Last != CS->body_end()); return Result; } diff --git a/clang/test/Refactor/Extract/extract-objc-property.m b/clang/test/Refactor/Extract/extract-objc-property.m index 94cb4ef70991a..7fc79193c18d6 100644 --- a/clang/test/Refactor/Extract/extract-objc-property.m +++ b/clang/test/Refactor/Extract/extract-objc-property.m @@ -45,3 +45,16 @@ - (void)prohibitSetterExtraction { // RUN: not clang-refactor-test initiate -action extract -selected=setter -selected=setter-pref -selected=implicit-setter -selected=implicit-setter-pref %s -fobjc-arc 2>&1 | FileCheck --check-prefix=CHECK-SETTER %s @end + +@interface HasIntProp +@property (readwrite) int item; +@end + +// AVOID-CRASH: "static void extracted(HasIntProp *f) {\nf.item = !f.item;\n}\n\n" +// avoid-extraction-crash-begin: +1:42 +void avoidExtractionCrash(HasIntProp *f) { + f.item = !f.item; +// avoid-extraction-crash-end: -1:5 +} + +// RUN: clang-refactor-test perform -action extract -selected=avoid-extraction-crash %s -fobjc-arc | FileCheck --check-prefix=AVOID-CRASH %s From 5480dba1c17059c51fc9c33d1ebc41a3dcd251c2 Mon Sep 17 00:00:00 2001 From: Ben Langmuir <blangmuir@apple.com> Date: Thu, 10 Aug 2017 13:34:52 -0700 Subject: [PATCH 193/582] [index] Fix a crash indexing a non-canonical template declaration rdar://problem/33811115 apple-llvm-split-commit: 549cfcef848556cc1f938766fb8e6dc31425a94d apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexRecordHasher.cpp | 2 +- clang/test/Index/Store/record-hash-crash.cpp | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp index dd3c11ddfc797..855f902f24e67 100644 --- a/clang/lib/Index/IndexRecordHasher.cpp +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -304,7 +304,7 @@ static hash_code computeHash(TemplateName Name, IndexRecordHasher &Hasher) { return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex()); } - return COMBINE_HASH(Hasher.hash(Template)); + return COMBINE_HASH(Hasher.hash(Template->getCanonicalDecl())); } // FIXME: Hash dependent template names. diff --git a/clang/test/Index/Store/record-hash-crash.cpp b/clang/test/Index/Store/record-hash-crash.cpp index 8239901669c04..3d71ac9234743 100644 --- a/clang/test/Index/Store/record-hash-crash.cpp +++ b/clang/test/Index/Store/record-hash-crash.cpp @@ -10,3 +10,22 @@ namespace crash1 { // CHECK: [[@LINE+1]]:6 | function/C auto getit() { return []() {}; } } + +namespace crash2 { +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Ref,RelCont | rel: 1 +template <typename T> +class Foo; // canonical decl + +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Def,RelChild | rel: 1 +template <typename T> +class Foo {}; + +// CHECK: [[@LINE+2]]:8 | struct(Gen)/C++ | c:@N@crash2@ST>1#t>1#pT@Wrapper | Def,RelChild | rel: 1 +template <template <typename... ARGS> class TYPE> +struct Wrapper {}; + +// CHECK: [[@LINE+3]]:8 | struct(Gen,TS)/C++ | c:@N@crash2@S@Wrapper>#@N@crash2@ST>1#T@Foo | Def,RelChild,RelSpecialization | rel: 2 +// CHECK: RelSpecialization | c:@N@crash2@ST>1#t>1#pT@Wrapper +template <> +struct Wrapper<Foo> {}; +} From dfd78494a79020fd560d54c543ad3ed3d9a2424f Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 11 Aug 2017 10:55:22 +0100 Subject: [PATCH 194/582] Add 'void' parameter to a function in Refactor.h to silence -Wstrict-prototypes rdar://33705266 apple-llvm-split-commit: 441482b531aa8c9c792f3b8e77df30b2284c1730 apple-llvm-split-dir: clang/ --- clang/include/clang-c/Refactor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h index 834584285ddc8..b11cfb83b121b 100644 --- a/clang/include/clang-c/Refactor.h +++ b/clang/include/clang-c/Refactor.h @@ -49,7 +49,7 @@ typedef void *CXRefactoringOptionSet; * \brief Returns a new option set. */ CINDEX_LINKAGE -CXRefactoringOptionSet clang_RefactoringOptionSet_create(); +CXRefactoringOptionSet clang_RefactoringOptionSet_create(void); /** * \brief Parses and returns a new option set or NULL if the given string is From d8d1fbf629a4187c73acffef41a42c462b8bec49 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 14 Aug 2017 19:45:22 +0100 Subject: [PATCH 195/582] Rename old refactoring engine's SymbolName and SymbolOccurrence to avoid conflict with upstream declarations apple-llvm-split-commit: 475774519180734239149b40f8b61b9a856e0e22 apple-llvm-split-dir: clang/ --- .../Tooling/Refactor/RefactoringReplacement.h | 7 +- .../Tooling/Refactor/RenameIndexedFile.h | 6 +- .../clang/Tooling/Refactor/RenamedSymbol.h | 26 +++--- .../Tooling/Refactor/RenamingOperation.h | 11 +-- .../clang/Tooling/Refactor/SymbolName.h | 20 ++--- .../Tooling/Refactor/SymbolOccurrenceFinder.h | 2 +- clang/lib/Tooling/Refactor/Extract.cpp | 6 +- .../ExtractRepeatedExpressionIntoVariable.cpp | 3 +- .../Tooling/Refactor/RenameIndexedFile.cpp | 47 +++++----- clang/lib/Tooling/Refactor/RenamedSymbol.cpp | 5 +- .../Tooling/Refactor/RenamingOperation.cpp | 13 +-- clang/lib/Tooling/Refactor/SymbolName.cpp | 10 +-- .../Refactor/SymbolOccurrenceFinder.cpp | 32 +++---- .../clang-refactor-test/ClangRefactorTest.cpp | 12 +-- clang/tools/libclang/CRefactor.cpp | 89 ++++++++++--------- 15 files changed, 151 insertions(+), 138 deletions(-) diff --git a/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h b/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h index 4aed8ab37e76a..86dd2fbe8a32d 100644 --- a/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h +++ b/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h @@ -26,12 +26,13 @@ namespace tooling { /// \brief Represent a symbol that can be used for an additional refactoring /// action that associated. class RefactoringResultAssociatedSymbol { - SymbolName Name; + OldSymbolName Name; public: - RefactoringResultAssociatedSymbol(SymbolName Name) : Name(std::move(Name)) {} + RefactoringResultAssociatedSymbol(OldSymbolName Name) + : Name(std::move(Name)) {} - const SymbolName &getName() const { return Name; } + const OldSymbolName &getName() const { return Name; } }; /// \brief A replacement range. diff --git a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h index d598259013b4a..001f645fedeef 100644 --- a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h +++ b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h @@ -36,12 +36,12 @@ struct IndexedOccurrence { }; struct IndexedSymbol { - SymbolName Name; + OldSymbolName Name; std::vector<IndexedOccurrence> IndexedOccurrences; /// Whether this symbol is an Objective-C selector. bool IsObjCSelector; - IndexedSymbol(SymbolName Name, + IndexedSymbol(OldSymbolName Name, std::vector<IndexedOccurrence> IndexedOccurrences, bool IsObjCSelector) : Name(std::move(Name)), @@ -55,7 +55,7 @@ struct IndexedSymbol { class IndexedFileOccurrenceConsumer { public: virtual ~IndexedFileOccurrenceConsumer() {} - virtual void handleOccurrence(const SymbolOccurrence &Occurrence, + virtual void handleOccurrence(const OldSymbolOccurrence &Occurrence, SourceManager &SM, const LangOptions &LangOpts) = 0; }; diff --git a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h index d54c49799e821..952c5464e0d95 100644 --- a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h +++ b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h @@ -28,7 +28,7 @@ namespace rename { /// \brief A symbol that has to be renamed. class Symbol { public: - SymbolName Name; + OldSymbolName Name; /// The index of this symbol in a \c SymbolOperation. unsigned SymbolIndex; /// The declaration that was used to initiate a refactoring operation for this @@ -53,7 +53,7 @@ class Symbol { /// A single occurrence of a symbol can span more than one source range to /// account for things like Objective-C selectors. // TODO: Rename -class SymbolOccurrence { +class OldSymbolOccurrence { /// The source locations that correspond to the occurence of the symbol. SmallVector<SourceLocation, 4> Locations; @@ -91,18 +91,18 @@ class SymbolOccurrence { /// occurrence. unsigned SymbolIndex; - SymbolOccurrence() + OldSymbolOccurrence() : Kind(MatchingSymbol), IsMacroExpansion(false), SymbolIndex(0) {} - SymbolOccurrence(OccurrenceKind Kind, bool IsMacroExpansion, - unsigned SymbolIndex, ArrayRef<SourceLocation> Locations) + OldSymbolOccurrence(OccurrenceKind Kind, bool IsMacroExpansion, + unsigned SymbolIndex, ArrayRef<SourceLocation> Locations) : Locations(Locations.begin(), Locations.end()), Kind(Kind), IsMacroExpansion(IsMacroExpansion), SymbolIndex(SymbolIndex) { assert(!Locations.empty() && "Renamed occurence without locations!"); } - SymbolOccurrence(SymbolOccurrence &&) = default; - SymbolOccurrence &operator=(SymbolOccurrence &&) = default; + OldSymbolOccurrence(OldSymbolOccurrence &&) = default; + OldSymbolOccurrence &operator=(OldSymbolOccurrence &&) = default; ArrayRef<SourceLocation> locations() const { if (Kind == MatchingImplicitProperty && Locations.size() == 2) @@ -124,17 +124,17 @@ class SymbolOccurrence { return SourceRange(Loc, EndLoc); } - friend bool operator<(const SymbolOccurrence &LHS, - const SymbolOccurrence &RHS); - friend bool operator==(const SymbolOccurrence &LHS, - const SymbolOccurrence &RHS); + friend bool operator<(const OldSymbolOccurrence &LHS, + const OldSymbolOccurrence &RHS); + friend bool operator==(const OldSymbolOccurrence &LHS, + const OldSymbolOccurrence &RHS); }; /// \brief Less-than operator between the two renamed symbol occurrences. -bool operator<(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS); +bool operator<(const OldSymbolOccurrence &LHS, const OldSymbolOccurrence &RHS); /// \brief Equal-to operator between the two renamed symbol occurrences. -bool operator==(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS); +bool operator==(const OldSymbolOccurrence &LHS, const OldSymbolOccurrence &RHS); } // end namespace rename } // end namespace tooling diff --git a/clang/include/clang/Tooling/Refactor/RenamingOperation.h b/clang/include/clang/Tooling/Refactor/RenamingOperation.h index bb360a0dc766f..9a2883e64751e 100644 --- a/clang/include/clang/Tooling/Refactor/RenamingOperation.h +++ b/clang/include/clang/Tooling/Refactor/RenamingOperation.h @@ -25,15 +25,16 @@ class SymbolOperation; namespace rename { /// Return true if the new name is a valid language identifier. -bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector, - IdentifierTable &IDs, const LangOptions &LangOpts); -bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation, +bool isNewNameValid(const OldSymbolName &NewName, bool IsSymbolObjCSelector, IdentifierTable &IDs, const LangOptions &LangOpts); +bool isNewNameValid(const OldSymbolName &NewName, + const SymbolOperation &Operation, IdentifierTable &IDs, + const LangOptions &LangOpts); /// \brief Finds the set of new names that apply to the symbols in the given /// \c SymbolOperation. -void determineNewNames(SymbolName NewName, const SymbolOperation &Operation, - SmallVectorImpl<SymbolName> &NewNames, +void determineNewNames(OldSymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl<OldSymbolName> &NewNames, const LangOptions &LangOpts); } // end namespace rename diff --git a/clang/include/clang/Tooling/Refactor/SymbolName.h b/clang/include/clang/Tooling/Refactor/SymbolName.h index f348a02a813e0..fdb9f340b6e51 100644 --- a/clang/include/clang/Tooling/Refactor/SymbolName.h +++ b/clang/include/clang/Tooling/Refactor/SymbolName.h @@ -26,21 +26,21 @@ namespace tooling { /// /// Names can be composed of multiple string, to account for things like /// Objective-C selectors. -class SymbolName { +class OldSymbolName { public: - SymbolName() {} + OldSymbolName() {} /// \brief Creates a \c SymbolName by decomposing the given \p Name using /// language specific logic. - SymbolName(StringRef Name, const LangOptions &LangOpts); - SymbolName(StringRef Name, bool IsObjectiveCSelector); - explicit SymbolName(ArrayRef<StringRef> Name); + OldSymbolName(StringRef Name, const LangOptions &LangOpts); + OldSymbolName(StringRef Name, bool IsObjectiveCSelector); + explicit OldSymbolName(ArrayRef<StringRef> Name); - SymbolName(SymbolName &&) = default; - SymbolName &operator=(SymbolName &&) = default; + OldSymbolName(OldSymbolName &&) = default; + OldSymbolName &operator=(OldSymbolName &&) = default; - SymbolName(const SymbolName &) = default; - SymbolName &operator=(const SymbolName &) = default; + OldSymbolName(const OldSymbolName &) = default; + OldSymbolName &operator=(const OldSymbolName &) = default; bool empty() const { return Strings.empty(); } @@ -66,7 +66,7 @@ class SymbolName { std::vector<std::string> Strings; }; -raw_ostream &operator<<(raw_ostream &OS, const SymbolName &N); +raw_ostream &operator<<(raw_ostream &OS, const OldSymbolName &N); } // end namespace tooling } // end namespace clang diff --git a/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h b/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h index 6ecc23b310011..d6f8e495e6037 100644 --- a/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h +++ b/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h @@ -27,7 +27,7 @@ namespace tooling { namespace rename { // FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree! -std::vector<SymbolOccurrence> +std::vector<OldSymbolOccurrence> findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl); } // end namespace rename diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index 57a8775f4b095..b4f7b2841eb4f 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -1482,8 +1482,8 @@ ExtractOperation::performExpressionExtraction(ASTContext &Context, QualType VarType = findExpressionLexicalType(FunctionLikeParentDecl, E, E->getType(), PP, Context); StringRef VarName = "extractedExpr"; - auto CreatedSymbol = - llvm::make_unique<RefactoringResultAssociatedSymbol>(SymbolName(VarName)); + auto CreatedSymbol = llvm::make_unique<RefactoringResultAssociatedSymbol>( + OldSymbolName(VarName)); SourceRange ExtractedTokenRange = CandidateExtractionInfo[0].Range; SourceRange ExtractedCharRange = SourceRange( @@ -1803,7 +1803,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( } std::unique_ptr<RefactoringResultAssociatedSymbol> CreatedSymbol = llvm::make_unique<RefactoringResultAssociatedSymbol>( - SymbolName(ExtractedNamePieces)); + OldSymbolName(ExtractedNamePieces)); SourceLocation FunctionExtractionLoc = computeFunctionExtractionLocation( FunctionLikeParentDecl, isMethodExtraction()); diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index 43718c37eabf0..29ecbb15e270c 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -257,7 +257,8 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( StringRef Name = nameForExtractedVariable(E); Result.AssociatedSymbols.push_back( - llvm::make_unique<RefactoringResultAssociatedSymbol>(SymbolName(Name))); + llvm::make_unique<RefactoringResultAssociatedSymbol>( + OldSymbolName(Name))); RefactoringResultAssociatedSymbol *CreatedSymbol = Result.AssociatedSymbols.back().get(); diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index ef88e1fac6b44..16141517d9bd6 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -137,7 +137,7 @@ class SelectorParser { Success }; ParseState State = None; - const SymbolName &Name; + const OldSymbolName &Name; ParseState stateForToken(const Token &RawTok); @@ -145,7 +145,7 @@ class SelectorParser { unsigned SymbolIndex; llvm::SmallVector<SourceLocation, 8> SelectorLocations; - SelectorParser(const SymbolName &Name, unsigned SymbolIndex) + SelectorParser(const OldSymbolName &Name, unsigned SymbolIndex) : Name(Name), SymbolIndex(SymbolIndex) {} /// Returns true if the parses has found a '@selector' expression. @@ -236,7 +236,7 @@ static void collectTextualMatchesInComment( ArrayRef<IndexedSymbol> Symbols, SourceLocation CommentLoc, StringRef Comment, llvm::SmallVectorImpl<TextualMatchOccurrence> &Result) { for (const auto &Symbol : llvm::enumerate(Symbols)) { - const SymbolName &Name = Symbol.value().Name; + const OldSymbolName &Name = Symbol.value().Name; if (Name.containsEmptyPiece()) // Ignore Objective-C selectors with empty // pieces. continue; @@ -258,7 +258,7 @@ static void findTextualMatchesInComment( const SourceManager &SM, const LangOptions &LangOpts, ArrayRef<IndexedSymbol> Symbols, ArrayRef<TextualMatchOccurrence> TextualMatches, SourceRange CommentRange, - llvm::function_ref<void(SymbolOccurrence::OccurrenceKind, + llvm::function_ref<void(OldSymbolOccurrence::OccurrenceKind, ArrayRef<SourceLocation> Locations, unsigned SymbolIndex)> MatchHandler) { @@ -266,11 +266,11 @@ static void findTextualMatchesInComment( Lexer::getSourceText(CharSourceRange::getCharRange(CommentRange), SM, LangOpts) .str(); - SymbolOccurrence::OccurrenceKind Kind = + OldSymbolOccurrence::OccurrenceKind Kind = RawComment(SM, CommentRange, /*Merged=*/false, /*ParseAllComments=*/false) .isDocumentation() - ? SymbolOccurrence::MatchingDocComment - : SymbolOccurrence::MatchingComment; + ? OldSymbolOccurrence::MatchingDocComment + : OldSymbolOccurrence::MatchingComment; // Replace some special characters with ' ' to avoid comments and literals. std::replace_if( Source.begin(), Source.end(), @@ -303,7 +303,7 @@ static void findTextualMatchesInComment( static void findMatchingTextualOccurrences( const SourceManager &SM, const LangOptions &LangOpts, ArrayRef<IndexedSymbol> Symbols, - llvm::function_ref<void(SymbolOccurrence::OccurrenceKind, + llvm::function_ref<void(OldSymbolOccurrence::OccurrenceKind, ArrayRef<SourceLocation> Locations, unsigned SymbolIndex)> MatchHandler) { @@ -336,7 +336,7 @@ static void findMatchingTextualOccurrences( } else if (!SelectorParsers.empty()) { for (auto &Parser : SelectorParsers) { if (Parser.handleToken(RawTok)) - MatchHandler(SymbolOccurrence::MatchingSelector, + MatchHandler(OldSymbolOccurrence::MatchingSelector, Parser.SelectorLocations, Parser.SymbolIndex); } } @@ -381,8 +381,8 @@ static void findInclusionDirectiveOccurrence( size_t NameOffset = Filename.rfind_lower(Symbol.Name[0]); if (NameOffset == StringRef::npos) return; - SymbolOccurrence Result( - SymbolOccurrence::MatchingFilename, + OldSymbolOccurrence Result( + OldSymbolOccurrence::MatchingFilename, /*IsMacroExpansion=*/false, SymbolIndex, RawTok.getLocation().getLocWithOffset( NameOffset + (Filename.data() - RawTok.getLiteralData()))); @@ -419,9 +419,9 @@ void IndexedFileOccurrenceProducer::ExecuteAction() { bool IsImpProp = Match == MatchKind::SourcePropSetterMatch; if (IsImpProp) Locs.push_back(SymbolRange.getEnd()); - SymbolOccurrence Result( - IsImpProp ? SymbolOccurrence::MatchingImplicitProperty - : SymbolOccurrence::MatchingSymbol, + OldSymbolOccurrence Result( + IsImpProp ? OldSymbolOccurrence::MatchingImplicitProperty + : OldSymbolOccurrence::MatchingSymbol, /*IsMacroExpansion=*/Match == MatchKind::MacroExpansion, Symbol.index(), Locs); Consumer.handleOccurrence(Result, SM, LangOpts); @@ -433,10 +433,10 @@ void IndexedFileOccurrenceProducer::ExecuteAction() { return; findMatchingTextualOccurrences( SM, LangOpts, Symbols, - [&](SymbolOccurrence::OccurrenceKind Kind, + [&](OldSymbolOccurrence::OccurrenceKind Kind, ArrayRef<SourceLocation> Locations, unsigned SymbolIndex) { - SymbolOccurrence Result(Kind, /*IsMacroExpansion=*/false, SymbolIndex, - Locations); + OldSymbolOccurrence Result(Kind, /*IsMacroExpansion=*/false, + SymbolIndex, Locations); Consumer.handleOccurrence(Result, SM, LangOpts); }); } @@ -460,7 +460,7 @@ static bool isMatchingSelectorName(const Token &Tok, const Token &Next, } static bool -findObjCSymbolSelectorPieces(ArrayRef<Token> Tokens, const SymbolName &Name, +findObjCSymbolSelectorPieces(ArrayRef<Token> Tokens, const OldSymbolName &Name, SmallVectorImpl<SourceLocation> &Pieces, ObjCSymbolSelectorKind Kind) { assert(!Tokens.empty() && "no tokens"); @@ -559,8 +559,9 @@ findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, continue; SourceLocation Loc = SymbolRange.getBegin(); if (Match == MatchKind::MacroExpansion) { - SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol, - /*IsMacroExpansion=*/true, Symbol.index(), Loc); + OldSymbolOccurrence Result(OldSymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/true, Symbol.index(), + Loc); Consumer.handleOccurrence(Result, SM, LangOpts); continue; } @@ -611,9 +612,9 @@ findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, if (findObjCSymbolSelectorPieces( llvm::makeArrayRef(Tokens).drop_front(I.index()), Symbols[SymbolIndex].Name, SelectorPieces, Kind)) { - SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol, - /*IsMacroExpansion=*/false, SymbolIndex, - std::move(SelectorPieces)); + OldSymbolOccurrence Result(OldSymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/false, SymbolIndex, + std::move(SelectorPieces)); Consumer.handleOccurrence(Result, SM, LangOpts); } } diff --git a/clang/lib/Tooling/Refactor/RenamedSymbol.cpp b/clang/lib/Tooling/Refactor/RenamedSymbol.cpp index 16ec01f7f04cc..c4f7d3403254b 100644 --- a/clang/lib/Tooling/Refactor/RenamedSymbol.cpp +++ b/clang/lib/Tooling/Refactor/RenamedSymbol.cpp @@ -25,12 +25,13 @@ Symbol::Symbol(const NamedDecl *FoundDecl, unsigned SymbolIndex, ObjCSelector = MD->getSelector(); } -bool operator<(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS) { +bool operator<(const OldSymbolOccurrence &LHS, const OldSymbolOccurrence &RHS) { assert(!LHS.Locations.empty() && !RHS.Locations.empty()); return LHS.Locations[0] < RHS.Locations[0]; } -bool operator==(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS) { +bool operator==(const OldSymbolOccurrence &LHS, + const OldSymbolOccurrence &RHS) { return LHS.Kind == RHS.Kind && LHS.SymbolIndex == RHS.SymbolIndex && std::equal(LHS.Locations.begin(), LHS.Locations.end(), RHS.Locations.begin()); diff --git a/clang/lib/Tooling/Refactor/RenamingOperation.cpp b/clang/lib/Tooling/Refactor/RenamingOperation.cpp index 80aa1bc6dd2c7..6c6d1284f5e27 100644 --- a/clang/lib/Tooling/Refactor/RenamingOperation.cpp +++ b/clang/lib/Tooling/Refactor/RenamingOperation.cpp @@ -29,7 +29,7 @@ namespace clang { namespace tooling { namespace rename { -bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector, +bool isNewNameValid(const OldSymbolName &NewName, bool IsSymbolObjCSelector, IdentifierTable &IDs, const LangOptions &LangOpts) { Token Tok; if (IsSymbolObjCSelector) { @@ -53,16 +53,17 @@ bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector, return true; } -bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation, - IdentifierTable &IDs, const LangOptions &LangOpts) { +bool isNewNameValid(const OldSymbolName &NewName, + const SymbolOperation &Operation, IdentifierTable &IDs, + const LangOptions &LangOpts) { assert(!Operation.symbols().empty()); return isNewNameValid(NewName, Operation.symbols().front().ObjCSelector.hasValue(), IDs, LangOpts); } -void determineNewNames(SymbolName NewName, const SymbolOperation &Operation, - SmallVectorImpl<SymbolName> &NewNames, +void determineNewNames(OldSymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl<OldSymbolName> &NewNames, const LangOptions &LangOpts) { auto Symbols = Operation.symbols(); assert(!Symbols.empty()); @@ -76,7 +77,7 @@ void determineNewNames(SymbolName NewName, const SymbolOperation &Operation, auto AddName = [&](const NamedDecl *D, StringRef Name) { assert(Symbols.front().FoundDecl == D && "decl is missing"); - NewNames.push_back(SymbolName(Name, LangOpts)); + NewNames.push_back(OldSymbolName(Name, LangOpts)); Symbols = Symbols.drop_front(); }; diff --git a/clang/lib/Tooling/Refactor/SymbolName.cpp b/clang/lib/Tooling/Refactor/SymbolName.cpp index 2d30d1834e479..2a02d1627eea3 100644 --- a/clang/lib/Tooling/Refactor/SymbolName.cpp +++ b/clang/lib/Tooling/Refactor/SymbolName.cpp @@ -28,20 +28,20 @@ static void initNames(std::vector<std::string> &Strings, StringRef Name, } while (!Name.empty()); } -SymbolName::SymbolName(StringRef Name, const LangOptions &LangOpts) { +OldSymbolName::OldSymbolName(StringRef Name, const LangOptions &LangOpts) { initNames(Strings, Name, LangOpts.ObjC1); } -SymbolName::SymbolName(StringRef Name, bool IsObjectiveCSelector) { +OldSymbolName::OldSymbolName(StringRef Name, bool IsObjectiveCSelector) { initNames(Strings, Name, IsObjectiveCSelector); } -SymbolName::SymbolName(ArrayRef<StringRef> Name) { +OldSymbolName::OldSymbolName(ArrayRef<StringRef> Name) { for (const auto &Piece : Name) Strings.push_back(Piece.str()); } -void SymbolName::print(raw_ostream &OS) const { +void OldSymbolName::print(raw_ostream &OS) const { for (size_t I = 0, E = Strings.size(); I != E; ++I) { if (I != 0) OS << ':'; @@ -49,7 +49,7 @@ void SymbolName::print(raw_ostream &OS) const { } } -raw_ostream &operator<<(raw_ostream &OS, const SymbolName &N) { +raw_ostream &operator<<(raw_ostream &OS, const OldSymbolName &N) { N.print(OS); return OS; } diff --git a/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp b/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp index 38f4cc8d401fb..aa90503847d3b 100644 --- a/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp +++ b/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp @@ -39,7 +39,7 @@ class SymbolOccurrenceFinderASTVisitor public: explicit SymbolOccurrenceFinderASTVisitor( const SymbolOperation &Operation, const ASTContext &Context, - std::vector<SymbolOccurrence> &Occurrences) + std::vector<OldSymbolOccurrence> &Occurrences) : Operation(Operation), Context(Context), Occurrences(Occurrences) {} /// Returns a \c Symbol if the given declaration corresponds to the symbol @@ -52,8 +52,8 @@ class SymbolOccurrenceFinderASTVisitor } void checkDecl(const Decl *D, SourceLocation Loc, - SymbolOccurrence::OccurrenceKind Kind = - SymbolOccurrence::MatchingSymbol) { + OldSymbolOccurrence::OccurrenceKind Kind = + OldSymbolOccurrence::MatchingSymbol) { if (!D) return; std::string USR = getUSRForDecl(D); @@ -252,12 +252,12 @@ class SymbolOccurrenceFinderASTVisitor } checkDecl(Expr->getImplicitPropertyGetter(), Expr->getLocation(), - SymbolOccurrence::MatchingImplicitProperty); + OldSymbolOccurrence::MatchingImplicitProperty); // Add a manual location for a setter since a token like 'property' won't // match the the name of the renamed symbol like 'setProperty'. if (const auto *S = symbolForDecl(Expr->getImplicitPropertySetter())) addLocation(S->SymbolIndex, Expr->getLocation(), - SymbolOccurrence::MatchingImplicitProperty); + OldSymbolOccurrence::MatchingImplicitProperty); return true; } checkDecl(Expr->getExplicitProperty(), Expr->getLocation()); @@ -336,8 +336,8 @@ class SymbolOccurrenceFinderASTVisitor void checkAndAddLocations(unsigned SymbolIndex, ArrayRef<SourceLocation> Locations, - SymbolOccurrence::OccurrenceKind Kind = - SymbolOccurrence::MatchingSymbol) { + OldSymbolOccurrence::OccurrenceKind Kind = + OldSymbolOccurrence::MatchingSymbol) { if (Locations.size() != Operation.symbols()[SymbolIndex].Name.size()) return; @@ -354,8 +354,8 @@ class SymbolOccurrenceFinderASTVisitor Loc = SM.getExpansionLoc(Loc); } if (IsMacroExpansion) { - Occurrences.push_back(SymbolOccurrence(Kind, /*IsMacroExpansion=*/true, - SymbolIndex, Loc)); + Occurrences.push_back(OldSymbolOccurrence( + Kind, /*IsMacroExpansion=*/true, SymbolIndex, Loc)); return; } size_t Offset = @@ -365,13 +365,13 @@ class SymbolOccurrenceFinderASTVisitor StringLocations.push_back(Loc.getLocWithOffset(Offset)); } - Occurrences.push_back(SymbolOccurrence(Kind, /*IsMacroExpansion=*/false, - SymbolIndex, StringLocations)); + Occurrences.push_back(OldSymbolOccurrence(Kind, /*IsMacroExpansion=*/false, + SymbolIndex, StringLocations)); } /// Adds a location without checking if the name is actually there. void addLocation(unsigned SymbolIndex, SourceLocation Location, - SymbolOccurrence::OccurrenceKind Kind) { + OldSymbolOccurrence::OccurrenceKind Kind) { if (1 != Operation.symbols()[SymbolIndex].Name.size()) return; bool IsMacroExpansion = Location.isMacroID(); @@ -384,18 +384,18 @@ class SymbolOccurrenceFinderASTVisitor Location = SM.getExpansionLoc(Location); } Occurrences.push_back( - SymbolOccurrence(Kind, IsMacroExpansion, SymbolIndex, Location)); + OldSymbolOccurrence(Kind, IsMacroExpansion, SymbolIndex, Location)); } const SymbolOperation &Operation; const ASTContext &Context; - std::vector<SymbolOccurrence> &Occurrences; + std::vector<OldSymbolOccurrence> &Occurrences; }; } // namespace -std::vector<SymbolOccurrence> +std::vector<OldSymbolOccurrence> findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl) { - std::vector<SymbolOccurrence> Occurrences; + std::vector<OldSymbolOccurrence> Occurrences; SymbolOccurrenceFinderASTVisitor Visitor(Operation, Decl->getASTContext(), Occurrences); Visitor.TraverseDecl(Decl); diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index 424d130d14f86..8211cab9b8b1f 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -360,8 +360,8 @@ static int apply(ArrayRef<CXRefactoringReplacement> Replacements, /// this occurrence. static std::string occurrenceToString(const CXSymbolOccurrence &Occurrence, bool IsLocal, - const tooling::SymbolName &NewName, - const tooling::SymbolName &ExpectedReplacementStrings, + const tooling::OldSymbolName &NewName, + const tooling::OldSymbolName &ExpectedReplacementStrings, StringRef Filename) { std::string Str; llvm::raw_string_ostream OS(Str); @@ -434,7 +434,7 @@ parseIndexedOccurrence(StringRef IndexedOccurrence, static bool compareOccurrences(ArrayRef<std::string> ExpectedReplacements, CXSymbolOccurrencesResult Occurrences, bool IsLocal, - const tooling::SymbolName &NewSymbolName, + const tooling::OldSymbolName &NewSymbolName, bool PrintFilenames) { unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Occurrences); size_t ExpectedReplacementIndex = 0; @@ -591,7 +591,7 @@ int rename(CXTranslationUnit TU, CXIndex CIdx, ArrayRef<const char *> Args) { // FIXME: This is a hack LangOptions LangOpts; LangOpts.ObjC1 = true; - tooling::SymbolName NewSymbolName(opts::rename::NewName, LangOpts); + tooling::OldSymbolName NewSymbolName(opts::rename::NewName, LangOpts); if (ExpectedReplacements.empty()) { if (opts::Apply) { @@ -726,7 +726,7 @@ int renameIndexedFile(CXIndex CIdx, ArrayRef<const char *> Args) { LangOptions LangOpts; LangOpts.ObjC1 = true; - tooling::SymbolName ExpectedReplacementStrings( + tooling::OldSymbolName ExpectedReplacementStrings( opts::rename::IndexedNewNames[0], LangOpts); // Print the occurrences. @@ -747,7 +747,7 @@ int renameIndexedFile(CXIndex CIdx, ArrayRef<const char *> Args) { .c_str(); LangOptions LangOpts; LangOpts.ObjC1 = true; - tooling::SymbolName NewSymbolName(NewName, LangOpts); + tooling::OldSymbolName NewSymbolName(NewName, LangOpts); outs() << occurrenceToString(FileResult.Occurrences[I], /*IsLocal*/ false, NewSymbolName, ExpectedReplacementStrings, diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index 45f21ba134206..a2465506a934b 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -65,19 +65,19 @@ translateRefactoringActionType(RefactoringActionType Action) { } static CXSymbolOccurrenceKind -translateOccurrenceKind(rename::SymbolOccurrence::OccurrenceKind Kind) { +translateOccurrenceKind(rename::OldSymbolOccurrence::OccurrenceKind Kind) { switch (Kind) { - case rename::SymbolOccurrence::MatchingSymbol: + case rename::OldSymbolOccurrence::MatchingSymbol: return CXSymbolOccurrence_MatchingSymbol; - case rename::SymbolOccurrence::MatchingSelector: + case rename::OldSymbolOccurrence::MatchingSelector: return CXSymbolOccurrence_MatchingSelector; - case rename::SymbolOccurrence::MatchingImplicitProperty: + case rename::OldSymbolOccurrence::MatchingImplicitProperty: return CXSymbolOccurrence_MatchingImplicitProperty; - case rename::SymbolOccurrence::MatchingComment: + case rename::OldSymbolOccurrence::MatchingComment: return CXSymbolOccurrence_MatchingCommentString; - case rename::SymbolOccurrence::MatchingDocComment: + case rename::OldSymbolOccurrence::MatchingDocComment: return CXSymbolOccurrence_MatchingDocCommentString; - case rename::SymbolOccurrence::MatchingFilename: + case rename::OldSymbolOccurrence::MatchingFilename: return CXSymbolOccurrence_MatchingFilename; } } @@ -98,7 +98,7 @@ class RenamingResult { llvm::SpecificBumpPtrAllocator<CXRefactoringReplacement_Old> Replacements; std::vector<std::vector<CXRenamedSymbolOccurrence>> Occurrences; - void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, + void addOccurrence(const rename::OldSymbolOccurrence &RenamedOccurrence, const SourceManager &SM, const LangOptions &LangOpts) { CXRefactoringReplacement_Old *OccurrenceReplacements = Replacements.Allocate(RenamedOccurrence.locations().size()); @@ -106,8 +106,10 @@ class RenamingResult { unsigned I = 0; const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; if (!RenamedOccurrence.IsMacroExpansion && - RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && - RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) + RenamedOccurrence.Kind != + rename::OldSymbolOccurrence::MatchingComment && + RenamedOccurrence.Kind != + rename::OldSymbolOccurrence::MatchingDocComment) assert(RenamedOccurrence.locations().size() == SymbolNameInfo.size()); for (const auto &Location : RenamedOccurrence.locations()) { CXSourceRange Range = cxloc::translateSourceRange( @@ -134,7 +136,7 @@ class RenamingResult { } public: - RenamingResult(ArrayRef<SymbolName> NewNames, + RenamingResult(ArrayRef<OldSymbolName> NewNames, ArrayRef<rename::Symbol> Symbols) { assert(NewNames.size() == Symbols.size()); for (size_t I = 0, E = NewNames.size(); I != E; ++I) { @@ -151,7 +153,8 @@ class RenamingResult { } // FIXME: Don't duplicate code, Use just one constructor. - RenamingResult(ArrayRef<SymbolName> NewNames, ArrayRef<SymbolName> OldNames) { + RenamingResult(ArrayRef<OldSymbolName> NewNames, + ArrayRef<OldSymbolName> OldNames) { assert(NewNames.size() == OldNames.size()); for (size_t I = 0, E = NewNames.size(); I != E; ++I) { const auto &NewName = NewNames[I]; @@ -176,7 +179,7 @@ class RenamingResult { void handleTUResults(CXTranslationUnit TU, - llvm::MutableArrayRef<rename::SymbolOccurrence> Results) { + llvm::MutableArrayRef<rename::OldSymbolOccurrence> Results) { ASTUnit *Unit = cxtu::getASTUnit(TU); assert(Unit && "Invalid TU"); auto &Ctx = Unit->getASTContext(); @@ -184,7 +187,7 @@ class RenamingResult { // Find the set of files that have to be modified and gather the indices of // the occurrences for each file. const SourceManager &SM = Ctx.getSourceManager(); - typedef std::set<rename::SymbolOccurrence> OccurrenceSet; + typedef std::set<rename::OldSymbolOccurrence> OccurrenceSet; llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; for (auto &Occurrence : Results) { const std::pair<FileID, unsigned> DecomposedLocation = @@ -223,15 +226,16 @@ class RenamingResult { void handleSingleFileTUResults(const ASTContext &Ctx, - ArrayRef<rename::SymbolOccurrence> Occurrences) { + ArrayRef<rename::OldSymbolOccurrence> Occurrences) { addMainFilename(Ctx.getSourceManager()); for (const auto &Occurrence : Occurrences) addOccurrence(Occurrence, Ctx.getSourceManager(), Ctx.getLangOpts()); } - void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence, - const SourceManager &SM, - const LangOptions &LangOpts) { + void + handleIndexedFileOccurrence(const rename::OldSymbolOccurrence &Occurrence, + const SourceManager &SM, + const LangOptions &LangOpts) { if (Filenames.empty()) { addMainFilename(SM); } @@ -257,7 +261,7 @@ class SymbolOccurrencesResult { llvm::SpecificBumpPtrAllocator<CXFileRange> Ranges; std::vector<std::vector<CXSymbolOccurrence>> SymbolOccurrences; - void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, + void addOccurrence(const rename::OldSymbolOccurrence &RenamedOccurrence, const SourceManager &SM, const LangOptions &LangOpts) { ArrayRef<SourceLocation> Locations = RenamedOccurrence.locations(); CXFileRange *OccurrenceRanges = Ranges.Allocate(Locations.size()); @@ -265,8 +269,10 @@ class SymbolOccurrencesResult { unsigned I = 0; const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; if (!RenamedOccurrence.IsMacroExpansion && - RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && - RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) + RenamedOccurrence.Kind != + rename::OldSymbolOccurrence::MatchingComment && + RenamedOccurrence.Kind != + rename::OldSymbolOccurrence::MatchingDocComment) assert(Locations.size() == SymbolNameInfo.size()); for (const auto &Location : Locations) { CXSourceRange Range = cxloc::translateSourceRange( @@ -291,7 +297,7 @@ class SymbolOccurrencesResult { public: SymbolOccurrencesResult(ArrayRef<rename::Symbol> Symbols) { for (const auto &Symbol : Symbols) { - const SymbolName &Name = Symbol.Name; + const OldSymbolName &Name = Symbol.Name; SymbolNameInfo Info; for (size_t I = 0, E = Name.size(); I != E; ++I) Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()}); @@ -299,8 +305,8 @@ class SymbolOccurrencesResult { } } - SymbolOccurrencesResult(ArrayRef<SymbolName> Names) { - for (const SymbolName &Name : Names) { + SymbolOccurrencesResult(ArrayRef<OldSymbolName> Names) { + for (const OldSymbolName &Name : Names) { SymbolNameInfo Info; for (size_t I = 0, E = Name.size(); I != E; ++I) Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()}); @@ -315,7 +321,7 @@ class SymbolOccurrencesResult { void handleTUResults(CXTranslationUnit TU, - llvm::MutableArrayRef<rename::SymbolOccurrence> Results) { + llvm::MutableArrayRef<rename::OldSymbolOccurrence> Results) { ASTUnit *Unit = cxtu::getASTUnit(TU); assert(Unit && "Invalid TU"); auto &Ctx = Unit->getASTContext(); @@ -323,7 +329,7 @@ class SymbolOccurrencesResult { // Find the set of files that have to be modified and gather the indices of // the occurrences for each file. const SourceManager &SM = Ctx.getSourceManager(); - typedef std::set<rename::SymbolOccurrence> OccurrenceSet; + typedef std::set<rename::OldSymbolOccurrence> OccurrenceSet; llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; for (auto &Occurrence : Results) { const std::pair<FileID, unsigned> DecomposedLocation = @@ -360,9 +366,10 @@ class SymbolOccurrencesResult { SymbolOccurrences.push_back(std::vector<CXSymbolOccurrence>()); } - void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence, - const SourceManager &SM, - const LangOptions &LangOpts) { + void + handleIndexedFileOccurrence(const rename::OldSymbolOccurrence &Occurrence, + const SourceManager &SM, + const LangOptions &LangOpts) { if (Filenames.empty()) { addMainFilename(SM); } @@ -381,7 +388,7 @@ class RenamingAction { LangOptions LangOpts; IdentifierTable IDs; // TODO: Remove - SmallVector<SymbolName, 4> NewNames; + SmallVector<OldSymbolName, 4> NewNames; SymbolOperation Operation; RenamingAction(const LangOptions &LangOpts, SymbolOperation Operation) @@ -390,7 +397,7 @@ class RenamingAction { /// \brief Sets the new renaming name and returns CXError_Success on success. // TODO: Remove CXErrorCode setNewName(StringRef Name) { - SymbolName NewSymbolName(Name, LangOpts); + OldSymbolName NewSymbolName(Name, LangOpts); if (NewSymbolName.size() != Operation.symbols()[0].Name.size()) return CXError_RefactoringNameSizeMismatch; if (!rename::isNewNameValid(NewSymbolName, Operation, IDs, LangOpts)) @@ -474,12 +481,12 @@ static bool isObjCSelector(const CXIndexedSymbol &Symbol) { // New names are initialized and verified after the LangOptions are created. CXErrorCode computeNewNames(ArrayRef<CXRenamedIndexedSymbol> Symbols, - ArrayRef<SymbolName> SymbolNames, + ArrayRef<OldSymbolName> SymbolNames, const LangOptions &LangOpts, - SmallVectorImpl<SymbolName> &NewNames) { + SmallVectorImpl<OldSymbolName> &NewNames) { IdentifierTable IDs(LangOpts); for (const auto &Symbol : Symbols) { - SymbolName NewSymbolName(Symbol.NewName, LangOpts); + OldSymbolName NewSymbolName(Symbol.NewName, LangOpts); if (NewSymbolName.size() != SymbolNames[0].size()) return CXError_RefactoringNameSizeMismatch; if (!rename::isNewNameValid(NewSymbolName, isObjCSelector(Symbol), IDs, @@ -555,7 +562,7 @@ CXErrorCode performIndexedFileRename( IndexedOccurrences.push_back(Result); } - IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector), + IndexedSymbols.emplace_back(OldSymbolName(Symbol.Name, IsObjCSelector), IndexedOccurrences, /*IsObjCSelector=*/IsObjCSelector); } @@ -581,16 +588,16 @@ CXErrorCode performIndexedFileRename( Options); } - void handleOccurrence(const rename::SymbolOccurrence &Occurrence, + void handleOccurrence(const rename::OldSymbolOccurrence &Occurrence, SourceManager &SM, const LangOptions &LangOpts) override { if (Err != CXError_Success) return; if (!Result) { - SmallVector<SymbolName, 4> SymbolNames; + SmallVector<OldSymbolName, 4> SymbolNames; for (const auto &Symbol : IndexedSymbols) SymbolNames.push_back(Symbol.Name); - SmallVector<SymbolName, 4> NewNames; + SmallVector<OldSymbolName, 4> NewNames; Err = computeNewNames(Symbols, SymbolNames, LangOpts, NewNames); if (Err != CXError_Success) return; @@ -664,7 +671,7 @@ CXErrorCode performIndexedSymbolSearch( IndexedOccurrences.push_back(Result); } - IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector), + IndexedSymbols.emplace_back(OldSymbolName(Symbol.Name, IsObjCSelector), IndexedOccurrences, /*IsObjCSelector=*/IsObjCSelector); } @@ -686,11 +693,11 @@ CXErrorCode performIndexedSymbolSearch( Options); } - void handleOccurrence(const rename::SymbolOccurrence &Occurrence, + void handleOccurrence(const rename::OldSymbolOccurrence &Occurrence, SourceManager &SM, const LangOptions &LangOpts) override { if (!Result) { - SmallVector<SymbolName, 4> SymbolNames; + SmallVector<OldSymbolName, 4> SymbolNames; for (const auto &Symbol : IndexedSymbols) SymbolNames.push_back(Symbol.Name); Result = new SymbolOccurrencesResult(SymbolNames); From 5c9882c0be6f8a9fd60e97309523846b0306d74c Mon Sep 17 00:00:00 2001 From: Ben Langmuir <blangmuir@apple.com> Date: Wed, 16 Aug 2017 16:29:08 -0700 Subject: [PATCH 196/582] [indexstore] Add unresolved-using declaration support to indexstore ... and fixup record hashing to account for differences in the types. rdar://problem/33883650 apple-llvm-split-commit: 9d9e1b8eb4883b8a044f0bf3f0952632d962100a apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 3 ++ clang/lib/Index/IndexDataStoreUtils.cpp | 12 +++++ clang/lib/Index/IndexRecordHasher.cpp | 12 +++++ clang/test/Index/Store/record-hash-using.cpp | 46 ++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 clang/test/Index/Store/record-hash-using.cpp diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 024cdf6c7967d..f4f41db002321 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -209,6 +209,7 @@ typedef enum { INDEXSTORE_SYMBOL_KIND_DESTRUCTOR = 23, INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION = 24, INDEXSTORE_SYMBOL_KIND_PARAMETER = 25, + INDEXSTORE_SYMBOL_KIND_USING = 26, INDEXSTORE_SYMBOL_KIND_COMMENTTAG = 1000, } indexstore_symbol_kind_t; @@ -219,6 +220,8 @@ typedef enum { INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR = 2, INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER = 3, INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER = 4, + INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME = 5, + INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE = 6, INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET = 1000, INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET = 1001, diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 0d9e754cf95fa..3efaec95e23e5 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -97,6 +97,8 @@ SymbolKind index::getSymbolKind(indexstore_symbol_kind_t K) { return SymbolKind::ConversionFunction; case INDEXSTORE_SYMBOL_KIND_PARAMETER: return SymbolKind::Parameter; + case INDEXSTORE_SYMBOL_KIND_USING: + return SymbolKind::Using; case INDEXSTORE_SYMBOL_KIND_COMMENTTAG: return SymbolKind::CommentTag; } @@ -115,6 +117,10 @@ SymbolSubKind index::getSymbolSubKind(indexstore_symbol_subkind_t K) { return SymbolSubKind::AccessorGetter; case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER: return SymbolSubKind::AccessorSetter; + case INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME: + return SymbolSubKind::UsingTypename; + case INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE: + return SymbolSubKind::UsingValue; case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET: return SymbolSubKind::SwiftAccessorWillSet; case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET: @@ -231,6 +237,8 @@ indexstore_symbol_kind_t index::getIndexStoreKind(SymbolKind K) { return INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION; case SymbolKind::Parameter: return INDEXSTORE_SYMBOL_KIND_PARAMETER; + case SymbolKind::Using: + return INDEXSTORE_SYMBOL_KIND_USING; case SymbolKind::CommentTag: return INDEXSTORE_SYMBOL_KIND_COMMENTTAG; } @@ -249,6 +257,10 @@ indexstore_symbol_subkind_t index::getIndexStoreSubKind(SymbolSubKind K) { return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER; case SymbolSubKind::AccessorSetter: return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER; + case SymbolSubKind::UsingTypename: + return INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME; + case SymbolSubKind::UsingValue: + return INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE; case SymbolSubKind::SwiftAccessorWillSet: return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET; case SymbolSubKind::SwiftAccessorDidSet: diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp index 855f902f24e67..1edf4c151ca12 100644 --- a/clang/lib/Index/IndexRecordHasher.cpp +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -103,6 +103,18 @@ class DeclHashVisitor : public ConstDeclVisitor<DeclHashVisitor, hash_code> { return Hash; } + hash_code VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + hash_code Hash = VisitNamedDecl(D); + COMBINE_HASH(Hasher.hash(D->getQualifier())); + return Hash; + } + + hash_code VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + hash_code Hash = VisitNamedDecl(D); + COMBINE_HASH(Hasher.hash(D->getQualifier())); + return Hash; + } + hash_code VisitDeclContext(const DeclContext *DC) { // FIXME: Add location if this is anonymous namespace ? DC = DC->getRedeclContext(); diff --git a/clang/test/Index/Store/record-hash-using.cpp b/clang/test/Index/Store/record-hash-using.cpp new file mode 100644 index 0000000000000..c8285d5e96c96 --- /dev/null +++ b/clang/test/Index/Store/record-hash-using.cpp @@ -0,0 +1,46 @@ +// XFAIL: linux + +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=B -DTYPE2=A -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=B -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=B -DTYPE2=B -DTYPE3=T -DTYPE4=T +// RUN: find %t/idx/*/records -name "record-hash*" | count 4 +// +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=U -DTYPE4=T +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=U +// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=U -DTYPE4=U +// RUN: find %t/idx/*/records -name "record-hash*" | count 4 + +template<typename T> +struct A { + typedef int X; + void foo(); +}; + +template<typename T> +struct B : public A<T> { + typedef float X; + void foo(int); +}; + +template<typename T> +struct C : public B<T> { +// This should result in different records, due to the different types. + using TYPE1<T>::X; + using TYPE2<T>::foo; +}; + +template <typename T> +struct D { + typedef T X; + void foo(T); +}; +template <typename T, typename U> +struct E : public D<T>, public D<U> { +// This should result in different records, due to the different template parameter. + using D<TYPE3>::X; + using D<TYPE4>::foo; +}; From 48c393133593b0e744a1381b0ba1f28ea8d49064 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 17 Aug 2017 16:16:46 +0100 Subject: [PATCH 197/582] Fix merged r311085 to account for refactoring changes apple-llvm-split-commit: 7221706617abb2c1ed23c9a6e8e0c315156d5427 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/DeclTemplate.h | 2 +- clang/include/clang/AST/PrettyPrinter.h | 2 +- clang/lib/AST/DeclPrinter.cpp | 3 ++- clang/lib/Tooling/Refactor/Extract.cpp | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index de5342568114f..1d0a203b8d087 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -158,7 +158,7 @@ class TemplateParameterList final } void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy, - unsigned Indentation = 0) const; + const ASTContext &Context, unsigned Indentation = 0) const; friend TrailingObjects; diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 8cef4ddea6c80..41281550f7f52 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -51,7 +51,7 @@ struct PrintingPolicy { TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), - UseStdFunctionForLambda(false), ConstantsAsWritten(false) { } + ConstantsAsWritten(false), UseStdFunctionForLambda(false) { } /// \brief Adjust this printing policy for cases where it's known that /// we're printing C++ code (for instance, if AST dumping reaches a diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 7ddbdde8f77c4..7b7a8b98c70a9 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -124,8 +124,9 @@ void Decl::print(raw_ostream &Out, const PrintingPolicy &Policy, void TemplateParameterList::print(raw_ostream &Out, const PrintingPolicy &Policy, + const ASTContext &Context, unsigned Indentation) const { - DeclPrinter Printer(Out, Policy, Indentation); + DeclPrinter Printer(Out, Policy, Context, Indentation); Printer.printTemplateParameters(this); } diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index b4f7b2841eb4f..4f6b4de0b594a 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -1844,7 +1844,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( for (unsigned I = 0, NumTemplateParams = FD->getNumTemplateParameterLists(); I < NumTemplateParams; ++I) { - FD->getTemplateParameterList(I)->print(OS, PP); + FD->getTemplateParameterList(I)->print(OS, PP, Context); OS << "\n"; } } From dbc57d6e0e6a12d0cb11cf986f44e4f7bdc68b56 Mon Sep 17 00:00:00 2001 From: Erich Keane <erich.keane@intel.com> Date: Mon, 28 Aug 2017 18:53:17 +0000 Subject: [PATCH 198/582] Change Diagnostic Category size error from runtime to compiletime Diagnostic Categories are fairly annoying, and are only enforced by a runtime-debug-only assert. This puts in a touch more work to get this all done at compile-time with static asserts Differential Revision: https://reviews.llvm.org/D37122 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@311905 91177308-0d34-0410-b5e6-96231b3b80d8 apple-llvm-split-commit: 00d1b6d2cadee0de2790f58bd00dc0d487fc3d3b apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h | 38 ++++++++++++++++------- clang/lib/Basic/DiagnosticIDs.cpp | 35 ++++++++++++++------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 14fb24c0c96e5..b665c6325144f 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -26,20 +26,34 @@ namespace clang { // Import the diagnostic enums themselves. namespace diag { + // Size of each of the diagnostic categories. + enum { + DIAG_SIZE_COMMON = 300, + DIAG_SIZE_DRIVER = 200, + DIAG_SIZE_FRONTEND = 100, + DIAG_SIZE_SERIALIZATION = 120, + DIAG_SIZE_LEX = 400, + DIAG_SIZE_PARSE = 500, + DIAG_SIZE_AST = 110, + DIAG_SIZE_COMMENT = 100, + DIAG_SIZE_SEMA = 3500, + DIAG_SIZE_ANALYSIS = 100, + DIAG_SIZE_REFACTORING = 100 + }; // Start position for diagnostics. enum { - DIAG_START_COMMON = 0, - DIAG_START_DRIVER = DIAG_START_COMMON + 300, - DIAG_START_FRONTEND = DIAG_START_DRIVER + 200, - DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + 100, - DIAG_START_LEX = DIAG_START_SERIALIZATION + 120, - DIAG_START_PARSE = DIAG_START_LEX + 400, - DIAG_START_AST = DIAG_START_PARSE + 500, - DIAG_START_COMMENT = DIAG_START_AST + 110, - DIAG_START_SEMA = DIAG_START_COMMENT + 100, - DIAG_START_ANALYSIS = DIAG_START_SEMA + 4000, - DIAG_START_REFACTORING = DIAG_START_ANALYSIS + 100, - DIAG_UPPER_LIMIT = DIAG_START_REFACTORING + 100 + DIAG_START_COMMON = 0, + DIAG_START_DRIVER = DIAG_START_COMMON + DIAG_SIZE_COMMON, + DIAG_START_FRONTEND = DIAG_START_DRIVER + DIAG_SIZE_DRIVER, + DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + DIAG_SIZE_FRONTEND, + DIAG_START_LEX = DIAG_START_SERIALIZATION + DIAG_SIZE_SERIALIZATION, + DIAG_START_PARSE = DIAG_START_LEX + DIAG_SIZE_LEX, + DIAG_START_AST = DIAG_START_PARSE + DIAG_SIZE_PARSE, + DIAG_START_COMMENT = DIAG_START_AST + DIAG_SIZE_AST, + DIAG_START_SEMA = DIAG_START_COMMENT + DIAG_SIZE_COMMENT, + DIAG_START_ANALYSIS = DIAG_START_SEMA + DIAG_SIZE_SEMA, + DIAG_START_REFACTORING = DIAG_START_ANALYSIS + DIAG_SIZE_ANALYSIS, + DIAG_UPPER_LIMIT = DIAG_START_REFACTORING + DIAG_SIZE_REFACTORING }; class CustomDiagInfo; diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index c7e413304a103..5c8dafcdf2e84 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -68,6 +68,29 @@ struct StaticDiagInfoRec { } }; +#define STRINGIFY_NAME(NAME) #NAME +#define VALIDATE_DIAG_SIZE(NAME) \ + static_assert( \ + static_cast<unsigned>(diag::NUM_BUILTIN_##NAME##_DIAGNOSTICS) < \ + static_cast<unsigned>(diag::DIAG_START_##NAME) + \ + static_cast<unsigned>(diag::DIAG_SIZE_##NAME), \ + STRINGIFY_NAME( \ + DIAG_SIZE_##NAME) " is insufficient to contain all " \ + "diagnostics, it may need to be made larger in " \ + "DiagnosticIDs.h."); +VALIDATE_DIAG_SIZE(COMMON) +VALIDATE_DIAG_SIZE(DRIVER) +VALIDATE_DIAG_SIZE(FRONTEND) +VALIDATE_DIAG_SIZE(SERIALIZATION) +VALIDATE_DIAG_SIZE(LEX) +VALIDATE_DIAG_SIZE(PARSE) +VALIDATE_DIAG_SIZE(AST) +VALIDATE_DIAG_SIZE(COMMENT) +VALIDATE_DIAG_SIZE(SEMA) +VALIDATE_DIAG_SIZE(ANALYSIS) +#undef VALIDATE_DIAG_SIZE +#undef STRINGIFY_NAME + } // namespace anonymous static const StaticDiagInfoRec StaticDiagInfo[] = { @@ -97,18 +120,6 @@ static const unsigned StaticDiagInfoSize = llvm::array_lengthof(StaticDiagInfo); /// GetDiagInfo - Return the StaticDiagInfoRec entry for the specified DiagID, /// or null if the ID is invalid. static const StaticDiagInfoRec *GetDiagInfo(unsigned DiagID) { - // If assertions are enabled, verify that the StaticDiagInfo array is sorted. -#ifndef NDEBUG - static bool IsFirst = true; // So the check is only performed on first call. - if (IsFirst) { - assert(std::is_sorted(std::begin(StaticDiagInfo), - std::end(StaticDiagInfo)) && - "Diag ID conflict, the enums at the start of clang::diag (in " - "DiagnosticIDs.h) probably need to be increased"); - IsFirst = false; - } -#endif - // Out of bounds diag. Can't be in the table. using namespace diag; if (DiagID >= DIAG_UPPER_LIMIT || DiagID <= DIAG_START_COMMON) From 2e986945d1746c722770d58baa80f6e368415cc0 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 6 Sep 2017 14:23:21 -0700 Subject: [PATCH 199/582] Soft-deprecate -fapinotes-cache-path; use -fmodules-cache-path instead. (#119) When Clang decided to change the default module cache path to include the current user's name, the API notes path didn't change. Rather than duplicate that logic, just put the API notes cache in with the module cache by default. We still support -fapinotes-cache-path in case anyone's using it, but from now on -fmodules-cache-path should be sufficient. (This is reasonable because API notes are currently linked to modules anyway.) https://bugs.swift.org/browse/SR-5806 apple-llvm-split-commit: 581f4ac5bb433ff350407fcb5cfcb6d9d7345372 apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticFrontendKinds.td | 2 +- clang/include/clang/Driver/Options.td | 2 +- clang/lib/Driver/ToolChains/Clang.cpp | 22 +++---------------- clang/lib/Frontend/CompilerInvocation.cpp | 11 +++++++--- clang/test/APINotes/cache.m | 16 ++++++++++---- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 446eb41ea02c8..3354048ef769b 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -230,7 +230,7 @@ def err_invalid_vfs_overlay : Error< "invalid virtual filesystem overlay file '%0'">, DefaultFatal; def err_no_apinotes_cache_path : Error< - "-fapinotes was provided without -fapinotes-cache-path=<directory>">, + "-fapinotes was provided without -fmodules-cache-path=<directory>">, DefaultFatal; def warn_option_invalid_ocl_version : Warning< diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 0323f7e0a5676..cfea4226d8c28 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -719,7 +719,7 @@ def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group<f_clang_Gr Flags<[CC1Option]>, HelpText<"Disable module-based external API notes support">; def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, Group<i_Group>, Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">, - HelpText<"Specify the API notes cache path">; + HelpText<"Specify the API notes cache path (defaults to -fmodules-cache-path)">; def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, Group<f_clang_Group>, Flags<[CC1Option]>, MetaVarName<"<version>">, HelpText<"Specify the Swift version to use when filtering API notes">; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index ce662bc4f6a7f..120bba268874f 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3985,28 +3985,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_apinotes_modules, false)) CmdArgs.push_back("-fapinotes-modules"); - SmallString<128> APINotesCachePath; if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) { - APINotesCachePath = A->getValue(); + SmallString<128> APINotesCachePath{"-fapinotes-cache-path="}; + APINotesCachePath += A->getValue(); + CmdArgs.push_back(Args.MakeArgString(APINotesCachePath)); } - if (C.isForDiagnostics()) { - // When generating crash reports, we want to emit the API notes along with - // the reproduction sources, so we ignore any provided API notes path. - APINotesCachePath = Output.getFilename(); - llvm::sys::path::replace_extension(APINotesCachePath, ".cache"); - llvm::sys::path::append(APINotesCachePath, "apinotes"); - } else if (APINotesCachePath.empty()) { - // No API notes path was provided: use the default. - llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, - APINotesCachePath); - llvm::sys::path::append(APINotesCachePath, "org.llvm.clang"); - llvm::sys::path::append(APINotesCachePath, "APINotesCache"); - } - const char Arg[] = "-fapinotes-cache-path="; - APINotesCachePath.insert(APINotesCachePath.begin(), Arg, Arg + strlen(Arg)); - CmdArgs.push_back(Args.MakeArgString(APINotesCachePath)); - Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version); } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index d1b3beaf7554b..dc8d6688e85d0 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2731,11 +2731,16 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; - // -fapinotes and -fapinotes-modules requires -fapinotes-cache-path=<directory>. + // -fapinotes and -fapinotes-modules requires -fmodules-cache-path=<directory>. if ((LangOpts.APINotes || LangOpts.APINotesModules) && Res.getFileSystemOpts().APINotesCachePath.empty()) { - Diags.Report(diag::err_no_apinotes_cache_path); - Success = false; + if (!Res.getHeaderSearchOpts().ModuleCachePath.empty()) { + Res.getFileSystemOpts().APINotesCachePath = + Res.getHeaderSearchOpts().ModuleCachePath; + } else { + Diags.Report(diag::err_no_apinotes_cache_path); + Success = false; + } } } diff --git a/clang/test/APINotes/cache.m b/clang/test/APINotes/cache.m index b87bdf14fecc8..08a2f263a5c77 100644 --- a/clang/test/APINotes/cache.m +++ b/clang/test/APINotes/cache.m @@ -1,4 +1,4 @@ -// RUN: rm -rf %t/APINotesCache +// RUN: rm -rf %t // RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // Check for the presence of the cached compiled form. @@ -6,11 +6,19 @@ // RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" // Run test again to ensure that caching doesn't cause problems. -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +// Check that the default path is taken from -fmodules-cache-path. +// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fmodules-cache-path=%t/ModuleCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: ls %t/ModuleCache | grep "APINotes-.*.apinotesc" +// RUN: ls %t/ModuleCache | grep "SomeKit-.*.apinotesc" + +// RUN: not %clang_cc1 -fapinotes -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2>&1 | FileCheck --check-prefix=CHECK-NO-CACHE %s +// CHECK-NO-CACHE: error: -fapinotes was provided without -fmodules-cache-path -// Check that the driver provides a default -fapinotes-cache-path= +// Check that the driver does not provide a default -fapinotes-cache-path=. // RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s -// CHECK-DEFAULT-PATH: -fapinotes-cache-path={{.*}}org.llvm.clang/APINotesCache +// CHECK-DEFAULT-PATH-NOT: -fapinotes-cache-path // Check that the driver passes through a provided -fapinotes-cache-path= // RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s From 1f6dff503c919b39ad6817e574485892d2b39610 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 6 Sep 2017 16:24:07 -0700 Subject: [PATCH 200/582] On second thought, don't bother caching compiled API notes at all. (#120) Because API notes are always used with modules, and modules are always cached in PCM files, we don't really achieve any major wins by caching the API notes information separately. Just rebuild the PCM when the API notes change, like we do for headers. The next step is to not use the binary form at all. We don't really need it, for the same reason we don't need the cache. rdar://problem/31745420, also a better solution to https://bugs.swift.org/browse/SR-5806 apple-llvm-split-commit: 13bdf7769e2f34671aae3e7ccec1b8ef2b2a653f apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesManager.h | 3 - .../clang/Basic/DiagnosticFrontendKinds.td | 4 - clang/include/clang/Basic/FileSystemOptions.h | 3 - clang/include/clang/Driver/Options.td | 4 +- clang/lib/APINotes/APINotesManager.cpp | 167 +----------------- clang/lib/Driver/ToolChains/Clang.cpp | 6 - clang/lib/Frontend/CompilerInvocation.cpp | 13 -- clang/test/APINotes/availability.m | 2 +- clang/test/APINotes/broken_types.m | 2 +- clang/test/APINotes/cache.m | 40 ----- clang/test/APINotes/cache_pruning.m | 49 ----- clang/test/APINotes/module-cache.m | 30 ++-- clang/test/APINotes/nullability.c | 2 +- clang/test/APINotes/nullability.m | 4 +- clang/test/APINotes/objc_designated_inits.m | 2 +- clang/test/APINotes/properties.m | 4 +- clang/test/APINotes/search-order.m | 6 +- clang/test/APINotes/types.m | 2 +- clang/test/APINotes/versioned.m | 8 +- clang/test/APINotes/yaml-convert-diags.c | 2 +- clang/test/APINotes/yaml-parse-diags.c | 2 +- 21 files changed, 43 insertions(+), 312 deletions(-) delete mode 100644 clang/test/APINotes/cache.m delete mode 100644 clang/test/APINotes/cache_pruning.m diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index 2adc29c0bf4c2..6eb0534060039 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -65,9 +65,6 @@ class APINotesManager { /// for private headers. APINotesReader *CurrentModuleReaders[2] = { nullptr, nullptr }; - /// Whether we have already pruned the API notes cache. - bool PrunedCache; - /// A mapping from header file directories to the API notes reader for /// that directory, or a redirection to another directory entry that may /// have more information, or NULL to indicate that there is no API notes diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 3354048ef769b..38bc7268a45fd 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -229,10 +229,6 @@ def err_missing_vfs_overlay_file : Error< def err_invalid_vfs_overlay : Error< "invalid virtual filesystem overlay file '%0'">, DefaultFatal; -def err_no_apinotes_cache_path : Error< - "-fapinotes was provided without -fmodules-cache-path=<directory>">, - DefaultFatal; - def warn_option_invalid_ocl_version : Warning< "OpenCL version %0 does not support the option '%1'">, InGroup<Deprecated>; } diff --git a/clang/include/clang/Basic/FileSystemOptions.h b/clang/include/clang/Basic/FileSystemOptions.h index 1beb2e2907210..38f1346312489 100644 --- a/clang/include/clang/Basic/FileSystemOptions.h +++ b/clang/include/clang/Basic/FileSystemOptions.h @@ -25,9 +25,6 @@ class FileSystemOptions { /// \brief If set, paths are resolved as if the working directory was /// set to the value of WorkingDir. std::string WorkingDir; - - /// The path to the API notes cache. - std::string APINotesCachePath; }; } // end namespace clang diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index cfea4226d8c28..d6ebc3d5af76c 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -718,8 +718,8 @@ def fno_apinotes : Flag<["-"], "fno-apinotes">, Group<f_clang_Group>, def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group<f_clang_Group>, Flags<[CC1Option]>, HelpText<"Disable module-based external API notes support">; def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, - Group<i_Group>, Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">, - HelpText<"Specify the API notes cache path (defaults to -fmodules-cache-path)">; + Group<i_Group>, Flags<[DriverOption]>, MetaVarName<"<directory>">, + HelpText<"Does nothing; API notes are no longer cached separately from modules">; def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, Group<f_clang_Group>, Flags<[CC1Option]>, MetaVarName<"<version>">, HelpText<"Specify the Swift version to use when filtering API notes">; diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 832f454c6c897..6fc0fda0872b7 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -46,12 +46,6 @@ STATISTIC(NumDirectoriesSearched, "header directories searched"); STATISTIC(NumDirectoryCacheHits, "directory cache hits"); -STATISTIC(NumBinaryCacheHits, - "binary form cache hits"); -STATISTIC(NumBinaryCacheMisses, - "binary form cache misses"); -STATISTIC(NumBinaryCacheRebuilds, - "binary form cache rebuilds"); namespace { /// Prints two successive strings, which much be kept alive as long as the @@ -69,8 +63,7 @@ namespace { APINotesManager::APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts) - : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes), - PrunedCache(false) { } + : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes) { } APINotesManager::~APINotesManager() { // Free the API notes readers. @@ -84,78 +77,8 @@ APINotesManager::~APINotesManager() { delete CurrentModuleReaders[1]; } -/// \brief Write a new timestamp file with the given path. -static void writeTimestampFile(StringRef TimestampFile) { - std::error_code EC; - llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::F_None); -} - -/// \brief Prune the API notes cache of API notes that haven't been accessed in -/// a long time. -static void pruneAPINotesCache(StringRef APINotesCachePath) { - struct stat StatBuf; - llvm::SmallString<128> TimestampFile; - TimestampFile = APINotesCachePath; - llvm::sys::path::append(TimestampFile, "APINotes.timestamp"); - - // Try to stat() the timestamp file. - if (::stat(TimestampFile.c_str(), &StatBuf)) { - // If the timestamp file wasn't there, create one now. - if (errno == ENOENT) { - llvm::sys::fs::create_directories(APINotesCachePath); - writeTimestampFile(TimestampFile); - } - return; - } - - const unsigned APINotesCachePruneInterval = 7 * 24 * 60 * 60; - const unsigned APINotesCachePruneAfter = 31 * 24 * 60 * 60; - - // Check whether the time stamp is older than our pruning interval. - // If not, do nothing. - time_t TimeStampModTime = StatBuf.st_mtime; - time_t CurrentTime = time(nullptr); - if (CurrentTime - TimeStampModTime <= time_t(APINotesCachePruneInterval)) - return; - - // Write a new timestamp file so that nobody else attempts to prune. - // There is a benign race condition here, if two Clang instances happen to - // notice at the same time that the timestamp is out-of-date. - writeTimestampFile(TimestampFile); - - // Walk the entire API notes cache, looking for unused compiled API notes. - std::error_code EC; - SmallString<128> APINotesCachePathNative; - llvm::sys::path::native(APINotesCachePath, APINotesCachePathNative); - for (llvm::sys::fs::directory_iterator - File(APINotesCachePathNative.str(), EC), DirEnd; - File != DirEnd && !EC; File.increment(EC)) { - StringRef Extension = llvm::sys::path::extension(File->path()); - if (Extension.empty()) - continue; - - if (Extension.substr(1) != BINARY_APINOTES_EXTENSION) - continue; - - // Look at this file. If we can't stat it, there's nothing interesting - // there. - if (::stat(File->path().c_str(), &StatBuf)) - continue; - - // If the file has been used recently enough, leave it there. - time_t FileAccessTime = StatBuf.st_atime; - if (CurrentTime - FileAccessTime <= time_t(APINotesCachePruneAfter)) { - continue; - } - - // Remove the file. - llvm::sys::fs::remove(File->path()); - } -} - std::unique_ptr<APINotesReader> APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { - FileManager &fileMgr = SourceMgr.getFileManager(); PrettyStackTraceDoubleString trace("Loading API notes from ", apiNotesFile->getName()); @@ -174,59 +97,6 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { return APINotesReader::getUnmanaged(buffer, SwiftVersion); } - // If we haven't pruned the API notes cache yet during this execution, do - // so now. - if (!PrunedCache) { - pruneAPINotesCache(fileMgr.getFileSystemOpts().APINotesCachePath); - PrunedCache = true; - } - - // Compute a hash of the API notes file's directory and the Clang version, - // to be used as part of the filename for the cached binary copy. - auto code = llvm::hash_value(StringRef(apiNotesFile->getDir()->getName())); - code = hash_combine(code, getClangFullRepositoryVersion()); - - // Determine the file name for the cached binary form. - SmallString<128> compiledFileName; - compiledFileName += fileMgr.getFileSystemOpts().APINotesCachePath; - assert(!compiledFileName.empty() && "No API notes cache path provided?"); - llvm::sys::path::append(compiledFileName, - (llvm::Twine(llvm::sys::path::stem(apiNotesFileName)) + "-" - + llvm::APInt(64, code).toString(36, /*Signed=*/false) + "." - + BINARY_APINOTES_EXTENSION)); - - // Try to open the cached binary form. - if (const FileEntry *compiledFile = fileMgr.getFile(compiledFileName, - /*openFile=*/true, - /*cacheFailure=*/false)) { - // Load the file contents. - if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { - // Load the file. - if (auto reader = APINotesReader::get(std::move(buffer.get()), - SwiftVersion)) { - bool outOfDate = false; - if (auto sizeAndModTime = reader->getSourceFileSizeAndModTime()) { - if (sizeAndModTime->first != apiNotesFile->getSize() || - sizeAndModTime->second != apiNotesFile->getModificationTime()) - outOfDate = true; - } - - if (!outOfDate) { - // Success. - ++NumBinaryCacheHits; - return reader; - } - } - } - - // The cache entry was somehow broken; delete this one so we can build a - // new one below. - llvm::sys::fs::remove(compiledFileName.str()); - ++NumBinaryCacheRebuilds; - } else { - ++NumBinaryCacheMisses; - } - // Open the source file. auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User); auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation()); @@ -235,6 +105,8 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { // Compile the API notes source into a buffer. // FIXME: Either propagate OSType through or, better yet, improve the binary // APINotes format to maintain complete availability information. + // FIXME: We don't even really need to go through the binary format at all; + // we're just going to immediately deserialize it again. llvm::SmallVector<char, 1024> apiNotesBuffer; std::unique_ptr<llvm::MemoryBuffer> compiledBuffer; { @@ -257,39 +129,6 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { StringRef(apiNotesBuffer.data(), apiNotesBuffer.size())); } - // Save the binary form into the cache. Perform this operation - // atomically. - SmallString<64> temporaryBinaryFileName = compiledFileName.str(); - temporaryBinaryFileName.erase( - temporaryBinaryFileName.end() - - llvm::sys::path::extension(temporaryBinaryFileName).size(), - temporaryBinaryFileName.end()); - temporaryBinaryFileName += "-%%%%%%."; - temporaryBinaryFileName += BINARY_APINOTES_EXTENSION; - - int temporaryFD; - llvm::sys::fs::create_directories( - fileMgr.getFileSystemOpts().APINotesCachePath); - if (!llvm::sys::fs::createUniqueFile(temporaryBinaryFileName.str(), - temporaryFD, temporaryBinaryFileName)) { - // Write the contents of the buffer. - bool hadError; - { - llvm::raw_fd_ostream out(temporaryFD, /*shouldClose=*/true); - out.write(compiledBuffer.get()->getBufferStart(), - compiledBuffer.get()->getBufferSize()); - out.flush(); - - hadError = out.has_error(); - } - - if (!hadError) { - // Rename the temporary file to the actual compiled file. - llvm::sys::fs::rename(temporaryBinaryFileName.str(), - compiledFileName.str()); - } - } - // Load the binary form we just compiled. auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion); assert(reader && "Could not load the API notes we just generated?"); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 120bba268874f..eab096591d405 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3985,12 +3985,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_apinotes_modules, false)) CmdArgs.push_back("-fapinotes-modules"); - if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) { - SmallString<128> APINotesCachePath{"-fapinotes-cache-path="}; - APINotesCachePath += A->getValue(); - CmdArgs.push_back(Args.MakeArgString(APINotesCachePath)); - } - Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version); } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index dc8d6688e85d0..e0b9bec99a42d 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1180,7 +1180,6 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) { Opts.WorkingDir = Args.getLastArgValue(OPT_working_directory); - Opts.APINotesCachePath = Args.getLastArgValue(OPT_fapinotes_cache_path); } /// Parse the argument to the -ftest-module-file-extension @@ -2730,18 +2729,6 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Res.getPreprocessorOpts(), Diags); if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; - - // -fapinotes and -fapinotes-modules requires -fmodules-cache-path=<directory>. - if ((LangOpts.APINotes || LangOpts.APINotesModules) && - Res.getFileSystemOpts().APINotesCachePath.empty()) { - if (!Res.getHeaderSearchOpts().ModuleCachePath.empty()) { - Res.getFileSystemOpts().APINotesCachePath = - Res.getHeaderSearchOpts().ModuleCachePath; - } else { - Diags.Report(diag::err_no_apinotes_cache_path); - Success = false; - } - } } if (LangOpts.CUDA) { diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m index f9bee1a000012..7231eb480b883 100644 --- a/clang/test/APINotes/availability.m +++ b/clang/test/APINotes/availability.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/broken_types.m b/clang/test/APINotes/broken_types.m index 164ae795f4c2d..ee33ff7c4b4b9 100644 --- a/clang/test/APINotes/broken_types.m +++ b/clang/test/APINotes/broken_types.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2> %t.err +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2> %t.err // RUN: FileCheck %s < %t.err #include "BrokenTypes.h" diff --git a/clang/test/APINotes/cache.m b/clang/test/APINotes/cache.m deleted file mode 100644 index 08a2f263a5c77..0000000000000 --- a/clang/test/APINotes/cache.m +++ /dev/null @@ -1,40 +0,0 @@ -// RUN: rm -rf %t -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify - -// Check for the presence of the cached compiled form. -// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" - -// Run test again to ensure that caching doesn't cause problems. -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify - -// Check that the default path is taken from -fmodules-cache-path. -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fmodules-cache-path=%t/ModuleCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -// RUN: ls %t/ModuleCache | grep "APINotes-.*.apinotesc" -// RUN: ls %t/ModuleCache | grep "SomeKit-.*.apinotesc" - -// RUN: not %clang_cc1 -fapinotes -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2>&1 | FileCheck --check-prefix=CHECK-NO-CACHE %s -// CHECK-NO-CACHE: error: -fapinotes was provided without -fmodules-cache-path - -// Check that the driver does not provide a default -fapinotes-cache-path=. -// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s -// CHECK-DEFAULT-PATH-NOT: -fapinotes-cache-path - -// Check that the driver passes through a provided -fapinotes-cache-path= -// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s -// CHECK-PATH: -fapinotes-cache-path=/wobble - -#include "HeaderLib.h" -#import <SomeKit/SomeKit.h> - -int main() { - int i; - i = unavailable_function(); // expected-error{{'unavailable_function' is unavailable: I beg you not to use this}} - // expected-note@HeaderLib.h:8{{'unavailable_function' has been explicitly marked unavailable here}} - - A *a = 0; - [a transform:a]; // expected-error{{'transform:' is unavailable: anything but this}} - // expected-note@SomeKit/SomeKit.h:6{{'transform:' has been explicitly marked unavailable here}} - - return 0; -} diff --git a/clang/test/APINotes/cache_pruning.m b/clang/test/APINotes/cache_pruning.m deleted file mode 100644 index 1a36570bb43f0..0000000000000 --- a/clang/test/APINotes/cache_pruning.m +++ /dev/null @@ -1,49 +0,0 @@ -// We need 'touch' and 'find' for this test to work. -// REQUIRES: shell - -// RUN: rm -rf %t/APINotesCache - -// Run Clang. This should generated the cached versions of both and a timestamp. -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB -// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" - -// Set the timestamp back a very long time. We should try to prune, -// but nothing gets pruned because the API Notes files are new enough. -// RUN: touch -m -a -t 201101010000 %t/APINotes.timestamp -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" - -// Set the HeaderLib access time back a very long time. -// This shouldn't prune anything, because the timestamp has been updated, so -// the pruning mechanism won't fire. -// RUN: find %t/APINotesCache -name APINotes-*.apinotesc | xargs touch -a -t 201101010000 -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" - -// Set the timestack back a very long time. This should prune the -// HeaderLib file, because the pruning mechanism should fire and -// HeaderLib is both old and not used. -// RUN: touch -m -a -t 201101010000 %t/APINotesCache/APINotes.timestamp -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -// RUN: ls %t/APINotesCache | not grep "APINotes-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" - -// Run Clang. This should generated the cached versions of both and a timestamp. -// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB -// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc" -// RUN: ls %t/APINotesCache | grep "APINotes.timestamp" - -#ifdef INCLUDE_HEADERLIB -#include "HeaderLib.h" -#endif -#include <SomeKit/SomeKit.h> - -int main() { return 0; } diff --git a/clang/test/APINotes/module-cache.m b/clang/test/APINotes/module-cache.m index 2324697a0f701..19d6a27bec18b 100644 --- a/clang/test/APINotes/module-cache.m +++ b/clang/test/APINotes/module-cache.m @@ -1,36 +1,46 @@ // RUN: rm -rf %t -// Set up a directory with API notes +// Set up directories // RUN: mkdir -p %t/APINotes // RUN: cp %S/Inputs/APINotes/SomeOtherKit.apinotes %t/APINotes/SomeOtherKit.apinotes +// RUN: mkdir -p %t/Frameworks +// RUN: cp -r %S/Inputs/Frameworks/SomeOtherKit.framework %t/Frameworks // First build: check that 'methodB' is unavailable but 'methodA' is available. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/before.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log // RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/before.log // RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log // Do it again; now we're using caches. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/before.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log // RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/before.log // RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log -// Change the API notes file. +// Add a blank line to the header to force the module to rebuild, without +// (yet) changing API notes. +// RUN: echo >> %t/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/before.log 2>&1 +// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log +// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/before.log +// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log + +// Change the API notes file, after the module has rebuilt once. // RUN: echo ' - Selector: "methodA"' >> %t/APINotes/SomeOtherKit.apinotes // RUN: echo ' MethodKind: Instance' >> %t/APINotes/SomeOtherKit.apinotes // RUN: echo ' Availability: none' >> %t/APINotes/SomeOtherKit.apinotes // RUN: echo ' AvailabilityMsg: "not here either"' >> %t/APINotes/SomeOtherKit.apinotes // Build again: check that both methods are now unavailable and that the module rebuilt. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/after.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log // RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/after.log // RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log // Run the build again: check that both methods are now unavailable -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/after.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log // RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/after.log @@ -43,13 +53,13 @@ // RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %S/Inputs/APINotes/SomeOtherKit.apinotes // First build: check that 'methodB' is unavailable but 'methodA' is available. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-before.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log // RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-before.log // RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log // Do it again; now we're using caches. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-before.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log // RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-before.log // RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log @@ -58,14 +68,14 @@ // RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %t/APINotes/SomeOtherKit.apinotes // Build again: check that both methods are now unavailable and that the module rebuilt. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-after.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log // RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-after.log // RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log // Run the build again: check that both methods are now unavailable -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1 +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-after.log 2>&1 // RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log // RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log // RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-after.log diff --git a/clang/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c index 1fcd0ee1b42bc..e07fc2e5c1174 100644 --- a/clang/test/APINotes/nullability.c +++ b/clang/test/APINotes/nullability.c @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index f70c3634b5997..65c9c2c9bc3d9 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -1,9 +1,9 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables. -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 -fmodules-ignore-macro=SWIFT_VERSION_3_0 +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 -fmodules-ignore-macro=SWIFT_VERSION_3_0 #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/objc_designated_inits.m b/clang/test/APINotes/objc_designated_inits.m index 1df8cf80edd0e..24b317c159c38 100644 --- a/clang/test/APINotes/objc_designated_inits.m +++ b/clang/test/APINotes/objc_designated_inits.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/properties.m b/clang/test/APINotes/properties.m index b1559b9002c81..fe3c738404492 100644 --- a/clang/test/APINotes/properties.m +++ b/clang/test/APINotes/properties.m @@ -1,7 +1,7 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' | FileCheck -check-prefix=CHECK -check-prefix=CHECK-4 %s -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' -fapinotes-swift-version=3 | FileCheck -check-prefix=CHECK -check-prefix=CHECK-3 %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' | FileCheck -check-prefix=CHECK -check-prefix=CHECK-4 %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' -fapinotes-swift-version=3 | FileCheck -check-prefix=CHECK -check-prefix=CHECK-3 %s // I know, FileChecking an AST dump is brittle. However, the attributes being // tested aren't used for anything by Clang, and don't even have a spelling. diff --git a/clang/test/APINotes/search-order.m b/clang/test/APINotes/search-order.m index 2c667be38d29b..aa2f21a2eaaa3 100644 --- a/clang/test/APINotes/search-order.m +++ b/clang/test/APINotes/search-order.m @@ -1,10 +1,10 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_SEARCH_PATH=1 -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_SEARCH_PATH=1 -verify -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -iapinotes-modules %S/Inputs/APINotes -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify @import SomeOtherKit; diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m index 9f8b970cde06f..a0f728b554729 100644 --- a/clang/test/APINotes/types.m +++ b/clang/test/APINotes/types.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 5657ae17658db..2f85735b7ccec 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -1,14 +1,14 @@ // RUN: rm -rf %t && mkdir -p %t // Build and check the unversioned module file. -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s // Build and check the versioned module file. -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s #import <VersionedKit/VersionedKit.h> diff --git a/clang/test/APINotes/yaml-convert-diags.c b/clang/test/APINotes/yaml-convert-diags.c index e8767f228e5e9..8d5c0fb70568e 100644 --- a/clang/test/APINotes/yaml-convert-diags.c +++ b/clang/test/APINotes/yaml-convert-diags.c @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: not %clang_cc1 -fsyntax-only -fapinotes -fapinotes-cache-path=%t %s -I %S/Inputs/BrokenHeaders2 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fsyntax-only -fapinotes %s -I %S/Inputs/BrokenHeaders2 2>&1 | FileCheck %s #include "SomeBrokenLib.h" diff --git a/clang/test/APINotes/yaml-parse-diags.c b/clang/test/APINotes/yaml-parse-diags.c index 4505e293ef898..a7b370aee942e 100644 --- a/clang/test/APINotes/yaml-parse-diags.c +++ b/clang/test/APINotes/yaml-parse-diags.c @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -fsyntax-only -fapinotes -fapinotes-cache-path=%t %s -I %S/Inputs/BrokenHeaders -verify +// RUN: %clang_cc1 -fsyntax-only -fapinotes %s -I %S/Inputs/BrokenHeaders -verify #include "SomeBrokenLib.h" From b7fcfbe1c0dcd506ed2a7711348443da672ec343 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 7 Sep 2017 17:00:38 -0700 Subject: [PATCH 201/582] [APINotes] Honor Swift 4 API notes in Swift 3 mode. More generally, change the meaning of the SwiftVersions section to be "this version or earlier" rather than "exactly this version". This keeps us from having to duplicate information for builds of Swift that support more than two possible versions (e.g. 3, 4, and 5). apple-llvm-split-commit: 503a9c431fb7050a1a7988d4cbc59128b56186b1 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesReader.cpp | 10 ++- .../Headers/VersionedKit.apinotes | 64 ++++++++++++++++- .../Headers/VersionedKit.h | 27 ++++++-- clang/test/APINotes/versioned-multi.c | 69 +++++++++++++++++++ clang/test/APINotes/versioned.m | 12 ++-- 5 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 clang/test/APINotes/versioned-multi.c diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 0b802b487e585..eddab497a6a2b 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -1492,9 +1492,13 @@ APINotesReader::VersionedInfo<T>::VersionedInfo( Selected = Results.size(); for (unsigned i = 0, n = Results.size(); i != n; ++i) { - if (Results[i].first == version) { - Selected = i; - break; + if (version && Results[i].first >= version) { + // Pick the closest match. If the current version is "4", then entries for + // 4 are better than entries for 5, but both are valid. + if (Selected == Results.size() || + Results[Selected].first > Results[i].first) { + Selected = i; + } } if (!Results[i].first) { diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index c02c6cbac9a17..9099615a1424c 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -36,6 +36,15 @@ Tags: EnumKind: NSClosedEnum - Name: UndoAllThatHasBeenDoneToMe EnumKind: none +Typedefs: + - Name: MultiVersionedTypedef34Notes + SwiftName: MultiVersionedTypedef34Notes_NEW + - Name: MultiVersionedTypedef345Notes + SwiftName: MultiVersionedTypedef345Notes_NEW + - Name: MultiVersionedTypedef4Notes + SwiftName: MultiVersionedTypedef4Notes_NEW + - Name: MultiVersionedTypedef45Notes + SwiftName: MultiVersionedTypedef45Notes_NEW SwiftVersions: - Version: 3.0 Classes: @@ -87,6 +96,55 @@ SwiftVersions: Typedefs: - Name: MyDoubleWrapper SwiftWrapper: none - - - \ No newline at end of file + - Name: MultiVersionedTypedef34 + SwiftName: MultiVersionedTypedef34_3 + - Name: MultiVersionedTypedef34Header + SwiftName: MultiVersionedTypedef34Header_3 + - Name: MultiVersionedTypedef34Notes + SwiftName: MultiVersionedTypedef34Notes_3 + - Name: MultiVersionedTypedef345 + SwiftName: MultiVersionedTypedef345_3 + - Name: MultiVersionedTypedef345Header + SwiftName: MultiVersionedTypedef345Header_3 + - Name: MultiVersionedTypedef345Notes + SwiftName: MultiVersionedTypedef345Notes_3 + - Version: 5 + Typedefs: + - Name: MultiVersionedTypedef345 + SwiftName: MultiVersionedTypedef345_5 + - Name: MultiVersionedTypedef345Header + SwiftName: MultiVersionedTypedef345Header_5 + - Name: MultiVersionedTypedef345Notes + SwiftName: MultiVersionedTypedef345Notes_5 + - Name: MultiVersionedTypedef45 + SwiftName: MultiVersionedTypedef45_5 + - Name: MultiVersionedTypedef45Header + SwiftName: MultiVersionedTypedef45Header_5 + - Name: MultiVersionedTypedef45Notes + SwiftName: MultiVersionedTypedef45Notes_5 + - Version: 4 # Versions are deliberately ordered as "3, 5, 4" to catch bugs. + Typedefs: + - Name: MultiVersionedTypedef34 + SwiftName: MultiVersionedTypedef34_4 + - Name: MultiVersionedTypedef34Header + SwiftName: MultiVersionedTypedef34Header_4 + - Name: MultiVersionedTypedef34Notes + SwiftName: MultiVersionedTypedef34Notes_4 + - Name: MultiVersionedTypedef345 + SwiftName: MultiVersionedTypedef345_4 + - Name: MultiVersionedTypedef345Header + SwiftName: MultiVersionedTypedef345Header_4 + - Name: MultiVersionedTypedef345Notes + SwiftName: MultiVersionedTypedef345Notes_4 + - Name: MultiVersionedTypedef4 + SwiftName: MultiVersionedTypedef4_4 + - Name: MultiVersionedTypedef4Header + SwiftName: MultiVersionedTypedef4Header_4 + - Name: MultiVersionedTypedef4Notes + SwiftName: MultiVersionedTypedef4Notes_4 + - Name: MultiVersionedTypedef45 + SwiftName: MultiVersionedTypedef45_4 + - Name: MultiVersionedTypedef45Header + SwiftName: MultiVersionedTypedef45Header_4 + - Name: MultiVersionedTypedef45Notes + SwiftName: MultiVersionedTypedef45Notes_4 diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index 61a10034503c7..b060c9423cd2f 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -2,6 +2,11 @@ void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:) void acceptClosure(void (^ __attribute__((noescape)) block)(void)); +void privateFunc(void) __attribute__((swift_private)); + +typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); + +#if __OBJC__ @class NSString; extern NSString *MyErrorDomain; @@ -14,10 +19,6 @@ __attribute__((swift_bridge("MyValueType"))) @interface MyReferenceType @end -void privateFunc(void) __attribute__((swift_private)); - -typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); - @interface TestProperties @property (nonatomic, readwrite, retain) id accessorsOnly; @property (nonatomic, readwrite, retain, class) id accessorsOnlyForClass; @@ -42,6 +43,7 @@ typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); __attribute__((swift_name("Swift4Name"))) @interface Swift3RenamedAlsoDUMP @end +#endif enum __attribute__((flag_enum)) FlagEnum { @@ -110,3 +112,20 @@ enum SoonToBeNSClosedEnum { enum UndoAllThatHasBeenDoneToMe { UndoAllThatHasBeenDoneToMeA = 1 } __attribute__((flag_enum)) __attribute__((enum_extensibility(closed))); + + +typedef int MultiVersionedTypedef4; +typedef int MultiVersionedTypedef4Notes; +typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_NEW"))); + +typedef int MultiVersionedTypedef34; +typedef int MultiVersionedTypedef34Notes; +typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_NEW"))); + +typedef int MultiVersionedTypedef45; +typedef int MultiVersionedTypedef45Notes; +typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_NEW"))); + +typedef int MultiVersionedTypedef345; +typedef int MultiVersionedTypedef345Notes; +typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_NEW"))); diff --git a/clang/test/APINotes/versioned-multi.c b/clang/test/APINotes/versioned-multi.c new file mode 100644 index 0000000000000..48c51fd932e17 --- /dev/null +++ b/clang/test/APINotes/versioned-multi.c @@ -0,0 +1,69 @@ +// RUN: rm -rf %t && mkdir -p %t + +// Build and check the unversioned module file. +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s + +// Build and check the various versions. +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned3 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned3/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-3 %s + +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned4 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=4 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned4/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-4 %s + +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned5 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=5 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned5/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-5 %s + +#import <VersionedKit/VersionedKit.h> + +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4; +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_NEW"))); +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_NEW"))); +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef34; +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_NEW"))); +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_NEW"))); +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef45; +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_NEW"))); +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_NEW"))); +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef345; +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_NEW"))); +// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_NEW"))); + +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef4 __attribute__((swift_name("MultiVersionedTypedef4_4"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_4"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_4"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef34 __attribute__((swift_name("MultiVersionedTypedef34_3"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_3"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_3"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef45 __attribute__((swift_name("MultiVersionedTypedef45_4"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_4"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_4"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef345 __attribute__((swift_name("MultiVersionedTypedef345_3"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_3"))); +// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_3"))); + +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef4 __attribute__((swift_name("MultiVersionedTypedef4_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef34 __attribute__((swift_name("MultiVersionedTypedef34_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef45 __attribute__((swift_name("MultiVersionedTypedef45_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef345 __attribute__((swift_name("MultiVersionedTypedef345_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_4"))); +// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_4"))); + +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef4; +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_NEW"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_NEW"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef34; +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_NEW"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_NEW"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef45 __attribute__((swift_name("MultiVersionedTypedef45_5"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_5"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_5"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef345 __attribute__((swift_name("MultiVersionedTypedef345_5"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_5"))); +// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_5"))); diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 2f85735b7ccec..905a9c7eb8a93 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -53,6 +53,10 @@ // CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape))); // CHECK-VERSIONED: void acceptClosure(void (^block)(void)); +// CHECK-UNVERSIONED: void privateFunc() __attribute__((swift_private)); + +// CHECK-UNVERSIONED: typedef double MyDoubleWrapper __attribute__((swift_wrapper("struct"))); + // CHECK-UNVERSIONED: enum MyErrorCode { // CHECK-UNVERSIONED-NEXT: MyErrorCodeFailed = 1 // CHECK-UNVERSIONED-NEXT: } __attribute__((ns_error_domain(MyErrorDomain))); @@ -60,9 +64,9 @@ // CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType"))) // CHECK-UNVERSIONED: @interface MyReferenceType -// CHECK-UNVERSIONED: void privateFunc() __attribute__((swift_private)); +// CHECK-VERSIONED: void privateFunc(); -// CHECK-UNVERSIONED: typedef double MyDoubleWrapper __attribute__((swift_wrapper("struct"))); +// CHECK-VERSIONED: typedef double MyDoubleWrapper; // CHECK-VERSIONED: enum MyErrorCode { // CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1 @@ -71,10 +75,6 @@ // CHECK-VERSIONED-NOT: __attribute__((swift_bridge("MyValueType"))) // CHECK-VERSIONED: @interface MyReferenceType -// CHECK-VERSIONED: void privateFunc(); - -// CHECK-VERSIONED: typedef double MyDoubleWrapper; - // CHECK-UNVERSIONED: __attribute__((swift_objc_members) // CHECK-UNVERSIONED-NEXT: @interface TestProperties // CHECK-VERSIONED-NOT: __attribute__((swift_objc_members) From 30147d6682b2fbd27c874746c56c5b23f9f6f06c Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 7 Sep 2017 17:27:34 -0700 Subject: [PATCH 202/582] [APINotes] Sort versioned info entries to make lookup easier. No functionality change. apple-llvm-split-commit: 202145d8d243e094e0d832c90d085672a185b083 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesReader.cpp | 35 +++++++++++++-------------- clang/lib/APINotes/APINotesWriter.cpp | 10 ++++++-- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index eddab497a6a2b..a5dd24d3300e8 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -1487,31 +1487,30 @@ APINotesReader::VersionedInfo<T>::VersionedInfo( SmallVector<std::pair<VersionTuple, T>, 1> results) : Results(std::move(results)) { - // Look for an exact version match. - Optional<unsigned> unversioned; - Selected = Results.size(); + assert(!Results.empty()); + assert(std::is_sorted(Results.begin(), Results.end(), + [](const std::pair<VersionTuple, T> &left, + const std::pair<VersionTuple, T> &right) -> bool { + assert(left.first != right.first && "two entries for the same version"); + return left.first < right.first; + })); + Selected = Results.size(); for (unsigned i = 0, n = Results.size(); i != n; ++i) { if (version && Results[i].first >= version) { - // Pick the closest match. If the current version is "4", then entries for - // 4 are better than entries for 5, but both are valid. - if (Selected == Results.size() || - Results[Selected].first > Results[i].first) { - Selected = i; - } - } - - if (!Results[i].first) { - assert(!unversioned && "Two unversioned entries?"); - unversioned = i; + // If the current version is "4", then entries for 4 are better than + // entries for 5, but both are valid. Because entries are sorted, we get + // that behavior by picking the first match. + Selected = i; + break; } } // If we didn't find a match but we have an unversioned result, use the - // unversioned result. - if (Selected == Results.size() && unversioned) { - Selected = *unversioned; - } + // unversioned result. This will always be the first entry because we encode + // it as version 0. + if (Selected == Results.size() && Results[0].first.empty()) + Selected = 0; } auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> { diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 285a35efe205a..b178e257e7006 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -475,10 +475,16 @@ namespace { template<typename T> void emitVersionedInfo( raw_ostream &out, - const SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray, + SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray, llvm::function_ref<void(raw_ostream &out, const typename MakeDependent<T>::Type& info)> emitInfo) { + std::sort(infoArray.begin(), infoArray.end(), + [](const std::pair<VersionTuple, T> &left, + const std::pair<VersionTuple, T> &right) -> bool { + assert(left.first != right.first && "two entries for the same version"); + return left.first < right.first; + }); endian::Writer<little> writer(out); writer.write<uint16_t>(infoArray.size()); for (const auto &element : infoArray) { @@ -528,7 +534,7 @@ namespace { using key_type_ref = key_type; using data_type = SmallVector<std::pair<VersionTuple, UnversionedDataType>, 1>; - using data_type_ref = const data_type &; + using data_type_ref = data_type &; using hash_value_type = size_t; using offset_type = unsigned; From b979e8d7be107e2909bffc1175beb750312b7cd0 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 14 Sep 2017 17:15:56 -0700 Subject: [PATCH 203/582] [APINotes] Record what version caused an annotation to get replaced. (#123) Now that -fapinotes-swift-version=3 can pick up API notes for "Version: 4", the information for /inactive/ API notes isn't sufficient to tell what would have happened under a different version. That is, I can't ask "what would happen under version 4", because I can't tell if the active annotations are active because they were specified for version 3 or version 4. The inactive, unversioned attributes were no help, either: they just said "version 0". Fix this by adding a new flag 'IsReplacedByActive' to SwiftVersionedAttr and SwiftVersionedRemovalAttr. When set, the 'Version' field refers to the API note that caused this annotation to become inactive. Note that "version 0" can still exist: when an attribute written in source is replaced by an unversioned attribute in the API notes. apple-llvm-split-commit: b9c72518b27163388c82f3fa5f2577c49a868070 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 6 ++- clang/lib/Sema/SemaAPINotes.cpp | 42 ++++++++++++------- .../Headers/VersionedKit.apinotes | 6 +++ .../Headers/VersionedKit.h | 6 +++ clang/test/APINotes/properties.m | 8 ++-- clang/test/APINotes/versioned.m | 29 +++++++++---- 6 files changed, 69 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index dcafc838548c1..63368868326ee 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1746,7 +1746,8 @@ def SwiftVersioned : Attr { // This attribute has no spellings as it is only ever created implicitly // from API notes. let Spellings = []; - let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">]; + let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">, + BoolArgument<"IsReplacedByActive">]; let SemaHandler = 0; let Documentation = [Undocumented]; } @@ -1755,7 +1756,8 @@ def SwiftVersionedRemoval : Attr { // This attribute has no spellings as it is only ever created implicitly // from API notes. let Spellings = []; - let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">]; + let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">, + BoolArgument<"IsReplacedByActive">]; let SemaHandler = 0; let Documentation = [Undocumented]; let AdditionalMembers = [{ diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 6145fef3de268..0b3cdbb960331 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -21,14 +21,21 @@ namespace { IsNotActive, IsActive }; + enum IsReplacement_t : bool { + IsNotReplacement, + IsReplacement + }; struct VersionedInfoMetadata { /// An empty version refers to unversioned metadata. VersionTuple Version; - bool IsActive; + unsigned IsActive: 1; + unsigned IsReplacement: 1; - VersionedInfoMetadata(VersionTuple version, IsActive_t active) - : Version(version), IsActive(active == IsActive_t::IsActive) {} + VersionedInfoMetadata(VersionTuple version, IsActive_t active, + IsReplacement_t replacement) + : Version(version), IsActive(active == IsActive_t::IsActive), + IsReplacement(replacement == IsReplacement_t::IsReplacement) {} }; } // end anonymous namespace @@ -148,9 +155,8 @@ namespace { if (existing != end) { // Remove the existing attribute, and treat it as a superseded // non-versioned attribute. - auto *versioned = - SwiftVersionedAttr::CreateImplicit(S.Context, clang::VersionTuple(), - *existing); + auto *versioned = SwiftVersionedAttr::CreateImplicit( + S.Context, metadata.Version, *existing, /*IsReplacedByActive*/true); D->getAttrs().erase(existing.getCurrent()); D->addAttr(versioned); @@ -166,19 +172,18 @@ namespace { } else { if (shouldAddAttribute) { if (auto attr = createAttr()) { - auto *versioned = - SwiftVersionedAttr::CreateImplicit(S.Context, metadata.Version, - attr); + auto *versioned = SwiftVersionedAttr::CreateImplicit( + S.Context, metadata.Version, attr, + /*IsReplacedByActive*/metadata.IsReplacement); D->addAttr(versioned); } } else { // FIXME: This isn't preserving enough information for things like // availability, where we're trying to remove a /specific/ kind of // attribute. - auto *versioned = - SwiftVersionedRemovalAttr::CreateImplicit(S.Context, - metadata.Version, - AttrKindFor<A>::value); + auto *versioned = SwiftVersionedRemovalAttr::CreateImplicit( + S.Context, metadata.Version, AttrKindFor<A>::value, + /*IsReplacedByActive*/metadata.IsReplacement); D->addAttr(versioned); } } @@ -684,7 +689,8 @@ static void maybeAttachUnversionedSwiftName( } // Then explicitly call that out with a removal attribute. - VersionedInfoMetadata DummyFutureMetadata(VersionTuple(), IsNotActive); + VersionedInfoMetadata DummyFutureMetadata(SelectedVersion, IsNotActive, + IsReplacement); handleAPINotedAttribute<SwiftNameAttr>(S, D, /*add*/false, DummyFutureMetadata, []() -> SwiftNameAttr * { @@ -709,7 +715,13 @@ static void ProcessVersionedAPINotes( for (unsigned i = 0, e = Info.size(); i != e; ++i) { std::tie(Version, InfoSlice) = Info[i]; auto Active = (i == Selected) ? IsActive : IsNotActive; - ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active)); + auto Replacement = IsNotReplacement; + if (Active == IsNotActive && Version.empty()) { + Replacement = IsReplacement; + Version = Info[Selected].first; + } + ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active, + Replacement)); } } diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index 9099615a1424c..572c714b3d61a 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -15,6 +15,9 @@ Classes: - Name: accessorsOnlyForClassExceptInVersion3 PropertyKind: Class SwiftImportAsAccessors: true +Functions: + - Name: unversionedRenameDUMP + SwiftName: 'unversionedRename_NOTES()' Tags: - Name: APINotedFlagEnum FlagEnum: true @@ -123,6 +126,9 @@ SwiftVersions: - Name: MultiVersionedTypedef45Notes SwiftName: MultiVersionedTypedef45Notes_5 - Version: 4 # Versions are deliberately ordered as "3, 5, 4" to catch bugs. + Classes: + - Name: Swift4RenamedDUMP + SwiftName: SpecialSwift4Name Typedefs: - Name: MultiVersionedTypedef34 SwiftName: MultiVersionedTypedef34_4 diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h index b060c9423cd2f..9ce95633c523b 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -1,5 +1,7 @@ void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); +void unversionedRenameDUMP(void) __attribute__((swift_name("unversionedRename_HEADER()"))); + void acceptClosure(void (^ __attribute__((noescape)) block)(void)); void privateFunc(void) __attribute__((swift_private)); @@ -43,6 +45,10 @@ __attribute__((swift_bridge("MyValueType"))) __attribute__((swift_name("Swift4Name"))) @interface Swift3RenamedAlsoDUMP @end + +@interface Swift4RenamedDUMP +@end + #endif diff --git a/clang/test/APINotes/properties.m b/clang/test/APINotes/properties.m index fe3c738404492..0b3d24482c073 100644 --- a/clang/test/APINotes/properties.m +++ b/clang/test/APINotes/properties.m @@ -18,25 +18,25 @@ // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id' // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0 +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}} // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id' // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0 +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}} // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id' -// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}} +// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 'id' -// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}} +// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 905a9c7eb8a93..e7ec2eb43378e 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -16,38 +16,53 @@ // CHECK-VERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); // CHECK-DUMP-LABEL: Dumping moveToPointDUMP -// CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 0 +// CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)" // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" // CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" // CHECK-DUMP-NOT: Attr +// CHECK-DUMP-LABEL: Dumping unversionedRenameDUMP +// CHECK-DUMP: in VersionedKit unversionedRenameDUMP +// CHECK-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 0 IsReplacedByActive{{$}} +// CHECK-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_HEADER()" +// CHECK-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_NOTES()" +// CHECK-DUMP-NOT: Attr + // CHECK-DUMP-LABEL: Dumping TestGenericDUMP // CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} Implicit -// CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} // CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} Implicit // CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping Swift3RenamedOnlyDUMP // CHECK-DUMP: in VersionedKit Swift3RenamedOnlyDUMP -// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 0 {{[0-9]+}} +// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} IsReplacedByActive{{$}} // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Name" -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Name" // CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping Swift3RenamedAlsoDUMP // CHECK-DUMP: in VersionedKit Swift3RenamedAlsoDUMP -// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 0 +// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> "Swift4Name" // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> "Swift4Name" -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Also" // CHECK-DUMP-NOT: Attr +// CHECK-DUMP-LABEL: Dumping Swift4RenamedDUMP +// CHECK-DUMP: in VersionedKit Swift4RenamedDUMP +// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 4 {{[0-9]+}} IsReplacedByActive{{$}} +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift4Name" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 4{{$}} +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift4Name" +// CHECK-DUMP-NOT: Attr + // CHECK-DUMP-NOT: Dumping // CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape))); From d0c92fd0f1ca62d8ad2f5630da4fb295ba187804 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Thu, 21 Sep 2017 20:54:08 -0700 Subject: [PATCH 204/582] Fix expected warning after r313945. apple-llvm-split-commit: 7751530df026441258cabf46f474761c85330f8d apple-llvm-split-dir: clang/ --- clang/test/Sema/attr-noescape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Sema/attr-noescape.c b/clang/test/Sema/attr-noescape.c index ec367b652543c..d342654281934 100644 --- a/clang/test/Sema/attr-noescape.c +++ b/clang/test/Sema/attr-noescape.c @@ -8,5 +8,5 @@ int *global_var __attribute((noescape)); // expected-warning{{'noescape' attribu void foo(__attribute__((noescape)) int *int_ptr, __attribute__((noescape)) int (^block)(int), - __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute ignored on parameter of non-pointer type 'int'}} + __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute only applies to pointer arguments}} } From b633d20337c86182efdd668744d4625e253f5dc3 Mon Sep 17 00:00:00 2001 From: Nathan Hawes <nhawes@apple.com> Date: Wed, 27 Sep 2017 13:52:49 -0700 Subject: [PATCH 205/582] [index] Fix shared libs build breakage: clangIndex and clangFrontend were mutually dependent apple-llvm-split-commit: 22006792b3fd720da52ff576e6b076860e73c1c0 apple-llvm-split-dir: clang/ --- clang/include/clang/Frontend/CompilerInstance.h | 15 +++++++++++++++ clang/lib/Frontend/CMakeLists.txt | 2 +- clang/lib/Frontend/CompilerInstance.cpp | 12 ++++++------ clang/lib/FrontendTool/CMakeLists.txt | 1 + .../FrontendTool/ExecuteCompilerInvocation.cpp | 3 +-- clang/lib/Index/CMakeLists.txt | 2 ++ 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index f79b37ebfd500..cd3043d25919d 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -183,6 +183,12 @@ class CompilerInstance : public ModuleLoader { /// The list of active output files. std::list<OutputFile> OutputFiles; + /// \brief An optional callback function used to wrap all FrontendActions + /// produced to generate imported modules before they are executed. + std::function<std::unique_ptr<FrontendAction> + (const FrontendOptions &opts, std::unique_ptr<FrontendAction> action)> + GenModuleActionWrapper; + CompilerInstance(const CompilerInstance &) = delete; void operator=(const CompilerInstance &) = delete; public: @@ -803,6 +809,15 @@ class CompilerInstance : public ModuleLoader { bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override; + void setGenModuleActionWrapper(std::function<std::unique_ptr<FrontendAction> + (const FrontendOptions &Opts, std::unique_ptr<FrontendAction> Action)> Wrapper) { + GenModuleActionWrapper = Wrapper; + }; + + std::function<std::unique_ptr<FrontendAction> + (const FrontendOptions &Opts, std::unique_ptr<FrontendAction> Action)> + getGenModuleActionWrapper() const { return GenModuleActionWrapper; } + void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) { DependencyCollectors.push_back(std::move(Listener)); } diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index 9f4f7d316cee5..da6a1616009b6 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -53,11 +53,11 @@ add_clang_library(clangFrontend ${optional_deps} LINK_LIBS + clangAPINotes clangAST clangBasic clangDriver clangEdit - clangIndex clangLex clangParse clangSema diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index d38d9e8b15bae..7b5108157bcea 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1172,6 +1172,10 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, SourceMgr.pushModuleBuildStack(ModuleName, FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager())); + // Pass along the GenModuleActionWrapper callback + auto wrapGenModuleAction = ImportingInstance.getGenModuleActionWrapper(); + Instance.setGenModuleActionWrapper(wrapGenModuleAction); + // If we're collecting module dependencies, we need to share a collector // between all of the module CompilerInstances. Other than that, we don't // want to produce any dependency output from the module build. @@ -1194,12 +1198,8 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, // probably not this. Interfaces changed upstream. std::unique_ptr<FrontendAction> Action( new GenerateModuleFromModuleMapAction); - - if (!FrontendOpts.IndexStorePath.empty()) { -#if defined(__APPLE__) - Action = index::createIndexDataRecordingAction(FrontendOpts, - std::move(Action)); -#endif + if (wrapGenModuleAction) { + Action = wrapGenModuleAction(FrontendOpts, std::move(Action)); } Instance.ExecuteAction(*Action); }, diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt index 7e11be0ce4c58..4a713e5902b2a 100644 --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -8,6 +8,7 @@ set(link_libs clangCodeGen clangDriver clangFrontend + clangIndex clangRewriteFrontend ) diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index e217bf60b3776..5715e8e2af948 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -166,9 +166,8 @@ CreateFrontendAction(CompilerInstance &CI) { #endif if (!FEOpts.IndexStorePath.empty()) { -#if defined(__APPLE__) Act = index::createIndexDataRecordingAction(FEOpts, std::move(Act)); -#endif + CI.setGenModuleActionWrapper(&index::createIndexDataRecordingAction); } // If there are any AST files to merge, create a frontend action diff --git a/clang/lib/Index/CMakeLists.txt b/clang/lib/Index/CMakeLists.txt index b5dea4cd05c50..bb17d15b55269 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + BitReader Core Support ) @@ -32,6 +33,7 @@ add_clang_library(clangIndex clangBasic clangFormat clangFrontend + clangLex clangRewrite clangSerialization clangToolingCore From 7b4f416744144b3b72a57623e17ab6abb402cc73 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Tue, 3 Oct 2017 15:39:27 -0700 Subject: [PATCH 206/582] [APINotes] Add 'RetainCountConvention' (#128) This is supported on ObjC methods, functions, and parameters, which is everywhere the {ns,cf}_returns{_not,}_retained attributes are supported. rdar://problem/34560650 apple-llvm-split-commit: 366b5ceb6a26a1c8a3c7f9100a5dde8be758dab2 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/Types.h | 57 ++++++++++- clang/lib/APINotes/APINotesReader.cpp | 17 +++- clang/lib/APINotes/APINotesWriter.cpp | 14 ++- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 96 ++++++++++++------- clang/lib/Sema/SemaAPINotes.cpp | 91 ++++++++++++++---- .../Headers/SimpleKit.apinotes | 36 +++++++ .../SimpleKit.framework/Headers/SimpleKit.h | 16 ++++ clang/test/APINotes/Inputs/roundtrip.apinotes | 12 +++ clang/test/APINotes/retain-count-convention.m | 20 ++++ clang/test/APINotes/yaml-roundtrip.c | 8 +- 10 files changed, 301 insertions(+), 66 deletions(-) create mode 100644 clang/test/APINotes/retain-count-convention.m diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 2213bad29a4d1..0438ab6f0a3b1 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -46,6 +46,15 @@ class ContextID { explicit ContextID(unsigned value) : Value(value) { } }; +enum class RetainCountConventionKind { + None, + CFReturnsRetained, + CFReturnsNotRetained, + NSReturnsRetained, + NSReturnsNotRetained, +}; + + /// Describes API notes data for any entity. /// /// This is used as the base of all API notes. @@ -447,8 +456,14 @@ class ParamInfo : public VariableInfo { /// Whether the this parameter has the 'noescape' attribute. unsigned NoEscape : 1; + /// A biased RetainCountConventionKind, where 0 means "unspecified". + /// + /// Only relevant for out-parameters. + unsigned RawRetainCountConvention : 3; + public: - ParamInfo() : VariableInfo(), NoEscapeSpecified(false), NoEscape(false) { } + ParamInfo() : VariableInfo(), NoEscapeSpecified(false), NoEscape(false), + RawRetainCountConvention() { } Optional<bool> isNoEscape() const { if (!NoEscapeSpecified) return None; @@ -464,19 +479,35 @@ class ParamInfo : public VariableInfo { } } + Optional<RetainCountConventionKind> getRetainCountConvention() const { + if (!RawRetainCountConvention) + return None; + return static_cast<RetainCountConventionKind>(RawRetainCountConvention - 1); + } + void setRetainCountConvention(Optional<RetainCountConventionKind> convention){ + if (convention) + RawRetainCountConvention = static_cast<unsigned>(convention.getValue())+1; + else + RawRetainCountConvention = 0; + assert(getRetainCountConvention() == convention && "bitfield too small"); + } + friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) { static_cast<VariableInfo &>(lhs) |= rhs; if (!lhs.NoEscapeSpecified && rhs.NoEscapeSpecified) { lhs.NoEscapeSpecified = true; lhs.NoEscape = rhs.NoEscape; } + if (!lhs.RawRetainCountConvention) + lhs.RawRetainCountConvention = rhs.RawRetainCountConvention; return lhs; } friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) { return static_cast<const VariableInfo &>(lhs) == rhs && lhs.NoEscapeSpecified == rhs.NoEscapeSpecified && - lhs.NoEscape == rhs.NoEscape; + lhs.NoEscape == rhs.NoEscape && + lhs.RawRetainCountConvention == rhs.RawRetainCountConvention; } friend bool operator!=(const ParamInfo &lhs, const ParamInfo &rhs) { @@ -511,6 +542,9 @@ class FunctionInfo : public CommonEntityInfo { /// Number of types whose nullability is encoded with the NullabilityPayload. unsigned NumAdjustedNullable : 8; + /// A biased RetainCountConventionKind, where 0 means "unspecified". + unsigned RawRetainCountConvention : 3; + /// Stores the nullability of the return type and the parameters. // NullabilityKindSize bits are used to encode the nullability. The info // about the return type is stored at position 0, followed by the nullability @@ -526,7 +560,8 @@ class FunctionInfo : public CommonEntityInfo { FunctionInfo() : CommonEntityInfo(), NullabilityAudited(false), - NumAdjustedNullable(0) { } + NumAdjustedNullable(0), + RawRetainCountConvention() { } static unsigned getMaxNullabilityIndex() { return ((sizeof(NullabilityPayload) * CHAR_BIT)/NullabilityKindSize); @@ -579,13 +614,27 @@ class FunctionInfo : public CommonEntityInfo { return getTypeInfo(0); } + Optional<RetainCountConventionKind> getRetainCountConvention() const { + if (!RawRetainCountConvention) + return None; + return static_cast<RetainCountConventionKind>(RawRetainCountConvention - 1); + } + void setRetainCountConvention(Optional<RetainCountConventionKind> convention){ + if (convention) + RawRetainCountConvention = static_cast<unsigned>(convention.getValue())+1; + else + RawRetainCountConvention = 0; + assert(getRetainCountConvention() == convention && "bitfield too small"); + } + friend bool operator==(const FunctionInfo &lhs, const FunctionInfo &rhs) { return static_cast<const CommonEntityInfo &>(lhs) == rhs && lhs.NullabilityAudited == rhs.NullabilityAudited && lhs.NumAdjustedNullable == rhs.NumAdjustedNullable && lhs.NullabilityPayload == rhs.NullabilityPayload && lhs.ResultType == rhs.ResultType && - lhs.Params == rhs.Params; + lhs.Params == rhs.Params && + lhs.RawRetainCountConvention == rhs.RawRetainCountConvention; } friend bool operator!=(const FunctionInfo &lhs, const FunctionInfo &rhs) { diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index a5dd24d3300e8..64b36e6a69b57 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -310,6 +310,11 @@ namespace { readVariableInfo(data, info); uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data); + if (auto rawConvention = payload & 0x7) { + auto convention = static_cast<RetainCountConventionKind>(rawConvention-1); + info.setRetainCountConvention(convention); + } + payload >>= 3; if (payload & 0x01) { info.setNoEscape(payload & 0x02); } @@ -319,8 +324,16 @@ namespace { /// Read serialized FunctionInfo. void readFunctionInfo(const uint8_t *&data, FunctionInfo &info) { readCommonEntityInfo(data, info); - info.NullabilityAudited - = endian::readNext<uint8_t, little, unaligned>(data); + + uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data); + if (auto rawConvention = payload & 0x7) { + auto convention = static_cast<RetainCountConventionKind>(rawConvention-1); + info.setRetainCountConvention(convention); + } + payload >>= 3; + info.NullabilityAudited = payload & 0x1; + payload >>= 1; assert(payload == 0 && "Bad API notes"); + info.NumAdjustedNullable = endian::readNext<uint8_t, little, unaligned>(data); info.NullabilityPayload diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index b178e257e7006..e0b6fafd6dbdc 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -724,6 +724,10 @@ namespace { if (*noescape) payload |= 0x02; } + payload <<= 3; + if (auto retainCountConvention = info.getRetainCountConvention()) { + payload |= static_cast<uint8_t>(retainCountConvention.getValue()) + 1; + } writer.write<uint8_t>(payload); } @@ -744,7 +748,15 @@ namespace { emitCommonEntityInfo(out, info); endian::Writer<little> writer(out); - writer.write<uint8_t>(info.NullabilityAudited); + + uint8_t payload = 0; + payload |= info.NullabilityAudited; + payload <<= 3; + if (auto retainCountConvention = info.getRetainCountConvention()) { + payload |= static_cast<uint8_t>(retainCountConvention.getValue()) + 1; + } + writer.write<uint8_t>(payload); + writer.write<uint8_t>(info.NumAdjustedNullable); writer.write<uint64_t>(info.NullabilityPayload); diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index f0f6cb8f95898..c1e342085f8b4 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -187,7 +187,8 @@ namespace { struct Param { unsigned Position; Optional<bool> NoEscape = false; - llvm::Optional<NullabilityKind> Nullability; + Optional<NullabilityKind> Nullability; + Optional<api_notes::RetainCountConventionKind> RetainCountConvention; StringRef Type; }; typedef std::vector<Param> ParamsSeq; @@ -197,7 +198,8 @@ namespace { MethodKind Kind; ParamsSeq Params; NullabilitySeq Nullability; - llvm::Optional<NullabilityKind> NullabilityOfRet; + Optional<NullabilityKind> NullabilityOfRet; + Optional<api_notes::RetainCountConventionKind> RetainCountConvention; AvailabilityItem Availability; Optional<bool> SwiftPrivate; StringRef SwiftName; @@ -239,7 +241,8 @@ namespace { StringRef Name; ParamsSeq Params; NullabilitySeq Nullability; - llvm::Optional<NullabilityKind> NullabilityOfRet; + Optional<NullabilityKind> NullabilityOfRet; + Optional<api_notes::RetainCountConventionKind> RetainCountConvention; AvailabilityItem Availability; Optional<bool> SwiftPrivate; StringRef SwiftName; @@ -409,6 +412,23 @@ namespace llvm { } }; + template<> + struct ScalarEnumerationTraits<api_notes::RetainCountConventionKind> { + static void enumeration(IO &io, + api_notes::RetainCountConventionKind &value) { + using api_notes::RetainCountConventionKind; + io.enumCase(value, "none", RetainCountConventionKind::None); + io.enumCase(value, "CFReturnsRetained", + RetainCountConventionKind::CFReturnsRetained); + io.enumCase(value, "CFReturnsNotRetained", + RetainCountConventionKind::CFReturnsNotRetained); + io.enumCase(value, "NSReturnsRetained", + RetainCountConventionKind::NSReturnsRetained); + io.enumCase(value, "NSReturnsNotRetained", + RetainCountConventionKind::NSReturnsNotRetained); + } + }; + template <> struct ScalarTraits<VersionTuple> { static void output(const VersionTuple &value, void*, @@ -430,11 +450,12 @@ namespace llvm { template <> struct MappingTraits<Param> { static void mapping(IO &io, Param& p) { - io.mapRequired("Position", p.Position); - io.mapOptional("Nullability", p.Nullability, - AbsentNullability); - io.mapOptional("NoEscape", p.NoEscape); - io.mapOptional("Type", p.Type, StringRef("")); + io.mapRequired("Position", p.Position); + io.mapOptional("Nullability", p.Nullability, + AbsentNullability); + io.mapOptional("RetainCountConvention", p.RetainCountConvention); + io.mapOptional("NoEscape", p.NoEscape); + io.mapOptional("Type", p.Type, StringRef("")); } }; @@ -457,21 +478,22 @@ namespace llvm { template <> struct MappingTraits<Method> { static void mapping(IO &io, Method& m) { - io.mapRequired("Selector", m.Selector); - io.mapRequired("MethodKind", m.Kind); - io.mapOptional("Parameters", m.Params); - io.mapOptional("Nullability", m.Nullability); - io.mapOptional("NullabilityOfRet", m.NullabilityOfRet, - AbsentNullability); - io.mapOptional("Availability", m.Availability.Mode); - io.mapOptional("AvailabilityMsg", m.Availability.Msg); - io.mapOptional("SwiftPrivate", m.SwiftPrivate); - io.mapOptional("SwiftName", m.SwiftName); - io.mapOptional("FactoryAsInit", m.FactoryAsInit, - FactoryAsInitKind::Infer); - io.mapOptional("DesignatedInit", m.DesignatedInit, false); - io.mapOptional("Required", m.Required, false); - io.mapOptional("ResultType", m.ResultType, StringRef("")); + io.mapRequired("Selector", m.Selector); + io.mapRequired("MethodKind", m.Kind); + io.mapOptional("Parameters", m.Params); + io.mapOptional("Nullability", m.Nullability); + io.mapOptional("NullabilityOfRet", m.NullabilityOfRet, + AbsentNullability); + io.mapOptional("RetainCountConvention", m.RetainCountConvention); + io.mapOptional("Availability", m.Availability.Mode); + io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftPrivate", m.SwiftPrivate); + io.mapOptional("SwiftName", m.SwiftName); + io.mapOptional("FactoryAsInit", m.FactoryAsInit, + FactoryAsInitKind::Infer); + io.mapOptional("DesignatedInit", m.DesignatedInit, false); + io.mapOptional("Required", m.Required, false); + io.mapOptional("ResultType", m.ResultType, StringRef("")); } }; @@ -496,16 +518,17 @@ namespace llvm { template <> struct MappingTraits<Function> { static void mapping(IO &io, Function& f) { - io.mapRequired("Name", f.Name); - io.mapOptional("Parameters", f.Params); - io.mapOptional("Nullability", f.Nullability); - io.mapOptional("NullabilityOfRet", f.NullabilityOfRet, - AbsentNullability); - io.mapOptional("Availability", f.Availability.Mode); - io.mapOptional("AvailabilityMsg", f.Availability.Msg); - io.mapOptional("SwiftPrivate", f.SwiftPrivate); - io.mapOptional("SwiftName", f.SwiftName); - io.mapOptional("ResultType", f.ResultType, StringRef("")); + io.mapRequired("Name", f.Name); + io.mapOptional("Parameters", f.Params); + io.mapOptional("Nullability", f.Nullability); + io.mapOptional("NullabilityOfRet", f.NullabilityOfRet, + AbsentNullability); + io.mapOptional("RetainCountConvention", f.RetainCountConvention); + io.mapOptional("Availability", f.Availability.Mode); + io.mapOptional("AvailabilityMsg", f.Availability.Msg); + io.mapOptional("SwiftPrivate", f.SwiftPrivate); + io.mapOptional("SwiftName", f.SwiftName); + io.mapOptional("ResultType", f.ResultType, StringRef("")); } }; @@ -681,6 +704,7 @@ namespace { pi.setNullabilityAudited(*p.Nullability); pi.setNoEscape(p.NoEscape); pi.setType(p.Type); + pi.setRetainCountConvention(p.RetainCountConvention); while (outInfo.Params.size() <= p.Position) { outInfo.Params.push_back(ParamInfo()); } @@ -782,6 +806,8 @@ namespace { convertNullability(meth.Nullability, meth.NullabilityOfRet, mInfo, meth.Selector); + mInfo.setRetainCountConvention(meth.RetainCountConvention); + // Write it. Writer->addObjCMethod(classID, selectorRef, meth.Kind == MethodKind::Instance, @@ -936,6 +962,7 @@ namespace { function.NullabilityOfRet, info, function.Name); info.ResultType = function.ResultType; + info.setRetainCountConvention(function.RetainCountConvention); Writer->addGlobalFunction(function.Name, info, swiftVersion); } @@ -1218,6 +1245,7 @@ namespace { p.Nullability = pi.getNullability(); p.NoEscape = pi.isNoEscape(); p.Type = copyString(pi.getType()); + p.RetainCountConvention = pi.getRetainCountConvention(); params.push_back(p); } } @@ -1287,6 +1315,7 @@ namespace { method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; method.ResultType = copyString(info.ResultType); + method.RetainCountConvention = info.getRetainCountConvention(); auto &items = getTopLevelItems(swiftVersion); knownContexts[contextID.Value].getContext(swiftVersion, items) .Methods.push_back(method); @@ -1326,6 +1355,7 @@ namespace { handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); function.ResultType = copyString(info.ResultType); + function.RetainCountConvention = info.getRetainCountConvention(); auto &items = getTopLevelItems(swiftVersion); items.Functions.push_back(function); } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 0b3cdbb960331..d472f3509592a 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -148,17 +148,16 @@ namespace { Sema &S, Decl *D, bool shouldAddAttribute, VersionedInfoMetadata metadata, llvm::function_ref<A *()> createAttr, - llvm::function_ref<specific_attr_iterator<A>(Decl*)> getExistingAttr) { + llvm::function_ref<Decl::attr_iterator(const Decl*)> getExistingAttr) { if (metadata.IsActive) { - auto end = D->specific_attr_end<A>(); auto existing = getExistingAttr(D); - if (existing != end) { + if (existing != D->attr_end()) { // Remove the existing attribute, and treat it as a superseded // non-versioned attribute. auto *versioned = SwiftVersionedAttr::CreateImplicit( S.Context, metadata.Version, *existing, /*IsReplacedByActive*/true); - D->getAttrs().erase(existing.getCurrent()); + D->getAttrs().erase(existing); D->addAttr(versioned); } @@ -195,12 +194,61 @@ namespace { VersionedInfoMetadata metadata, llvm::function_ref<A *()> createAttr) { handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, createAttr, - [](Decl *decl) { - return decl->specific_attr_begin<A>(); + [](const Decl *decl) { + return llvm::find_if(decl->attrs(), [](const Attr *next) { + return isa<A>(next); + }); }); } } +template <typename A = CFReturnsRetainedAttr> +static void handleAPINotedRetainCountAttribute(Sema &S, Decl *D, + bool shouldAddAttribute, + VersionedInfoMetadata metadata) { + // The template argument has a default to make the "removal" case more + // concise; it doesn't matter /which/ attribute is being removed. + handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, [&] { + return A::CreateImplicit(S.Context); + }, [](const Decl *D) -> Decl::attr_iterator { + return llvm::find_if(D->attrs(), [](const Attr *next) -> bool { + return isa<CFReturnsRetainedAttr>(next) || + isa<CFReturnsNotRetainedAttr>(next) || + isa<NSReturnsRetainedAttr>(next) || + isa<NSReturnsNotRetainedAttr>(next); + }); + }); +} + +static void handleAPINotedRetainCountConvention( + Sema &S, Decl *D, VersionedInfoMetadata metadata, + Optional<api_notes::RetainCountConventionKind> convention) { + if (!convention) + return; + switch (convention.getValue()) { + case api_notes::RetainCountConventionKind::None: + handleAPINotedRetainCountAttribute(S, D, /*shouldAddAttribute*/false, + metadata); + break; + case api_notes::RetainCountConventionKind::CFReturnsRetained: + handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>( + S, D, /*shouldAddAttribute*/true, metadata); + break; + case api_notes::RetainCountConventionKind::CFReturnsNotRetained: + handleAPINotedRetainCountAttribute<CFReturnsNotRetainedAttr>( + S, D, /*shouldAddAttribute*/true, metadata); + break; + case api_notes::RetainCountConventionKind::NSReturnsRetained: + handleAPINotedRetainCountAttribute<NSReturnsRetainedAttr>( + S, D, /*shouldAddAttribute*/true, metadata); + break; + case api_notes::RetainCountConventionKind::NSReturnsNotRetained: + handleAPINotedRetainCountAttribute<NSReturnsNotRetainedAttr>( + S, D, /*shouldAddAttribute*/true, metadata); + break; + } +} + static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonEntityInfo &info, VersionedInfoMetadata metadata) { @@ -227,19 +275,16 @@ static void ProcessAPINotes(Sema &S, Decl *D, /*Strict=*/false, /*Replacement=*/StringRef()); }, - [](Decl *decl) { - auto existing = decl->specific_attr_begin<AvailabilityAttr>(), - end = decl->specific_attr_end<AvailabilityAttr>(); - while (existing != end) { - if (auto platform = (*existing)->getPlatform()) { - if (platform->isStr("swift")) - break; - } - - ++existing; - } - - return existing; + [](const Decl *decl) { + return llvm::find_if(decl->attrs(), [](const Attr *next) -> bool { + auto *AA = dyn_cast<AvailabilityAttr>(next); + if (!AA) + return false; + const IdentifierInfo *platform = AA->getPlatform(); + if (!platform) + return false; + return platform->isStr("swift"); + }); }); } @@ -373,6 +418,10 @@ static void ProcessAPINotes(Sema &S, ParmVarDecl *D, }); } + // Retain count convention + handleAPINotedRetainCountConvention(S, D, metadata, + info.getRetainCountConvention()); + // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info), metadata); @@ -503,6 +552,10 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, } } + // Retain count convention + handleAPINotedRetainCountConvention(S, D, metadata, + info.getRetainCountConvention()); + // Handle common entity information. ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info), metadata); diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes index 336f1685703c6..f66038b8ec1ff 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes @@ -4,3 +4,39 @@ Tags: SwiftName: SuccessfullyRenamedA - Name: RenamedAgainInAPINotesB SwiftName: SuccessfullyRenamedB +Functions: +- Name: getCFOwnedToUnowned + RetainCountConvention: CFReturnsNotRetained +- Name: getCFUnownedToOwned + RetainCountConvention: CFReturnsRetained +- Name: getCFOwnedToNone + RetainCountConvention: none +- Name: getObjCOwnedToUnowned + RetainCountConvention: NSReturnsNotRetained +- Name: getObjCUnownedToOwned + RetainCountConvention: NSReturnsRetained +- Name: indirectGetCFOwnedToUnowned + Parameters: + - Position: 0 + RetainCountConvention: CFReturnsNotRetained +- Name: indirectGetCFUnownedToOwned + Parameters: + - Position: 0 + RetainCountConvention: CFReturnsRetained +- Name: indirectGetCFOwnedToNone + Parameters: + - Position: 0 + RetainCountConvention: none +- Name: indirectGetCFNoneToOwned + Parameters: + - Position: 0 + RetainCountConvention: CFReturnsNotRetained +Classes: +- Name: MethodTest + Methods: + - Selector: getOwnedToUnowned + MethodKind: Instance + RetainCountConvention: NSReturnsNotRetained + - Selector: getUnownedToOwned + MethodKind: Instance + RetainCountConvention: NSReturnsRetained diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h index c30a1e75641b8..e11adb715c6ac 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h @@ -5,3 +5,19 @@ struct RenamedAgainInAPINotesA { struct __attribute__((swift_name("bad"))) RenamedAgainInAPINotesB { int field; }; + +void *getCFOwnedToUnowned(void) __attribute__((cf_returns_retained)); +void *getCFUnownedToOwned(void) __attribute__((cf_returns_not_retained)); +void *getCFOwnedToNone(void) __attribute__((cf_returns_retained)); +id getObjCOwnedToUnowned(void) __attribute__((ns_returns_retained)); +id getObjCUnownedToOwned(void) __attribute__((ns_returns_not_retained)); + +int indirectGetCFOwnedToUnowned(void **out __attribute__((cf_returns_retained))); +int indirectGetCFUnownedToOwned(void **out __attribute__((cf_returns_not_retained))); +int indirectGetCFOwnedToNone(void **out __attribute__((cf_returns_retained))); +int indirectGetCFNoneToOwned(void **out); + +@interface MethodTest +- (id)getOwnedToUnowned __attribute__((ns_returns_retained)); +- (id)getUnownedToOwned __attribute__((ns_returns_not_retained)); +@end diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 65e39283cd8d5..d6e6cfbad4e5d 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -14,6 +14,7 @@ Classes: Methods: - Selector: 'cellWithImage:' MethodKind: Class + RetainCountConvention: NSReturnsNotRetained Availability: available AvailabilityMsg: '' SwiftPrivate: false @@ -111,6 +112,16 @@ Classes: SwiftName: '' SwiftImportAsAccessors: false Functions: + - Name: CFURLCopyResourcePropertiesForKeys + Parameters: + - Position: 0 + - Position: 1 + - Position: 2 + RetainCountConvention: CFReturnsRetained + RetainCountConvention: CFReturnsRetained + Availability: available + AvailabilityMsg: '' + SwiftName: '' - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available @@ -183,3 +194,4 @@ SwiftVersions: AvailabilityMsg: '' SwiftName: '' SwiftImportAsAccessors: true +... diff --git a/clang/test/APINotes/retain-count-convention.m b/clang/test/APINotes/retain-count-convention.m new file mode 100644 index 0000000000000..46fb90caa6932 --- /dev/null +++ b/clang/test/APINotes/retain-count-convention.m @@ -0,0 +1,20 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fdisable-module-hash -fsyntax-only -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s + +#import <SimpleKit/SimpleKit.h> + +// CHECK: void *getCFOwnedToUnowned() __attribute__((cf_returns_not_retained)); +// CHECK: void *getCFUnownedToOwned() __attribute__((cf_returns_retained)); +// CHECK: void *getCFOwnedToNone(); +// CHECK: id getObjCOwnedToUnowned() __attribute__((ns_returns_not_retained)); +// CHECK: id getObjCUnownedToOwned() __attribute__((ns_returns_retained)); +// CHECK: int indirectGetCFOwnedToUnowned(void * _Nullable *out __attribute__((cf_returns_not_retained))); +// CHECK: int indirectGetCFUnownedToOwned(void * _Nullable *out __attribute__((cf_returns_retained))); +// CHECK: int indirectGetCFOwnedToNone(void * _Nullable *out); +// CHECK: int indirectGetCFNoneToOwned(void **out __attribute__((cf_returns_not_retained))); + +// CHECK-LABEL: @interface MethodTest +// CHECK: - (id)getOwnedToUnowned __attribute__((ns_returns_not_retained)); +// CHECK: - (id)getUnownedToOwned __attribute__((ns_returns_retained)); +// CHECK: @end diff --git a/clang/test/APINotes/yaml-roundtrip.c b/clang/test/APINotes/yaml-roundtrip.c index b721b6a6e0167..f14d18f3e85fa 100644 --- a/clang/test/APINotes/yaml-roundtrip.c +++ b/clang/test/APINotes/yaml-roundtrip.c @@ -1,10 +1,4 @@ # RUN: %clang -cc1apinotes -yaml-to-binary -o %t.apinotesc %S/Inputs/roundtrip.apinotes # RUN: %clang -cc1apinotes -binary-to-yaml -o %t.apinotes %t.apinotesc - -# Handle the infurating '...' the YAML writer adds but the parser -# can't read. - -# RUN: cp %S/Inputs/roundtrip.apinotes %t-reference.apinotes -# RUN: echo "..." >> %t-reference.apinotes -# RUN: diff %t-reference.apinotes %t.apinotes +# RUN: diff %S/Inputs/roundtrip.apinotes %t.apinotes From 171c3b0dfa514ed415a99466c3745d0c6c7426fa Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 3 Oct 2017 18:55:44 -0400 Subject: [PATCH 207/582] Extend coroutines to support a "returned continuation" lowering. A quick contrast of this ABI with the currently-implemented ABI: - Allocation is implicitly managed by the lowering passes, which is fine for frontends that are fine with assuming that allocation cannot fail. This assumption is necessary to implement dynamic allocas anyway. - The lowering attempts to fit the coroutine frame into an opaque, statically-sized buffer before falling back on allocation; the same buffer must be provided to every resume point. A buffer must be at least pointer-sized. - The resume and destroy functions have been combined; the continuation function takes a parameter indicating whether it has succeeded. - Conversely, every suspend point begins its own continuation function. - The continuation function pointer is directly returned to the caller instead of being stored in the frame. The continuation can therefore directly destroy the frame when exiting the coroutine instead of having to leave it in a defunct state. - Other values can be returned directly to the caller instead of going through a promise allocation. The frontend provides a "prototype" function declaration from which the type, calling convention, and attributes of the continuation functions are taken. - On the caller side, the frontend can generate natural IR that directly uses the continuation functions as long as it prevents IPO with the coroutine until lowering has happened. In combination with the point above, the frontend is almost totally in charge of the ABI of the coroutine. - Unique-yield coroutines are given some special treatment. I intend to upstream this patch (and its follow-ups) after we validate that it'll serve our purposes in Swift. apple-llvm-split-commit: eb7e7fd4f6c0d4c4c97879d9439d82f7eadde324 apple-llvm-split-dir: llvm/ --- llvm/docs/Coroutines.rst | 303 +++++- llvm/include/llvm/IR/Intrinsics.td | 11 + .../lib/Transforms/Coroutines/CoroCleanup.cpp | 5 +- llvm/lib/Transforms/Coroutines/CoroEarly.cpp | 13 +- llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 131 ++- llvm/lib/Transforms/Coroutines/CoroInstr.h | 151 ++- llvm/lib/Transforms/Coroutines/CoroInternal.h | 145 ++- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 937 ++++++++++++++---- llvm/lib/Transforms/Coroutines/Coroutines.cpp | 292 +++++- llvm/test/Transforms/Coroutines/coro-debug.ll | 6 +- .../Coroutines/coro-retcon-once-value.ll | 116 +++ .../Coroutines/coro-retcon-once-value2.ll | 73 ++ .../Coroutines/coro-retcon-value.ll | 102 ++ .../test/Transforms/Coroutines/coro-retcon.ll | 94 ++ 14 files changed, 2080 insertions(+), 299 deletions(-) create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-value.ll create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon.ll diff --git a/llvm/docs/Coroutines.rst b/llvm/docs/Coroutines.rst index 1bea04ebdd2ac..914b8424520b1 100644 --- a/llvm/docs/Coroutines.rst +++ b/llvm/docs/Coroutines.rst @@ -41,32 +41,144 @@ then destroy it: In addition to the function stack frame which exists when a coroutine is executing, there is an additional region of storage that contains objects that keep the coroutine state when a coroutine is suspended. This region of storage -is called **coroutine frame**. It is created when a coroutine is called and -destroyed when a coroutine runs to completion or destroyed by a call to -the `coro.destroy`_ intrinsic. +is called the **coroutine frame**. It is created when a coroutine is called +and destroyed when a coroutine either runs to completion or is destroyed +while suspended. + +LLVM currently supports two styles of coroutine lowering. These styles +support substantially different sets of features, have substantially +different ABIs, and expect substantially different patterns of frontend +code generation. However, the styles also have a great deal in common. + +In all cases, an LLVM coroutine is initially represented as an ordinary LLVM +function that has calls to `coroutine intrinsics`_ defining the structure of +the coroutine. The coroutine function is then, in the most general case, +rewritten by the coroutine lowering passes to become the "ramp function", +the initial entrypoint of the coroutine, which executes until a suspend point +is first reached. The remainder of the original coroutine function is split +out into some number of "resume functions". Any state which must persist +across suspensions is stored in the coroutine frame. The resume functions +must somehow be able to handle either a "normal" resumption, which continues +the normal execution of the coroutine, or an "abnormal" resumption, which +must unwind the coroutine without attempting to suspend it. + +Switched-Resume Lowering +------------------------ + +In LLVM's standard switched-resume lowering, signaled by the use of +`llvm.coro.id`, the coroutine frame is stored as part of a "coroutine +object" which represents a handle to a particular invocation of the +coroutine. All coroutine objects support a common ABI allowing certain +features to be used without knowing anything about the coroutine's +implementation: + +- A coroutine object can be queried to see if it has reached completion + with `llvm.coro.done`. + +- A coroutine object can be resumed normally if it has not already reached + completion with `llvm.coro.resume`. + +- A coroutine object can be destroyed, invalidating the coroutine object, + with `llvm.coro.destroy`. This must be done separately even if the + coroutine has reached completion normally. + +- "Promise" storage, which is known to have a certain size and alignment, + can be projected out of the coroutine object with `llvm.coro.promise`. + The coroutine implementation must have been compiled to define a promise + of the same size and alignment. + +In general, interacting with a coroutine object in any of these ways while +it is running has undefined behavior. + +The coroutine function is split into three functions, representing three +different ways that control can enter the coroutine: + +1. the ramp function that is initially invoked, which takes arbitrary + arguments and returns a pointer to the coroutine object; -An LLVM coroutine is represented as an LLVM function that has calls to -`coroutine intrinsics`_ defining the structure of the coroutine. -After lowering, a coroutine is split into several -functions that represent three different ways of how control can enter the -coroutine: +2. a coroutine resume function that is invoked when the coroutine is resumed, + which takes a pointer to the coroutine object and returns `void`; -1. a ramp function, which represents an initial invocation of the coroutine that - creates the coroutine frame and executes the coroutine code until it - encounters a suspend point or reaches the end of the function; +3. a coroutine destroy function that is invoked when the coroutine is + destroyed, which takes a pointer to the coroutine object and returns + `void`. -2. a coroutine resume function that is invoked when the coroutine is resumed; +Because the resume and destroy functions are shared across all suspend +points, suspend points must store the index of the active suspend in +the coroutine object, and the resume/destroy functions must switch over +that index to get back to the correct point. Hence the name of this +lowering. -3. a coroutine destroy function that is invoked when the coroutine is destroyed. +Pointers to the resume and destroy functions are stored in the coroutine +object at known offsets which are fixed for all coroutines. A completed +coroutine is represented with a null resume function. -.. note:: Splitting out resume and destroy functions are just one of the - possible ways of lowering the coroutine. We chose it for initial - implementation as it matches closely the mental model and results in - reasonably nice code. +There is a somewhat complex protocol of intrinsics for allocating and +deallocating the coroutine object. It is complex in order to allow the +allocation to be elided due to inlining. This protocol is discussed +in further detail below. + +The frontend may generate code to call the coroutine function directly; +this will become a call to the ramp function and will return a pointer +to the coroutine object. The frontend should always resume or destroy +the coroutine using the corresping intrinsics. + +Returned-Continuation Lowering +------------------------------ + +In returned-continuation lowering, signaled by the use of +`llvm.coro.id.retcon` or `llvm.coro.id.retcon.once`, some aspects of +the ABI must be handled more explicitly by the frontend. + +In this lowering, every suspend point takes a list of "yielded values" +which are returned back to the caller along with a function pointer, +called the continuation function. The coroutine is resumed by simply +calling this continuation function pointer. The original coroutine +is divided into the ramp function and then an arbitrary number of +these continuation functions, one for each suspend point. + +LLVM actually supports two closely-related returned-continuation +lowerings: + +- In normal returned-continuation lowering, the coroutine may suspend + itself multiple times. This means that a continuation function + itself returns another continuation pointer, as well as a list of + yielded values. + + The coroutine indicates that it has run to completion by returning + a null continuation pointer. Any yielded values will be `undef` + should be ignored. + +- In yield-once returned-continuation lowering, the coroutine must + suspend itself exactly once (or throw an exception). The ramp + function returns a continuation function pointer and yielded + values, but the continuation function simply returns `void` + when the coroutine has run to completion. + +The coroutine frame is maintained in a fixed-size buffer that is +passed to the `coro.id` intrinsic, which guarantees a certain size +and alignment statically. The same buffer must be passed to the +continuation function(s). The coroutine will allocate memory if the +buffer is insufficient, in which case it will need to store at +least that pointer in the buffer; therefore the buffer must always +be at least pointer-sized. How the coroutine uses the buffer may +vary between suspend points. + +In addition to the buffer pointer, continuation functions take an +argument indicating whether the coroutine is being resumed normally +(zero) or abnormally (non-zero). + +LLVM is currently ineffective at statically eliminating allocations +after fully inlining returned-continuation coroutines into a caller. +This may be acceptable if LLVM's coroutine support is primarily being +used for low-level lowering and inlining is expected to be applied +earlier in the pipeline. Coroutines by Example ===================== +The examples below are all of switched-resume coroutines. + Coroutine Representation ------------------------ @@ -554,6 +666,7 @@ and python iterator `__next__` would look like: return *(int*)coro.promise(hdl, 4, false); } + Intrinsics ========== @@ -580,7 +693,7 @@ Overview: """"""""" The '``llvm.coro.destroy``' intrinsic destroys a suspended -coroutine. +switched-resume coroutine. Arguments: """""""""" @@ -607,7 +720,7 @@ frame. Destroying a coroutine that is not suspended leads to undefined behavior. Overview: """"""""" -The '``llvm.coro.resume``' intrinsic resumes a suspended coroutine. +The '``llvm.coro.resume``' intrinsic resumes a suspended switched-resume coroutine. Arguments: """""""""" @@ -634,8 +747,8 @@ Resuming a coroutine that is not suspended leads to undefined behavior. Overview: """"""""" -The '``llvm.coro.done``' intrinsic checks whether a suspended coroutine is at -the final suspend point or not. +The '``llvm.coro.done``' intrinsic checks whether a suspended +switched-resume coroutine is at the final suspend point or not. Arguments: """""""""" @@ -661,7 +774,7 @@ Overview: """"""""" The '``llvm.coro.promise``' intrinsic obtains a pointer to a -`coroutine promise`_ given a coroutine handle and vice versa. +`coroutine promise`_ given a switched-resume coroutine handle and vice versa. Arguments: """""""""" @@ -739,7 +852,8 @@ Overview: """"""""" The '``llvm.coro.size``' intrinsic returns the number of bytes -required to store a `coroutine frame`_. +required to store a `coroutine frame`_. This is only supported for +switched-resume coroutines. Arguments: """""""""" @@ -772,7 +886,8 @@ The first argument is a token returned by a call to '``llvm.coro.id``' identifying the coroutine. The second argument is a pointer to a block of memory where coroutine frame -will be stored if it is allocated dynamically. +will be stored if it is allocated dynamically. This pointer is ignored +for returned-continuation coroutines. Semantics: """""""""" @@ -798,7 +913,8 @@ Overview: The '``llvm.coro.free``' intrinsic returns a pointer to a block of memory where coroutine frame is stored or `null` if this instance of a coroutine did not use -dynamically allocated memory for its coroutine frame. +dynamically allocated memory for its coroutine frame. This intrinsic is not +supported for returned-continuation coroutines. Arguments: """""""""" @@ -847,6 +963,7 @@ Overview: The '``llvm.coro.alloc``' intrinsic returns `true` if dynamic allocation is required to obtain a memory for the coroutine frame and `false` otherwise. +This is not supported for returned-continuation coroutines. Arguments: """""""""" @@ -918,7 +1035,8 @@ coroutine frame. Overview: """"""""" -The '``llvm.coro.id``' intrinsic returns a token identifying a coroutine. +The '``llvm.coro.id``' intrinsic returns a token identifying a +switched-resume coroutine. Arguments: """""""""" @@ -949,6 +1067,79 @@ duplicated. A frontend should emit exactly one `coro.id` intrinsic per coroutine. +.. _coro.id.retcon: + +'llvm.coro.id.retcon' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + declare token @llvm.coro.id.retcon(i32 <size>, i32 <align>, i8* <buffer>, + i8* <continuation prototype>, + i8* <alloc>, i8* <dealloc>) + +Overview: +""""""""" + +The '``llvm.coro.id.retcon``' intrinsic returns a token identifying a +multiple-suspend returned-continuation coroutine. + +The 'result-type sequence' of the coroutine is defined as follows: + +- if the return type of the coroutine function is ``void``, it is the + empty sequence; + +- if the return type of the coroutine function is a ``struct``, it is the + element types of that ``struct`` in order; + +- otherwise, it is just the return type of the coroutine function. + +The first element of the result-type sequence must be a pointer type; +continuation functions will be coerced to this type. The rest of +the sequence are the 'yield types', and any suspends in the coroutine +must take arguments of these types. + +Arguments: +"""""""""" + +The first and second arguments are the expected size and alignment of +the buffer provided as the third argument. They must be constant. + +The fourth argument must be a reference to a global function, called +the 'continuation prototype function'. The type, calling convention, +and attributes of any continuation functions will be taken from this +declaration. The return type of the prototype function must match the +return type of the current function. The first parameter type must be +a pointer type. The second parameter type must be an integer type; +it will be used only as a boolean flag. + +The fifth argument must be a reference to a global function that will +be used to allocate memory. It may not fail, either by returning null +or throwing an exception. It must take an integer and return a pointer. + +The sixth argument must be a reference to a global function that will +be used to deallocate memory. It must take a pointer and return ``void``. + +'llvm.coro.id.retcon.once' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + declare token @llvm.coro.id.retcon.once(i32 <size>, i32 <align>, i8* <buffer>, + i8* <prototype>, + i8* <alloc>, i8* <dealloc>) + +Overview: +""""""""" + +The '``llvm.coro.id.retcon.once``' intrinsic returns a token identifying a +unique-suspend returned-continuation coroutine. + +Arguments: +"""""""""" + +As for ``llvm.core.id.retcon``, except that the return type of the +continuation prototype must be `void` instead of matching the +coroutine's return type. + .. _coro.end: 'llvm.coro.end' Intrinsic @@ -982,6 +1173,17 @@ The purpose of this intrinsic is to allow frontends to mark the cleanup and other code that is only relevant during the initial invocation of the coroutine and should not be present in resume and destroy parts. +In returned-continuation lowering, ``llvm.coro.end`` fully destroys the +coroutine frame. If the second argument is `false`, it also returns from +the coroutine with a null continuation pointer, and the next instruction +will be unreachable. If the second argument is `true`, it falls through +so that the following logic can resume unwinding. In a yield-once +coroutine, reaching a non-unwind ``llvm.coro.end`` without having first +reached a ``llvm.coro.suspend.retcon`` has undefined behavior. + +The remainder of this section describes the behavior under switched-resume +lowering. + This intrinsic is lowered when a coroutine is split into the start, resume and destroy parts. In the start part, it is a no-op, in resume and destroy parts, it is replaced with `ret void` instruction and @@ -1052,11 +1254,11 @@ The following table summarizes the handling of `coro.end`_ intrinsic. Overview: """"""""" -The '``llvm.coro.suspend``' marks the point where execution of the coroutine -need to get suspended and control returned back to the caller. -Conditional branches consuming the result of this intrinsic lead to basic blocks -where coroutine should proceed when suspended (-1), resumed (0) or destroyed -(1). +The '``llvm.coro.suspend``' marks the point where execution of a +switched-resume coroutine is suspended and control is returned back +to the caller. Conditional branches consuming the result of this +intrinsic lead to basic blocks where coroutine should proceed when +suspended (-1), resumed (0) or destroyed (1). Arguments: """""""""" @@ -1152,6 +1354,45 @@ to the coroutine: switch i8 %suspend1, label %suspend [i8 0, label %resume1 i8 1, label %cleanup] +.. _coro.suspend.retcon: + +'llvm.coro.suspend.retcon' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + declare i1 @llvm.coro.suspend.retcon(...) + +Overview: +""""""""" + +The '``llvm.coro.suspend.retcon``' intrinsic marks the point where +execution of a returned-continuation coroutine is suspended and control +is returned back to the caller. + +`llvm.coro.suspend.retcon`` does not support separate save points; +they are not useful when the continuation function is not locally +accessible. That would be a more appropriate feature for a ``passcon`` +lowering that is not yet implemented. + +Arguments: +"""""""""" + +The types of the arguments must exactly match the yielded-types sequence +of the coroutine. They will be turned into return values from the ramp +and continuation functions, along with the next continuation function. + +Semantics: +"""""""""" + +The result of the intrinsic indicates whether the coroutine should resume +abnormally (non-zero). + +In a normal coroutine, it is undefined behavior if the coroutine executes +a call to ``llvm.coro.suspend.retcon`` after resuming abnormally. + +In a yield-once coroutine, it is undefined behavior if the coroutine +executes a call to ``llvm.coro.suspend.retcon`` after resuming in any way. + .. _coro.param: 'llvm.coro.param' Intrinsic diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index cb16c3d48495f..cb23f5f39caf0 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -756,6 +756,14 @@ def int_coro_id : Intrinsic<[llvm_token_ty], [llvm_i32_ty, llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty], [IntrArgMemOnly, IntrReadMem, ReadNone<1>, ReadOnly<2>, NoCapture<2>]>; +def int_coro_id_retcon : Intrinsic<[llvm_token_ty], + [llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty, + llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty], + []>; +def int_coro_id_retcon_once : Intrinsic<[llvm_token_ty], + [llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty, + llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty], + []>; def int_coro_alloc : Intrinsic<[llvm_i1_ty], [llvm_token_ty], []>; def int_coro_begin : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty], [WriteOnly<1>]>; @@ -770,6 +778,9 @@ def int_coro_size : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>; def int_coro_save : Intrinsic<[llvm_token_ty], [llvm_ptr_ty], []>; def int_coro_suspend : Intrinsic<[llvm_i8_ty], [llvm_token_ty, llvm_i1_ty], []>; +def int_coro_suspend_retcon : Intrinsic<[llvm_i1_ty], [llvm_vararg_ty], []>; +def int_coro_prepare_retcon : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty], + [IntrNoMem]>; def int_coro_param : Intrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_ptr_ty], [IntrNoMem, ReadNone<0>, ReadNone<1>]>; diff --git a/llvm/lib/Transforms/Coroutines/CoroCleanup.cpp b/llvm/lib/Transforms/Coroutines/CoroCleanup.cpp index 359876627fce1..e3aab962bb01c 100644 --- a/llvm/lib/Transforms/Coroutines/CoroCleanup.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroCleanup.cpp @@ -74,6 +74,8 @@ bool Lowerer::lowerRemainingCoroIntrinsics(Function &F) { II->replaceAllUsesWith(ConstantInt::getTrue(Context)); break; case Intrinsic::coro_id: + case Intrinsic::coro_id_retcon: + case Intrinsic::coro_id_retcon_once: II->replaceAllUsesWith(ConstantTokenNone::get(Context)); break; case Intrinsic::coro_subfn_addr: @@ -112,7 +114,8 @@ struct CoroCleanup : FunctionPass { bool doInitialization(Module &M) override { if (coro::declaresIntrinsics(M, {"llvm.coro.alloc", "llvm.coro.begin", "llvm.coro.subfn.addr", "llvm.coro.free", - "llvm.coro.id"})) + "llvm.coro.id", "llvm.coro.id.retcon", + "llvm.coro.id.retcon.once"})) L = llvm::make_unique<Lowerer>(M); return false; } diff --git a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp index ba05896af150c..60e26733be065 100644 --- a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp @@ -90,13 +90,14 @@ void Lowerer::lowerCoroDone(IntrinsicInst *II) { Value *Operand = II->getArgOperand(0); // ResumeFnAddr is the first pointer sized element of the coroutine frame. + static_assert(coro::Shape::SwitchFieldIndex::Resume == 0, + "resume function not at offset zero"); auto *FrameTy = Int8Ptr; PointerType *FramePtrTy = FrameTy->getPointerTo(); Builder.SetInsertPoint(II); auto *BCI = Builder.CreateBitCast(Operand, FramePtrTy); - auto *Gep = Builder.CreateConstInBoundsGEP1_32(FrameTy, BCI, 0); - auto *Load = Builder.CreateLoad(Gep); + auto *Load = Builder.CreateLoad(BCI); auto *Cond = Builder.CreateICmpEQ(Load, NullPtr); II->replaceAllUsesWith(Cond); @@ -150,6 +151,10 @@ bool Lowerer::lowerEarlyIntrinsics(Function &F) { } } break; + case Intrinsic::coro_id_retcon: + case Intrinsic::coro_id_retcon_once: + F.addFnAttr(CORO_PRESPLIT_ATTR, PREPARED_FOR_SPLIT); + break; case Intrinsic::coro_resume: lowerResumeOrDestroy(CS, CoroSubFnInst::ResumeIndex); break; @@ -192,7 +197,9 @@ struct CoroEarly : public FunctionPass { // This pass has work to do only if we find intrinsics we are going to lower // in the module. bool doInitialization(Module &M) override { - if (coro::declaresIntrinsics(M, {"llvm.coro.id", "llvm.coro.destroy", + if (coro::declaresIntrinsics(M, {"llvm.coro.id", "llvm.coro.id.retcon", + "llvm.coro.id.retcon.once", + "llvm.coro.destroy", "llvm.coro.done", "llvm.coro.end", "llvm.coro.free", "llvm.coro.promise", "llvm.coro.resume", "llvm.coro.suspend"})) diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp index d192fa699f360..96d367a9d9fd4 100644 --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -120,6 +120,15 @@ struct SuspendCrossingInfo { return false; BasicBlock *UseBB = I->getParent(); + + // As a special case, treat uses by an llvm.coro.suspend.retcon + // as if they were uses in the suspend's single predecessor: the + // uses conceptually occur before the suspend. + if (isa<CoroSuspendRetconInst>(I)) { + UseBB = UseBB->getSinglePredecessor(); + assert(UseBB && "should have split coro.suspend into its own block"); + } + return hasPathCrossingSuspendPoint(DefBB, UseBB); } @@ -128,7 +137,17 @@ struct SuspendCrossingInfo { } bool isDefinitionAcrossSuspend(Instruction &I, User *U) const { - return isDefinitionAcrossSuspend(I.getParent(), U); + auto *DefBB = I.getParent(); + + // As a special case, treat values produced by an llvm.coro.suspend.* + // as if they were defined in the single successor: the uses + // conceptually occur after the suspend. + if (isa<AnyCoroSuspendInst>(I)) { + DefBB = DefBB->getSingleSuccessor(); + assert(DefBB && "should have split coro.suspend into its own block"); + } + + return isDefinitionAcrossSuspend(DefBB, U); } }; } // end anonymous namespace @@ -183,9 +202,10 @@ SuspendCrossingInfo::SuspendCrossingInfo(Function &F, coro::Shape &Shape) B.Suspend = true; B.Kills |= B.Consumes; }; - for (CoroSuspendInst *CSI : Shape.CoroSuspends) { + for (auto *CSI : Shape.CoroSuspends) { markSuspendBlock(CSI); - markSuspendBlock(CSI->getCoroSave()); + if (auto *Save = CSI->getCoroSave()) + markSuspendBlock(Save); } // Iterate propagating consumes and kills until they stop changing. @@ -310,18 +330,30 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, SmallString<32> Name(F.getName()); Name.append(".Frame"); StructType *FrameTy = StructType::create(C, Name); - auto *FramePtrTy = FrameTy->getPointerTo(); - auto *FnTy = FunctionType::get(Type::getVoidTy(C), FramePtrTy, - /*IsVarArgs=*/false); - auto *FnPtrTy = FnTy->getPointerTo(); - - // Figure out how wide should be an integer type storing the suspend index. - unsigned IndexBits = std::max(1U, Log2_64_Ceil(Shape.CoroSuspends.size())); - Type *PromiseType = Shape.PromiseAlloca - ? Shape.PromiseAlloca->getType()->getElementType() - : Type::getInt1Ty(C); - SmallVector<Type *, 8> Types{FnPtrTy, FnPtrTy, PromiseType, - Type::getIntNTy(C, IndexBits)}; + SmallVector<Type *, 8> Types; + + AllocaInst *PromiseAlloca = Shape.getPromiseAlloca(); + + if (Shape.ABI == coro::ABI::Switch) { + auto *FramePtrTy = FrameTy->getPointerTo(); + auto *FnTy = FunctionType::get(Type::getVoidTy(C), FramePtrTy, + /*IsVarArgs=*/false); + auto *FnPtrTy = FnTy->getPointerTo(); + + // Figure out how wide should be an integer type storing the suspend index. + unsigned IndexBits = std::max(1U, Log2_64_Ceil(Shape.CoroSuspends.size())); + Type *PromiseType = PromiseAlloca + ? PromiseAlloca->getType()->getElementType() + : Type::getInt1Ty(C); + Type *IndexType = Type::getIntNTy(C, IndexBits); + Types.push_back(FnPtrTy); + Types.push_back(FnPtrTy); + Types.push_back(PromiseType); + Types.push_back(IndexType); + } else { + assert(PromiseAlloca == nullptr && "lowering doesn't support promises"); + } + Value *CurrentDef = nullptr; // Create an entry for every spilled value. @@ -331,7 +363,7 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, CurrentDef = S.def(); // PromiseAlloca was already added to Types array earlier. - if (CurrentDef == Shape.PromiseAlloca) + if (CurrentDef == PromiseAlloca) continue; Type *Ty = nullptr; @@ -344,6 +376,22 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, } FrameTy->setBody(Types); + switch (Shape.ABI) { + case coro::ABI::Switch: + break; + + // Remember whether the frame is inline in the storage. + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: { + auto &Layout = F.getParent()->getDataLayout(); + auto Id = Shape.getRetconCoroId(); + Shape.RetconLowering.IsFrameInlineInStorage + = (Layout.getTypeAllocSize(FrameTy) <= Id->getStorageSize() && + Layout.getABITypeAlignment(FrameTy) <= Id->getStorageAlignment()); + break; + } + } + return FrameTy; } @@ -401,7 +449,7 @@ static Instruction *insertSpills(SpillInfo &Spills, coro::Shape &Shape) { Value *CurrentValue = nullptr; BasicBlock *CurrentBlock = nullptr; Value *CurrentReload = nullptr; - unsigned Index = coro::Shape::LastKnownField; + unsigned Index = Shape.getFirstSpillFieldIndex() - 1; // We need to keep track of any allocas that need "spilling" // since they will live in the coroutine frame now, all access to them @@ -409,9 +457,11 @@ static Instruction *insertSpills(SpillInfo &Spills, coro::Shape &Shape) { // we remember allocas and their indices to be handled once we processed // all the spills. SmallVector<std::pair<AllocaInst *, unsigned>, 4> Allocas; - // Promise alloca (if present) has a fixed field number (Shape::PromiseField) - if (Shape.PromiseAlloca) - Allocas.emplace_back(Shape.PromiseAlloca, coro::Shape::PromiseField); + // Promise alloca (if present) has a fixed field number. + if (auto *PromiseAlloca = Shape.getPromiseAlloca()) { + assert(Shape.ABI == coro::ABI::Switch); + Allocas.emplace_back(PromiseAlloca, coro::Shape::SwitchFieldIndex::Promise); + } // Create a load instruction to reload the spilled value from the coroutine // frame. @@ -433,7 +483,7 @@ static Instruction *insertSpills(SpillInfo &Spills, coro::Shape &Shape) { CurrentBlock = nullptr; CurrentReload = nullptr; - ++Index; + Index++; if (auto *AI = dyn_cast<AllocaInst>(CurrentValue)) { // Spilled AllocaInst will be replaced with GEP from the coroutine frame @@ -446,23 +496,33 @@ static Instruction *insertSpills(SpillInfo &Spills, coro::Shape &Shape) { // coroutine frame. Instruction *InsertPt = nullptr; - if (isa<Argument>(CurrentValue)) { + if (auto Arg = dyn_cast<Argument>(CurrentValue)) { // For arguments, we will place the store instruction right after // the coroutine frame pointer instruction, i.e. bitcast of // coro.begin from i8* to %f.frame*. InsertPt = FramePtr->getNextNode(); + + // If we're spilling an Argument, make sure we clear 'nocapture' + // from the coroutine function. + Arg->getParent()->removeParamAttr(Arg->getArgNo(), + Attribute::NoCapture); + } else if (auto *II = dyn_cast<InvokeInst>(CurrentValue)) { // If we are spilling the result of the invoke instruction, split the // normal edge and insert the spill in the new block. auto NewBB = SplitEdge(II->getParent(), II->getNormalDest()); InsertPt = NewBB->getTerminator(); - } else if (dyn_cast<PHINode>(CurrentValue)) { + } else if (isa<PHINode>(CurrentValue)) { // Skip the PHINodes and EH pads instructions. BasicBlock *DefBlock = cast<Instruction>(E.def())->getParent(); if (auto *CSI = dyn_cast<CatchSwitchInst>(DefBlock->getTerminator())) InsertPt = splitBeforeCatchSwitch(CSI); else InsertPt = &*DefBlock->getFirstInsertionPt(); + } else if (auto CSI = dyn_cast<AnyCoroSuspendInst>(CurrentValue)) { + // Don't spill immediately after a suspend; splitting assumes + // that the suspend will be followed by a branch. + InsertPt = CSI->getParent()->getSingleSuccessor()->getFirstNonPHI(); } else { // For all other values, the spill is placed immediately after // the definition. @@ -500,13 +560,14 @@ static Instruction *insertSpills(SpillInfo &Spills, coro::Shape &Shape) { } BasicBlock *FramePtrBB = FramePtr->getParent(); - Shape.AllocaSpillBlock = - FramePtrBB->splitBasicBlock(FramePtr->getNextNode(), "AllocaSpillBB"); - Shape.AllocaSpillBlock->splitBasicBlock(&Shape.AllocaSpillBlock->front(), - "PostSpill"); - Builder.SetInsertPoint(&Shape.AllocaSpillBlock->front()); + auto SpillBlock = + FramePtrBB->splitBasicBlock(FramePtr->getNextNode(), "AllocaSpillBB"); + SpillBlock->splitBasicBlock(&SpillBlock->front(), "PostSpill"); + Shape.AllocaSpillBlock = SpillBlock; + // If we found any allocas, replace all of their remaining uses with Geps. + Builder.SetInsertPoint(&SpillBlock->front()); for (auto &P : Allocas) { auto *G = Builder.CreateConstInBoundsGEP2_32(FrameTy, FramePtr, 0, P.second); @@ -786,16 +847,17 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { // access to local variables. LowerDbgDeclare(F); - Shape.PromiseAlloca = Shape.CoroBegin->getId()->getPromise(); - if (Shape.PromiseAlloca) { - Shape.CoroBegin->getId()->clearPromise(); + if (Shape.ABI == coro::ABI::Switch && + Shape.SwitchLowering.PromiseAlloca) { + Shape.getSwitchCoroId()->clearPromise(); } // Make sure that all coro.save, coro.suspend and the fallthrough coro.end // intrinsics are in their own blocks to simplify the logic of building up // SuspendCrossing data. - for (CoroSuspendInst *CSI : Shape.CoroSuspends) { - splitAround(CSI->getCoroSave(), "CoroSave"); + for (auto *CSI : Shape.CoroSuspends) { + if (auto *Save = CSI->getCoroSave()) + splitAround(Save, "CoroSave"); splitAround(CSI, "CoroSuspend"); } @@ -843,7 +905,8 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { continue; // The Coroutine Promise always included into coroutine frame, no need to // check for suspend crossing. - if (Shape.PromiseAlloca == &I) + if (Shape.ABI == coro::ABI::Switch && + Shape.SwitchLowering.PromiseAlloca == &I) continue; for (User *U : I.users()) diff --git a/llvm/lib/Transforms/Coroutines/CoroInstr.h b/llvm/lib/Transforms/Coroutines/CoroInstr.h index 9a8cc5a2591c9..cc4a55caf7de9 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInstr.h +++ b/llvm/lib/Transforms/Coroutines/CoroInstr.h @@ -28,6 +28,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { @@ -78,10 +79,8 @@ class LLVM_LIBRARY_VISIBILITY CoroAllocInst : public IntrinsicInst { } }; -/// This represents the llvm.coro.alloc instruction. -class LLVM_LIBRARY_VISIBILITY CoroIdInst : public IntrinsicInst { - enum { AlignArg, PromiseArg, CoroutineArg, InfoArg }; - +/// This represents a common base class for llvm.coro.id instructions. +class LLVM_LIBRARY_VISIBILITY AnyCoroIdInst : public IntrinsicInst { public: CoroAllocInst *getCoroAlloc() { for (User *U : users()) @@ -98,6 +97,24 @@ class LLVM_LIBRARY_VISIBILITY CoroIdInst : public IntrinsicInst { llvm_unreachable("no coro.begin associated with coro.id"); } + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + auto ID = I->getIntrinsicID(); + return ID == Intrinsic::coro_id || + ID == Intrinsic::coro_id_retcon || + ID == Intrinsic::coro_id_retcon_once; + } + + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + +/// This represents the llvm.coro.id instruction. +class LLVM_LIBRARY_VISIBILITY CoroIdInst : public AnyCoroIdInst { + enum { AlignArg, PromiseArg, CoroutineArg, InfoArg }; + +public: AllocaInst *getPromise() const { Value *Arg = getArgOperand(PromiseArg); return isa<ConstantPointerNull>(Arg) @@ -183,6 +200,80 @@ class LLVM_LIBRARY_VISIBILITY CoroIdInst : public IntrinsicInst { } }; +/// This represents either the llvm.coro.id.retcon or +/// llvm.coro.id.retcon.once instruction. +class LLVM_LIBRARY_VISIBILITY AnyCoroIdRetconInst : public AnyCoroIdInst { + enum { SizeArg, AlignArg, StorageArg, PrototypeArg, AllocArg, DeallocArg }; + +public: + void checkWellFormed() const; + + uint64_t getStorageSize() const { + return cast<ConstantInt>(getArgOperand(SizeArg))->getZExtValue(); + } + + uint64_t getStorageAlignment() const { + return cast<ConstantInt>(getArgOperand(AlignArg))->getZExtValue(); + } + + Value *getStorage() const { + return getArgOperand(StorageArg); + } + + /// Return the prototype for the continuation function. The type, + /// attributes, and calling convention of the continuation function(s) + /// are taken from this declaration. + Function *getPrototype() const { + return cast<Function>(getArgOperand(PrototypeArg)->stripPointerCasts()); + } + + /// Return the function to use for allocating memory. + Function *getAllocFunction() const { + return cast<Function>(getArgOperand(AllocArg)->stripPointerCasts()); + } + + /// Return the function to use for deallocating memory. + Function *getDeallocFunction() const { + return cast<Function>(getArgOperand(DeallocArg)->stripPointerCasts()); + } + + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + auto ID = I->getIntrinsicID(); + return ID == Intrinsic::coro_id_retcon + || ID == Intrinsic::coro_id_retcon_once; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + +/// This represents the llvm.coro.id.retcon instruction. +class LLVM_LIBRARY_VISIBILITY CoroIdRetconInst + : public AnyCoroIdRetconInst { +public: + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::coro_id_retcon; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + +/// This represents the llvm.coro.id.retcon.once instruction. +class LLVM_LIBRARY_VISIBILITY CoroIdRetconOnceInst + : public AnyCoroIdRetconInst { +public: + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::coro_id_retcon_once; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + /// This represents the llvm.coro.frame instruction. class LLVM_LIBRARY_VISIBILITY CoroFrameInst : public IntrinsicInst { public: @@ -216,7 +307,9 @@ class LLVM_LIBRARY_VISIBILITY CoroBeginInst : public IntrinsicInst { enum { IdArg, MemArg }; public: - CoroIdInst *getId() const { return cast<CoroIdInst>(getArgOperand(IdArg)); } + AnyCoroIdInst *getId() const { + return cast<AnyCoroIdInst>(getArgOperand(IdArg)); + } Value *getMem() const { return getArgOperand(MemArg); } @@ -262,8 +355,22 @@ class LLVM_LIBRARY_VISIBILITY CoroPromiseInst : public IntrinsicInst { } }; +class LLVM_LIBRARY_VISIBILITY AnyCoroSuspendInst : public IntrinsicInst { +public: + CoroSaveInst *getCoroSave() const; + + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::coro_suspend || + I->getIntrinsicID() == Intrinsic::coro_suspend_retcon; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + /// This represents the llvm.coro.suspend instruction. -class LLVM_LIBRARY_VISIBILITY CoroSuspendInst : public IntrinsicInst { +class LLVM_LIBRARY_VISIBILITY CoroSuspendInst : public AnyCoroSuspendInst { enum { SaveArg, FinalArg }; public: @@ -274,6 +381,7 @@ class LLVM_LIBRARY_VISIBILITY CoroSuspendInst : public IntrinsicInst { assert(isa<ConstantTokenNone>(Arg)); return nullptr; } + bool isFinal() const { return cast<Constant>(getArgOperand(FinalArg))->isOneValue(); } @@ -287,6 +395,37 @@ class LLVM_LIBRARY_VISIBILITY CoroSuspendInst : public IntrinsicInst { } }; +inline CoroSaveInst *AnyCoroSuspendInst::getCoroSave() const { + if (auto Suspend = dyn_cast<CoroSuspendInst>(this)) + return Suspend->getCoroSave(); + return nullptr; +} + +/// This represents the llvm.coro.suspend.retcon instruction. +class LLVM_LIBRARY_VISIBILITY CoroSuspendRetconInst : public AnyCoroSuspendInst { +public: + op_iterator value_begin() { return arg_begin(); } + const_op_iterator value_begin() const { return arg_begin(); } + + op_iterator value_end() { return arg_end(); } + const_op_iterator value_end() const { return arg_end(); } + + iterator_range<op_iterator> value_operands() { + return make_range(value_begin(), value_end()); + } + iterator_range<const_op_iterator> value_operands() const { + return make_range(value_begin(), value_end()); + } + + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::coro_suspend_retcon; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + /// This represents the llvm.coro.size instruction. class LLVM_LIBRARY_VISIBILITY CoroSizeInst : public IntrinsicInst { public: diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h index 1eac88dbac3a1..7a5fc7f4fa29f 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -13,6 +13,7 @@ #define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H #include "CoroInstr.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/Transforms/Coroutines.h" namespace llvm { @@ -62,38 +63,156 @@ struct LowererBase { Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt); }; +enum class ABI { + /// The "resume-switch" lowering, where there are separate resume and + /// destroy functions that are shared between all suspend points. The + /// coroutine frame implicitly stores the resume and destroy functions, + /// the current index, and any promise value. + Switch, + + /// The "returned-continuation" lowering, where each suspend point creates a + /// single continuation function that is used for both resuming and + /// destroying. Does not support promises. + Retcon, + + /// The "unique returned-continuation" lowering, where each suspend point + /// creates a single continuation function that is used for both resuming + /// and destroying. Does not support promises. The function is known to + /// suspend at most once during its execution, and the return value of + /// the continuation is void. + RetconOnce, +}; + // Holds structural Coroutine Intrinsics for a particular function and other // values used during CoroSplit pass. struct LLVM_LIBRARY_VISIBILITY Shape { CoroBeginInst *CoroBegin; SmallVector<CoroEndInst *, 4> CoroEnds; SmallVector<CoroSizeInst *, 2> CoroSizes; - SmallVector<CoroSuspendInst *, 4> CoroSuspends; - - // Field Indexes for known coroutine frame fields. - enum { - ResumeField, - DestroyField, - PromiseField, - IndexField, - LastKnownField = IndexField + SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends; + + // Field indexes for special fields in the switch lowering. + struct SwitchFieldIndex { + enum { + Resume, + Destroy, + Promise, + Index, + /// The index of the first spill field. + FirstSpill + }; }; + coro::ABI ABI; + StructType *FrameTy; Instruction *FramePtr; BasicBlock *AllocaSpillBlock; - SwitchInst *ResumeSwitch; - AllocaInst *PromiseAlloca; - bool HasFinalSuspend; + + struct SwitchLoweringStorage { + SwitchInst *ResumeSwitch; + AllocaInst *PromiseAlloca; + BasicBlock *ResumeEntryBlock; + bool HasFinalSuspend; + }; + + struct RetconLoweringStorage { + Function *ResumePrototype; + Function *Alloc; + Function *Dealloc; + BasicBlock *ReturnBlock; + bool IsFrameInlineInStorage; + }; + + union { + SwitchLoweringStorage SwitchLowering; + RetconLoweringStorage RetconLowering; + }; + + CoroIdInst *getSwitchCoroId() const { + assert(ABI == coro::ABI::Switch); + return cast<CoroIdInst>(CoroBegin->getId()); + } + + AnyCoroIdRetconInst *getRetconCoroId() const { + assert(ABI == coro::ABI::Retcon || + ABI == coro::ABI::RetconOnce); + return cast<AnyCoroIdRetconInst>(CoroBegin->getId()); + } IntegerType *getIndexType() const { + assert(ABI == coro::ABI::Switch); assert(FrameTy && "frame type not assigned"); - return cast<IntegerType>(FrameTy->getElementType(IndexField)); + return cast<IntegerType>(FrameTy->getElementType(SwitchFieldIndex::Index)); } ConstantInt *getIndex(uint64_t Value) const { return ConstantInt::get(getIndexType(), Value); } + FunctionType *getResumeFunctionType() const { + switch (ABI) { + case coro::ABI::Switch: { + assert(FrameTy && "frame type not assigned"); + auto *FnPtrTy = FrameTy->getElementType(SwitchFieldIndex::Resume); + return cast<FunctionType>(FnPtrTy->getPointerElementType()); + } + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + return RetconLowering.ResumePrototype->getFunctionType(); + } + } + + ArrayRef<Type*> getRetconResultTypes() const { + assert(ABI == coro::ABI::Retcon || + ABI == coro::ABI::RetconOnce); + auto FTy = CoroBegin->getFunction()->getFunctionType(); + + // This is checked by AnyCoroIdRetconInst::isWellFormed(). + if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) { + return STy->elements().slice(1); + } else { + return ArrayRef<Type*>(); + } + } + + CallingConv::ID getResumeFunctionCC() const { + switch (ABI) { + case coro::ABI::Switch: + return CallingConv::Fast; + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + return RetconLowering.ResumePrototype->getCallingConv(); + } + } + + unsigned getFirstSpillFieldIndex() const { + switch (ABI) { + case coro::ABI::Switch: + return SwitchFieldIndex::FirstSpill; + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + return 0; + } + } + + AllocaInst *getPromiseAlloca() const { + if (ABI == coro::ABI::Switch) + return SwitchLowering.PromiseAlloca; + return nullptr; + } + + /// Allocate memory according to the rules of the active lowering. + /// + /// \param CG - if non-null, will be updated for the new call + Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const; + + /// Deallocate memory according to the rules of the active lowering. + /// + /// \param CG - if non-null, will be updated for the new call + void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const; + Shape() = default; explicit Shape(Function &F) { buildFrom(F); } void buildFrom(Function &F); diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 8712ca4823c67..e3fc2b9e0eff7 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -71,9 +71,193 @@ using namespace llvm; #define DEBUG_TYPE "coro-split" +namespace { + +/// A little helper class for building +class CoroCloner { +public: + enum class Kind { + /// The shared resume function for a switch lowering. + SwitchResume, + + /// The shared unwind function for a switch lowering. + SwitchUnwind, + + /// The shared cleanup function for a switch lowering. + SwitchCleanup, + + /// An individual continuation function. + Continuation, + }; +private: + Function &OrigF; + Function *NewF; + const Twine &Suffix; + coro::Shape &Shape; + Kind FKind; + ValueToValueMapTy VMap; + IRBuilder<> Builder; + Value *NewFramePtr = nullptr; + + /// The active suspend instruction; meaningful only for continuation ABIs. + AnyCoroSuspendInst *ActiveSuspend = nullptr; + +public: + /// Create a cloner for a switch lowering. + CoroCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape, + Kind FKind) + : OrigF(OrigF), NewF(nullptr), Suffix(Suffix), Shape(Shape), + FKind(FKind), Builder(OrigF.getContext()) { + assert(Shape.ABI == coro::ABI::Switch); + } + + /// Create a cloner for a continuation lowering. + CoroCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape, + Function *NewF, AnyCoroSuspendInst *ActiveSuspend) + : OrigF(OrigF), NewF(NewF), Suffix(Suffix), Shape(Shape), + FKind(Kind::Continuation), Builder(OrigF.getContext()), + ActiveSuspend(ActiveSuspend) { + assert(Shape.ABI == coro::ABI::Retcon || + Shape.ABI == coro::ABI::RetconOnce); + assert(NewF && "need existing function for continuation"); + assert(ActiveSuspend && "need active suspend point for continuation"); + } + + Function *getFunction() const { + assert(NewF != nullptr && "declaration not yet set"); + return NewF; + } + + void create(); + +private: + bool isSwitchDestroyFunction() { + switch (FKind) { + case Kind::Continuation: + case Kind::SwitchResume: + return false; + case Kind::SwitchUnwind: + case Kind::SwitchCleanup: + return true; + } + } + + void createDeclaration(); + void replaceEntryBlock(); + Value *deriveNewFramePointer(); + void replaceCoroSuspends(); + void replaceCoroEnds(); + void handleFinalSuspend(); + void maybeFreeContinuationStorage(); +}; + +} // end anonymous namespace + +static void maybeFreeRetconStorage(IRBuilder<> &Builder, coro::Shape &Shape, + Value *FramePtr, CallGraph *CG) { + assert(Shape.ABI == coro::ABI::Retcon || + Shape.ABI == coro::ABI::RetconOnce); + if (Shape.RetconLowering.IsFrameInlineInStorage) + return; + + Shape.emitDealloc(Builder, FramePtr, CG); +} + +/// Replace a non-unwind call to llvm.coro.end. +static void replaceFallthroughCoroEnd(CoroEndInst *End, coro::Shape &Shape, + Value *FramePtr, bool InResume, + CallGraph *CG) { + // Start inserting right before the coro.end. + IRBuilder<> Builder(End); + + // Create the return instruction. + switch (Shape.ABI) { + // The cloned functions in switch-lowering always return void. + case coro::ABI::Switch: + // coro.end doesn't immediately end the coroutine in the main function + // in this lowering, because we need to deallocate the coroutine. + if (!InResume) + return; + Builder.CreateRetVoid(); + break; + + // In unique continuation lowering, the continuations always return void. + // But we may have implicitly allocated storage. + case coro::ABI::RetconOnce: + maybeFreeRetconStorage(Builder, Shape, FramePtr, CG); + Builder.CreateRetVoid(); + break; + + // In non-unique continuation lowering, we signal completion by returning + // a null continuation. + case coro::ABI::Retcon: { + maybeFreeRetconStorage(Builder, Shape, FramePtr, CG); + auto RetTy = Shape.getResumeFunctionType()->getReturnType(); + auto RetStructTy = dyn_cast<StructType>(RetTy); + PointerType *ContinuationTy = + cast<PointerType>(RetStructTy ? RetStructTy->getElementType(0) : RetTy); + + Value *ReturnValue = ConstantPointerNull::get(ContinuationTy); + if (RetStructTy) { + ReturnValue = Builder.CreateInsertValue(UndefValue::get(RetStructTy), + ReturnValue, 0); + } + Builder.CreateRet(ReturnValue); + break; + } + } + + // Remove the rest of the block, by splitting it into an unreachable block. + auto *BB = End->getParent(); + BB->splitBasicBlock(End); + BB->getTerminator()->eraseFromParent(); +} + +/// Replace an unwind call to llvm.coro.end. +static void replaceUnwindCoroEnd(CoroEndInst *End, coro::Shape &Shape, + Value *FramePtr, bool InResume, CallGraph *CG){ + IRBuilder<> Builder(End); + + switch (Shape.ABI) { + // In switch-lowering, this does nothing in the main function. + case coro::ABI::Switch: + if (!InResume) + return; + break; + + // In continuation-lowering, this frees the continuation storage. + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + maybeFreeRetconStorage(Builder, Shape, FramePtr, CG); + break; + } + + // If coro.end has an associated bundle, add cleanupret instruction. + if (auto Bundle = End->getOperandBundle(LLVMContext::OB_funclet)) { + auto *FromPad = cast<CleanupPadInst>(Bundle->Inputs[0]); + auto *CleanupRet = Builder.CreateCleanupRet(FromPad, nullptr); + End->getParent()->splitBasicBlock(End); + CleanupRet->getParent()->getTerminator()->eraseFromParent(); + } +} + +static void replaceCoroEnd(CoroEndInst *End, coro::Shape &Shape, + Value *FramePtr, bool InResume, CallGraph *CG) { + if (End->isUnwind()) + replaceUnwindCoroEnd(End, Shape, FramePtr, InResume, CG); + else + replaceFallthroughCoroEnd(End, Shape, FramePtr, InResume, CG); + + auto &Context = End->getContext(); + End->replaceAllUsesWith(InResume ? ConstantInt::getTrue(Context) + : ConstantInt::getFalse(Context)); + End->eraseFromParent(); +} + // Create an entry block for a resume function with a switch that will jump to // suspend points. -static BasicBlock *createResumeEntryBlock(Function &F, coro::Shape &Shape) { +static void createResumeEntryBlock(Function &F, coro::Shape &Shape) { + assert(Shape.ABI == coro::ABI::Switch); LLVMContext &C = F.getContext(); // resume.entry: @@ -92,15 +276,16 @@ static BasicBlock *createResumeEntryBlock(Function &F, coro::Shape &Shape) { IRBuilder<> Builder(NewEntry); auto *FramePtr = Shape.FramePtr; auto *FrameTy = Shape.FrameTy; - auto *GepIndex = Builder.CreateConstInBoundsGEP2_32( - FrameTy, FramePtr, 0, coro::Shape::IndexField, "index.addr"); + auto *GepIndex = Builder.CreateStructGEP( + FrameTy, FramePtr, coro::Shape::SwitchFieldIndex::Index, "index.addr"); auto *Index = Builder.CreateLoad(GepIndex, "index"); auto *Switch = Builder.CreateSwitch(Index, UnreachBB, Shape.CoroSuspends.size()); - Shape.ResumeSwitch = Switch; + Shape.SwitchLowering.ResumeSwitch = Switch; size_t SuspendIndex = 0; - for (CoroSuspendInst *S : Shape.CoroSuspends) { + for (auto *AnyS : Shape.CoroSuspends) { + auto *S = cast<CoroSuspendInst>(AnyS); ConstantInt *IndexVal = Shape.getIndex(SuspendIndex); // Replace CoroSave with a store to Index: @@ -110,14 +295,15 @@ static BasicBlock *createResumeEntryBlock(Function &F, coro::Shape &Shape) { Builder.SetInsertPoint(Save); if (S->isFinal()) { // Final suspend point is represented by storing zero in ResumeFnAddr. - auto *GepIndex = Builder.CreateConstInBoundsGEP2_32(FrameTy, FramePtr, 0, - 0, "ResumeFn.addr"); + auto *GepIndex = Builder.CreateStructGEP(FrameTy, FramePtr, + coro::Shape::SwitchFieldIndex::Resume, + "ResumeFn.addr"); auto *NullPtr = ConstantPointerNull::get(cast<PointerType>( cast<PointerType>(GepIndex->getType())->getElementType())); Builder.CreateStore(NullPtr, GepIndex); } else { - auto *GepIndex = Builder.CreateConstInBoundsGEP2_32( - FrameTy, FramePtr, 0, coro::Shape::IndexField, "index.addr"); + auto *GepIndex = Builder.CreateStructGEP( + FrameTy, FramePtr, coro::Shape::SwitchFieldIndex::Index, "index.addr"); Builder.CreateStore(IndexVal, GepIndex); } Save->replaceAllUsesWith(ConstantTokenNone::get(C)); @@ -165,48 +351,9 @@ static BasicBlock *createResumeEntryBlock(Function &F, coro::Shape &Shape) { Builder.SetInsertPoint(UnreachBB); Builder.CreateUnreachable(); - return NewEntry; -} - -// In Resumers, we replace fallthrough coro.end with ret void and delete the -// rest of the block. -static void replaceFallthroughCoroEnd(IntrinsicInst *End, - ValueToValueMapTy &VMap) { - auto *NewE = cast<IntrinsicInst>(VMap[End]); - ReturnInst::Create(NewE->getContext(), nullptr, NewE); - - // Remove the rest of the block, by splitting it into an unreachable block. - auto *BB = NewE->getParent(); - BB->splitBasicBlock(NewE); - BB->getTerminator()->eraseFromParent(); + Shape.SwitchLowering.ResumeEntryBlock = NewEntry; } -// In Resumers, we replace unwind coro.end with True to force the immediate -// unwind to caller. -static void replaceUnwindCoroEnds(coro::Shape &Shape, ValueToValueMapTy &VMap) { - if (Shape.CoroEnds.empty()) - return; - - LLVMContext &Context = Shape.CoroEnds.front()->getContext(); - auto *True = ConstantInt::getTrue(Context); - for (CoroEndInst *CE : Shape.CoroEnds) { - if (!CE->isUnwind()) - continue; - - auto *NewCE = cast<IntrinsicInst>(VMap[CE]); - - // If coro.end has an associated bundle, add cleanupret instruction. - if (auto Bundle = NewCE->getOperandBundle(LLVMContext::OB_funclet)) { - Value *FromPad = Bundle->Inputs[0]; - auto *CleanupRet = CleanupReturnInst::Create(FromPad, nullptr, NewCE); - NewCE->getParent()->splitBasicBlock(NewCE); - CleanupRet->getParent()->getTerminator()->eraseFromParent(); - } - - NewCE->replaceAllUsesWith(True); - NewCE->eraseFromParent(); - } -} // Rewrite final suspend point handling. We do not use suspend index to // represent the final suspend point. Instead we zero-out ResumeFnAddr in the @@ -217,81 +364,238 @@ static void replaceUnwindCoroEnds(coro::Shape &Shape, ValueToValueMapTy &VMap) { // In the destroy function, we add a code sequence to check if ResumeFnAddress // is Null, and if so, jump to the appropriate label to handle cleanup from the // final suspend point. -static void handleFinalSuspend(IRBuilder<> &Builder, Value *FramePtr, - coro::Shape &Shape, SwitchInst *Switch, - bool IsDestroy) { - assert(Shape.HasFinalSuspend); +void CoroCloner::handleFinalSuspend() { + assert(Shape.ABI == coro::ABI::Switch && + Shape.SwitchLowering.HasFinalSuspend); + auto *Switch = cast<SwitchInst>(VMap[Shape.SwitchLowering.ResumeSwitch]); auto FinalCaseIt = std::prev(Switch->case_end()); BasicBlock *ResumeBB = FinalCaseIt->getCaseSuccessor(); Switch->removeCase(FinalCaseIt); - if (IsDestroy) { + if (isSwitchDestroyFunction()) { BasicBlock *OldSwitchBB = Switch->getParent(); auto *NewSwitchBB = OldSwitchBB->splitBasicBlock(Switch, "Switch"); Builder.SetInsertPoint(OldSwitchBB->getTerminator()); - auto *GepIndex = Builder.CreateConstInBoundsGEP2_32(Shape.FrameTy, FramePtr, - 0, 0, "ResumeFn.addr"); + auto *GepIndex = Builder.CreateStructGEP(Shape.FrameTy, NewFramePtr, + coro::Shape::SwitchFieldIndex::Resume, + "ResumeFn.addr"); auto *Load = Builder.CreateLoad(GepIndex); - auto *NullPtr = - ConstantPointerNull::get(cast<PointerType>(Load->getType())); - auto *Cond = Builder.CreateICmpEQ(Load, NullPtr); + auto *Cond = Builder.CreateIsNull(Load); Builder.CreateCondBr(Cond, ResumeBB, NewSwitchBB); OldSwitchBB->getTerminator()->eraseFromParent(); } } -// Create a resume clone by cloning the body of the original function, setting -// new entry block and replacing coro.suspend an appropriate value to force -// resume or cleanup pass for every suspend point. -static Function *createClone(Function &F, Twine Suffix, coro::Shape &Shape, - BasicBlock *ResumeEntry, int8_t FnIndex) { - Module *M = F.getParent(); - auto *FrameTy = Shape.FrameTy; - auto *FnPtrTy = cast<PointerType>(FrameTy->getElementType(0)); - auto *FnTy = cast<FunctionType>(FnPtrTy->getElementType()); +static Function *createCloneDeclaration(Function &OrigF, coro::Shape &Shape, + const Twine &Suffix, + Module::iterator InsertBefore) { + Module *M = OrigF.getParent(); + auto *FnTy = Shape.getResumeFunctionType(); - Function *NewF = - Function::Create(FnTy, GlobalValue::LinkageTypes::InternalLinkage, - F.getName() + Suffix, M); - NewF->addParamAttr(0, Attribute::NonNull); - NewF->addParamAttr(0, Attribute::NoAlias); + auto *F = Function::Create(FnTy, GlobalValue::LinkageTypes::InternalLinkage, + OrigF.getName() + Suffix); + + M->getFunctionList().insert(InsertBefore, F); + + return F; +} + +void CoroCloner::replaceCoroSuspends() { + Value *SuspendResult; + + switch (Shape.ABI) { + // In switch lowering, replace coro.suspend with the appropriate value + // for the type of function we're extracting. + // Replacing coro.suspend with (0) will result in control flow proceeding to + // a resume label associated with a suspend point, replacing it with (1) will + // result in control flow proceeding to a cleanup label associated with this + // suspend point. + case coro::ABI::Switch: + SuspendResult = Builder.getInt8(isSwitchDestroyFunction() ? 1 : 0); + break; + + // In continuation lowering, replace all of the suspend uses with false to + // indicate that they're not unwinding resumes. We've already mapped the + // active suspend to the appropriate argument, so any other suspend values + // that are still being used must be from previous suspends. It's UB to try + // to suspend during unwind, so they must be from regular resumes. + case coro::ABI::RetconOnce: + case coro::ABI::Retcon: + SuspendResult = Builder.getInt1(false); + break; + } + + for (AnyCoroSuspendInst *CS : Shape.CoroSuspends) { + // The active suspend was handled earlier. + if (CS == ActiveSuspend) continue; + + auto *MappedCS = cast<AnyCoroSuspendInst>(VMap[CS]); + MappedCS->replaceAllUsesWith(SuspendResult); + MappedCS->eraseFromParent(); + } +} + +void CoroCloner::replaceCoroEnds() { + for (CoroEndInst *CE : Shape.CoroEnds) { + // We use a null call graph because there's no call graph node for + // the cloned function yet. We'll just be rebuilding that later. + auto NewCE = cast<CoroEndInst>(VMap[CE]); + replaceCoroEnd(NewCE, Shape, NewFramePtr, /*in resume*/ true, nullptr); + } +} + +void CoroCloner::replaceEntryBlock() { + // In the original function, the AllocaSpillBlock is a block immediately + // following the allocation of the frame object which defines GEPs for + // all the allocas that have been moved into the frame, and it ends by + // branching to the original beginning of the coroutine. Make this + // the entry block of the cloned function. + auto *Entry = cast<BasicBlock>(VMap[Shape.AllocaSpillBlock]); + Entry->setName("entry" + Suffix); + Entry->moveBefore(&NewF->getEntryBlock()); + Entry->getTerminator()->eraseFromParent(); + + // Clear all predecessors of the new entry block. There should be + // exactly one predecessor, which we created when splitting out + // AllocaSpillBlock to begin with. + assert(Entry->hasOneUse()); + auto BranchToEntry = cast<BranchInst>(Entry->user_back()); + assert(BranchToEntry->isUnconditional()); + Builder.SetInsertPoint(BranchToEntry); + Builder.CreateUnreachable(); + BranchToEntry->eraseFromParent(); + + // TODO: move any allocas into Entry that weren't moved into the frame. + // (Currently we move all allocas into the frame.) + + // Branch from the entry to the appropriate place. + Builder.SetInsertPoint(Entry); + switch (Shape.ABI) { + case coro::ABI::Switch: { + // In switch-lowering, we built a resume-entry block in the original + // function. Make the entry block branch to this. + auto *SwitchBB = + cast<BasicBlock>(VMap[Shape.SwitchLowering.ResumeEntryBlock]); + Builder.CreateBr(SwitchBB); + break; + } + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: { + // In continuation ABIs, we want to branch to immediately after the + // active suspend point. Earlier phases will have put the suspend in its + // own basic block, so just thread our jump directly to its successor. + auto MappedCS = cast<CoroSuspendRetconInst>(VMap[ActiveSuspend]); + auto Branch = cast<BranchInst>(MappedCS->getNextNode()); + assert(Branch->isUnconditional()); + Builder.CreateBr(Branch->getSuccessor(0)); + break; + } + } +} + +/// Derive the value of the new frame pointer. +Value *CoroCloner::deriveNewFramePointer() { + // Builder should be inserting to the front of the new entry block. + + switch (Shape.ABI) { + // In switch-lowering, the argument is the frame pointer. + case coro::ABI::Switch: + return &*NewF->arg_begin(); + + // In continuation-lowering, the argument is the opaque storage. + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: { + Argument *NewStorage = &*NewF->arg_begin(); + auto FramePtrTy = Shape.FrameTy->getPointerTo(); + + // If the storage is inline, just bitcast to the storage to the frame type. + if (Shape.RetconLowering.IsFrameInlineInStorage) + return Builder.CreateBitCast(NewStorage, FramePtrTy); + + // Otherwise, load the real frame from the opaque storage. + auto FramePtrPtr = + Builder.CreateBitCast(NewStorage, FramePtrTy->getPointerTo()); + return Builder.CreateLoad(FramePtrPtr); + } + } + llvm_unreachable("bad ABI"); +} + +/// Clone the body of the original function into a resume function of +/// some sort. +void CoroCloner::create() { + // Create the new function if we don't already have one. + if (!NewF) { + NewF = createCloneDeclaration(OrigF, Shape, Suffix, + OrigF.getParent()->end()); + } - ValueToValueMapTy VMap; // Replace all args with undefs. The buildCoroutineFrame algorithm already // rewritten access to the args that occurs after suspend points with loads // and stores to/from the coroutine frame. - for (Argument &A : F.args()) + for (Argument &A : OrigF.args()) VMap[&A] = UndefValue::get(A.getType()); SmallVector<ReturnInst *, 4> Returns; - CloneFunctionInto(NewF, &F, VMap, /*ModuleLevelChanges=*/true, Returns); + CloneFunctionInto(NewF, &OrigF, VMap, /*ModuleLevelChanges=*/true, Returns); - // Remove old returns. - for (ReturnInst *Return : Returns) - changeToUnreachable(Return, /*UseLLVMTrap=*/false); + auto &Context = NewF->getContext(); - // Remove old return attributes. - NewF->removeAttributes( - AttributeList::ReturnIndex, - AttributeFuncs::typeIncompatible(NewF->getReturnType())); + // Replace the attributes of the new function: + auto OrigAttrs = NewF->getAttributes(); + auto NewAttrs = AttributeList(); - // Make AllocaSpillBlock the new entry block. - auto *SwitchBB = cast<BasicBlock>(VMap[ResumeEntry]); - auto *Entry = cast<BasicBlock>(VMap[Shape.AllocaSpillBlock]); - Entry->moveBefore(&NewF->getEntryBlock()); - Entry->getTerminator()->eraseFromParent(); - BranchInst::Create(SwitchBB, Entry); - Entry->setName("entry" + Suffix); + switch (Shape.ABI) { + case coro::ABI::Switch: + // Bootstrap attributes by copying function attributes from the + // original function. This should include optimization settings and so on. + NewAttrs = NewAttrs.addAttributes(Context, AttributeList::FunctionIndex, + OrigAttrs.getFnAttributes()); + break; + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + // If we have a continuation prototype, just use its attributes, + // full-stop. + NewAttrs = Shape.RetconLowering.ResumePrototype->getAttributes(); + break; + } + + // Make the frame parameter nonnull and noalias. + NewAttrs = NewAttrs.addParamAttribute(Context, 0, Attribute::NonNull); + NewAttrs = NewAttrs.addParamAttribute(Context, 0, Attribute::NoAlias); + + switch (Shape.ABI) { + // In these ABIs, the cloned functions always return 'void', and the + // existing return sites are meaningless. Note that for unique + // continuations, this includes the returns associated with suspends; + // this is fine because we can't suspend twice. + case coro::ABI::Switch: + case coro::ABI::RetconOnce: + // Remove old returns. + for (ReturnInst *Return : Returns) + changeToUnreachable(Return, /*UseLLVMTrap=*/false); + break; + + // With multi-suspend continuations, we'll already have eliminated the + // original returns and inserted returns before all the suspend points, + // so we want to leave any returns in place. + case coro::ABI::Retcon: + break; + } - // Clear all predecessors of the new entry block. - auto *Switch = cast<SwitchInst>(VMap[Shape.ResumeSwitch]); - Entry->replaceAllUsesWith(Switch->getDefaultDest()); + NewF->setAttributes(NewAttrs); + NewF->setCallingConv(Shape.getResumeFunctionCC()); - IRBuilder<> Builder(&NewF->getEntryBlock().front()); + // Set up the new entry block. + replaceEntryBlock(); + + Builder.SetInsertPoint(&NewF->getEntryBlock().front()); + NewFramePtr = deriveNewFramePointer(); // Remap frame pointer. - Argument *NewFramePtr = &*NewF->arg_begin(); - Value *OldFramePtr = cast<Value>(VMap[Shape.FramePtr]); + Value *OldFramePtr = VMap[Shape.FramePtr]; NewFramePtr->takeName(OldFramePtr); OldFramePtr->replaceAllUsesWith(NewFramePtr); @@ -301,50 +605,55 @@ static Function *createClone(Function &F, Twine Suffix, coro::Shape &Shape, Value *OldVFrame = cast<Value>(VMap[Shape.CoroBegin]); OldVFrame->replaceAllUsesWith(NewVFrame); - // Rewrite final suspend handling as it is not done via switch (allows to - // remove final case from the switch, since it is undefined behavior to resume - // the coroutine suspended at the final suspend point. - if (Shape.HasFinalSuspend) { - auto *Switch = cast<SwitchInst>(VMap[Shape.ResumeSwitch]); - bool IsDestroy = FnIndex != 0; - handleFinalSuspend(Builder, NewFramePtr, Shape, Switch, IsDestroy); + switch (Shape.ABI) { + case coro::ABI::Switch: + // Rewrite final suspend handling as it is not done via switch (allows to + // remove final case from the switch, since it is undefined behavior to + // resume the coroutine suspended at the final suspend point. + if (Shape.SwitchLowering.HasFinalSuspend) + handleFinalSuspend(); + break; + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + // Replace the active suspend with the should-unwind argument. + // Coerce it to i1 if necessary. + assert(ActiveSuspend != nullptr && + "no active suspend when lowering a continuation-style coroutine"); + Value *ShouldUnwind = &*std::next(NewF->arg_begin()); + if (!ShouldUnwind->getType()->isIntegerTy(1)) + ShouldUnwind = Builder.CreateIsNotNull(ShouldUnwind); + VMap[ActiveSuspend]->replaceAllUsesWith(ShouldUnwind); + break; } - // Replace coro suspend with the appropriate resume index. - // Replacing coro.suspend with (0) will result in control flow proceeding to - // a resume label associated with a suspend point, replacing it with (1) will - // result in control flow proceeding to a cleanup label associated with this - // suspend point. - auto *NewValue = Builder.getInt8(FnIndex ? 1 : 0); - for (CoroSuspendInst *CS : Shape.CoroSuspends) { - auto *MappedCS = cast<CoroSuspendInst>(VMap[CS]); - MappedCS->replaceAllUsesWith(NewValue); - MappedCS->eraseFromParent(); - } + // Handle suspends. + replaceCoroSuspends(); // Remove coro.end intrinsics. - replaceFallthroughCoroEnd(Shape.CoroEnds.front(), VMap); - replaceUnwindCoroEnds(Shape, VMap); + replaceCoroEnds(); + // Eliminate coro.free from the clones, replacing it with 'null' in cleanup, // to suppress deallocation code. - coro::replaceCoroFree(cast<CoroIdInst>(VMap[Shape.CoroBegin->getId()]), - /*Elide=*/FnIndex == 2); - - NewF->setCallingConv(CallingConv::Fast); - - return NewF; + if (Shape.ABI == coro::ABI::Switch) + coro::replaceCoroFree(cast<CoroIdInst>(VMap[Shape.CoroBegin->getId()]), + /*Elide=*/ FKind == CoroCloner::Kind::SwitchCleanup); } -static void removeCoroEnds(coro::Shape &Shape) { - if (Shape.CoroEnds.empty()) - return; - - LLVMContext &Context = Shape.CoroEnds.front()->getContext(); - auto *False = ConstantInt::getFalse(Context); +// Create a resume clone by cloning the body of the original function, setting +// new entry block and replacing coro.suspend an appropriate value to force +// resume or cleanup pass for every suspend point. +static Function *createClone(Function &F, const Twine &Suffix, + coro::Shape &Shape, CoroCloner::Kind FKind) { + CoroCloner Cloner(F, Suffix, Shape, FKind); + Cloner.create(); + return Cloner.getFunction(); +} - for (CoroEndInst *CE : Shape.CoroEnds) { - CE->replaceAllUsesWith(False); - CE->eraseFromParent(); +/// Remove calls to llvm.coro.end in the original function. +static void removeCoroEnds(coro::Shape &Shape, CallGraph *CG) { + for (auto End : Shape.CoroEnds) { + replaceCoroEnd(End, Shape, Shape.FramePtr, /*in resume*/ false, CG); } } @@ -376,8 +685,12 @@ static void replaceFrameSize(coro::Shape &Shape) { // i8* bitcast([2 x void(%f.frame*)*] * @f.resumers to i8*)) // // Assumes that all the functions have the same signature. -static void setCoroInfo(Function &F, CoroBeginInst *CoroBegin, - std::initializer_list<Function *> Fns) { +static void setCoroInfo(Function &F, coro::Shape &Shape, + ArrayRef<Function *> Fns) { + // This only works under the switch-lowering ABI because coro elision + // only works on the switch-lowering ABI. + assert(Shape.ABI == coro::ABI::Switch); + SmallVector<Constant *, 4> Args(Fns.begin(), Fns.end()); assert(!Args.empty()); Function *Part = *Fns.begin(); @@ -392,29 +705,31 @@ static void setCoroInfo(Function &F, CoroBeginInst *CoroBegin, // Update coro.begin instruction to refer to this constant. LLVMContext &C = F.getContext(); auto *BC = ConstantExpr::getPointerCast(GV, Type::getInt8PtrTy(C)); - CoroBegin->getId()->setInfo(BC); + Shape.getSwitchCoroId()->setInfo(BC); } // Store addresses of Resume/Destroy/Cleanup functions in the coroutine frame. static void updateCoroFrame(coro::Shape &Shape, Function *ResumeFn, Function *DestroyFn, Function *CleanupFn) { + assert(Shape.ABI == coro::ABI::Switch); + IRBuilder<> Builder(Shape.FramePtr->getNextNode()); - auto *ResumeAddr = Builder.CreateConstInBoundsGEP2_32( - Shape.FrameTy, Shape.FramePtr, 0, coro::Shape::ResumeField, + auto *ResumeAddr = Builder.CreateStructGEP( + Shape.FrameTy, Shape.FramePtr, coro::Shape::SwitchFieldIndex::Resume, "resume.addr"); Builder.CreateStore(ResumeFn, ResumeAddr); Value *DestroyOrCleanupFn = DestroyFn; - CoroIdInst *CoroId = Shape.CoroBegin->getId(); + CoroIdInst *CoroId = Shape.getSwitchCoroId(); if (CoroAllocInst *CA = CoroId->getCoroAlloc()) { // If there is a CoroAlloc and it returns false (meaning we elide the // allocation, use CleanupFn instead of DestroyFn). DestroyOrCleanupFn = Builder.CreateSelect(CA, DestroyFn, CleanupFn); } - auto *DestroyAddr = Builder.CreateConstInBoundsGEP2_32( - Shape.FrameTy, Shape.FramePtr, 0, coro::Shape::DestroyField, + auto *DestroyAddr = Builder.CreateStructGEP( + Shape.FrameTy, Shape.FramePtr, coro::Shape::SwitchFieldIndex::Destroy, "destroy.addr"); Builder.CreateStore(DestroyOrCleanupFn, DestroyAddr); } @@ -521,21 +836,34 @@ static void addMustTailToCoroResumes(Function &F) { // Coroutine has no suspend points. Remove heap allocation for the coroutine // frame if possible. -static void handleNoSuspendCoroutine(CoroBeginInst *CoroBegin, Type *FrameTy) { +static void handleNoSuspendCoroutine(coro::Shape &Shape) { + auto *CoroBegin = Shape.CoroBegin; auto *CoroId = CoroBegin->getId(); auto *AllocInst = CoroId->getCoroAlloc(); - coro::replaceCoroFree(CoroId, /*Elide=*/AllocInst != nullptr); - if (AllocInst) { - IRBuilder<> Builder(AllocInst); - // FIXME: Need to handle overaligned members. - auto *Frame = Builder.CreateAlloca(FrameTy); - auto *VFrame = Builder.CreateBitCast(Frame, Builder.getInt8PtrTy()); - AllocInst->replaceAllUsesWith(Builder.getFalse()); - AllocInst->eraseFromParent(); - CoroBegin->replaceAllUsesWith(VFrame); - } else { - CoroBegin->replaceAllUsesWith(CoroBegin->getMem()); + switch (Shape.ABI) { + case coro::ABI::Switch: { + auto SwitchId = cast<CoroIdInst>(CoroId); + coro::replaceCoroFree(SwitchId, /*Elide=*/AllocInst != nullptr); + if (AllocInst) { + IRBuilder<> Builder(AllocInst); + // FIXME: Need to handle overaligned members. + auto *Frame = Builder.CreateAlloca(Shape.FrameTy); + auto *VFrame = Builder.CreateBitCast(Frame, Builder.getInt8PtrTy()); + AllocInst->replaceAllUsesWith(Builder.getFalse()); + AllocInst->eraseFromParent(); + CoroBegin->replaceAllUsesWith(VFrame); + } else { + CoroBegin->replaceAllUsesWith(CoroBegin->getMem()); + } + break; + } + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + CoroBegin->replaceAllUsesWith(UndefValue::get(CoroBegin->getType())); + break; } + CoroBegin->eraseFromParent(); } @@ -603,12 +931,16 @@ static bool simplifySuspendPoint(CoroSuspendInst *Suspend, // Remove suspend points that are simplified. static void simplifySuspendPoints(coro::Shape &Shape) { + // Currently, the only simplification we do is switch-lowering-specific. + if (Shape.ABI != coro::ABI::Switch) + return; + auto &S = Shape.CoroSuspends; size_t I = 0, N = S.size(); if (N == 0) return; while (true) { - if (simplifySuspendPoint(S[I], Shape.CoroBegin)) { + if (simplifySuspendPoint(cast<CoroSuspendInst>(S[I]), Shape.CoroBegin)) { if (--N == I) break; std::swap(S[I], S[N]); @@ -694,6 +1026,180 @@ static void relocateInstructionBefore(CoroBeginInst *CoroBegin, Function &F) { } } +static void splitSwitchCoroutine(Function &F, coro::Shape &Shape, + SmallVectorImpl<Function *> &Clones) { + assert(Shape.ABI == coro::ABI::Switch); + + createResumeEntryBlock(F, Shape); + auto ResumeClone = createClone(F, ".resume", Shape, + CoroCloner::Kind::SwitchResume); + auto DestroyClone = createClone(F, ".destroy", Shape, + CoroCloner::Kind::SwitchUnwind); + auto CleanupClone = createClone(F, ".cleanup", Shape, + CoroCloner::Kind::SwitchCleanup); + + postSplitCleanup(*ResumeClone); + postSplitCleanup(*DestroyClone); + postSplitCleanup(*CleanupClone); + + addMustTailToCoroResumes(*ResumeClone); + + // Store addresses resume/destroy/cleanup functions in the coroutine frame. + updateCoroFrame(Shape, ResumeClone, DestroyClone, CleanupClone); + + assert(Clones.empty()); + Clones.push_back(ResumeClone); + Clones.push_back(DestroyClone); + Clones.push_back(CleanupClone); + + // Create a constant array referring to resume/destroy/clone functions pointed + // by the last argument of @llvm.coro.info, so that CoroElide pass can + // determined correct function to call. + setCoroInfo(F, Shape, Clones); +} + +static void splitRetconCoroutine(Function &F, coro::Shape &Shape, + SmallVectorImpl<Function *> &Clones) { + assert(Shape.ABI == coro::ABI::Retcon || + Shape.ABI == coro::ABI::RetconOnce); + assert(Clones.empty()); + + // Reset various things that the optimizer might have decided it + // "knows" about the coroutine function due to not seeing a return. + F.removeFnAttr(Attribute::NoReturn); + F.removeAttribute(AttributeList::ReturnIndex, Attribute::NoAlias); + F.removeAttribute(AttributeList::ReturnIndex, Attribute::NonNull); + + // Allocate the frame. + auto *Id = cast<AnyCoroIdRetconInst>(Shape.CoroBegin->getId()); + Value *RawFramePtr; + if (Shape.RetconLowering.IsFrameInlineInStorage) { + RawFramePtr = Id->getStorage(); + } else { + IRBuilder<> Builder(Id); + + // Determine the size of the frame. + const DataLayout &DL = F.getParent()->getDataLayout(); + auto Size = DL.getTypeAllocSize(Shape.FrameTy); + + // Allocate. We don't need to update the call graph node because we're + // going to recompute it from scratch after splitting. + RawFramePtr = Shape.emitAlloc(Builder, Builder.getInt64(Size), nullptr); + RawFramePtr = + Builder.CreateBitCast(RawFramePtr, Shape.CoroBegin->getType()); + + // Stash the allocated frame pointer in the continuation storage. + auto Dest = Builder.CreateBitCast(Id->getStorage(), + RawFramePtr->getType()->getPointerTo()); + Builder.CreateStore(RawFramePtr, Dest); + } + + // Map all uses of llvm.coro.begin to the allocated frame pointer. + { + // Make sure we don't invalidate Shape.FramePtr. + TrackingVH<Instruction> Handle(Shape.FramePtr); + Shape.CoroBegin->replaceAllUsesWith(RawFramePtr); + Shape.FramePtr = Handle.getValPtr(); + } + + // Create a unique return block. + BasicBlock *ReturnBB = nullptr; + SmallVector<PHINode *, 4> ReturnPHIs; + + // Create all the functions in order after the main function. + auto NextF = std::next(F.getIterator()); + + // Create a continuation function for each of the suspend points. + Clones.reserve(Shape.CoroSuspends.size()); + for (size_t i = 0, e = Shape.CoroSuspends.size(); i != e; ++i) { + auto Suspend = cast<CoroSuspendRetconInst>(Shape.CoroSuspends[i]); + + // Create the clone declaration. + auto Continuation = + createCloneDeclaration(F, Shape, ".resume." + Twine(i), NextF); + Clones.push_back(Continuation); + + // Insert a branch to the unified return block immediately before + // the suspend point. + auto SuspendBB = Suspend->getParent(); + auto NewSuspendBB = SuspendBB->splitBasicBlock(Suspend); + auto Branch = cast<BranchInst>(SuspendBB->getTerminator()); + + // Create the unified return block. + if (!ReturnBB) { + // Place it before the first suspend. + ReturnBB = BasicBlock::Create(F.getContext(), "coro.return", &F, + NewSuspendBB); + Shape.RetconLowering.ReturnBlock = ReturnBB; + + IRBuilder<> Builder(ReturnBB); + + // Create PHIs for all the return values. + assert(ReturnPHIs.empty()); + + // First, the continuation. + ReturnPHIs.push_back(Builder.CreatePHI(Continuation->getType(), + Shape.CoroSuspends.size())); + + // Next, all the directly-yielded values. + for (auto ResultTy : Shape.getRetconResultTypes()) + ReturnPHIs.push_back(Builder.CreatePHI(ResultTy, + Shape.CoroSuspends.size())); + + // Build the return value. + auto RetTy = F.getReturnType(); + + // Cast the continuation value if necessary. + // We can't rely on the types matching up because that type would + // have to be infinite. + auto CastedContinuationTy = + (ReturnPHIs.size() == 1 ? RetTy : RetTy->getStructElementType(0)); + auto *CastedContinuation = + Builder.CreateBitCast(ReturnPHIs[0], CastedContinuationTy); + + Value *RetV; + if (ReturnPHIs.size() == 1) { + RetV = CastedContinuation; + } else { + RetV = UndefValue::get(RetTy); + RetV = Builder.CreateInsertValue(RetV, CastedContinuation, 0); + for (size_t I = 1, E = ReturnPHIs.size(); I != E; ++I) + RetV = Builder.CreateInsertValue(RetV, ReturnPHIs[I], I); + } + + Builder.CreateRet(RetV); + } + + // Branch to the return block. + Branch->setSuccessor(0, ReturnBB); + ReturnPHIs[0]->addIncoming(Continuation, SuspendBB); + size_t NextPHIIndex = 1; + for (auto &VUse : Suspend->value_operands()) + ReturnPHIs[NextPHIIndex++]->addIncoming(&*VUse, SuspendBB); + assert(NextPHIIndex == ReturnPHIs.size()); + } + + assert(Clones.size() == Shape.CoroSuspends.size()); + for (size_t i = 0, e = Shape.CoroSuspends.size(); i != e; ++i) { + auto Suspend = Shape.CoroSuspends[i]; + auto Clone = Clones[i]; + + CoroCloner(F, "resume." + Twine(i), Shape, Clone, Suspend).create(); + } +} + +static void splitCoroutine(Function &F, coro::Shape &Shape, + SmallVectorImpl<Function *> &Clones) { + switch (Shape.ABI) { + case coro::ABI::Switch: + return splitSwitchCoroutine(F, Shape, Clones); + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + return splitRetconCoroutine(F, Shape, Clones); + } + llvm_unreachable("bad ABI kind"); +} + static void splitCoroutine(Function &F, CallGraph &CG, CallGraphSCC &SCC) { coro::Shape Shape(F); if (!Shape.CoroBegin) @@ -704,41 +1210,21 @@ static void splitCoroutine(Function &F, CallGraph &CG, CallGraphSCC &SCC) { buildCoroutineFrame(F, Shape); replaceFrameSize(Shape); + SmallVector<Function*, 4> Clones; + // If there are no suspend points, no split required, just remove // the allocation and deallocation blocks, they are not needed. if (Shape.CoroSuspends.empty()) { - handleNoSuspendCoroutine(Shape.CoroBegin, Shape.FrameTy); - removeCoroEnds(Shape); - postSplitCleanup(F); - coro::updateCallGraph(F, {}, CG, SCC); - return; + handleNoSuspendCoroutine(Shape); + } else { + splitCoroutine(F, Shape, Clones); } - auto *ResumeEntry = createResumeEntryBlock(F, Shape); - auto ResumeClone = createClone(F, ".resume", Shape, ResumeEntry, 0); - auto DestroyClone = createClone(F, ".destroy", Shape, ResumeEntry, 1); - auto CleanupClone = createClone(F, ".cleanup", Shape, ResumeEntry, 2); - - // We no longer need coro.end in F. - removeCoroEnds(Shape); - + removeCoroEnds(Shape, &CG); postSplitCleanup(F); - postSplitCleanup(*ResumeClone); - postSplitCleanup(*DestroyClone); - postSplitCleanup(*CleanupClone); - - addMustTailToCoroResumes(*ResumeClone); - - // Store addresses resume/destroy/cleanup functions in the coroutine frame. - updateCoroFrame(Shape, ResumeClone, DestroyClone, CleanupClone); - - // Create a constant array referring to resume/destroy/clone functions pointed - // by the last argument of @llvm.coro.info, so that CoroElide pass can - // determined correct function to call. - setCoroInfo(F, Shape.CoroBegin, {ResumeClone, DestroyClone, CleanupClone}); // Update call graph and add the functions we created to the SCC. - coro::updateCallGraph(F, {ResumeClone, DestroyClone, CleanupClone}, CG, SCC); + coro::updateCallGraph(F, Clones, CG, SCC); } // When we see the coroutine the first time, we insert an indirect call to a @@ -794,6 +1280,78 @@ static void createDevirtTriggerFunc(CallGraph &CG, CallGraphSCC &SCC) { SCC.initialize(Nodes); } +/// Replace a call to llvm.coro.prepare.retcon. +static void replacePrepare(CallInst *Prepare, CallGraph &CG) { + auto CastFn = Prepare->getArgOperand(0); // as an i8* + auto Fn = CastFn->stripPointerCasts(); // as its original type + + // Find call graph nodes for the preparation. + CallGraphNode *PrepareUserNode = nullptr, *FnNode = nullptr; + if (auto ConcreteFn = dyn_cast<Function>(Fn)) { + PrepareUserNode = CG[Prepare->getFunction()]; + FnNode = CG[ConcreteFn]; + } + + // Attempt to peephole this pattern: + // %0 = bitcast [[TYPE]] @some_function to i8* + // %1 = call @llvm.coro.prepare.retcon(i8* %0) + // %2 = bitcast %1 to [[TYPE]] + // ==> + // %2 = @some_function + for (auto UI = Prepare->use_begin(), UE = Prepare->use_end(); + UI != UE; ) { + // Look for bitcasts back to the original function type. + auto *Cast = dyn_cast<BitCastInst>((UI++)->getUser()); + if (!Cast || Cast->getType() != Fn->getType()) continue; + + // Check whether the replacement will introduce new direct calls. + // If so, we'll need to update the call graph. + if (PrepareUserNode) { + for (auto &Use : Cast->uses()) { + auto CS = CallSite(Use.getUser()); + if (!CS || !CS.isCallee(&Use)) continue; + PrepareUserNode->removeCallEdgeFor(CS); + PrepareUserNode->addCalledFunction(CS, FnNode); + } + } + + // Replace and remove the cast. + Cast->replaceAllUsesWith(Fn); + Cast->eraseFromParent(); + } + + // Replace any remaining uses with the function as an i8*. + // This can never directly be a callee, so we don't need to update CG. + Prepare->replaceAllUsesWith(CastFn); + Prepare->eraseFromParent(); + + // Kill dead bitcasts. + while (auto *Cast = dyn_cast<BitCastInst>(CastFn)) { + if (!Cast->use_empty()) break; + CastFn = Cast->getOperand(0); + Cast->eraseFromParent(); + } +} + +/// Remove calls to llvm.coro.prepare.retcon, a barrier meant to prevent +/// IPO from operating on calls to a retcon coroutine before it's been +/// split. This is only safe to do after we've split all retcon +/// coroutines in the module. We can do that this in this pass because +/// this pass does promise to split all retcon coroutines (as opposed to +/// switch coroutines, which are lowered in multiple stages). +static bool replaceAllPrepares(Function *PrepareFn, CallGraph &CG) { + bool Changed = false; + for (auto PI = PrepareFn->use_begin(), PE = PrepareFn->use_end(); + PI != PE; ) { + // Intrinsics can only be used in calls. + auto *Prepare = cast<CallInst>((PI++)->getUser()); + replacePrepare(Prepare, CG); + Changed = true; + } + + return Changed; +} + //===----------------------------------------------------------------------===// // Top Level Driver //===----------------------------------------------------------------------===// @@ -812,7 +1370,9 @@ struct CoroSplit : public CallGraphSCCPass { // A coroutine is identified by the presence of coro.begin intrinsic, if // we don't have any, this pass has nothing to do. bool doInitialization(CallGraph &CG) override { - Run = coro::declaresIntrinsics(CG.getModule(), {"llvm.coro.begin"}); + Run = coro::declaresIntrinsics(CG.getModule(), + {"llvm.coro.begin", + "llvm.coro.prepare.retcon"}); return CallGraphSCCPass::doInitialization(CG); } @@ -820,6 +1380,12 @@ struct CoroSplit : public CallGraphSCCPass { if (!Run) return false; + // Check for uses of llvm.coro.prepare.retcon. + auto PrepareFn = + SCC.getCallGraph().getModule().getFunction("llvm.coro.prepare.retcon"); + if (PrepareFn && PrepareFn->use_empty()) + PrepareFn = nullptr; + // Find coroutines for processing. SmallVector<Function *, 4> Coroutines; for (CallGraphNode *CGN : SCC) @@ -827,12 +1393,17 @@ struct CoroSplit : public CallGraphSCCPass { if (F->hasFnAttribute(CORO_PRESPLIT_ATTR)) Coroutines.push_back(F); - if (Coroutines.empty()) + if (Coroutines.empty() && !PrepareFn) return false; CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph(); + + if (Coroutines.empty()) + return replaceAllPrepares(PrepareFn, CG); + createDevirtTriggerFunc(CG, SCC); + // Split all the coroutines. for (Function *F : Coroutines) { Attribute Attr = F->getFnAttribute(CORO_PRESPLIT_ATTR); StringRef Value = Attr.getValueAsString(); @@ -845,6 +1416,10 @@ struct CoroSplit : public CallGraphSCCPass { F->removeFnAttr(CORO_PRESPLIT_ATTR); splitCoroutine(*F, CG, SCC); } + + if (PrepareFn) + replaceAllPrepares(PrepareFn, CG); + return true; } diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp index 10411c1bd65da..95cdf25872bd5 100644 --- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -123,11 +123,25 @@ Value *coro::LowererBase::makeSubFnCall(Value *Arg, int Index, static bool isCoroutineIntrinsicName(StringRef Name) { // NOTE: Must be sorted! static const char *const CoroIntrinsics[] = { - "llvm.coro.alloc", "llvm.coro.begin", "llvm.coro.destroy", - "llvm.coro.done", "llvm.coro.end", "llvm.coro.frame", - "llvm.coro.free", "llvm.coro.id", "llvm.coro.param", - "llvm.coro.promise", "llvm.coro.resume", "llvm.coro.save", - "llvm.coro.size", "llvm.coro.subfn.addr", "llvm.coro.suspend", + "llvm.coro.alloc", + "llvm.coro.begin", + "llvm.coro.destroy", + "llvm.coro.done", + "llvm.coro.end", + "llvm.coro.frame", + "llvm.coro.free", + "llvm.coro.id", + "llvm.coro.id.retcon", + "llvm.coro.id.retcon.once", + "llvm.coro.param", + "llvm.coro.prepare.retcon", + "llvm.coro.promise", + "llvm.coro.resume", + "llvm.coro.save", + "llvm.coro.size", + "llvm.coro.subfn.addr", + "llvm.coro.suspend", + "llvm.coro.suspend.retcon", }; return Intrinsic::lookupLLVMIntrinsicByName(CoroIntrinsics, Name) != -1; } @@ -216,9 +230,6 @@ static void clear(coro::Shape &Shape) { Shape.FrameTy = nullptr; Shape.FramePtr = nullptr; Shape.AllocaSpillBlock = nullptr; - Shape.ResumeSwitch = nullptr; - Shape.PromiseAlloca = nullptr; - Shape.HasFinalSuspend = false; } static CoroSaveInst *createCoroSave(CoroBeginInst *CoroBegin, @@ -234,6 +245,7 @@ static CoroSaveInst *createCoroSave(CoroBeginInst *CoroBegin, // Collect "interesting" coroutine intrinsics. void coro::Shape::buildFrom(Function &F) { + bool HasFinalSuspend = false; size_t FinalSuspendIndex = 0; clear(*this); SmallVector<CoroFrameInst *, 8> CoroFrames; @@ -256,9 +268,15 @@ void coro::Shape::buildFrom(Function &F) { if (II->use_empty()) UnusedCoroSaves.push_back(cast<CoroSaveInst>(II)); break; - case Intrinsic::coro_suspend: - CoroSuspends.push_back(cast<CoroSuspendInst>(II)); - if (CoroSuspends.back()->isFinal()) { + case Intrinsic::coro_suspend_retcon: { + auto Suspend = cast<CoroSuspendRetconInst>(II); + CoroSuspends.push_back(Suspend); + break; + } + case Intrinsic::coro_suspend: { + auto Suspend = cast<CoroSuspendInst>(II); + CoroSuspends.push_back(Suspend); + if (Suspend->isFinal()) { if (HasFinalSuspend) report_fatal_error( "Only one suspend point can be marked as final"); @@ -266,18 +284,23 @@ void coro::Shape::buildFrom(Function &F) { FinalSuspendIndex = CoroSuspends.size() - 1; } break; + } case Intrinsic::coro_begin: { auto CB = cast<CoroBeginInst>(II); - if (CB->getId()->getInfo().isPreSplit()) { - if (CoroBegin) - report_fatal_error( + + // Ignore coro id's that aren't pre-split. + auto Id = dyn_cast<CoroIdInst>(CB->getId()); + if (Id && !Id->getInfo().isPreSplit()) + break; + + if (CoroBegin) + report_fatal_error( "coroutine should have exactly one defining @llvm.coro.begin"); - CB->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull); - CB->addAttribute(AttributeList::ReturnIndex, Attribute::NoAlias); - CB->removeAttribute(AttributeList::FunctionIndex, - Attribute::NoDuplicate); - CoroBegin = CB; - } + CB->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull); + CB->addAttribute(AttributeList::ReturnIndex, Attribute::NoAlias); + CB->removeAttribute(AttributeList::FunctionIndex, + Attribute::NoDuplicate); + CoroBegin = CB; break; } case Intrinsic::coro_end: @@ -309,7 +332,7 @@ void coro::Shape::buildFrom(Function &F) { // Replace all coro.suspend with undef and remove related coro.saves if // present. - for (CoroSuspendInst *CS : CoroSuspends) { + for (AnyCoroSuspendInst *CS : CoroSuspends) { CS->replaceAllUsesWith(UndefValue::get(CS->getType())); CS->eraseFromParent(); if (auto *CoroSave = CS->getCoroSave()) @@ -323,19 +346,87 @@ void coro::Shape::buildFrom(Function &F) { return; } + auto Id = CoroBegin->getId(); + switch (auto IdIntrinsic = Id->getIntrinsicID()) { + case Intrinsic::coro_id: { + auto SwitchId = cast<CoroIdInst>(Id); + this->ABI = coro::ABI::Switch; + this->SwitchLowering.HasFinalSuspend = HasFinalSuspend; + this->SwitchLowering.ResumeSwitch = nullptr; + this->SwitchLowering.PromiseAlloca = SwitchId->getPromise(); + this->SwitchLowering.ResumeEntryBlock = nullptr; + + for (auto AnySuspend : CoroSuspends) { + auto Suspend = dyn_cast<CoroSuspendInst>(AnySuspend); + if (!Suspend) { + AnySuspend->dump(); + report_fatal_error("coro.id must be paired with coro.suspend"); + } + + if (!Suspend->getCoroSave()) + createCoroSave(CoroBegin, Suspend); + } + break; + } + + case Intrinsic::coro_id_retcon: + case Intrinsic::coro_id_retcon_once: { + auto ContinuationId = cast<AnyCoroIdRetconInst>(Id); + ContinuationId->checkWellFormed(); + this->ABI = (IdIntrinsic == Intrinsic::coro_id_retcon + ? coro::ABI::Retcon + : coro::ABI::RetconOnce); + auto Prototype = ContinuationId->getPrototype(); + this->RetconLowering.ResumePrototype = Prototype; + this->RetconLowering.Alloc = ContinuationId->getAllocFunction(); + this->RetconLowering.Dealloc = ContinuationId->getDeallocFunction(); + this->RetconLowering.ReturnBlock = nullptr; + this->RetconLowering.IsFrameInlineInStorage = false; + + // Determine the result value types, and make sure they match up with + // the values passed to the suspends. + auto ResultTys = getRetconResultTypes(); + + for (auto AnySuspend : CoroSuspends) { + auto Suspend = dyn_cast<CoroSuspendRetconInst>(AnySuspend); + if (!Suspend) { + AnySuspend->dump(); + report_fatal_error("coro.id.retcon.* must be paired with " + "coro.suspend.retcon"); + } + + auto SI = Suspend->value_begin(), SE = Suspend->value_end(); + auto RI = ResultTys.begin(), RE = ResultTys.end(); + for (; SI != SE && RI != RE; ++SI, ++RI) { + if ((*SI)->getType() != *RI) { + Suspend->dump(); + Prototype->getFunctionType()->dump(); + report_fatal_error("argument to coro.suspend.retcon does not " + "match corresponding prototype function result"); + } + } + if (SI != SE || RI != RE) { + Suspend->dump(); + Prototype->getFunctionType()->dump(); + report_fatal_error("wrong number of arguments to coro.suspend.retcon"); + } + } + break; + } + + default: + llvm_unreachable("coro.begin is not dependent on a coro.id call"); + } + // The coro.free intrinsic is always lowered to the result of coro.begin. for (CoroFrameInst *CF : CoroFrames) { CF->replaceAllUsesWith(CoroBegin); CF->eraseFromParent(); } - // Canonicalize coro.suspend by inserting a coro.save if needed. - for (CoroSuspendInst *CS : CoroSuspends) - if (!CS->getCoroSave()) - createCoroSave(CoroBegin, CS); - // Move final suspend to be the last element in the CoroSuspends vector. - if (HasFinalSuspend && + if (ABI == coro::ABI::Switch && + SwitchLowering.HasFinalSuspend && FinalSuspendIndex != CoroSuspends.size() - 1) std::swap(CoroSuspends[FinalSuspendIndex], CoroSuspends.back()); @@ -343,3 +434,150 @@ void coro::Shape::buildFrom(Function &F) { for (CoroSaveInst *CoroSave : UnusedCoroSaves) CoroSave->eraseFromParent(); } + +static void propagateCallAttrsFromCallee(CallInst *Call, Function *Callee) { + Call->setCallingConv(Callee->getCallingConv()); + // TODO: attributes? +} + +static void addCallToCallGraph(CallGraph *CG, CallInst *Call, Function *Callee){ + if (CG) + (*CG)[Call->getFunction()]->addCalledFunction(Call, (*CG)[Callee]); +} + +Value *coro::Shape::emitAlloc(IRBuilder<> &Builder, Value *Size, + CallGraph *CG) const { + switch (ABI) { + case coro::ABI::Switch: + llvm_unreachable("can't allocate memory in coro switch-lowering"); + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: { + auto Alloc = RetconLowering.Alloc; + Size = Builder.CreateIntCast(Size, + Alloc->getFunctionType()->getParamType(0), + /*is signed*/ false); + auto *Call = Builder.CreateCall(Alloc, Size); + propagateCallAttrsFromCallee(Call, Alloc); + addCallToCallGraph(CG, Call, Alloc); + return Call; + } + } +} + +void coro::Shape::emitDealloc(IRBuilder<> &Builder, Value *Ptr, + CallGraph *CG) const { + switch (ABI) { + case coro::ABI::Switch: + llvm_unreachable("can't allocate memory in coro switch-lowering"); + + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: { + auto Dealloc = RetconLowering.Dealloc; + Ptr = Builder.CreateBitCast(Ptr, + Dealloc->getFunctionType()->getParamType(0)); + auto *Call = Builder.CreateCall(Dealloc, Ptr); + propagateCallAttrsFromCallee(Call, Dealloc); + addCallToCallGraph(CG, Call, Dealloc); + return; + } + } +} + +LLVM_ATTRIBUTE_NORETURN +static void fail(const Instruction *I, const char *Reason, Value *V) { + I->dump(); + if (V) { + errs() << " Value: "; + V->printAsOperand(llvm::errs()); + errs() << '\n'; + } + report_fatal_error(Reason); +} + +/// Check that the given value is a well-formed prototype for the +/// llvm.coro.id.retcon.* intrinsics. +static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) { + auto F = dyn_cast<Function>(V->stripPointerCasts()); + if (!F) + fail(I, "llvm.coro.retcon.* prototype not a Function", V); + + auto FT = F->getFunctionType(); + + if (isa<CoroIdRetconInst>(I)) { + bool ResultOkay; + if (FT->getReturnType()->isPointerTy()) { + ResultOkay = true; + } else if (auto SRetTy = dyn_cast<StructType>(FT->getReturnType())) { + ResultOkay = (!SRetTy->isOpaque() && + SRetTy->getNumElements() > 0 && + SRetTy->getElementType(0)->isPointerTy()); + } else { + ResultOkay = false; + } + if (!ResultOkay) + fail(I, "llvm.coro.retcon prototype must return pointer as first result", + F); + + if (FT->getReturnType() != + I->getFunction()->getFunctionType()->getReturnType()) + fail(I, "llvm.coro.retcon.* prototype return type must be same as" + "current function return type", F); + } else { + // No meaningful validation to do here for llvm.coro.id.unique.once. + } + + if (FT->getNumParams() != 2) + fail(I, "llvm.coro.retcon.* prototype must take exactly two parameters", F); + if (!FT->getParamType(0)->isPointerTy()) + fail(I, "llvm.coro.retcon.* prototype must take pointer as 1st param", F); + if (!FT->getParamType(1)->isIntegerTy()) // an i1, but not for abi purposes + fail(I, "llvm.coro.retcon.* prototype must take integer as 2nd param", F); +} + +/// Check that the given value is a well-formed allocator. +static void checkWFAlloc(const Instruction *I, Value *V) { + auto F = dyn_cast<Function>(V->stripPointerCasts()); + if (!F) + fail(I, "llvm.coro.* allocator not a Function", V); + + auto FT = F->getFunctionType(); + if (!FT->getReturnType()->isPointerTy()) + fail(I, "llvm.coro.* allocator must return a pointer", F); + + if (FT->getNumParams() != 1 || + !FT->getParamType(0)->isIntegerTy()) + fail(I, "llvm.coro.* allocator must take integer as only param", F); +} + +/// Check that the given value is a well-formed deallocator. +static void checkWFDealloc(const Instruction *I, Value *V) { + auto F = dyn_cast<Function>(V->stripPointerCasts()); + if (!F) + fail(I, "llvm.coro.* deallocator not a Function", V); + + auto FT = F->getFunctionType(); + if (!FT->getReturnType()->isVoidTy()) + fail(I, "llvm.coro.* deallocator must return void", F); + + if (FT->getNumParams() != 1 || + !FT->getParamType(0)->isPointerTy()) + fail(I, "llvm.coro.* deallocator must take pointer as only param", F); +} + +static void checkConstantInt(const Instruction *I, Value *V, + const char *Reason) { + if (!isa<ConstantInt>(V)) { + fail(I, Reason, V); + } +} + +void AnyCoroIdRetconInst::checkWellFormed() const { + checkConstantInt(this, getArgOperand(SizeArg), + "size argument to coro.id.retcon.* must be constant"); + checkConstantInt(this, getArgOperand(AlignArg), + "alignment argument to coro.id.retcon.* must be constant"); + checkWFRetconPrototype(this, getArgOperand(PrototypeArg)); + checkWFAlloc(this, getArgOperand(AllocArg)); + checkWFDealloc(this, getArgOperand(DeallocArg)); +} diff --git a/llvm/test/Transforms/Coroutines/coro-debug.ll b/llvm/test/Transforms/Coroutines/coro-debug.ll index 4da545499f94d..a58e97f8ebb58 100644 --- a/llvm/test/Transforms/Coroutines/coro-debug.ll +++ b/llvm/test/Transforms/Coroutines/coro-debug.ll @@ -127,9 +127,9 @@ attributes #7 = { noduplicate } !24 = !DILocation(line: 62, column: 3, scope: !6) ; CHECK: define i8* @f(i32 %x) #0 !dbg ![[ORIG:[0-9]+]] -; CHECK: define internal fastcc void @f.resume(%f.Frame* %FramePtr) #0 !dbg ![[RESUME:[0-9]+]] -; CHECK: define internal fastcc void @f.destroy(%f.Frame* %FramePtr) #0 !dbg ![[DESTROY:[0-9]+]] -; CHECK: define internal fastcc void @f.cleanup(%f.Frame* %FramePtr) #0 !dbg ![[CLEANUP:[0-9]+]] +; CHECK: define internal fastcc void @f.resume(%f.Frame* noalias nonnull %FramePtr) #0 !dbg ![[RESUME:[0-9]+]] +; CHECK: define internal fastcc void @f.destroy(%f.Frame* noalias nonnull %FramePtr) #0 !dbg ![[DESTROY:[0-9]+]] +; CHECK: define internal fastcc void @f.cleanup(%f.Frame* noalias nonnull %FramePtr) #0 !dbg ![[CLEANUP:[0-9]+]] ; CHECK: ![[ORIG]] = distinct !DISubprogram(name: "f", linkageName: "flink" ; CHECK: !DILocalVariable(name: "x", arg: 1, scope: ![[ORIG]] diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll new file mode 100644 index 0000000000000..8f9cdf1774722 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll @@ -0,0 +1,116 @@ +; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.12.0" + +define {i8*, i32} @f(i8* %buffer, i32* %array) { +entry: + %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + %load = load i32, i32* %array + %load.pos = icmp sgt i32 %load, 0 + br i1 %load.pos, label %pos, label %neg + +pos: + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon(i32 %load) + br i1 %unwind0, label %cleanup, label %pos.cont + +pos.cont: + store i32 0, i32* %array, align 4 + br label %cleanup + +neg: + %unwind1 = call i1 (...) @llvm.coro.suspend.retcon(i32 0) + br i1 %unwind1, label %cleanup, label %neg.cont + +neg.cont: + store i32 10, i32* %array, align 4 + br label %cleanup + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define { i8*, i32 } @f(i8* %buffer, i32* %array) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32** +; CHECK-NEXT: store i32* %array, i32** [[T0]], align 8 +; CHECK-NEXT: %load = load i32, i32* %array, align 4 +; CHECK-NEXT: %load.pos = icmp sgt i32 %load, 0 +; CHECK-NEXT: [[CONT:%.*]] = select i1 %load.pos, void (i8*, i8)* @f.resume.0, void (i8*, i8)* @f.resume.1 +; CHECK-NEXT: [[VAL:%.*]] = select i1 %load.pos, i32 %load, i32 0 +; CHECK-NEXT: [[CONT_CAST:%.*]] = bitcast void (i8*, i8)* [[CONT]] to i8* +; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32 } undef, i8* [[CONT_CAST]], 0 +; CHECK-NEXT: [[T1:%.*]] = insertvalue { i8*, i32 } [[T0]], i32 [[VAL]], 1 +; CHECK-NEXT: ret { i8*, i32 } [[T1]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 +; CHECK-NEXT: br i1 [[T0]], +; CHECK: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32** +; CHECK-NEXT: [[RELOAD:%.*]] = load i32*, i32** [[T0]], align 8 +; CHECK-NEXT: store i32 0, i32* [[RELOAD]], align 4 +; CHECK-NEXT: br label +; CHECK: : +; CHECK-NEXT: ret void +; CHECK-NEXT: } + +; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull, i8 zeroext) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 +; CHECK-NEXT: br i1 [[T0]], +; CHECK: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32** +; CHECK-NEXT: [[RELOAD:%.*]] = load i32*, i32** [[T0]], align 8 +; CHECK-NEXT: store i32 10, i32* [[RELOAD]], align 4 +; CHECK-NEXT: br label +; CHECK: : +; CHECK-NEXT: ret void +; CHECK-NEXT: } + +define void @test(i32* %array) { +entry: + %0 = alloca [8 x i8], align 8 + %buffer = bitcast [8 x i8]* %0 to i8* + %prepare = call i8* @llvm.coro.prepare.retcon(i8* bitcast ({i8*, i32} (i8*, i32*)* @f to i8*)) + %f = bitcast i8* %prepare to {i8*, i32} (i8*, i32*)* + %result = call {i8*, i32} %f(i8* %buffer, i32* %array) + %value = extractvalue {i8*, i32} %result, 1 + call void @print(i32 %value) + %cont = extractvalue {i8*, i32} %result, 0 + %cont.cast = bitcast i8* %cont to void (i8*, i8)* + call void %cont.cast(i8* %buffer, i8 zeroext 0) + ret void +} + +; Unfortunately, we don't seem to fully optimize this right now due +; to some sort of phase-ordering thing. +; CHECK-LABEL: define void @test(i32* %array) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BUFFER:%.*]] = alloca i32*, align 8 +; CHECK-NEXT: [[BUFFER_CAST:%.*]] = bitcast i32** [[BUFFER]] to i8* +; CHECK-NEXT: store i32* %array, i32** [[BUFFER]], align 8 +; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* %array, align 4 +; CHECK-NEXT: [[LOAD_POS:%.*]] = icmp sgt i32 [[LOAD]], 0 +; CHECK-NEXT: [[CONT:%.*]] = select i1 [[LOAD_POS]], void (i8*, i8)* @f.resume.0, void (i8*, i8)* @f.resume.1 +; CHECK-NEXT: [[VAL:%.*]] = select i1 [[LOAD_POS]], i32 [[LOAD]], i32 0 +; CHECK-NEXT: call void @print(i32 [[VAL]]) +; CHECK-NEXT: call void [[CONT]](i8* nonnull [[BUFFER_CAST]], i8 zeroext 0) +; CHECK-NEXT: ret void + +declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare void @prototype(i8*, i8 zeroext) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare void @print(i32) + diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll new file mode 100644 index 0000000000000..5e7946ba477cd --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll @@ -0,0 +1,73 @@ +; RUN: opt < %s -coro-split -coro-cleanup -S | FileCheck %s +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.12.0" + +define {i8*, i32*} @f(i8* %buffer, i32* %ptr) "coroutine.presplit"="1" { +entry: + %temp = alloca i32, align 4 + %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + %oldvalue = load i32, i32* %ptr + store i32 %oldvalue, i32* %temp + %unwind = call i1 (...) @llvm.coro.suspend.retcon(i32* %temp) + br i1 %unwind, label %cleanup, label %cont + +cont: + %newvalue = load i32, i32* %temp + store i32 %newvalue, i32* %ptr + br label %cleanup + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define { i8*, i32* } @f(i8* %buffer, i32* %ptr) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ALLOC:%.*]] = call i8* @allocate(i32 16) +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i8** +; CHECK-NEXT: store i8* [[ALLOC]], i8** [[T0]] +; CHECK-NEXT: [[FRAME:%.*]] = bitcast i8* [[ALLOC]] to [[FRAME_T:%.*]]* +; CHECK-NEXT: %temp = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: [[SPILL:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 +; CHECK-NEXT: store i32* %ptr, i32** [[SPILL]] +; CHECK-NEXT: %oldvalue = load i32, i32* %ptr +; CHECK-NEXT: store i32 %oldvalue, i32* %temp +; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32* } { i8* bitcast (void (i8*, i8)* @f.resume.0 to i8*), i32* undef }, i32* %temp, 1 +; CHECK-NEXT: ret { i8*, i32* } [[T0]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** +; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] +; CHECK-NEXT: bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: [[T0:%.*]] = icmp ne i8 %1, 0 +; CHECK-NEXT: %temp = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: br i1 [[T0]], +; CHECK: : +; CHECK-NEXT: [[TEMP_SLOT:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: [[PTR_SLOT:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 +; CHECK-NEXT: [[PTR_RELOAD:%.*]] = load i32*, i32** [[PTR_SLOT]] +; CHECK-NEXT: %newvalue = load i32, i32* [[TEMP_SLOT]] +; CHECK-NEXT: store i32 %newvalue, i32* [[PTR_RELOAD]] +; CHECK-NEXT: br label +; CHECK: : +; CHECK-NEXT: [[T0:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: call fastcc void @deallocate(i8* [[T0]]) +; CHECK-NEXT: ret void +; CHECK-NEXT: } + +declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare void @prototype(i8*, i8 zeroext) + +declare noalias i8* @allocate(i32 %size) +declare fastcc void @deallocate(i8* %ptr) + +declare void @print(i32) + diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll new file mode 100644 index 0000000000000..56d0e3b5210ea --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll @@ -0,0 +1,102 @@ +; First example from Doc/Coroutines.rst (two block loop) converted to retcon +; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s + +define {i8*, i32} @f(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast ({i8*, i32} (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon(i32 %n.val) + br i1 %unwind0, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define { i8*, i32 } @f(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*, i8)* @f.resume.0 to i8*), i32 undef }, i32 %n, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal { i8*, i32 } @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 +; CHECK-NEXT: br i1 [[T0]], +; CHECK: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: %inc = add i32 [[T1]], 1 +; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*, i8)* @f.resume.0 to i8*), i32 undef }, i32 %inc, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK: : +; CHECK-NEXT: ret { i8*, i32 } { i8* null, i32 undef } +; CHECK-NEXT: } + +define i32 @main() { +entry: + %0 = alloca [8 x i8], align 4 + %buffer = bitcast [8 x i8]* %0 to i8* + %prepare = call i8* @llvm.coro.prepare.retcon(i8* bitcast ({i8*, i32} (i8*, i32)* @f to i8*)) + %f = bitcast i8* %prepare to {i8*, i32} (i8*, i32)* + %result0 = call {i8*, i32} %f(i8* %buffer, i32 4) + %value0 = extractvalue {i8*, i32} %result0, 1 + call void @print(i32 %value0) + %cont0 = extractvalue {i8*, i32} %result0, 0 + %cont0.cast = bitcast i8* %cont0 to {i8*, i32} (i8*, i8)* + %result1 = call {i8*, i32} %cont0.cast(i8* %buffer, i8 zeroext 0) + %value1 = extractvalue {i8*, i32} %result1, 1 + call void @print(i32 %value1) + %cont1 = extractvalue {i8*, i32} %result1, 0 + %cont1.cast = bitcast i8* %cont1 to {i8*, i32} (i8*, i8)* + %result2 = call {i8*, i32} %cont1.cast(i8* %buffer, i8 zeroext 0) + %value2 = extractvalue {i8*, i32} %result2, 1 + call void @print(i32 %value2) + %cont2 = extractvalue {i8*, i32} %result2, 0 + %cont2.cast = bitcast i8* %cont2 to {i8*, i32} (i8*, i8)* + call {i8*, i32} %cont2.cast(i8* %buffer, i8 zeroext 1) + ret i32 0 +} + +; Unfortunately, we don't seem to fully optimize this right now due +; to some sort of phase-ordering thing. +; CHECK-LABEL: define i32 @main +; CHECK-NEXT: entry: +; CHECK: [[BUFFER:%.*]] = alloca [8 x i8], align 4 +; CHECK: [[SLOT:%.*]] = bitcast [8 x i8]* [[BUFFER]] to i32* +; CHECK-NEXT: store i32 4, i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 4) +; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[SLOT]], align 4 +; CHECK-NEXT: [[INC:%.*]] = add i32 [[LOAD]], 1 +; CHECK-NEXT: store i32 [[INC]], i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 [[INC]]) +; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[SLOT]], align 4 +; CHECK-NEXT: [[INC:%.*]] = add i32 [[LOAD]], 1 +; CHECK-NEXT: store i32 [[INC]], i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 [[INC]]) +; CHECK-NEXT: ret i32 0 + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare {i8*, i32} @prototype(i8*, i8 zeroext) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare void @print(i32) + diff --git a/llvm/test/Transforms/Coroutines/coro-retcon.ll b/llvm/test/Transforms/Coroutines/coro-retcon.ll new file mode 100644 index 0000000000000..a790cf9fd8c0a --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon.ll @@ -0,0 +1,94 @@ +; First example from Doc/Coroutines.rst (two block loop) converted to retcon +; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s + +define i8* @f(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + call void @print(i32 %n.val) + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon() + br i1 %unwind0, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define i8* @f(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: call void @print(i32 %n) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i8)* @f.resume.0 to i8*) +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 +; CHECK-NEXT: br i1 [[T0]], +; CHECK: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: %inc = add i32 [[T1]], 1 +; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 +; CHECK-NEXT: call void @print(i32 %inc) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i8)* @f.resume.0 to i8*) +; CHECK: : +; CHECK-NEXT: ret i8* null +; CHECK-NEXT: } + +define i32 @main() { +entry: + %0 = alloca [8 x i8], align 4 + %buffer = bitcast [8 x i8]* %0 to i8* + %prepare = call i8* @llvm.coro.prepare.retcon(i8* bitcast (i8* (i8*, i32)* @f to i8*)) + %f = bitcast i8* %prepare to i8* (i8*, i32)* + %cont0 = call i8* %f(i8* %buffer, i32 4) + %cont0.cast = bitcast i8* %cont0 to i8* (i8*, i8)* + %cont1 = call i8* %cont0.cast(i8* %buffer, i8 zeroext 0) + %cont1.cast = bitcast i8* %cont1 to i8* (i8*, i8)* + %cont2 = call i8* %cont1.cast(i8* %buffer, i8 zeroext 0) + %cont2.cast = bitcast i8* %cont2 to i8* (i8*, i8)* + call i8* %cont2.cast(i8* %buffer, i8 zeroext 1) + ret i32 0 +} + +; Unfortunately, we don't seem to fully optimize this right now due +; to some sort of phase-ordering thing. +; CHECK-LABEL: define i32 @main +; CHECK-NEXT: entry: +; CHECK: [[BUFFER:%.*]] = alloca [8 x i8], align 4 +; CHECK: [[SLOT:%.*]] = bitcast [8 x i8]* [[BUFFER]] to i32* +; CHECK-NEXT: store i32 4, i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 4) +; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[SLOT]], align 4 +; CHECK-NEXT: [[INC:%.*]] = add i32 [[LOAD]], 1 +; CHECK-NEXT: store i32 [[INC]], i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 [[INC]]) +; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[SLOT]], align 4 +; CHECK-NEXT: [[INC:%.*]] = add i32 [[LOAD]], 1 +; CHECK-NEXT: store i32 [[INC]], i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 [[INC]]) +; CHECK-NEXT: ret i32 0 + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare i8* @prototype(i8*, i8 zeroext) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare void @print(i32) + From b127680180c38d8c502ec465c535027ba3dfc76c Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Thu, 5 Oct 2017 02:55:00 -0400 Subject: [PATCH 208/582] Generalize llvm.coro.suspend.retcon to allow an arbitrary number of argyments to be passed back to the continuation function. apple-llvm-split-commit: eba643352bc0c5203097a16b0095d84f0a8b41c7 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/IR/Intrinsics.td | 2 +- llvm/lib/Transforms/Coroutines/CoroInternal.h | 11 +- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 67 +++++++++--- llvm/lib/Transforms/Coroutines/Coroutines.cpp | 39 +++++-- .../Coroutines/coro-retcon-once-value.ll | 32 +++--- .../Coroutines/coro-retcon-once-value2.ll | 16 ++- .../Coroutines/coro-retcon-resume-values.ll | 85 +++++++++++++++ .../Coroutines/coro-retcon-resume-values2.ll | 100 ++++++++++++++++++ .../Coroutines/coro-retcon-value.ll | 5 +- .../test/Transforms/Coroutines/coro-retcon.ll | 29 +++-- 10 files changed, 318 insertions(+), 68 deletions(-) create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index cb23f5f39caf0..388cac6f52c66 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -778,7 +778,7 @@ def int_coro_size : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>; def int_coro_save : Intrinsic<[llvm_token_ty], [llvm_ptr_ty], []>; def int_coro_suspend : Intrinsic<[llvm_i8_ty], [llvm_token_ty, llvm_i1_ty], []>; -def int_coro_suspend_retcon : Intrinsic<[llvm_i1_ty], [llvm_vararg_ty], []>; +def int_coro_suspend_retcon : Intrinsic<[llvm_any_ty], [llvm_vararg_ty], []>; def int_coro_prepare_retcon : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty], [IntrNoMem]>; diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h index 7a5fc7f4fa29f..db9033a3b5019 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -167,7 +167,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape { ABI == coro::ABI::RetconOnce); auto FTy = CoroBegin->getFunction()->getFunctionType(); - // This is checked by AnyCoroIdRetconInst::isWellFormed(). + // The safety of all this is checked by checkWFRetconPrototype. if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) { return STy->elements().slice(1); } else { @@ -175,6 +175,15 @@ struct LLVM_LIBRARY_VISIBILITY Shape { } } + ArrayRef<Type*> getRetconResumeTypes() const { + assert(ABI == coro::ABI::Retcon || + ABI == coro::ABI::RetconOnce); + + // The safety of all this is checked by checkWFRetconPrototype. + auto FTy = RetconLowering.ResumePrototype->getFunctionType(); + return FTy->params().slice(1); + } + CallingConv::ID getResumeFunctionCC() const { switch (ABI) { case coro::ABI::Switch: diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index e3fc2b9e0eff7..fcc349a33b4d1 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -145,6 +145,7 @@ class CoroCloner { void createDeclaration(); void replaceEntryBlock(); Value *deriveNewFramePointer(); + void replaceRetconSuspendUses(); void replaceCoroSuspends(); void replaceCoroEnds(); void handleFinalSuspend(); @@ -399,6 +400,52 @@ static Function *createCloneDeclaration(Function &OrigF, coro::Shape &Shape, return F; } +/// Replace uses of the active llvm.coro.suspend.retcon call with the +/// arguments to the continuation function. +/// +/// This assumes that the builder has a meaningful insertion point. +void CoroCloner::replaceRetconSuspendUses() { + assert(Shape.ABI == coro::ABI::Retcon || + Shape.ABI == coro::ABI::RetconOnce); + + auto NewS = VMap[ActiveSuspend]; + if (NewS->use_empty()) return; + + // Copy out all the continuation arguments after the buffer pointer into + // an easily-indexed data structure for convenience. + SmallVector<Value*, 8> Args; + for (auto I = std::next(NewF->arg_begin()), E = NewF->arg_end(); I != E; ++I) + Args.push_back(&*I); + + // If the suspend returns a single scalar value, we can just do a simple + // replacement. + if (!isa<StructType>(NewS->getType())) { + assert(Args.size() == 1); + NewS->replaceAllUsesWith(Args.front()); + return; + } + + // Try to peephole extracts of an aggregate return. + for (auto UI = NewS->use_begin(), UE = NewS->use_end(); UI != UE; ) { + auto EVI = dyn_cast<ExtractValueInst>((UI++)->getUser()); + if (!EVI || EVI->getNumIndices() != 1) + continue; + + EVI->replaceAllUsesWith(Args[EVI->getIndices().front()]); + EVI->eraseFromParent(); + } + + // If we have no remaining uses, we're done. + if (NewS->use_empty()) return; + + // Otherwise, we need to create an aggregate. + Value *Agg = UndefValue::get(NewS->getType()); + for (size_t I = 0, E = Args.size(); I != E; ++I) + Agg = Builder.CreateInsertValue(Agg, Args[I], I); + + NewS->replaceAllUsesWith(Agg); +} + void CoroCloner::replaceCoroSuspends() { Value *SuspendResult; @@ -413,15 +460,12 @@ void CoroCloner::replaceCoroSuspends() { SuspendResult = Builder.getInt8(isSwitchDestroyFunction() ? 1 : 0); break; - // In continuation lowering, replace all of the suspend uses with false to - // indicate that they're not unwinding resumes. We've already mapped the - // active suspend to the appropriate argument, so any other suspend values - // that are still being used must be from previous suspends. It's UB to try - // to suspend during unwind, so they must be from regular resumes. + // In returned-continuation lowering, the arguments from earlier + // continuations are theoretically arbitrary, and they should have been + // spilled. case coro::ABI::RetconOnce: case coro::ABI::Retcon: - SuspendResult = Builder.getInt1(false); - break; + return; } for (AnyCoroSuspendInst *CS : Shape.CoroSuspends) { @@ -616,14 +660,11 @@ void CoroCloner::create() { case coro::ABI::Retcon: case coro::ABI::RetconOnce: - // Replace the active suspend with the should-unwind argument. - // Coerce it to i1 if necessary. + // Replace uses of the active suspend with the corresponding + // continuation-function arguments. assert(ActiveSuspend != nullptr && "no active suspend when lowering a continuation-style coroutine"); - Value *ShouldUnwind = &*std::next(NewF->arg_begin()); - if (!ShouldUnwind->getType()->isIntegerTy(1)) - ShouldUnwind = Builder.CreateIsNotNull(ShouldUnwind); - VMap[ActiveSuspend]->replaceAllUsesWith(ShouldUnwind); + replaceRetconSuspendUses(); break; } diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp index 95cdf25872bd5..8d915673b5efc 100644 --- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -386,6 +386,7 @@ void coro::Shape::buildFrom(Function &F) { // Determine the result value types, and make sure they match up with // the values passed to the suspends. auto ResultTys = getRetconResultTypes(); + auto ResumeTys = getRetconResumeTypes(); for (auto AnySuspend : CoroSuspends) { auto Suspend = dyn_cast<CoroSuspendRetconInst>(AnySuspend); @@ -395,6 +396,7 @@ void coro::Shape::buildFrom(Function &F) { "coro.suspend.retcon"); } + // Check that the argument types of the suspend match the results. auto SI = Suspend->value_begin(), SE = Suspend->value_end(); auto RI = ResultTys.begin(), RE = ResultTys.end(); for (; SI != SE && RI != RE; ++SI, ++RI) { @@ -410,6 +412,26 @@ void coro::Shape::buildFrom(Function &F) { Prototype->getFunctionType()->dump(); report_fatal_error("wrong number of arguments to coro.suspend.retcon"); } + + // Check that the result type of the suspend matches the resume types. + Type *SResultTy = Suspend->getType(); + ArrayRef<Type*> SuspendResultTys = + (isa<StructType>(SResultTy) + ? cast<StructType>(SResultTy)->elements() + : SResultTy); // forms an ArrayRef using SResultTy, be careful + if (SuspendResultTys.size() != ResumeTys.size()) { + Suspend->dump(); + Prototype->getFunctionType()->dump(); + report_fatal_error("wrong number of results from coro.suspend.retcon"); + } + for (size_t I = 0, E = ResumeTys.size(); I != E; ++I) { + if (SuspendResultTys[I] != ResumeTys[I]) { + Suspend->dump(); + Prototype->getFunctionType()->dump(); + report_fatal_error("result from coro.suspend.retcon does not " + "match corresponding prototype function param"); + } + } } break; } @@ -500,7 +522,7 @@ static void fail(const Instruction *I, const char *Reason, Value *V) { static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) { auto F = dyn_cast<Function>(V->stripPointerCasts()); if (!F) - fail(I, "llvm.coro.retcon.* prototype not a Function", V); + fail(I, "llvm.coro.id.retcon.* prototype not a Function", V); auto FT = F->getFunctionType(); @@ -516,23 +538,20 @@ static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) { ResultOkay = false; } if (!ResultOkay) - fail(I, "llvm.coro.retcon prototype must return pointer as first result", - F); + fail(I, "llvm.coro.id.retcon prototype must return pointer as first " + "result", F); if (FT->getReturnType() != I->getFunction()->getFunctionType()->getReturnType()) - fail(I, "llvm.coro.retcon.* prototype return type must be same as" + fail(I, "llvm.coro.id.retcon prototype return type must be same as" "current function return type", F); } else { // No meaningful validation to do here for llvm.coro.id.unique.once. } - if (FT->getNumParams() != 2) - fail(I, "llvm.coro.retcon.* prototype must take exactly two parameters", F); - if (!FT->getParamType(0)->isPointerTy()) - fail(I, "llvm.coro.retcon.* prototype must take pointer as 1st param", F); - if (!FT->getParamType(1)->isIntegerTy()) // an i1, but not for abi purposes - fail(I, "llvm.coro.retcon.* prototype must take integer as 2nd param", F); + if (FT->getNumParams() == 0 || !FT->getParamType(0)->isPointerTy()) + fail(I, "llvm.coro.id.retcon.* prototype must take pointer as " + "its first parameter", F); } /// Check that the given value is a well-formed allocator. diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll index 8f9cdf1774722..cb7cfda6df0b2 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll @@ -4,14 +4,14 @@ target triple = "x86_64-apple-macosx10.12.0" define {i8*, i32} @f(i8* %buffer, i32* %array) { entry: - %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) %hdl = call i8* @llvm.coro.begin(token %id, i8* null) %load = load i32, i32* %array %load.pos = icmp sgt i32 %load, 0 br i1 %load.pos, label %pos, label %neg pos: - %unwind0 = call i1 (...) @llvm.coro.suspend.retcon(i32 %load) + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1(i32 %load) br i1 %unwind0, label %cleanup, label %pos.cont pos.cont: @@ -19,7 +19,7 @@ pos.cont: br label %cleanup neg: - %unwind1 = call i1 (...) @llvm.coro.suspend.retcon(i32 0) + %unwind1 = call i1 (...) @llvm.coro.suspend.retcon.i1(i32 0) br i1 %unwind1, label %cleanup, label %neg.cont neg.cont: @@ -37,18 +37,17 @@ cleanup: ; CHECK-NEXT: store i32* %array, i32** [[T0]], align 8 ; CHECK-NEXT: %load = load i32, i32* %array, align 4 ; CHECK-NEXT: %load.pos = icmp sgt i32 %load, 0 -; CHECK-NEXT: [[CONT:%.*]] = select i1 %load.pos, void (i8*, i8)* @f.resume.0, void (i8*, i8)* @f.resume.1 +; CHECK-NEXT: [[CONT:%.*]] = select i1 %load.pos, void (i8*, i1)* @f.resume.0, void (i8*, i1)* @f.resume.1 ; CHECK-NEXT: [[VAL:%.*]] = select i1 %load.pos, i32 %load, i32 0 -; CHECK-NEXT: [[CONT_CAST:%.*]] = bitcast void (i8*, i8)* [[CONT]] to i8* +; CHECK-NEXT: [[CONT_CAST:%.*]] = bitcast void (i8*, i1)* [[CONT]] to i8* ; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32 } undef, i8* [[CONT_CAST]], 0 ; CHECK-NEXT: [[T1:%.*]] = insertvalue { i8*, i32 } [[T0]], i32 [[VAL]], 1 ; CHECK-NEXT: ret { i8*, i32 } [[T1]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i1 zeroext) ; CHECK-NEXT: : -; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32** ; CHECK-NEXT: [[RELOAD:%.*]] = load i32*, i32** [[T0]], align 8 @@ -58,10 +57,9 @@ cleanup: ; CHECK-NEXT: ret void ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull, i8 zeroext) +; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull, i1 zeroext) ; CHECK-NEXT: : -; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32** ; CHECK-NEXT: [[RELOAD:%.*]] = load i32*, i32** [[T0]], align 8 @@ -81,8 +79,8 @@ entry: %value = extractvalue {i8*, i32} %result, 1 call void @print(i32 %value) %cont = extractvalue {i8*, i32} %result, 0 - %cont.cast = bitcast i8* %cont to void (i8*, i8)* - call void %cont.cast(i8* %buffer, i8 zeroext 0) + %cont.cast = bitcast i8* %cont to void (i8*, i1)* + call void %cont.cast(i8* %buffer, i1 zeroext 0) ret void } @@ -95,19 +93,19 @@ entry: ; CHECK-NEXT: store i32* %array, i32** [[BUFFER]], align 8 ; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* %array, align 4 ; CHECK-NEXT: [[LOAD_POS:%.*]] = icmp sgt i32 [[LOAD]], 0 -; CHECK-NEXT: [[CONT:%.*]] = select i1 [[LOAD_POS]], void (i8*, i8)* @f.resume.0, void (i8*, i8)* @f.resume.1 +; CHECK-NEXT: [[CONT:%.*]] = select i1 [[LOAD_POS]], void (i8*, i1)* @f.resume.0, void (i8*, i1)* @f.resume.1 ; CHECK-NEXT: [[VAL:%.*]] = select i1 [[LOAD_POS]], i32 [[LOAD]], i32 0 ; CHECK-NEXT: call void @print(i32 [[VAL]]) -; CHECK-NEXT: call void [[CONT]](i8* nonnull [[BUFFER_CAST]], i8 zeroext 0) +; CHECK-NEXT: call void [[CONT]](i8* nonnull [[BUFFER_CAST]], i1 zeroext false) ; CHECK-NEXT: ret void declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.suspend.retcon.i1(...) declare i1 @llvm.coro.end(i8*, i1) declare i8* @llvm.coro.prepare.retcon(i8*) -declare void @prototype(i8*, i8 zeroext) +declare void @prototype(i8*, i1 zeroext) declare noalias i8* @allocate(i32 %size) declare void @deallocate(i8* %ptr) diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll index 5e7946ba477cd..d1f312e33ac7c 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll @@ -5,11 +5,11 @@ target triple = "x86_64-apple-macosx10.12.0" define {i8*, i32*} @f(i8* %buffer, i32* %ptr) "coroutine.presplit"="1" { entry: %temp = alloca i32, align 4 - %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) %hdl = call i8* @llvm.coro.begin(token %id, i8* null) %oldvalue = load i32, i32* %ptr store i32 %oldvalue, i32* %temp - %unwind = call i1 (...) @llvm.coro.suspend.retcon(i32* %temp) + %unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(i32* %temp) br i1 %unwind, label %cleanup, label %cont cont: @@ -33,18 +33,17 @@ cleanup: ; CHECK-NEXT: store i32* %ptr, i32** [[SPILL]] ; CHECK-NEXT: %oldvalue = load i32, i32* %ptr ; CHECK-NEXT: store i32 %oldvalue, i32* %temp -; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32* } { i8* bitcast (void (i8*, i8)* @f.resume.0 to i8*), i32* undef }, i32* %temp, 1 +; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32* } { i8* bitcast (void (i8*, i1)* @f.resume.0 to i8*), i32* undef }, i32* %temp, 1 ; CHECK-NEXT: ret { i8*, i32* } [[T0]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i1 zeroext) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] ; CHECK-NEXT: bitcast [[FRAME_T]]* [[FRAME]] to i8* -; CHECK-NEXT: [[T0:%.*]] = icmp ne i8 %1, 0 ; CHECK-NEXT: %temp = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[TEMP_SLOT:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 ; CHECK-NEXT: [[PTR_SLOT:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 @@ -60,11 +59,10 @@ cleanup: declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.suspend.retcon.i1(...) declare i1 @llvm.coro.end(i8*, i1) -declare i8* @llvm.coro.prepare.retcon(i8*) -declare void @prototype(i8*, i8 zeroext) +declare void @prototype(i8*, i1 zeroext) declare noalias i8* @allocate(i32 %size) declare fastcc void @deallocate(i8* %ptr) diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll new file mode 100644 index 0000000000000..3f613ed5f45ad --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll @@ -0,0 +1,85 @@ +; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s + +define i8* @f(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i32, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %sum, %resume ] + %values = call { i32, i1 } (...) @llvm.coro.suspend.retcon.sl_i32i1s() + %finished = extractvalue { i32, i1 } %values, 1 + br i1 %finished, label %cleanup, label %resume + +resume: + %input = extractvalue { i32, i1 } %values, 0 + %sum = add i32 %n.val, %input + br label %loop + +cleanup: + call void @print(i32 %n.val) + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define i8* @f(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32, i1)* @f.resume.0 to i8*) +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i32, i1 zeroext) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: br i1 %2, +; CHECK: : +; CHECK-NEXT: %sum = add i32 [[T1]], %1 +; CHECK-NEXT: store i32 %sum, i32* [[T0]], align 4 +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32, i1)* @f.resume.0 to i8*) +; CHECK: : +; CHECK-NEXT: call void @print(i32 [[T1]]) +; CHECK-NEXT: ret i8* null +; CHECK-NEXT: } + +define i32 @main() { +entry: + %0 = alloca [8 x i8], align 4 + %buffer = bitcast [8 x i8]* %0 to i8* + %prepare = call i8* @llvm.coro.prepare.retcon(i8* bitcast (i8* (i8*, i32)* @f to i8*)) + %f = bitcast i8* %prepare to i8* (i8*, i32)* + %cont0 = call i8* %f(i8* %buffer, i32 1) + %cont0.cast = bitcast i8* %cont0 to i8* (i8*, i32, i1)* + %cont1 = call i8* %cont0.cast(i8* %buffer, i32 2, i1 zeroext false) + %cont1.cast = bitcast i8* %cont1 to i8* (i8*, i32, i1)* + %cont2 = call i8* %cont1.cast(i8* %buffer, i32 4, i1 zeroext false) + %cont2.cast = bitcast i8* %cont2 to i8* (i8*, i32, i1)* + call i8* %cont2.cast(i8* %buffer, i32 100, i1 zeroext true) + ret i32 0 +} + +; Unfortunately, we don't seem to fully optimize this right now due +; to some sort of phase-ordering thing. +; CHECK-LABEL: define i32 @main +; CHECK-NEXT: entry: +; CHECK: [[BUFFER:%.*]] = alloca [8 x i8], align 4 +; CHECK: [[SLOT:%.*]] = bitcast [8 x i8]* [[BUFFER]] to i32* +; CHECK-NEXT: store i32 7, i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 7) +; CHECK-NEXT: ret i32 0 + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare { i32, i1 } @llvm.coro.suspend.retcon.sl_i32i1s(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare i8* @prototype(i8*, i32, i1 zeroext) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare void @print(i32) + diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll new file mode 100644 index 0000000000000..bdc2f0f9be9d9 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll @@ -0,0 +1,100 @@ +; RUN: opt < %s -coro-split -coro-cleanup -S | FileCheck %s + +define i8* @f(i8* %buffer, i32 %n) "coroutine.presplit"="1" { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i32)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + %value0 = call i32 (...) @llvm.coro.suspend.retcon.i32() + %sum0 = call i32 @add(i32 %n, i32 %value0) + %value1 = call i32 (...) @llvm.coro.suspend.retcon.i32() + %sum1 = call i32 @add(i32 %sum0, i32 %value0) + %sum2 = call i32 @add(i32 %sum1, i32 %value1) + %value2 = call i32 (...) @llvm.coro.suspend.retcon.i32() + %sum3 = call i32 @add(i32 %sum2, i32 %value0) + %sum4 = call i32 @add(i32 %sum3, i32 %value1) + %sum5 = call i32 @add(i32 %sum4, i32 %value2) + call void @print(i32 %sum5) + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define i8* @f(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ALLOC:%.*]] = call i8* @allocate(i32 20) +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i8** +; CHECK-NEXT: store i8* [[ALLOC]], i8** [[T0]] +; CHECK-NEXT: [[FRAME:%.*]] = bitcast i8* [[ALLOC]] to [[FRAME_T:%.*]]* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 +; CHECK-NEXT: store i32 %n, i32* [[T0]] +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32)* @f.resume.0 to i8*) +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i32) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** +; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] +; CHECK-NEXT: [[VFRAME:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: store i32 %1, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 +; CHECK-NEXT: [[N:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: %sum0 = call i32 @add(i32 [[N]], i32 %1) +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 2 +; CHECK-NEXT: store i32 %sum0, i32* [[T0]] +; CHECK-NEXT: [[CONT:%.*]] = bitcast i8* (i8*, i32)* @f.resume.1 to i8* +; CHECK-NEXT: ret i8* [[CONT]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.1(i8* noalias nonnull, i32) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** +; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] +; CHECK-NEXT: [[VFRAME:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 3 +; CHECK-NEXT: store i32 %1, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 2 +; CHECK-NEXT: [[SUM0:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: [[VALUE0:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: %sum1 = call i32 @add(i32 [[SUM0]], i32 [[VALUE0]]) +; CHECK-NEXT: %sum2 = call i32 @add(i32 %sum1, i32 %1) +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 4 +; CHECK-NEXT: store i32 %sum2, i32* [[T0]] +; CHECK-NEXT: [[CONT:%.*]] = bitcast i8* (i8*, i32)* @f.resume.2 to i8* +; CHECK-NEXT: ret i8* [[CONT]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.2(i8* noalias nonnull, i32) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** +; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] +; CHECK-NEXT: [[VFRAME:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 4 +; CHECK-NEXT: [[SUM2:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 3 +; CHECK-NEXT: [[VALUE1:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: [[VALUE0:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: %sum3 = call i32 @add(i32 [[SUM2]], i32 [[VALUE0]]) +; CHECK-NEXT: %sum4 = call i32 @add(i32 %sum3, i32 [[VALUE1]]) +; CHECK-NEXT: %sum5 = call i32 @add(i32 %sum4, i32 %1) +; CHECK-NEXT: call void @print(i32 %sum5) +; CHECK-NEXT: [[CONT:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: call void @deallocate(i8* [[CONT]]) +; CHECK-NEXT: ret i8* null +; CHECK-NEXT: } + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare i32 @llvm.coro.suspend.retcon.i32(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare i8* @prototype(i8*, i32) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare i32 @add(i32, i32) +declare void @print(i32) + diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll index 56d0e3b5210ea..bf4dc8af72e15 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll @@ -9,7 +9,8 @@ entry: loop: %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] - %unwind0 = call i1 (...) @llvm.coro.suspend.retcon(i32 %n.val) + %unwind = call i8 (...) @llvm.coro.suspend.retcon.i8(i32 %n.val) + %unwind0 = icmp ne i8 %unwind, 0 br i1 %unwind0, label %cleanup, label %resume resume: @@ -89,7 +90,7 @@ entry: declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i8 @llvm.coro.suspend.retcon.i8(...) declare i1 @llvm.coro.end(i8*, i1) declare i8* @llvm.coro.prepare.retcon(i8*) diff --git a/llvm/test/Transforms/Coroutines/coro-retcon.ll b/llvm/test/Transforms/Coroutines/coro-retcon.ll index a790cf9fd8c0a..355cdae393208 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon.ll @@ -3,14 +3,14 @@ define i8* @f(i8* %buffer, i32 %n) { entry: - %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) %hdl = call i8* @llvm.coro.begin(token %id, i8* null) br label %loop loop: %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] call void @print(i32 %n.val) - %unwind0 = call i1 (...) @llvm.coro.suspend.retcon() + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1() br i1 %unwind0, label %cleanup, label %resume resume: @@ -27,20 +27,19 @@ cleanup: ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* ; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 ; CHECK-NEXT: call void @print(i32 %n) -; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i8)* @f.resume.0 to i8*) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i1 zeroext) ; CHECK-NEXT: : -; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* ; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 ; CHECK-NEXT: %inc = add i32 [[T1]], 1 ; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 ; CHECK-NEXT: call void @print(i32 %inc) -; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i8)* @f.resume.0 to i8*) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @f.resume.0 to i8*) ; CHECK: : ; CHECK-NEXT: ret i8* null ; CHECK-NEXT: } @@ -52,12 +51,12 @@ entry: %prepare = call i8* @llvm.coro.prepare.retcon(i8* bitcast (i8* (i8*, i32)* @f to i8*)) %f = bitcast i8* %prepare to i8* (i8*, i32)* %cont0 = call i8* %f(i8* %buffer, i32 4) - %cont0.cast = bitcast i8* %cont0 to i8* (i8*, i8)* - %cont1 = call i8* %cont0.cast(i8* %buffer, i8 zeroext 0) - %cont1.cast = bitcast i8* %cont1 to i8* (i8*, i8)* - %cont2 = call i8* %cont1.cast(i8* %buffer, i8 zeroext 0) - %cont2.cast = bitcast i8* %cont2 to i8* (i8*, i8)* - call i8* %cont2.cast(i8* %buffer, i8 zeroext 1) + %cont0.cast = bitcast i8* %cont0 to i8* (i8*, i1)* + %cont1 = call i8* %cont0.cast(i8* %buffer, i1 zeroext false) + %cont1.cast = bitcast i8* %cont1 to i8* (i8*, i1)* + %cont2 = call i8* %cont1.cast(i8* %buffer, i1 zeroext false) + %cont2.cast = bitcast i8* %cont2 to i8* (i8*, i1)* + call i8* %cont2.cast(i8* %buffer, i1 zeroext true) ret i32 0 } @@ -81,11 +80,11 @@ entry: declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.suspend.retcon.i1(...) declare i1 @llvm.coro.end(i8*, i1) declare i8* @llvm.coro.prepare.retcon(i8*) -declare i8* @prototype(i8*, i8 zeroext) +declare i8* @prototype(i8*, i1 zeroext) declare noalias i8* @allocate(i32 %size) declare void @deallocate(i8* %ptr) From 885e2a44784eedbb1d76ec03f6420459239e7b38 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Thu, 5 Oct 2017 05:29:22 -0400 Subject: [PATCH 209/582] Guard dumps in the coro intrinsic validation logic behind NDEBUG checks. dump() is not guaranteed to be defined in all builds. apple-llvm-split-commit: ba90a8f44e4ac0b540c4e23c2a197679d8bb177a apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/Coroutines.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp index 8d915673b5efc..aa785d5a329c6 100644 --- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -359,7 +359,9 @@ void coro::Shape::buildFrom(Function &F) { for (auto AnySuspend : CoroSuspends) { auto Suspend = dyn_cast<CoroSuspendInst>(AnySuspend); if (!Suspend) { +#ifndef NDEBUG AnySuspend->dump(); +#endif report_fatal_error("coro.id must be paired with coro.suspend"); } @@ -391,7 +393,9 @@ void coro::Shape::buildFrom(Function &F) { for (auto AnySuspend : CoroSuspends) { auto Suspend = dyn_cast<CoroSuspendRetconInst>(AnySuspend); if (!Suspend) { +#ifndef NDEBUG AnySuspend->dump(); +#endif report_fatal_error("coro.id.retcon.* must be paired with " "coro.suspend.retcon"); } @@ -401,15 +405,19 @@ void coro::Shape::buildFrom(Function &F) { auto RI = ResultTys.begin(), RE = ResultTys.end(); for (; SI != SE && RI != RE; ++SI, ++RI) { if ((*SI)->getType() != *RI) { +#ifndef NDEBUG Suspend->dump(); Prototype->getFunctionType()->dump(); +#endif report_fatal_error("argument to coro.suspend.retcon does not " "match corresponding prototype function result"); } } if (SI != SE || RI != RE) { +#ifndef NDEBUG Suspend->dump(); Prototype->getFunctionType()->dump(); +#endif report_fatal_error("wrong number of arguments to coro.suspend.retcon"); } @@ -420,14 +428,18 @@ void coro::Shape::buildFrom(Function &F) { ? cast<StructType>(SResultTy)->elements() : SResultTy); // forms an ArrayRef using SResultTy, be careful if (SuspendResultTys.size() != ResumeTys.size()) { +#ifndef NDEBUG Suspend->dump(); Prototype->getFunctionType()->dump(); +#endif report_fatal_error("wrong number of results from coro.suspend.retcon"); } for (size_t I = 0, E = ResumeTys.size(); I != E; ++I) { if (SuspendResultTys[I] != ResumeTys[I]) { +#ifndef NDEBUG Suspend->dump(); Prototype->getFunctionType()->dump(); +#endif report_fatal_error("result from coro.suspend.retcon does not " "match corresponding prototype function param"); } @@ -508,12 +520,14 @@ void coro::Shape::emitDealloc(IRBuilder<> &Builder, Value *Ptr, LLVM_ATTRIBUTE_NORETURN static void fail(const Instruction *I, const char *Reason, Value *V) { +#ifndef NDEBUG I->dump(); if (V) { errs() << " Value: "; V->printAsOperand(llvm::errs()); errs() << '\n'; } +#endif report_fatal_error(Reason); } From 096e15b5ff4f108bb853f937f8ff495d21361b64 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 11 Oct 2017 17:36:23 -0700 Subject: [PATCH 210/582] Tweak doc comment for RecordLayout.h. The old syntax still works, but this one is nicer for registering abbrev codes in place. apple-llvm-split-commit: 602738fcc0d31280670cd84d811502a31e4e8010 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Bitcode/RecordLayout.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/Bitcode/RecordLayout.h b/llvm/include/llvm/Bitcode/RecordLayout.h index c415eeb9d750a..c59b13199699f 100644 --- a/llvm/include/llvm/Bitcode/RecordLayout.h +++ b/llvm/include/llvm/Bitcode/RecordLayout.h @@ -19,9 +19,8 @@ /// BCFixed<16>, // Module format minor version /// BCBlob // misc. version information /// >; -/// unsigned MetadataAbbrevCode = Metadata::emitAbbrev(Out); -/// Metadata::emitRecord(Out, ScratchRecord, MetadataAbbrevCode, -/// VERSION_MAJOR, VERSION_MINOR, extraData); +/// Metadata metadata(Out); +/// metadata.emit(ScratchRecord, VERSION_MAJOR, VERSION_MINOR, extraData); /// \endcode /// /// For details on the bitcode format, see From 0c0036063de6b9c5b3c4c4efe9c9b84e2615c781 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 13 Oct 2017 12:57:26 -0700 Subject: [PATCH 211/582] Rename the previous RefactoringOption class to OldRefactoringOption The new refactoring engine introduces a new RefactoringOption class rdar://34983672 apple-llvm-split-commit: 91834c5fa3af0c1df0d17f116ad87583212741d1 apple-llvm-split-dir: clang/ --- clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h | 6 +++--- clang/include/clang/Tooling/Refactor/RefactoringOptions.h | 2 +- clang/lib/Tooling/Refactor/RefactoringOptions.cpp | 2 +- clang/unittests/Tooling/RefactoringTest.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h b/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h index c3f05ac0f436d..0368122cbf050 100644 --- a/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h +++ b/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h @@ -24,8 +24,8 @@ class IO; namespace clang { namespace tooling { -struct RefactoringOption { - virtual ~RefactoringOption() = default; +struct OldRefactoringOption { + virtual ~OldRefactoringOption() = default; struct SerializationContext { llvm::yaml::IO &IO; @@ -39,7 +39,7 @@ struct RefactoringOption { /// \brief A set of refactoring options that can be given to a refactoring /// operation. class RefactoringOptionSet final { - llvm::StringMap<std::unique_ptr<RefactoringOption>> Options; + llvm::StringMap<std::unique_ptr<OldRefactoringOption>> Options; public: RefactoringOptionSet() {} diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOptions.h b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h index f0ce4ba007d0b..81e52fc0fb86d 100644 --- a/clang/include/clang/Tooling/Refactor/RefactoringOptions.h +++ b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h @@ -25,7 +25,7 @@ namespace option { namespace detail { -struct BoolOptionBase : RefactoringOption { +struct BoolOptionBase : OldRefactoringOption { protected: bool Value = false; void serializeImpl(const SerializationContext &Context, const char *Name); diff --git a/clang/lib/Tooling/Refactor/RefactoringOptions.cpp b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp index fd2d8d1583a6a..ca62890edb638 100644 --- a/clang/lib/Tooling/Refactor/RefactoringOptions.cpp +++ b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp @@ -57,7 +57,7 @@ RefactoringOptionSet::parse(StringRef Source) { return std::move(Result); } -void RefactoringOption::serialize(const SerializationContext &) {} +void OldRefactoringOption::serialize(const SerializationContext &) {} void clang::tooling::option::detail::BoolOptionBase::serializeImpl( const SerializationContext &Context, const char *Name) { diff --git a/clang/unittests/Tooling/RefactoringTest.cpp b/clang/unittests/Tooling/RefactoringTest.cpp index 41fabd4e14eac..4b76c8f410bed 100644 --- a/clang/unittests/Tooling/RefactoringTest.cpp +++ b/clang/unittests/Tooling/RefactoringTest.cpp @@ -1094,7 +1094,7 @@ TEST(DeduplicateByFileTest, NonExistingFilePath) { } namespace { -struct TestRefactoringValueOption final : RefactoringOption { +struct TestRefactoringValueOption final : OldRefactoringOption { int Value; TestRefactoringValueOption(int Value) : Value(Value) {} @@ -1119,7 +1119,7 @@ TEST(RefactoringOptionSet, AddGet) { } namespace { -struct TestRefactoringOption final : RefactoringOption { +struct TestRefactoringOption final : OldRefactoringOption { int &Counter; TestRefactoringOption(int &Counter) : Counter(Counter) {} ~TestRefactoringOption() { ++Counter; } From 1d89634fdf0fdd8dd08aa831a735e92df504ab18 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 13 Oct 2017 13:01:01 -0700 Subject: [PATCH 212/582] Rename the previous ParsedSourceRange class to OldParsedSourceRange The new refactoring engine introduces a new ParsedSourceRange class rdar://34983833 apple-llvm-split-commit: 9ace232297466d2fdf8674e0cad8ae540bc7262d apple-llvm-split-dir: clang/ --- .../clang-refactor-test/ClangRefactorTest.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index 8211cab9b8b1f..db50367c4ecf8 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -801,14 +801,14 @@ struct ParsedSourceLineRange : ParsedSourceLocation { } }; -struct ParsedSourceRange { +struct OldParsedSourceRange { ParsedSourceLocation Begin, End; - ParsedSourceRange(const ParsedSourceLocation &Begin, + OldParsedSourceRange(const ParsedSourceLocation &Begin, const ParsedSourceLocation &End) : Begin(Begin), End(End) {} - static Optional<ParsedSourceRange> FromString(StringRef Str) { + static Optional<OldParsedSourceRange> FromString(StringRef Str) { std::pair<StringRef, StringRef> RangeSplit = Str.rsplit('-'); auto Begin = ParsedSourceLocation::FromString(RangeSplit.first); if (Begin.FileName.empty()) @@ -817,7 +817,7 @@ struct ParsedSourceRange { auto End = ParsedSourceLocation::FromString(EndString); if (End.FileName.empty()) return None; - return ParsedSourceRange(Begin, End); + return OldParsedSourceRange(Begin, End); } }; @@ -831,7 +831,7 @@ int listRefactoringActions(CXTranslationUnit TU) { CXSourceRange Range; if (!opts::listActions::SelectedRange.empty()) { auto SelectionRange = - ParsedSourceRange::FromString(opts::listActions::SelectedRange); + OldParsedSourceRange::FromString(opts::listActions::SelectedRange); if (!SelectionRange) { errs() << "error: The -selected option must use the " "<file:line:column-line:column> format\n"; @@ -1047,7 +1047,7 @@ static Optional<ParsedSourceLocation> selectionLocForFile(StringRef Filename, return ParsedSourceLocation::FromString(OS.str()); } -static Optional<ParsedSourceRange> selectionRangeForFile(StringRef Filename, +static Optional<OldParsedSourceRange> selectionRangeForFile(StringRef Filename, StringRef Name) { auto Buf = llvm::MemoryBuffer::getFile(Filename); if (!Buf) @@ -1066,7 +1066,7 @@ static Optional<ParsedSourceRange> selectionRangeForFile(StringRef Filename, llvm::raw_string_ostream OS(Str); OS << Filename << ":" << Start->first << ":" << Start->second << "-" << End->first << ":" << End->second; - return ParsedSourceRange::FromString(OS.str()); + return OldParsedSourceRange::FromString(OS.str()); } bool performOperation(CXRefactoringAction Action, ArrayRef<const char *> Args, @@ -1160,7 +1160,7 @@ bool performOperation(CXRefactoringAction Action, ArrayRef<const char *> Args, int initiateAndPerformAction(CXTranslationUnit TU, ArrayRef<const char *> Args, CXIndex CIdx) { std::vector<ParsedSourceLineRange> Ranges; - std::vector<ParsedSourceRange> SelectionRanges; + std::vector<OldParsedSourceRange> SelectionRanges; for (const auto &Range : opts::initiateAndPerform::InLocationRanges) { auto ParsedLineRange = ParsedSourceLineRange::FromString(Range); if (!ParsedLineRange) { @@ -1194,7 +1194,7 @@ int initiateAndPerformAction(CXTranslationUnit TU, ArrayRef<const char *> Args, } for (const auto &Range : opts::initiateAndPerform::SelectedRanges) { auto ParsedRange = StringRef(Range).contains(':') - ? ParsedSourceRange::FromString(Range) + ? OldParsedSourceRange::FromString(Range) : selectionRangeForFile(opts::FileName, Range); if (!ParsedRange) { errs() << "error: The -selected option must use the " @@ -1233,7 +1233,7 @@ int initiateAndPerformAction(CXTranslationUnit TU, ArrayRef<const char *> Args, Optional<std::string> LocationCandidateInformation; auto InitiateAndPerform = [&](const ParsedSourceLocation &Location, unsigned Column, - Optional<ParsedSourceRange> SelectionRange = None) -> bool { + Optional<OldParsedSourceRange> SelectionRange = None) -> bool { CXSourceLocation Loc = clang_getLocation(TU, clang_getFile(TU, Location.FileName.c_str()), Location.Line, Column); @@ -1326,7 +1326,7 @@ int initiateAndPerformAction(CXTranslationUnit TU, ArrayRef<const char *> Args, } } - for (const ParsedSourceRange &SelectionRange : SelectionRanges) { + for (const OldParsedSourceRange &SelectionRange : SelectionRanges) { if (InitiateAndPerform(SelectionRange.Begin, SelectionRange.Begin.Column, SelectionRange)) return 1; From f98ac0260c3834d4d58f64f645ce6be5ca7eaa02 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 13 Oct 2017 16:49:40 -0700 Subject: [PATCH 213/582] Rename header guard for refactoring options to avoid module build errors rdar://34987030 apple-llvm-split-commit: bfdca98bc799f071ac42ef9fda55635c9672dfbd apple-llvm-split-dir: clang/ --- clang/include/clang/Tooling/Refactor/RefactoringOptions.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOptions.h b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h index 81e52fc0fb86d..60db7cc09b151 100644 --- a/clang/include/clang/Tooling/Refactor/RefactoringOptions.h +++ b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H -#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H +#ifndef LLVM_CLANG_TOOLING_REFACTOR_OLD_REFACTORING_OPTIONS_H +#define LLVM_CLANG_TOOLING_REFACTOR_OLD_REFACTORING_OPTIONS_H #include "clang/AST/DeclBase.h" #include "clang/Basic/LLVM.h" @@ -56,4 +56,4 @@ struct AvoidTextualMatches final : detail::BoolOption<AvoidTextualMatches> { } // end namespace tooling } // end namespace clang -#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H +#endif // LLVM_CLANG_TOOLING_REFACTOR_OLD_REFACTORING_OPTIONS_H From 54c94d609b8d31ce4dfcba3cd3d2a30598ac9a2d Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Tue, 17 Oct 2017 17:11:15 -0700 Subject: [PATCH 214/582] master-next: Temporary workaround for llvm r315852. The change in llvm r315852 is causing an assertion failure for Swift. I've notified the author of that change and am hoping for a fix. In the meantime, here is a workaround. I'm doing this instead of reverting the offending commit from swift-llvm's upstream-with-swift branch in anticipation of a merge conflict from the hoped-for fix. I'll remove this workaround when there is a real fix. rdar://problem/35017353 apple-llvm-split-commit: ea55aaade6fb6211040ec040b1dbd64d93cab10a apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Transforms/Utils/FunctionComparator.h | 9 ++++----- llvm/lib/Transforms/Utils/FunctionComparator.cpp | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h index e0c79a1027e6a..559c60456b4fc 100644 --- a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h +++ b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h @@ -33,7 +33,6 @@ class APInt; class BasicBlock; class Constant; class Function; -class GlobalValue; class InlineAsm; class Instruction; class MDNode; @@ -53,14 +52,14 @@ class Value; /// compare those, but this would not work for stripped bitcodes or for those /// few symbols without a name. class GlobalNumberState { - struct Config : ValueMapConfig<GlobalValue *> { + struct Config : ValueMapConfig<Value *> { enum { FollowRAUW = false }; }; // Each GlobalValue is mapped to an identifier. The Config ensures when RAUW // occurs, the mapping does not change. Tracking changes is unnecessary, and // also problematic for weak symbols (which may be overwritten). - using ValueNumberMap = ValueMap<GlobalValue *, uint64_t, Config>; + using ValueNumberMap = ValueMap<Value *, uint64_t, Config>; ValueNumberMap GlobalNumbers; // The next unused serial number to assign to a global. @@ -69,7 +68,7 @@ class GlobalNumberState { public: GlobalNumberState() = default; - uint64_t getNumber(GlobalValue* Global) { + uint64_t getNumber(Value* Global) { ValueNumberMap::iterator MapIter; bool Inserted; std::tie(MapIter, Inserted) = GlobalNumbers.insert({Global, NextNumber}); @@ -220,7 +219,7 @@ class FunctionComparator { /// Compares two global values by number. Uses the GlobalNumbersState to /// identify the same gobals across function calls. - int cmpGlobalValues(GlobalValue *L, GlobalValue *R) const; + int cmpGlobalValues(Value *L, Value *R) const; /// Assign or look up previously assigned numbers for the two values, and /// return whether the numbers are equal. Numbers are assigned in the order diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp index bddcbd86e914d..e4ec9e7e9ef55 100644 --- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp +++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp @@ -253,8 +253,8 @@ int FunctionComparator::cmpConstants(const Constant *L, if (!L->isNullValue() && R->isNullValue()) return -1; - auto GlobalValueL = const_cast<GlobalValue *>(dyn_cast<GlobalValue>(L)); - auto GlobalValueR = const_cast<GlobalValue *>(dyn_cast<GlobalValue>(R)); + auto GlobalValueL = const_cast<Value *>(dyn_cast<Value>(L)); + auto GlobalValueR = const_cast<Value *>(dyn_cast<Value>(R)); if (GlobalValueL && GlobalValueR) { return cmpGlobalValues(GlobalValueL, GlobalValueR); } @@ -383,7 +383,7 @@ int FunctionComparator::cmpConstants(const Constant *L, } } -int FunctionComparator::cmpGlobalValues(GlobalValue *L, GlobalValue *R) const { +int FunctionComparator::cmpGlobalValues(Value *L, Value *R) const { uint64_t LNumber = GlobalNumbers->getNumber(L); uint64_t RNumber = GlobalNumbers->getNumber(R); return cmpNumbers(LNumber, RNumber); From eade542f1a1e08d933f459a54eef6cb9f6ad24e2 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Tue, 17 Oct 2017 19:11:33 -0700 Subject: [PATCH 215/582] Revert "master-next: Temporary workaround for llvm r315852." This reverts commit ea55aaade6fb6211040ec040b1dbd64d93cab10a. I had tested this with the Swift tests, but it looks like it breaks some of the LLVM tests. apple-llvm-split-commit: 14c9e6ece886d43950e165fcaef52f9266e36462 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Transforms/Utils/FunctionComparator.h | 9 +++++---- llvm/lib/Transforms/Utils/FunctionComparator.cpp | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h index 559c60456b4fc..e0c79a1027e6a 100644 --- a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h +++ b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h @@ -33,6 +33,7 @@ class APInt; class BasicBlock; class Constant; class Function; +class GlobalValue; class InlineAsm; class Instruction; class MDNode; @@ -52,14 +53,14 @@ class Value; /// compare those, but this would not work for stripped bitcodes or for those /// few symbols without a name. class GlobalNumberState { - struct Config : ValueMapConfig<Value *> { + struct Config : ValueMapConfig<GlobalValue *> { enum { FollowRAUW = false }; }; // Each GlobalValue is mapped to an identifier. The Config ensures when RAUW // occurs, the mapping does not change. Tracking changes is unnecessary, and // also problematic for weak symbols (which may be overwritten). - using ValueNumberMap = ValueMap<Value *, uint64_t, Config>; + using ValueNumberMap = ValueMap<GlobalValue *, uint64_t, Config>; ValueNumberMap GlobalNumbers; // The next unused serial number to assign to a global. @@ -68,7 +69,7 @@ class GlobalNumberState { public: GlobalNumberState() = default; - uint64_t getNumber(Value* Global) { + uint64_t getNumber(GlobalValue* Global) { ValueNumberMap::iterator MapIter; bool Inserted; std::tie(MapIter, Inserted) = GlobalNumbers.insert({Global, NextNumber}); @@ -219,7 +220,7 @@ class FunctionComparator { /// Compares two global values by number. Uses the GlobalNumbersState to /// identify the same gobals across function calls. - int cmpGlobalValues(Value *L, Value *R) const; + int cmpGlobalValues(GlobalValue *L, GlobalValue *R) const; /// Assign or look up previously assigned numbers for the two values, and /// return whether the numbers are equal. Numbers are assigned in the order diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp index e4ec9e7e9ef55..bddcbd86e914d 100644 --- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp +++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp @@ -253,8 +253,8 @@ int FunctionComparator::cmpConstants(const Constant *L, if (!L->isNullValue() && R->isNullValue()) return -1; - auto GlobalValueL = const_cast<Value *>(dyn_cast<Value>(L)); - auto GlobalValueR = const_cast<Value *>(dyn_cast<Value>(R)); + auto GlobalValueL = const_cast<GlobalValue *>(dyn_cast<GlobalValue>(L)); + auto GlobalValueR = const_cast<GlobalValue *>(dyn_cast<GlobalValue>(R)); if (GlobalValueL && GlobalValueR) { return cmpGlobalValues(GlobalValueL, GlobalValueR); } @@ -383,7 +383,7 @@ int FunctionComparator::cmpConstants(const Constant *L, } } -int FunctionComparator::cmpGlobalValues(Value *L, Value *R) const { +int FunctionComparator::cmpGlobalValues(GlobalValue *L, GlobalValue *R) const { uint64_t LNumber = GlobalNumbers->getNumber(L); uint64_t RNumber = GlobalNumbers->getNumber(R); return cmpNumbers(LNumber, RNumber); From 7e76838bae8b7c34e38123fe907a09e4707e981d Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 25 Oct 2017 18:07:28 -0700 Subject: [PATCH 216/582] [refactor][extract-repeated] Don't add extraneous base 'self'/'this' expression rdar://34202062 apple-llvm-split-commit: 56c0ea86d3309b272dadd2061e7a98ba5ef43cc0 apple-llvm-split-dir: clang/ --- .../ExtractRepeatedExpressionIntoVariable.cpp | 5 ++++- .../extract-repeated-expr-perform.cpp | 16 ++++++++++++++ .../extract-repeated-expr-perform.m | 21 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index 29ecbb15e270c..4641e0e9238b1 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -275,7 +275,10 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( // instead and get the offset from it. unsigned NameOffset = StringRef(OS.str()).find(Name); OS << " = "; - E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy()); + PrintingPolicy ExprPP = Context.getPrintingPolicy(); + ExprPP.SuppressStrongLifetime = true; + ExprPP.SuppressImplicitBase = true; + E->printPretty(OS, /*Helper=*/nullptr, ExprPP); OS << ";\n"; Replacements.push_back(RefactoringReplacement( SourceRange(InsertionLoc, InsertionLoc), OS.str(), CreatedSymbol, diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp index 9329502b4c232..09cfc7b1070a4 100644 --- a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp @@ -98,3 +98,19 @@ void checkFirstStmtInCompoundPlacement(AWrapper &ref) { } // RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:94:5 %s | FileCheck --check-prefix=CHECK6 %s + +class ImplicitThisRewrite { + AWrapper &ref; + ImplicitThisRewrite(AWrapper &ref) : ref(ref) {} + + void method() { + // implicit-this: +1:5 // IMPLICIT-THIS: "AClass &object = this->ref.object(1);\nobject" [[@LINE+1]]:5 -> [[@LINE+1]]:18 + ref.object(1).method(); // IMPLICIT-NO-THIS: "AClass &object = ref.object(1);\nobject" [[@LINE]]:5 -> [[@LINE]]:18 + ref.object(1).constMethod(); // IMPLICIT-THIS-ME: "object" [[@LINE]]:5 -> [[@LINE]]:18 + // implicit-this2: +1:5 + this->ref.object(1).method(); // IMPLICIT-THIS-MENEXT: "object" [[@LINE]]:5 -> [[@LINE]]:24 + } +}; + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=implicit-this %s | FileCheck --check-prefixes=IMPLICIT-NO-THIS,IMPLICIT-THIS-ME %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=implicit-this2 %s | FileCheck --check-prefixes=IMPLICIT-THIS,IMPLICIT-THIS-ME %s diff --git a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m index 23ed359b61df6..cc14c23a866b4 100644 --- a/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m +++ b/clang/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m @@ -96,3 +96,24 @@ void macroArgument(Wrapper *ref) { // RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=macro-arg1 %s | FileCheck --check-prefix=MACRO-ARG1 %s // RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=macro-arg2 %s | FileCheck --check-prefix=MACRO-ARG2 %s + +@interface IVarSelf { + Wrapper *ref; +} + +@end + +@implementation IVarSelf + +- (void)foo { + // ivar-self: +1:3 // IVAR-SELF: "Object *object = self->ref.object;\nobject" [[@LINE+1]]:3 -> [[@LINE+1]]:13 + ref.object.prop = 0; // IVAR-NO-SELF: "Object *object = ref.object;\nobject" [[@LINE]]:3 -> [[@LINE]]:13 + ref.object->ivar = 1; // IVAR: "object" [[@LINE]]:3 -> [[@LINE]]:13 + // ivar-self2: +1:3 + self->ref.object.prop = 2; // IVAR-NEXT: "object" [[@LINE]]:3 -> [[@LINE]]:19 +} + +@end + +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=ivar-self %s | FileCheck --check-prefixes=IVAR-NO-SELF,IVAR %s +// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=ivar-self2 %s | FileCheck --check-prefixes=IVAR-SELF,IVAR %s From 439f6e1d53c759634ac7623da5c6f6864158e3fe Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 10 Nov 2017 10:26:02 -0800 Subject: [PATCH 217/582] fixup index store tests after change in r317832 rdar://35470125 apple-llvm-split-commit: 3123c0b136581df3e1e3603f15ba0807d48f9c02 apple-llvm-split-dir: clang/ --- clang/test/Index/Store/external-source-symbol-hash.m | 2 +- clang/test/Index/Store/record-hash-crash.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/Index/Store/external-source-symbol-hash.m b/clang/test/Index/Store/external-source-symbol-hash.m index 243616d94854f..20dca7cf8ed77 100644 --- a/clang/test/Index/Store/external-source-symbol-hash.m +++ b/clang/test/Index/Store/external-source-symbol-hash.m @@ -18,7 +18,7 @@ @protocol P1; @class I2; // CHECK: [[@LINE-1]]:8 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref | rel: 0 enum E3: int; -// CHECK: [[@LINE-1]]:6 | enum/Swift | c:@M@third_module@E@E3 | Ref | rel: 0 +// CHECK: [[@LINE-1]]:6 | enum/Swift | c:@M@third_module@E@E3 | Decl | rel: 0 void test(id<P1> first, I2 *second, enum E3 third) {} // CHECK: [[@LINE-1]]:14 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref,RelCont | rel: 1 diff --git a/clang/test/Index/Store/record-hash-crash.cpp b/clang/test/Index/Store/record-hash-crash.cpp index 3d71ac9234743..c0f79120c66e4 100644 --- a/clang/test/Index/Store/record-hash-crash.cpp +++ b/clang/test/Index/Store/record-hash-crash.cpp @@ -12,7 +12,7 @@ auto getit() { return []() {}; } } namespace crash2 { -// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Ref,RelCont | rel: 1 +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Decl,RelChild | rel: 1 template <typename T> class Foo; // canonical decl From c1026ff51fb9b8a4d30a20051d6b748a595f249d Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 14 Nov 2017 15:12:59 -0800 Subject: [PATCH 218/582] [refactor][selection] canonicalize callees while looking through implicit casts rdar://35040347 apple-llvm-split-commit: 4a1c53ba01459b9e27b250ef6a0ed7e90f41d423 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/ASTSlice.cpp | 17 ++++++++++++++--- .../Refactor/Extract/extract-expression.cpp | 8 ++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/clang/lib/Tooling/Refactor/ASTSlice.cpp b/clang/lib/Tooling/Refactor/ASTSlice.cpp index 5447571caee8c..ebf6abc286b81 100644 --- a/clang/lib/Tooling/Refactor/ASTSlice.cpp +++ b/clang/lib/Tooling/Refactor/ASTSlice.cpp @@ -246,8 +246,7 @@ canonicalizeSelectedExpr(const Stmt *S, unsigned Index, const Stmt *Parent = NodeTree[Index + 1].getStmtOrNull(); if (!Parent) return Same; - - const auto Next = std::make_pair(Parent, Index + 1); + auto Next = std::make_pair(Parent, Index + 1); // The entire pseudo expression is selected when just its syntactic // form is selected. if (isa<Expr>(S)) { @@ -256,6 +255,18 @@ canonicalizeSelectedExpr(const Stmt *S, unsigned Index, return Next; } } + + // Look through the implicit casts in the parents. + unsigned ParentIndex = Index + 1; + for (; ParentIndex <= NodeTree.size() && isa<ImplicitCastExpr>(Parent); + ++ParentIndex) { + const Stmt *NewParent = NodeTree[ParentIndex + 1].getStmtOrNull(); + if (!NewParent) + break; + Parent = NewParent; + } + Next = std::make_pair(Parent, ParentIndex); + // The entire ObjC string literal is selected when just its string // literal is selected. if (isa<StringLiteral>(S) && isa<ObjCStringLiteral>(Parent)) @@ -271,7 +282,7 @@ canonicalizeSelectedExpr(const Stmt *S, unsigned Index, if (Call->getCalleeDecl() == DRE->getDecl()) return Next; } - } + } return Same; } diff --git a/clang/test/Refactor/Extract/extract-expression.cpp b/clang/test/Refactor/Extract/extract-expression.cpp index 56b26df8487b6..3cef26cd78795 100644 --- a/clang/test/Refactor/Extract/extract-expression.cpp +++ b/clang/test/Refactor/Extract/extract-expression.cpp @@ -58,3 +58,11 @@ void extractStatementExpression(const Rectangle &r) { // CHECK5-NEXT: "extracted(r)" [[@LINE-3]]:3 -> [[@LINE-3]]:21 // RUN: clang-refactor-test perform -action extract -selected=%s:55:3-55:21 %s | FileCheck --check-prefix=CHECK5 %s +; +void extractFunctionCall() { + sumArea(0, 1); +} +// CHECK6: "static int extracted() {\nreturn sumArea(0, 1);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1 +// CHECK6-NEXT: "extracted()" [[@LINE-3]]:3 -> [[@LINE-3]]:16 + +// RUN: clang-refactor-test perform -action extract -selected=%s:63:3-63:10 %s | FileCheck --check-prefix=CHECK6 %s From 8ae004436b5e6e664e1031309482da54b8b59afe Mon Sep 17 00:00:00 2001 From: Ben Langmuir <blangmuir@apple.com> Date: Fri, 17 Nov 2017 10:48:20 -0800 Subject: [PATCH 219/582] [c-index-test] Use BuryPointer for an intentional leak When the index datastore is large, cleaning up can be expensive, and it's pointless since the process is about to exit. Use BuryPointer() so it doesn't get reported as a leak. rdar://35600302 apple-llvm-split-commit: 33fe3c1fe8d3e37fd3915db99044a30e17b3666b apple-llvm-split-dir: clang/ --- clang/tools/c-index-test/JSONAggregation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/tools/c-index-test/JSONAggregation.cpp b/clang/tools/c-index-test/JSONAggregation.cpp index c7f4136bde7e6..83118fcc2385b 100644 --- a/clang/tools/c-index-test/JSONAggregation.cpp +++ b/clang/tools/c-index-test/JSONAggregation.cpp @@ -9,6 +9,7 @@ #include "JSONAggregation.h" #include "indexstore/IndexStoreCXX.h" +#include "clang/Frontend/Utils.h" #include "clang/Index/IndexDataStoreSymbolUtils.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Allocator.h" @@ -398,6 +399,7 @@ bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { if (err) return true; aggregator->dumpJSON(OS); + BuryPointer(aggregator); return false; } From b93d590c15dd31a15b36e25de5b9d4b37c23a68e Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 17 Nov 2017 14:18:45 -0800 Subject: [PATCH 220/582] clang-refactor-test: dispose of the TU rdar://35618000 apple-llvm-split-commit: 65703087ccfeef001abc80fbff775f338a18c7e3 apple-llvm-split-dir: clang/ --- .../clang-refactor-test/ClangRefactorTest.cpp | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index db50367c4ecf8..34532198250a2 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -1350,39 +1350,51 @@ int initiateAndPerformAction(CXTranslationUnit TU, ArrayRef<const char *> Args, return 0; } +struct MainTU { + CXIndex CIdx; + CXTranslationUnit TU = nullptr; + + MainTU() { + CIdx = clang_createIndex(0, 0); + } + ~MainTU() { + if (TU) + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + } +}; + int main(int argc, const char **argv) { cl::HideUnrelatedOptions(opts::ClangRefactorTestOptions); cl::ParseCommandLineOptions(argc, argv, "Clang refactoring test tool\n"); cl::PrintOptionValues(); - CXIndex CIdx = clang_createIndex(0, 0); + MainTU MainTranslationUnit; std::vector<const char *> Args; for (const auto &Arg : opts::CompilerArguments) { Args.push_back(Arg.c_str()); } - CXTranslationUnit TU; CXErrorCode Err = clang_parseTranslationUnit2( - CIdx, + MainTranslationUnit.CIdx, opts::IgnoreFilenameForInitiationTU ? nullptr : opts::FileName.c_str(), - Args.data(), Args.size(), 0, 0, CXTranslationUnit_KeepGoing, &TU); + Args.data(), Args.size(), 0, 0, CXTranslationUnit_KeepGoing, + &MainTranslationUnit.TU); if (Err != CXError_Success) { errs() << "error: failed to load '" << opts::FileName << "'\n"; return 1; } if (opts::RenameInitiateSubcommand || opts::RenameInitiateUSRSubcommand) - return rename(TU, CIdx, Args); + return rename(MainTranslationUnit.TU, MainTranslationUnit.CIdx, Args); else if (opts::RenameIndexedFileSubcommand) - return renameIndexedFile(CIdx, Args); + return renameIndexedFile(MainTranslationUnit.CIdx, Args); else if (opts::ListRefactoringActionsSubcommand) - return listRefactoringActions(TU); + return listRefactoringActions(MainTranslationUnit.TU); else if (opts::InitiateActionSubcommand || opts::PerformActionSubcommand) - return initiateAndPerformAction(TU, Args, CIdx); - - clang_disposeTranslationUnit(TU); - clang_disposeIndex(CIdx); + return initiateAndPerformAction(MainTranslationUnit.TU, Args, + MainTranslationUnit.CIdx); return 0; } From c10ad61a56fbc65271dc65ddd9281aacccf7b34b Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 17 Nov 2017 15:35:32 -0800 Subject: [PATCH 221/582] Add virtual destructor to DeclPredicateNode to avoid a leak in subclass rdar://35618000 apple-llvm-split-commit: b2b1e3151c4e85bbc48ad428c182f60f7626daf9 apple-llvm-split-dir: clang/ --- clang/include/clang/Tooling/Refactor/IndexerQuery.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/include/clang/Tooling/Refactor/IndexerQuery.h b/clang/include/clang/Tooling/Refactor/IndexerQuery.h index 48728930b182e..b90f6d7130255 100644 --- a/clang/include/clang/Tooling/Refactor/IndexerQuery.h +++ b/clang/include/clang/Tooling/Refactor/IndexerQuery.h @@ -145,6 +145,8 @@ class DeclPredicateNode { const char *NameUID; DeclPredicateNode(const char *NameUID) : NameUID(NameUID) {} + virtual ~DeclPredicateNode() { } + static std::unique_ptr<DeclPredicateNode> create(const DeclPredicate &Predicate); static std::unique_ptr<DeclPredicateNode> From d3912a88f5d12ac4f3d1a6d8044f7f714ab0edbc Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 17 Nov 2017 15:37:17 -0800 Subject: [PATCH 222/582] [refactor] dispose of the previous replacement string when merging replacements rdar://35618000 apple-llvm-split-commit: 759e1d864b88782ee07aa8dd079183e3eda15ff1 apple-llvm-split-dir: clang/ --- clang/tools/libclang/CRefactor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index a2465506a934b..201bb5424a3fd 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -910,6 +910,8 @@ class RefactoringResultWrapper { std::string(clang_getCString( FileReplacements[I - NumRemoved - 1].ReplacementString)) + RefReplacement.ReplacementString; + clang_disposeString( + FileReplacements[I - NumRemoved - 1].ReplacementString); FileReplacements[I - NumRemoved - 1].ReplacementString = cxstring::createDup(Replacement); NumRemoved++; @@ -969,6 +971,8 @@ class RefactoringResultWrapper { std::string(clang_getCString( FileReplacements[I - NumRemoved - 1].ReplacementString)) + RefReplacement.ReplacementString; + clang_disposeString( + FileReplacements[I - NumRemoved - 1].ReplacementString); FileReplacements[I - NumRemoved - 1].ReplacementString = cxstring::createDup(Replacement); NumRemoved++; From 782dc0119505515bcfa37078124a8bca187a780a Mon Sep 17 00:00:00 2001 From: Adam Nemet <anemet@apple.com> Date: Fri, 13 Oct 2017 09:50:40 -0700 Subject: [PATCH 223/582] Handle/assert DK_Remark in SourceMgrAdapter We don't generate remarks for API Notes so no need to handle these for now. (cherry picked from commit 3e3c07968dd669600354971757c7c075bdef9e65) apple-llvm-split-commit: d004d20e0c4242d6b5a7dcf1157290c5edaddd03 apple-llvm-split-dir: clang/ --- clang/lib/Basic/SourceMgrAdapter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/lib/Basic/SourceMgrAdapter.cpp b/clang/lib/Basic/SourceMgrAdapter.cpp index 1d52a2438594b..c80112e0dbb15 100644 --- a/clang/lib/Basic/SourceMgrAdapter.cpp +++ b/clang/lib/Basic/SourceMgrAdapter.cpp @@ -112,6 +112,9 @@ void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &diag) { diagID = WarningDiagID; break; + case llvm::SourceMgr::DK_Remark: + llvm_unreachable("remarks not implemented"); + case llvm::SourceMgr::DK_Note: diagID = NoteDiagID; break; From 567d480d538bc8649dcbec824de42459664eb7b1 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai <vsapsai@apple.com> Date: Tue, 28 Nov 2017 12:08:41 -0800 Subject: [PATCH 224/582] Add missing diagnostic string for ObjCClassMethod SubsetSubject. Fixes the build in response to r319002 which added a new parameter to SubsetSubject. rdar://problem/35701313 apple-llvm-split-commit: b94d53a8b4f3c6be05e039a2816939882b5fda85 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index acafa70ebbeb2..5188ba731c87c 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -93,7 +93,8 @@ def NonBitField : SubsetSubject<Field, "non-bit-field non-static data members">; def ObjCClassMethod : SubsetSubject<ObjCMethod, - [{!S->isInstanceMethod()}]>; + [{!S->isInstanceMethod()}], + "Objective-C class methods">; def ObjCInstanceMethod : SubsetSubject<ObjCMethod, [{S->isInstanceMethod()}], From 2763aa71cdff16a7a96ebc0e830248b0ad81cd98 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai <vsapsai@apple.com> Date: Tue, 28 Nov 2017 15:21:57 -0800 Subject: [PATCH 225/582] Add a missing comma lost during merge. rdar://problem/35701313 apple-llvm-split-commit: 9e355bbead50a23cc0d113ecf6ae886d4b831a74 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/PrettyPrinter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index dd99d6b42ad24..76f3ca49139a9 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -52,7 +52,7 @@ struct PrintingPolicy { Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), ConstantsAsWritten(false), SuppressImplicitBase(false), - UseStdFunctionForLambda(false) + UseStdFunctionForLambda(false), FullyQualifiedName(false) { } /// Adjust this printing policy for cases where it's known that we're From 3171ceb84ebe4872da6638b0b90ad6367a7dacf9 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai <vsapsai@apple.com> Date: Wed, 29 Nov 2017 13:08:31 -0800 Subject: [PATCH 226/582] Update test for the new diagnostic text. Change is caused by r319002 which introduced taking attribute subject for diagnostic from declarative information. Swap order of subjects for SwiftError to preserve order of entities in the diagnostic. rdar://problem/35701313 apple-llvm-split-commit: ab58a5c1c7b53f3b4008b07262fc19faa99f377a apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 2 +- clang/test/Misc/pragma-attribute-supported-attributes-list.test | 2 +- clang/test/SemaObjC/attr-swift.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 5188ba731c87c..abdf418dae3ab 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1693,7 +1693,7 @@ def SwiftError : InheritableAttr { let Args = [EnumArgument<"Convention", "ConventionKind", ["none", "nonnull_error", "null_result", "zero_result", "nonzero_result"], ["None", "NonNullError", "NullResult", "ZeroResult", "NonZeroResult"]>]; - let Subjects = SubjectList<[ObjCMethod, Function], ErrorDiag>; + let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; let Documentation = [SwiftErrorDocs]; } diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index ab7fe80bdea87..448196fd6534a 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -62,7 +62,7 @@ // CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) // CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter) -// CHECK-NEXT: SwiftError (SubjectMatchRule_objc_method, SubjectMatchRule_function) +// CHECK-NEXT: SwiftError (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: SwiftNewtype (SubjectMatchRule_type_alias) diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index bdcbbd0ace605..e87ac913dd804 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -214,4 +214,4 @@ - (instancetype) harry4 __attribute__((swift_error(zero_result))); // expected-e extern void *wilma4(void) __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}} -extern _Bool suzanne __attribute__((swift_error(none))); // expected-error {{'swift_error' attribute only applies to functions and methods}} +extern _Bool suzanne __attribute__((swift_error(none))); // expected-error {{'swift_error' attribute only applies to functions and Objective-C methods}} From 47ee151b7b21916ba43905b86de7998fdd7284fd Mon Sep 17 00:00:00 2001 From: Thomas Roughton <troughton@users.noreply.github.com> Date: Thu, 30 Nov 2017 13:41:23 -0500 Subject: [PATCH 227/582] Fix Windows support for Driver and add support to Index (#127) * Migrate uses of timespec to llvm::sys::TimePoint<> * Remove now-unnecessary toTimeSpec from DirectoryWatcher * Use SafelyCloseFileDescriptor instead of ::close * Remove <io.h> include. * Remove now-unnecessary <unistd.h> include. apple-llvm-split-commit: 88871e532dc99b29af093222ea63dbe453d120dc apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexDataStore.h | 5 +++-- .../lib/DirectoryWatcher/DirectoryWatcher.cpp | 20 ++++--------------- clang/lib/Index/IndexUnitReader.cpp | 5 ++--- clang/tools/IndexStore/IndexStore.cpp | 2 +- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h index 714ccddc8c450..c6b3e40e39650 100644 --- a/clang/include/clang/Index/IndexDataStore.h +++ b/clang/include/clang/Index/IndexDataStore.h @@ -14,6 +14,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Chrono.h" #include <functional> #include <memory> #include <string> @@ -38,7 +39,7 @@ class AbstractDirectoryWatcher { struct Event { EventKind Kind; std::string Filename; - timespec ModTime; + llvm::sys::TimePoint<> ModTime; }; typedef std::function<void(ArrayRef<Event> Events, bool isInitial)> EventReceiver; @@ -71,7 +72,7 @@ class IndexDataStore { struct UnitEvent { UnitEventKind Kind; StringRef UnitName; - timespec ModTime; + llvm::sys::TimePoint<> ModTime; }; struct UnitEventNotification { bool IsInitial; diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp index 3a90526aac756..b5434476504f3 100644 --- a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp +++ b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp @@ -31,24 +31,12 @@ using namespace clang; using namespace llvm; -static timespec toTimeSpec(sys::TimePoint<> tp) { - std::chrono::seconds sec = std::chrono::time_point_cast<std::chrono::seconds>( - tp).time_since_epoch(); - std::chrono::nanoseconds nsec = - std::chrono::time_point_cast<std::chrono::nanoseconds>(tp - sec) - .time_since_epoch(); - timespec ts; - ts.tv_sec = sec.count(); - ts.tv_nsec = nsec.count(); - return ts; -} - -static Optional<timespec> getModTime(StringRef path) { +static Optional<llvm::sys::TimePoint<>> getModTime(StringRef path) { sys::fs::file_status Status; std::error_code EC = status(path, Status); if (EC) return None; - return toTimeSpec(Status.getLastModificationTime()); + return Status.getLastModificationTime(); } struct DirectoryWatcher::Implementation { @@ -96,7 +84,7 @@ static void eventStreamCallback( const FSEventStreamEventFlags flags = eventFlags[i]; if (!(flags & kFSEventStreamEventFlagItemIsFile)) { if ((flags & kFSEventStreamEventFlagItemRemoved) && path == ctx->WatchedPath) { - DirectoryWatcher::Event Evt{DirectoryWatcher::EventKind::DirectoryDeleted, path, timespec{}}; + DirectoryWatcher::Event Evt{DirectoryWatcher::EventKind::DirectoryDeleted, path, llvm::sys::TimePoint<>{} }; Events.push_back(Evt); break; } @@ -108,7 +96,7 @@ static void eventStreamCallback( K = DirectoryWatcher::EventKind::Added; if (flags & kFSEventStreamEventFlagItemRemoved) K = DirectoryWatcher::EventKind::Removed; - timespec modTime{}; + llvm::sys::TimePoint<> modTime{}; if (K != DirectoryWatcher::EventKind::Removed) { auto modTimeOpt = getModTime(path); if (!modTimeOpt.hasValue()) diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp index 12a905616f16b..fa8e9e924ef35 100644 --- a/clang/lib/Index/IndexUnitReader.cpp +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -19,10 +19,9 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" -#include <unistd.h> - using namespace clang; using namespace clang::index; using namespace clang::index::store; @@ -400,7 +399,7 @@ IndexUnitReader::createWithFilePath(StringRef FilePath, std::string &Error) { int FD; AutoFDClose(int FD) : FD(FD) {} ~AutoFDClose() { - ::close(FD); + llvm::sys::Process::SafelyCloseFileDescriptor(FD); } } AutoFDClose(FD); diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index 422652499c994..b24cde8663a7e 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -139,7 +139,7 @@ indexstore_unit_event_get_unit_name(indexstore_unit_event_t c_evt) { timespec indexstore_unit_event_get_modification_time(indexstore_unit_event_t c_evt) { auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt); - return evt->ModTime; + return toTimeSpec(evt->ModTime); } void From f3640bebd105cce825cd367be187f37fd9f32d70 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 6 Dec 2017 17:13:28 -0500 Subject: [PATCH 228/582] Add intrinsics for doing frame-bound dynamic allocations within a coroutine. These rely on having an allocator provided to the coroutine and thus, for now, only work in retcon lowerings. apple-llvm-split-commit: 09293c6c48bbeb84ae5e4c982d71d684ac6426fe apple-llvm-split-dir: llvm/ --- llvm/include/llvm/IR/Intrinsics.td | 4 + llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 178 ++++++++++++++ llvm/lib/Transforms/Coroutines/CoroInstr.h | 54 +++++ llvm/lib/Transforms/Coroutines/Coroutines.cpp | 13 +- .../Coroutines/coro-retcon-alloca.ll | 219 ++++++++++++++++++ 5 files changed, 464 insertions(+), 4 deletions(-) create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index 52ed1c3769862..41f888013cbcc 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -781,6 +781,10 @@ def int_coro_suspend : Intrinsic<[llvm_i8_ty], [llvm_token_ty, llvm_i1_ty], []>; def int_coro_suspend_retcon : Intrinsic<[llvm_any_ty], [llvm_vararg_ty], []>; def int_coro_prepare_retcon : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty], [IntrNoMem]>; +def int_coro_alloca_alloc : Intrinsic<[llvm_token_ty], + [llvm_anyint_ty, llvm_i32_ty], []>; +def int_coro_alloca_get : Intrinsic<[llvm_ptr_ty], [llvm_token_ty], []>; +def int_coro_alloca_free : Intrinsic<[], [llvm_token_ty], []>; def int_coro_param : Intrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_ptr_ty], [IntrNoMem, ReadNone<0>, ReadNone<1>]>; diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp index cee26be8df5a1..95d4100a6c945 100644 --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -840,6 +840,155 @@ static void splitAround(Instruction *I, const Twine &Name) { splitBlockIfNotFirst(I->getNextNode(), "After" + Name); } +static bool isSuspendBlock(BasicBlock *BB) { + return isa<AnyCoroSuspendInst>(BB->front()); +} + +typedef SmallPtrSet<BasicBlock*, 8> VisitedBlocksSet; + +/// Does control flow starting at the given block ever reach a suspend +/// instruction before reaching a block in VisitedOrFreeBBs? +static bool isSuspendReachableFrom(BasicBlock *From, + VisitedBlocksSet &VisitedOrFreeBBs) { + // Eagerly try to add this block to the visited set. If it's already + // there, stop recursing; this path doesn't reach a suspend before + // either looping or reaching a freeing block. + if (!VisitedOrFreeBBs.insert(From).second) + return false; + + // We assume that we'll already have split suspends into their own blocks. + if (isSuspendBlock(From)) + return true; + + // Recurse on the successors. + for (auto Succ : successors(From)) { + if (isSuspendReachableFrom(Succ, VisitedOrFreeBBs)) + return true; + } + + return false; +} + +/// Is the given alloca "local", i.e. bounded in lifetime to not cross a +/// suspend point? +static bool isLocalAlloca(CoroAllocaAllocInst *AI) { + // Seed the visited set with all the basic blocks containing a free + // so that we won't pass them up. + VisitedBlocksSet VisitedOrFreeBBs; + for (auto User : AI->users()) { + if (auto FI = dyn_cast<CoroAllocaFreeInst>(User)) + VisitedOrFreeBBs.insert(FI->getParent()); + } + + return !isSuspendReachableFrom(AI->getParent(), VisitedOrFreeBBs); +} + +/// After we split the coroutine, will the given basic block be along +/// an obvious exit path for the resumption function? +static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB, + unsigned depth = 3) { + // If we've bottomed out our depth count, stop searching and assume + // that the path might loop back. + if (depth == 0) return false; + + // If this is a suspend block, we're about to exit the resumption function. + if (isSuspendBlock(BB)) return true; + + // Recurse into the successors. + for (auto Succ : successors(BB)) { + if (!willLeaveFunctionImmediatelyAfter(Succ, depth - 1)) + return false; + } + + // If none of the successors leads back in a loop, we're on an exit/abort. + return true; +} + +static bool localAllocaNeedsStackSave(CoroAllocaAllocInst *AI) { + // Look for a free that isn't sufficiently obviously followed by + // either a suspend or a termination, i.e. something that will leave + // the coro resumption frame. + for (auto U : AI->users()) { + auto FI = dyn_cast<CoroAllocaFreeInst>(U); + if (!FI) continue; + + if (!willLeaveFunctionImmediatelyAfter(FI->getParent())) + return true; + } + + // If we never found one, we don't need a stack save. + return false; +} + +/// Turn each of the given local allocas into a normal (dynamic) alloca +/// instruction. +static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas) { + for (auto AI : LocalAllocas) { + auto M = AI->getModule(); + IRBuilder<> Builder(AI); + + // Save the stack depth. Try to avoid doing this if the stackrestore + // is going to immediately precede a return or something. + Value *StackSave = nullptr; + if (localAllocaNeedsStackSave(AI)) + StackSave = Builder.CreateCall( + Intrinsic::getDeclaration(M, Intrinsic::stacksave)); + + // Allocate memory. + auto Alloca = Builder.CreateAlloca(Builder.getInt8Ty(), AI->getSize()); + Alloca->setAlignment(AI->getAlignment()); + + for (auto U : AI->users()) { + // Replace gets with the allocation. + if (isa<CoroAllocaGetInst>(U)) { + U->replaceAllUsesWith(Alloca); + + // Replace frees with stackrestores. This is safe because + // alloca.alloc is required to obey a stack discipline, although we + // don't enforce that structurally. + } else { + auto FI = cast<CoroAllocaFreeInst>(U); + if (StackSave) { + Builder.SetInsertPoint(FI); + Builder.CreateCall( + Intrinsic::getDeclaration(M, Intrinsic::stackrestore), + StackSave); + } + } + cast<Instruction>(U)->eraseFromParent(); + } + + AI->eraseFromParent(); + } +} + +/// Turn the given coro.alloca.alloc call into a dynamic allocation. +/// This happens during the all-instructions iteration, so it must not +/// delete the call. +static Instruction *lowerNonLocalAlloca(CoroAllocaAllocInst *AI, + coro::Shape &Shape, + SmallVectorImpl<Instruction*> &DeadInsts) { + IRBuilder<> Builder(AI); + auto Alloc = Shape.emitAlloc(Builder, AI->getSize(), nullptr); + + for (User *U : AI->users()) { + if (isa<CoroAllocaGetInst>(U)) { + U->replaceAllUsesWith(Alloc); + } else { + auto FI = cast<CoroAllocaFreeInst>(U); + Builder.SetInsertPoint(FI); + Shape.emitDealloc(Builder, Alloc, nullptr); + } + DeadInsts.push_back(cast<Instruction>(U)); + } + + // Push this on last so that it gets deleted after all the others. + DeadInsts.push_back(AI); + + // Return the new allocation value so that we can check for needed spills. + return cast<Instruction>(Alloc); +} + void coro::buildCoroutineFrame(Function &F, Shape &Shape) { // Lower coro.dbg.declare to coro.dbg.value, since we are going to rewrite // access to local variables. @@ -872,6 +1021,8 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { IRBuilder<> Builder(F.getContext()); SpillInfo Spills; + SmallVector<CoroAllocaAllocInst*, 4> LocalAllocas; + SmallVector<Instruction*, 4> DeadInstructions; for (int Repeat = 0; Repeat < 4; ++Repeat) { // See if there are materializable instructions across suspend points. @@ -901,12 +1052,35 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { // of the Coroutine Frame. if (isCoroutineStructureIntrinsic(I) || &I == Shape.CoroBegin) continue; + // The Coroutine Promise always included into coroutine frame, no need to // check for suspend crossing. if (Shape.ABI == coro::ABI::Switch && Shape.SwitchLowering.PromiseAlloca == &I) continue; + // Handle alloca.alloc specially here. + if (auto AI = dyn_cast<CoroAllocaAllocInst>(&I)) { + // Check whether the alloca's lifetime is bounded by suspend points. + if (isLocalAlloca(AI)) { + LocalAllocas.push_back(AI); + continue; + } + + // If not, do a quick rewrite of the alloca and then add spills of + // the rewritten value. The rewrite doesn't invalidate anything in + // Spills because the other alloca intrinsics have no other operands + // besides AI, and it doesn't invalidate the iteration because we delay + // erasing AI. + auto Alloc = lowerNonLocalAlloca(AI, Shape, DeadInstructions); + + for (User *U : Alloc->users()) { + if (Checker.isDefinitionAcrossSuspend(*Alloc, U)) + Spills.emplace_back(Alloc, U); + } + continue; + } + for (User *U : I.users()) if (Checker.isDefinitionAcrossSuspend(I, U)) { // We cannot spill a token. @@ -920,4 +1094,8 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { moveSpillUsesAfterCoroBegin(F, Spills, Shape.CoroBegin); Shape.FrameTy = buildFrameType(F, Shape, Spills); Shape.FramePtr = insertSpills(Spills, Shape); + lowerLocalAllocas(LocalAllocas); + + for (auto I : DeadInstructions) + I->eraseFromParent(); } diff --git a/llvm/lib/Transforms/Coroutines/CoroInstr.h b/llvm/lib/Transforms/Coroutines/CoroInstr.h index cc4a55caf7de9..bdb6532da3633 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInstr.h +++ b/llvm/lib/Transforms/Coroutines/CoroInstr.h @@ -457,6 +457,60 @@ class LLVM_LIBRARY_VISIBILITY CoroEndInst : public IntrinsicInst { } }; +/// This represents the llvm.coro.alloca.alloc instruction. +class LLVM_LIBRARY_VISIBILITY CoroAllocaAllocInst : public IntrinsicInst { + enum { SizeArg, AlignArg }; +public: + Value *getSize() const { + return getArgOperand(SizeArg); + } + unsigned getAlignment() const { + return cast<ConstantInt>(getArgOperand(AlignArg))->getZExtValue(); + } + + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::coro_alloca_alloc; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + +/// This represents the llvm.coro.alloca.get instruction. +class LLVM_LIBRARY_VISIBILITY CoroAllocaGetInst : public IntrinsicInst { + enum { AllocArg }; +public: + CoroAllocaAllocInst *getAlloc() const { + return cast<CoroAllocaAllocInst>(getArgOperand(AllocArg)); + } + + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::coro_alloca_get; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + +/// This represents the llvm.coro.alloca.free instruction. +class LLVM_LIBRARY_VISIBILITY CoroAllocaFreeInst : public IntrinsicInst { + enum { AllocArg }; +public: + CoroAllocaAllocInst *getAlloc() const { + return cast<CoroAllocaAllocInst>(getArgOperand(AllocArg)); + } + + // Methods to support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::coro_alloca_free; + } + static bool classof(const Value *V) { + return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V)); + } +}; + } // End namespace llvm. #endif diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp index aa785d5a329c6..951e85a0e89ca 100644 --- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -423,10 +423,15 @@ void coro::Shape::buildFrom(Function &F) { // Check that the result type of the suspend matches the resume types. Type *SResultTy = Suspend->getType(); - ArrayRef<Type*> SuspendResultTys = - (isa<StructType>(SResultTy) - ? cast<StructType>(SResultTy)->elements() - : SResultTy); // forms an ArrayRef using SResultTy, be careful + ArrayRef<Type*> SuspendResultTys; + if (SResultTy->isVoidTy()) { + // leave as empty array + } else if (auto SResultStructTy = dyn_cast<StructType>(SResultTy)) { + SuspendResultTys = SResultStructTy->elements(); + } else { + // forms an ArrayRef using SResultTy, be careful + SuspendResultTys = SResultTy; + } if (SuspendResultTys.size() != ResumeTys.size()) { #ifndef NDEBUG Suspend->dump(); diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll new file mode 100644 index 0000000000000..65ef3c48d6560 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll @@ -0,0 +1,219 @@ +; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s + +target datalayout = "p:64:64:64" + +declare {i8*, i8*, i32} @prototype_f(i8*, i1) +define {i8*, i8*, i32} @f(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 1024, i32 8, i8* %buffer, i8* bitcast ({i8*, i8*, i32} (i8*, i1)* @prototype_f to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + %alloca = call token @llvm.coro.alloca.alloc.i32(i32 %n.val, i32 8) + %ptr = call i8* @llvm.coro.alloca.get(token %alloca) + %unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(i8* %ptr, i32 %n.val) + call void @llvm.coro.alloca.free(token %alloca) + br i1 %unwind, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define { i8*, i8*, i32 } @f(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: [[ALLOC:%.*]] = tail call i8* @allocate(i32 %n) +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, i8* %buffer, i64 8 +; CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i8** +; CHECK-NEXT: store i8* [[ALLOC]], i8** [[T1]], align 8 +; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i8*, i32 } { i8* bitcast ({ i8*, i8*, i32 } (i8*, i1)* @f.resume.0 to i8*), i8* undef, i32 undef }, i8* [[ALLOC]], 1 +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i8*, i32 } [[T0]], i32 %n, 2 +; CHECK-NEXT: ret { i8*, i8*, i32 } [[RET]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal { i8*, i8*, i32 } @f.resume.0(i8* noalias nonnull, i1) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, i8* %0, i64 8 +; CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i8** +; CHECK-NEXT: [[ALLOC:%.*]] = load i8*, i8** [[T1]], align 8 +; CHECK-NEXT: tail call void @deallocate(i8* [[ALLOC]]) +; CHECK-NEXT: br i1 %1, + +declare {i8*, i32} @prototype_g(i8*, i1) +define {i8*, i32} @g(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 1024, i32 8, i8* %buffer, i8* bitcast ({i8*, i32} (i8*, i1)* @prototype_g to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + %alloca = call token @llvm.coro.alloca.alloc.i32(i32 %n.val, i32 8) + %ptr = call i8* @llvm.coro.alloca.get(token %alloca) + call void @use(i8* %ptr) + call void @llvm.coro.alloca.free(token %alloca) + %unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(i32 %n.val) + br i1 %unwind, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define { i8*, i32 } @g(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: [[T0:%.*]] = zext i32 %n to i64 +; CHECK-NEXT: [[ALLOC:%.*]] = alloca i8, i64 [[T0]], align 8 +; CHECK-NEXT: call void @use(i8* nonnull [[ALLOC]]) +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*, i1)* @g.resume.0 to i8*), i32 undef }, i32 %n, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal { i8*, i32 } @g.resume.0(i8* noalias nonnull, i1) +; CHECK-NEXT: : +; CHECK-NEXT: br i1 %1, +; CHECK: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: %inc = add i32 [[T1]], 1 +; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 +; CHECK-NEXT: [[T0:%.*]] = zext i32 %inc to i64 +; CHECK-NEXT: [[ALLOC:%.*]] = alloca i8, i64 [[T0]], align 8 +; CHECK-NEXT: call void @use(i8* nonnull [[ALLOC]]) +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*, i1)* @g.resume.0 to i8*), i32 undef }, i32 %inc, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK: : +; CHECK-NEXT: ret { i8*, i32 } { i8* null, i32 undef } + +declare {i8*, i32} @prototype_h(i8*, i1) +define {i8*, i32} @h(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 1024, i32 8, i8* %buffer, i8* bitcast ({i8*, i32} (i8*, i1)* @prototype_h to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + %unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(i32 %n.val) + br i1 %unwind, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + %alloca = call token @llvm.coro.alloca.alloc.i32(i32 %inc, i32 8) + %ptr = call i8* @llvm.coro.alloca.get(token %alloca) + call void @use(i8* %ptr) + call void @llvm.coro.alloca.free(token %alloca) + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define { i8*, i32 } @h(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*, i1)* @h.resume.0 to i8*), i32 undef }, i32 %n, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal { i8*, i32 } @h.resume.0(i8* noalias nonnull, i1) +; CHECK-NEXT: : +; CHECK-NEXT: br i1 %1, +; CHECK: : +; CHECK-NEXT: [[NSLOT:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[NSLOT]], align 4 +; CHECK-NEXT: %inc = add i32 [[T1]], 1 +; CHECK-NEXT: [[T0:%.*]] = zext i32 %inc to i64 +; CHECK-NEXT: [[ALLOC:%.*]] = alloca i8, i64 [[T0]], align 8 +; CHECK-NEXT: call void @use(i8* nonnull [[ALLOC]]) +; CHECK-NEXT: store i32 %inc, i32* [[NSLOT]], align 4 +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*, i1)* @h.resume.0 to i8*), i32 undef }, i32 %inc, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK: : +; CHECK-NEXT: ret { i8*, i32 } { i8* null, i32 undef } + +declare {i8*, i32} @prototype_i(i8*) +define {i8*, i32} @i(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 1024, i32 8, i8* %buffer, i8* bitcast ({i8*, i32} (i8*)* @prototype_i to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %k, %loop2 ] + call void (...) @llvm.coro.suspend.retcon.isVoid(i32 %n.val) + %inc = add i32 %n.val, 1 + br label %loop2 + +loop2: + %k = phi i32 [ %inc, %loop ], [ %k2, %loop2 ] + %alloca = call token @llvm.coro.alloca.alloc.i32(i32 %k, i32 8) + %ptr = call i8* @llvm.coro.alloca.get(token %alloca) + call void @use(i8* %ptr) + call void @llvm.coro.alloca.free(token %alloca) + %k2 = lshr i32 %k, 1 + %cmp = icmp ugt i32 %k, 128 + br i1 %cmp, label %loop2, label %loop +} + +; CHECK-LABEL: define { i8*, i32 } @i(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*)* @i.resume.0 to i8*), i32 undef }, i32 %n, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal { i8*, i32 } @i.resume.0(i8* noalias nonnull) +; CHECK-NEXT: : +; CHECK-NEXT: [[NSLOT:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[NSLOT]], align 4 +; CHECK-NEXT: %inc = add i32 [[T1]], 1 +; CHECK-NEXT: br label %loop2 +; CHECK: : +; CHECK-NEXT: store i32 %k, i32* [[NSLOT]], align 4 +; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*)* @i.resume.0 to i8*), i32 undef }, i32 %k, 1 +; CHECK-NEXT: ret { i8*, i32 } [[RET]] +; CHECK: loop2: +; CHECK-NEXT: %k = phi i32 [ %inc, {{.*}} ], [ %k2, %loop2 ] +; CHECK-NEXT: [[SAVE:%.*]] = call i8* @llvm.stacksave() +; CHECK-NEXT: [[T0:%.*]] = zext i32 %k to i64 +; CHECK-NEXT: [[ALLOC:%.*]] = alloca i8, i64 [[T0]], align 8 +; CHECK-NEXT: call void @use(i8* nonnull [[ALLOC]]) +; CHECK-NEXT: call void @llvm.stackrestore(i8* [[SAVE]]) +; CHECK-NEXT: %cmp = icmp ugt i32 %k, 128 +; CHECK-NEXT: %k2 = lshr i32 %k, 1 +; CHECK-NEXT: br i1 %cmp, label %loop2, +; CHECK-NEXT: } + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare i1 @llvm.coro.suspend.retcon.i1(...) +declare void @llvm.coro.suspend.retcon.isVoid(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) +declare token @llvm.coro.alloca.alloc.i32(i32, i32) +declare i8* @llvm.coro.alloca.get(token) +declare void @llvm.coro.alloca.free(token) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare void @print(i32) +declare void @use(i8*) From 099c5297c42c4277a36477e9fb92d47e0255928c Mon Sep 17 00:00:00 2001 From: Davide Italiano <ditaliano@apple.com> Date: Wed, 6 Dec 2017 19:49:15 -0800 Subject: [PATCH 229/582] [tools] Add private to link_target_libraries. apple-llvm-split-commit: 0e79e80d40bdef3ee6f01dff9a029bca153aae9e apple-llvm-split-dir: clang/ --- clang/tools/c-index-test/CMakeLists.txt | 2 +- clang/tools/clang-refactor-test/CMakeLists.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/tools/c-index-test/CMakeLists.txt b/clang/tools/c-index-test/CMakeLists.txt index 7af1447b5d74d..82245dc08ad53 100644 --- a/clang/tools/c-index-test/CMakeLists.txt +++ b/clang/tools/c-index-test/CMakeLists.txt @@ -60,7 +60,7 @@ endif() if(APPLE) check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) if(HAVE_CORESERVICES_H) - target_link_libraries(c-index-test "-framework CoreServices") + target_link_libraries(c-index-test PRIVATE "-framework CoreServices") endif() endif() diff --git a/clang/tools/clang-refactor-test/CMakeLists.txt b/clang/tools/clang-refactor-test/CMakeLists.txt index f742327ea3989..1817ed00addbb 100644 --- a/clang/tools/clang-refactor-test/CMakeLists.txt +++ b/clang/tools/clang-refactor-test/CMakeLists.txt @@ -8,10 +8,12 @@ add_clang_executable(clang-refactor-test if (LLVM_BUILD_STATIC) target_link_libraries(clang-refactor-test + PRIVATE libclang_static ) else() target_link_libraries(clang-refactor-test + PRIVATE libclang clangBasic clangFrontend From 2f75858fffd02f3b4f3804607fadc055c193b2ab Mon Sep 17 00:00:00 2001 From: Davide Italiano <ditaliano@apple.com> Date: Thu, 7 Dec 2017 12:59:02 -0800 Subject: [PATCH 230/582] [IndexingAction] Fix build failure due to a mismerge. I need this to unbreak swift-lldb. apple-llvm-split-commit: d58a78c93494199877fca9abc93436a2a2193c22 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexingAction.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index ad66e8cc161c9..28dedea6a703b 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -451,7 +451,8 @@ class IndexRecordActionBase { DepCollector.setSysrootPath(IndexCtx.getSysrootPath()); DepCollector.attachToPreprocessor(PP); - return llvm::make_unique<IndexASTConsumer>(IndexCtx); + return llvm::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), + IndexCtx); } void finish(CompilerInstance &CI); From c90a5909ed68c21bfcde3f59f94a134b68e95979 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Sat, 9 Dec 2017 21:54:33 -0800 Subject: [PATCH 231/582] Use add_llvm_install_targets for IndexStore. rdar://35954715 apple-llvm-split-commit: f304e4dd156269d2c0c71fb61cf35ad617af4f36 apple-llvm-split-dir: clang/ --- clang/tools/IndexStore/CMakeLists.txt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/tools/IndexStore/CMakeLists.txt b/clang/tools/IndexStore/CMakeLists.txt index 8ad6499117616..616c402c145a0 100644 --- a/clang/tools/IndexStore/CMakeLists.txt +++ b/clang/tools/IndexStore/CMakeLists.txt @@ -74,11 +74,9 @@ if (LLVM_INSTALL_TOOLCHAIN_ONLY) RUNTIME DESTINATION bin) if (NOT CMAKE_CONFIGURATION_TYPES) - add_custom_target(install-IndexStore - DEPENDS IndexStore - COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=IndexStore - -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + add_llvm_install_targets(install-IndexStore + DEPENDS IndexStore + COMPONENT IndexStore) endif() endif() From 868a471bdc4cc9b41b11dc04cd6b026ae53c72c4 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <aleksei_lorenz@apple.com> Date: Mon, 11 Dec 2017 13:45:42 -0800 Subject: [PATCH 232/582] Fix the DirectoryWatcher link failrue in shared libs build of clang rdar://33603328 apple-llvm-split-commit: d322ea715b3f492d64a171f788ad208b34ae34cd apple-llvm-split-dir: clang/ --- clang/lib/DirectoryWatcher/CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clang/lib/DirectoryWatcher/CMakeLists.txt b/clang/lib/DirectoryWatcher/CMakeLists.txt index 425a40ff4509e..b70e92526592e 100644 --- a/clang/lib/DirectoryWatcher/CMakeLists.txt +++ b/clang/lib/DirectoryWatcher/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CheckIncludeFiles) + set(LLVM_LINK_COMPONENTS support) add_clang_library(clangDirectoryWatcher @@ -6,3 +8,14 @@ add_clang_library(clangDirectoryWatcher LINK_LIBS clangBasic ) + +if(BUILD_SHARED_LIBS) + if(APPLE) + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + set(DIRECTORY_WATCHER_FLAGS "${DIRECTORY_WATCHER_FLAGS} -framework CoreServices") + endif() + set_property(TARGET clangDirectoryWatcher APPEND_STRING PROPERTY + LINK_FLAGS ${DIRECTORY_WATCHER_FLAGS}) + endif() +endif() From 85fbb31f785405544d2149df7b1bd80105c7a621 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <aleksei_lorenz@apple.com> Date: Mon, 11 Dec 2017 13:48:28 -0800 Subject: [PATCH 233/582] The refactoring library should link with libFrontend rdar://33603328 apple-llvm-split-commit: c1b3d5a87bd44fcd3abb52dbe2d1e72afd0e7303 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Tooling/Refactor/CMakeLists.txt b/clang/lib/Tooling/Refactor/CMakeLists.txt index 9828c1ac6813c..bd93afa13d0a0 100644 --- a/clang/lib/Tooling/Refactor/CMakeLists.txt +++ b/clang/lib/Tooling/Refactor/CMakeLists.txt @@ -37,6 +37,7 @@ add_clang_library(clangToolingRefactor clangASTMatchers clangBasic clangEdit + clangFrontend clangIndex clangLex clangToolingCore From 6e4a2f275ccc0f218a7528f4705f2d67c3056568 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <aleksei_lorenz@apple.com> Date: Mon, 11 Dec 2017 13:53:11 -0800 Subject: [PATCH 234/582] Fixup missing refactoring libs rdar://33603328 apple-llvm-split-commit: f6c2277604c880eda197cb817f4b4f4cc2093ad4 apple-llvm-split-dir: clang/ --- clang/tools/libclang/CMakeLists.txt | 1 + clang/unittests/Tooling/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt index d19320c5b2c02..c16e7d3841d41 100644 --- a/clang/tools/libclang/CMakeLists.txt +++ b/clang/tools/libclang/CMakeLists.txt @@ -43,6 +43,7 @@ set(LIBS clangLex clangSema clangTooling + clangToolingRefactor ) if (CLANG_ENABLE_ARCMT) diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt index 557d1007ae2c6..8135d884eb8ff 100644 --- a/clang/unittests/Tooling/CMakeLists.txt +++ b/clang/unittests/Tooling/CMakeLists.txt @@ -46,4 +46,5 @@ target_link_libraries(ToolingTests clangTooling clangToolingCore clangToolingRefactor + clangToolingRefactoring ) From 1edf40f570edb20fdb02fc4c2d532e1cc5c9ca10 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <aleksei_lorenz@apple.com> Date: Mon, 11 Dec 2017 14:39:53 -0800 Subject: [PATCH 235/582] Fixup remaining internal refactoring shared libs linkage issues rdar://problem/33603328 apple-llvm-split-commit: f24715bfa870c0dc82467e2ebeb95474c7f8e8bd apple-llvm-split-dir: clang/ --- clang/tools/clang-refactor/CMakeLists.txt | 1 + clang/tools/clang-rename/CMakeLists.txt | 1 + clang/unittests/Rename/CMakeLists.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/clang/tools/clang-refactor/CMakeLists.txt b/clang/tools/clang-refactor/CMakeLists.txt index d2029066b9b76..527751c344d10 100644 --- a/clang/tools/clang-refactor/CMakeLists.txt +++ b/clang/tools/clang-refactor/CMakeLists.txt @@ -19,6 +19,7 @@ target_link_libraries(clang-refactor clangTooling clangToolingCore clangToolingRefactor + clangToolingRefactoring ) install(TARGETS clang-refactor RUNTIME DESTINATION bin) diff --git a/clang/tools/clang-rename/CMakeLists.txt b/clang/tools/clang-rename/CMakeLists.txt index 9689e1c6804d4..46f39f2d0ca55 100644 --- a/clang/tools/clang-rename/CMakeLists.txt +++ b/clang/tools/clang-rename/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries(clang-rename clangTooling clangToolingCore clangToolingRefactor + clangToolingRefactoring ) install(PROGRAMS clang-rename.py diff --git a/clang/unittests/Rename/CMakeLists.txt b/clang/unittests/Rename/CMakeLists.txt index b625a7a691fbb..b1ed1fea2d972 100644 --- a/clang/unittests/Rename/CMakeLists.txt +++ b/clang/unittests/Rename/CMakeLists.txt @@ -24,4 +24,5 @@ target_link_libraries(ClangRenameTests clangTooling clangToolingCore clangToolingRefactor + clangToolingRefactoring ) From de0156416afa13fea9ce733ec66ca1da2a625393 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 13 Nov 2017 14:34:54 -0800 Subject: [PATCH 236/582] [rename] Find ObjC class textual occurrences in string literals rdar://33861206 apple-llvm-split-commit: 67399e97f6e36ebd58246b154377a25e0033474a apple-llvm-split-dir: clang/ --- clang/include/clang-c/Refactor.h | 6 +++ .../Tooling/Refactor/RenameIndexedFile.h | 9 +++- .../clang/Tooling/Refactor/RenamedSymbol.h | 5 +- .../Tooling/Refactor/RenameIndexedFile.cpp | 51 +++++++++++++++++-- .../Rename/IndexedObjCClassStringLiteral.mm | 21 ++++++++ .../test/Refactor/Rename/IndexedObjCMethod.m | 2 +- .../clang-refactor-test/ClangRefactorTest.cpp | 3 ++ clang/tools/libclang/CRefactor.cpp | 10 ++-- 8 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 clang/test/Refactor/Rename/IndexedObjCClassStringLiteral.mm diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h index b11cfb83b121b..d6e8d84eb64d1 100644 --- a/clang/include/clang-c/Refactor.h +++ b/clang/include/clang-c/Refactor.h @@ -625,6 +625,11 @@ enum CXSymbolOccurrenceKind { */ CXSymbolOccurrence_MatchingFilename = 5, + /** + * \brief This is an occurrence of an symbol name in a string literal. + */ + CXSymbolOccurrence_MatchingStringLiteral = 6, + /** * \brief This is an occurrence of a symbol name that belongs to the extracted * declaration. Note: this occurrence can be in two replacements as we might @@ -914,6 +919,7 @@ typedef struct { * engine requires the following cursor kinds for the following renamed * declaration: * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC class: CXCursor_ObjCInterfaceDecl * Other declarations can use any other cursor cursor kinds. */ enum CXCursorKind CursorKind; diff --git a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h index 001f645fedeef..51508618f46f7 100644 --- a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h +++ b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h @@ -40,13 +40,18 @@ struct IndexedSymbol { std::vector<IndexedOccurrence> IndexedOccurrences; /// Whether this symbol is an Objective-C selector. bool IsObjCSelector; + /// If true, indexed file renamer will look for matching textual occurrences + /// in string literal tokens. + bool SearchForStringLiteralOccurrences; IndexedSymbol(OldSymbolName Name, std::vector<IndexedOccurrence> IndexedOccurrences, - bool IsObjCSelector) + bool IsObjCSelector, + bool SearchForStringLiteralOccurrences = false) : Name(std::move(Name)), IndexedOccurrences(std::move(IndexedOccurrences)), - IsObjCSelector(IsObjCSelector) {} + IsObjCSelector(IsObjCSelector), + SearchForStringLiteralOccurrences(SearchForStringLiteralOccurrences) {} IndexedSymbol(IndexedSymbol &&Other) = default; IndexedSymbol &operator=(IndexedSymbol &&Other) = default; }; diff --git a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h index 952c5464e0d95..cf795334a5fa9 100644 --- a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h +++ b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h @@ -79,7 +79,10 @@ class OldSymbolOccurrence { MatchingDocComment, /// \brief This is an occurrence of a symbol in an inclusion directive. - MatchingFilename + MatchingFilename, + + /// \brief This is a textual occurrence of a symbol in a string literal. + MatchingStringLiteral }; OccurrenceKind Kind; diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index 16141517d9bd6..6d976748f9849 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -14,6 +14,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/LiteralSupport.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Refactor/RefactoringOptions.h" #include "llvm/ADT/STLExtras.h" @@ -161,6 +162,23 @@ class InclusionLexer final : public Lexer { void IndirectLex(Token &Result) override { LexFromRawLexer(Result); } }; +/// Finds matching textual occurrences in string literals. +class StringLiteralTextualParser { + const OldSymbolName &Name; + +public: + unsigned SymbolIndex; + + StringLiteralTextualParser(const OldSymbolName &Name, unsigned SymbolIndex) + : Name(Name), SymbolIndex(SymbolIndex) { + assert(Name.size() == 1 && "can't search for multi-piece names in strings"); + } + + /// Returns the name's location if the parses has found a matching textual + /// name in a string literal. + SourceLocation handleToken(const Token &RawTok, Preprocessor &PP); +}; + } // end anonymous namespace SelectorParser::ParseState SelectorParser::stateForToken(const Token &RawTok) { @@ -232,6 +250,19 @@ bool SelectorParser::handleToken(const Token &RawTok) { return true; } +SourceLocation StringLiteralTextualParser::handleToken(const Token &RawTok, + Preprocessor &PP) { + if (!tok::isStringLiteral(RawTok.getKind())) + return SourceLocation(); + StringLiteralParser Literal(RawTok, PP); + if (Literal.hadError) + return SourceLocation(); + return Literal.GetString() == Name[0] + ? RawTok.getLocation().getLocWithOffset( + Literal.getOffsetOfStringByte(RawTok, 0)) + : SourceLocation(); +} + static void collectTextualMatchesInComment( ArrayRef<IndexedSymbol> Symbols, SourceLocation CommentLoc, StringRef Comment, llvm::SmallVectorImpl<TextualMatchOccurrence> &Result) { @@ -301,7 +332,7 @@ static void findTextualMatchesInComment( } static void findMatchingTextualOccurrences( - const SourceManager &SM, const LangOptions &LangOpts, + Preprocessor &PP, const SourceManager &SM, const LangOptions &LangOpts, ArrayRef<IndexedSymbol> Symbols, llvm::function_ref<void(OldSymbolOccurrence::OccurrenceKind, ArrayRef<SourceLocation> Locations, @@ -318,9 +349,17 @@ static void findMatchingTextualOccurrences( SelectorParsers.push_back( SelectorParser(Symbol.value().Name, Symbol.index())); } + llvm::SmallVector<StringLiteralTextualParser, 1> StringParsers; + for (const auto &Symbol : llvm::enumerate(Symbols)) { + if (Symbol.value().SearchForStringLiteralOccurrences) + StringParsers.push_back( + StringLiteralTextualParser(Symbol.value().Name, Symbol.index())); + } Token RawTok; RawLex.LexFromRawLexer(RawTok); + bool ScanNonCommentTokens = + !SelectorParsers.empty() || !StringParsers.empty(); while (RawTok.isNot(tok::eof)) { if (RawTok.is(tok::comment)) { SourceRange Range(RawTok.getLocation(), RawTok.getEndLoc()); @@ -333,12 +372,18 @@ static void findMatchingTextualOccurrences( Range, MatchHandler); CommentMatches.clear(); } - } else if (!SelectorParsers.empty()) { + } else if (ScanNonCommentTokens) { for (auto &Parser : SelectorParsers) { if (Parser.handleToken(RawTok)) MatchHandler(OldSymbolOccurrence::MatchingSelector, Parser.SelectorLocations, Parser.SymbolIndex); } + for (auto &Parser : StringParsers) { + SourceLocation Loc = Parser.handleToken(RawTok, PP); + if (Loc.isValid()) + MatchHandler(OldSymbolOccurrence::MatchingStringLiteral, Loc, + Parser.SymbolIndex); + } } RawLex.LexFromRawLexer(RawTok); } @@ -432,7 +477,7 @@ void IndexedFileOccurrenceProducer::ExecuteAction() { if (Options && Options->get(option::AvoidTextualMatches())) return; findMatchingTextualOccurrences( - SM, LangOpts, Symbols, + PP, SM, LangOpts, Symbols, [&](OldSymbolOccurrence::OccurrenceKind Kind, ArrayRef<SourceLocation> Locations, unsigned SymbolIndex) { OldSymbolOccurrence Result(Kind, /*IsMacroExpansion=*/false, diff --git a/clang/test/Refactor/Rename/IndexedObjCClassStringLiteral.mm b/clang/test/Refactor/Rename/IndexedObjCClassStringLiteral.mm new file mode 100644 index 0000000000000..4375a4c337d05 --- /dev/null +++ b/clang/test/Refactor/Rename/IndexedObjCClassStringLiteral.mm @@ -0,0 +1,21 @@ +@interface Test + +@end + +void foo() { + "Test"; // CHECK: string-literal [[@LINE]]:4 -> [[@LINE]]:8 + @"Test"; // CHECK: string-literal [[@LINE]]:5 -> [[@LINE]]:9 + u8"Test"; // CHECK: string-literal [[@LINE]]:6 -> [[@LINE]]:10 + // CHECK-NOT: string-literal + "Test.h"; + " Test "; + "test"; + "TEST"; +} + +// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=foo -indexed-file=%s -indexed-at=1:12 -indexed-symbol-kind=objc-class %s -std=c++11 | FileCheck %s +// It should be possible to find a string-literal in a file without any indexed occurrences: +// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=foo -indexed-file=%s -indexed-symbol-kind=objc-class %s -std=c++11 | FileCheck %s + +// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=foo -indexed-file=%s -indexed-at=1:12 %s -std=c++11 | FileCheck --check-prefix=NOTCLASS %s +// NOTCLASS-NOT: string-literal diff --git a/clang/test/Refactor/Rename/IndexedObjCMethod.m b/clang/test/Refactor/Rename/IndexedObjCMethod.m index f9182c6c1eca5..0f9ff5b4ebce5 100644 --- a/clang/test/Refactor/Rename/IndexedObjCMethod.m +++ b/clang/test/Refactor/Rename/IndexedObjCMethod.m @@ -96,7 +96,7 @@ @interface SemicolonIsExcluded // CHECK3-NOT: comment // CHECK3-NOT: documentation // CHECK3-NOT: selector - +// CHECK3-NOT: string-literal // It should be possible to find a selector in a file without any indexed occurrences: // CHECK4: selector [[@LINE+1]]:11 diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index 34532198250a2..3bca876ce4126 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -255,6 +255,8 @@ static const char *renameOccurrenceKindString(CXSymbolOccurrenceKind Kind, return "documentation"; case CXSymbolOccurrence_MatchingFilename: return "filename"; + case CXSymbolOccurrence_MatchingStringLiteral: + return "string-literal"; case CXSymbolOccurrence_ExtractedDeclaration: return "extracted-decl"; case CXSymbolOccurrence_ExtractedDeclaration_Reference: @@ -397,6 +399,7 @@ renameIndexedOccurrenceKindStringToKind(StringRef Str, CXCursorKind Default) { .Case("objc-cm", CXCursor_ObjCClassMethodDecl) .Case("objc-message", CXCursor_ObjCMessageExpr) .Case("include", CXCursor_InclusionDirective) + .Case("objc-class", CXCursor_ObjCInterfaceDecl) .Default(Default); } diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index 201bb5424a3fd..aa9667f51b834 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -79,6 +79,8 @@ translateOccurrenceKind(rename::OldSymbolOccurrence::OccurrenceKind Kind) { return CXSymbolOccurrence_MatchingDocCommentString; case rename::OldSymbolOccurrence::MatchingFilename: return CXSymbolOccurrence_MatchingFilename; + case rename::OldSymbolOccurrence::MatchingStringLiteral: + return CXSymbolOccurrence_MatchingStringLiteral; } } @@ -671,9 +673,11 @@ CXErrorCode performIndexedSymbolSearch( IndexedOccurrences.push_back(Result); } - IndexedSymbols.emplace_back(OldSymbolName(Symbol.Name, IsObjCSelector), - IndexedOccurrences, - /*IsObjCSelector=*/IsObjCSelector); + IndexedSymbols.emplace_back( + OldSymbolName(Symbol.Name, IsObjCSelector), IndexedOccurrences, + /*IsObjCSelector=*/IsObjCSelector, + /*SearchForStringLiteralOccurrences=*/ + Symbol.CursorKind == CXCursor_ObjCInterfaceDecl); } class ToolRunner final : public FrontendActionFactory, From b409adf918f77180c0615ef091d36158b1f8dc28 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 12 Dec 2017 21:19:38 -0500 Subject: [PATCH 237/582] Fix a use-after-free in the coro.alloca treatment. apple-llvm-split-commit: 97dd50f29e92bd80ad28db008f42073d178f2b38 apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 14 +++++++--- .../Coroutines/coro-retcon-alloca.ll | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp index 95d4100a6c945..a1e8ff047376d 100644 --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -922,7 +922,8 @@ static bool localAllocaNeedsStackSave(CoroAllocaAllocInst *AI) { /// Turn each of the given local allocas into a normal (dynamic) alloca /// instruction. -static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas) { +static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas, + SmallVectorImpl<Instruction*> &DeadInsts) { for (auto AI : LocalAllocas) { auto M = AI->getModule(); IRBuilder<> Builder(AI); @@ -955,10 +956,10 @@ static void lowerLocalAllocas(ArrayRef<CoroAllocaAllocInst*> LocalAllocas) { StackSave); } } - cast<Instruction>(U)->eraseFromParent(); + DeadInsts.push_back(cast<Instruction>(U)); } - AI->eraseFromParent(); + DeadInsts.push_back(AI); } } @@ -1081,6 +1082,11 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { continue; } + // Ignore alloca.get; we process this as part of coro.alloca.alloc. + if (isa<CoroAllocaGetInst>(I)) { + continue; + } + for (User *U : I.users()) if (Checker.isDefinitionAcrossSuspend(I, U)) { // We cannot spill a token. @@ -1094,7 +1100,7 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) { moveSpillUsesAfterCoroBegin(F, Spills, Shape.CoroBegin); Shape.FrameTy = buildFrameType(F, Shape, Spills); Shape.FramePtr = insertSpills(Spills, Shape); - lowerLocalAllocas(LocalAllocas); + lowerLocalAllocas(LocalAllocas, DeadInstructions); for (auto I : DeadInstructions) I->eraseFromParent(); diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll index 65ef3c48d6560..749abb2718428 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll @@ -202,6 +202,34 @@ loop2: ; CHECK-NEXT: br i1 %cmp, label %loop2, ; CHECK-NEXT: } +declare {i8*, i32} @prototype_j(i8*) +define {i8*, i32} @j(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 1024, i32 8, i8* %buffer, i8* bitcast ({i8*, i32} (i8*)* @prototype_j to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %forward + +back: + ; We should encounter this 'get' before we encounter the 'alloc'. + %ptr = call i8* @llvm.coro.alloca.get(token %alloca) + call void @use(i8* %ptr) + call void @llvm.coro.alloca.free(token %alloca) + %k = add i32 %n.val, 1 + %cmp = icmp ugt i32 %k, 128 + br i1 %cmp, label %forward, label %end + +forward: + %n.val = phi i32 [ %n, %entry ], [ %k, %back ] + call void (...) @llvm.coro.suspend.retcon.isVoid(i32 %n.val) + %alloca = call token @llvm.coro.alloca.alloc.i32(i32 %n.val, i32 8) + %inc = add i32 %n.val, 1 + br label %back + +end: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) declare i1 @llvm.coro.suspend.retcon.i1(...) From 2b75e43c64bc301575eaf902522a9f327e5f3ec4 Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Tue, 19 Dec 2017 13:39:29 +0000 Subject: [PATCH 238/582] [YAML] Fix YAML API usage API Changed in llvm trunk r320996. apple-llvm-split-commit: 67c6fde24bdcb024933eb6cc54322b0726da7b7e apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index c1e342085f8b4..bccb72f4de2a4 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -444,7 +444,7 @@ namespace llvm { return StringRef(); } - static bool mustQuote(StringRef) { return false; } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template <> From 17ba4be35f1793d541511bae26df7a00d06016b1 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Thu, 28 Dec 2017 13:42:07 -0800 Subject: [PATCH 239/582] Update c-index-test to match LLVM r321411 The SymbolPropertySet subfield of the SymbolInfo struct was moved to the end. apple-llvm-split-commit: ebdb6833f992d6a766610fe5cf954519d05458ed apple-llvm-split-dir: clang/ --- clang/tools/c-index-test/core_main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index 298c55cf9cd64..0a6ee1b5fa90e 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -670,8 +670,8 @@ static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS) { static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS) { SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), getSymbolSubKind(Sym.getSubKind()), - SymbolPropertySet(Sym.getProperties()), - getSymbolLanguage(Sym.getLanguage())}; + getSymbolLanguage(Sym.getLanguage()), + SymbolPropertySet(Sym.getProperties())}; printSymbolInfo(SymInfo, OS); OS << " | "; @@ -705,8 +705,8 @@ static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS auto Sym = Occur.getSymbol(); SymbolInfo SymInfo{getSymbolKind(Sym.getKind()), getSymbolSubKind(Sym.getSubKind()), - SymbolPropertySet(Sym.getProperties()), - getSymbolLanguage(Sym.getLanguage())}; + getSymbolLanguage(Sym.getLanguage()), + SymbolPropertySet(Sym.getProperties())}; printSymbolInfo(SymInfo, OS); OS << " | "; From 4c441769e7b3bfb22be79d8beacd6be60e44defa Mon Sep 17 00:00:00 2001 From: Alex Lorenz <aleksei_lorenz@apple.com> Date: Tue, 2 Jan 2018 13:44:43 -0800 Subject: [PATCH 240/582] Silence -Wcast-qual warning rdar://36136721 apple-llvm-split-commit: f8452f7d6f2c1ab3ec1f455371b9d65f7b6c5ca3 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexRecordReader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Index/IndexRecordReader.cpp b/clang/lib/Index/IndexRecordReader.cpp index bd2ec1f571dba..c657d07bea40d 100644 --- a/clang/lib/Index/IndexRecordReader.cpp +++ b/clang/lib/Index/IndexRecordReader.cpp @@ -306,7 +306,8 @@ class IndexBitstreamVisitor : public BitstreamVisitor<IndexBitstreamVisitor> { } case REC_DECLOFFSETS_BLOCK_ID: assert(RecID == REC_DECLOFFSETS); - Reader.setDeclOffsets(makeArrayRef((uint32_t*)Blob.data(), Record[0])); + Reader.setDeclOffsets(makeArrayRef((const uint32_t*)Blob.data(), + Record[0])); break; case REC_DECLS_BLOCK_ID: From a85a1b8eb8d32364bcad65e7cdae83230a70fa3b Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Wed, 3 Jan 2018 19:06:03 -0800 Subject: [PATCH 241/582] Remove Swift-specific changes for SSA updater phi iteration. With my change in llvm r321783, we can now handle this differently. apple-llvm-split-commit: d7b735a3d452ebacbbe1409a50b48ead3d771af9 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h | 1 - llvm/lib/CodeGen/MachineSSAUpdater.cpp | 5 ----- llvm/lib/Transforms/Utils/SSAUpdater.cpp | 5 ----- 3 files changed, 11 deletions(-) diff --git a/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h b/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h index ba03b2d2be7be..3c8bd1724e628 100644 --- a/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h +++ b/llvm/include/llvm/Transforms/Utils/SSAUpdaterImpl.h @@ -36,7 +36,6 @@ class SSAUpdaterImpl { using BlkT = typename Traits::BlkT; using ValT = typename Traits::ValT; using PhiT = typename Traits::PhiT; - using PhiItT = typename Traits::PhiItT; /// BBInfo - Per-basic block information used internally by SSAUpdaterImpl. /// The predecessors of each block are cached here since pred_iterator is diff --git a/llvm/lib/CodeGen/MachineSSAUpdater.cpp b/llvm/lib/CodeGen/MachineSSAUpdater.cpp index 22a5989f9db0c..36844e9fb30a4 100644 --- a/llvm/lib/CodeGen/MachineSSAUpdater.cpp +++ b/llvm/lib/CodeGen/MachineSSAUpdater.cpp @@ -249,11 +249,6 @@ class SSAUpdaterTraits<MachineSSAUpdater> { static BlkSucc_iterator BlkSucc_begin(BlkT *BB) { return BB->succ_begin(); } static BlkSucc_iterator BlkSucc_end(BlkT *BB) { return BB->succ_end(); } - /// Iterator over phis in a block. - typedef BlkT::iterator PhiItT; - static PhiItT PhiItT_begin(BlkT *BB) { return BB->begin(); } - static PhiItT PhiItT_end(BlkT *BB) { return BB->end(); } - /// Iterator for PHI operands. class PHI_iterator { private: diff --git a/llvm/lib/Transforms/Utils/SSAUpdater.cpp b/llvm/lib/Transforms/Utils/SSAUpdater.cpp index 79a3e51b6bb5e..b2231d68a301a 100644 --- a/llvm/lib/Transforms/Utils/SSAUpdater.cpp +++ b/llvm/lib/Transforms/Utils/SSAUpdater.cpp @@ -224,11 +224,6 @@ class SSAUpdaterTraits<SSAUpdater> { static BlkSucc_iterator BlkSucc_begin(BlkT *BB) { return succ_begin(BB); } static BlkSucc_iterator BlkSucc_end(BlkT *BB) { return succ_end(BB); } - /// Iterator over phis in a block. - typedef BlkT::iterator PhiItT; - static PhiItT PhiItT_begin(BlkT *BB) { return BB->begin(); } - static PhiItT PhiItT_end(BlkT *BB) { return BB->end(); } - class PHI_iterator { private: PHINode *PHI; From a984baa1c1e07b100e4ba762ad4dc5f0fe630477 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 9 Jan 2018 22:54:38 -0500 Subject: [PATCH 242/582] In coro.retcon lowering, don't explode if the optimizer messes around with the linkage of the prototype or the exact types of the yielded values. apple-llvm-split-commit: 31e64ae97518e19f4c0eac5b272eed23448a6b5f apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 17 ++++++++++++++++ llvm/lib/Transforms/Coroutines/Coroutines.cpp | 12 ++++++++++- .../test/Transforms/Coroutines/coro-retcon.ll | 20 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 898c12453fa31..e2f367f2e6759 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -582,8 +582,25 @@ void CoroCloner::create() { SmallVector<ReturnInst *, 4> Returns; + // Ignore attempts to change certain attributes of the function. + // TODO: maybe there should be a way to suppress this during cloning? + auto savedVisibility = NewF->getVisibility(); + auto savedUnnamedAddr = NewF->getUnnamedAddr(); + auto savedDLLStorageClass = NewF->getDLLStorageClass(); + + // NewF's linkage (which CloneFunctionInto does *not* change) might not + // be compatible with the visibility of OrigF (which it *does* change), + // so protect against that. + auto savedLinkage = NewF->getLinkage(); + NewF->setLinkage(llvm::GlobalValue::ExternalLinkage); + CloneFunctionInto(NewF, &OrigF, VMap, /*ModuleLevelChanges=*/true, Returns); + NewF->setLinkage(savedLinkage); + NewF->setVisibility(savedVisibility); + NewF->setUnnamedAddr(savedUnnamedAddr); + NewF->setDLLStorageClass(savedDLLStorageClass); + auto &Context = NewF->getContext(); // Replace the attributes of the new function: diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp index 951e85a0e89ca..5840c886b2d94 100644 --- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -404,7 +404,17 @@ void coro::Shape::buildFrom(Function &F) { auto SI = Suspend->value_begin(), SE = Suspend->value_end(); auto RI = ResultTys.begin(), RE = ResultTys.end(); for (; SI != SE && RI != RE; ++SI, ++RI) { - if ((*SI)->getType() != *RI) { + auto SrcTy = (*SI)->getType(); + if (SrcTy != *RI) { + // The optimizer likes to eliminate bitcasts leading into variadic + // calls, but that messes with our invariants. Re-insert the + // bitcast and ignore this type mismatch. + if (CastInst::isBitCastable(SrcTy, *RI)) { + auto BCI = new BitCastInst(*SI, *RI, "", Suspend); + SI->set(BCI); + continue; + } + #ifndef NDEBUG Suspend->dump(); Prototype->getFunctionType()->dump(); diff --git a/llvm/test/Transforms/Coroutines/coro-retcon.ll b/llvm/test/Transforms/Coroutines/coro-retcon.ll index 355cdae393208..9d86fba729a5a 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon.ll @@ -78,6 +78,25 @@ entry: ; CHECK-NEXT: call void @print(i32 [[INC]]) ; CHECK-NEXT: ret i32 0 +define hidden { i8*, i8* } @g(i8* %buffer, i16* %ptr) { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast ({ i8*, i8* } (i8*, i1)* @g_prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %ptr2 = bitcast i16* %ptr to i8* + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1(i8* %ptr2) + br i1 %unwind0, label %cleanup, label %resume + +resume: + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) declare i1 @llvm.coro.suspend.retcon.i1(...) @@ -85,6 +104,7 @@ declare i1 @llvm.coro.end(i8*, i1) declare i8* @llvm.coro.prepare.retcon(i8*) declare i8* @prototype(i8*, i1 zeroext) +declare {i8*,i8*} @g_prototype(i8*, i1 zeroext) declare noalias i8* @allocate(i32 %size) declare void @deallocate(i8* %ptr) From 1827af49a0d03e6dcb12f0e463bac9fe9b65d733 Mon Sep 17 00:00:00 2001 From: Michael Zolotukhin <mzolotukhin@apple.com> Date: Mon, 15 Jan 2018 20:45:06 -0800 Subject: [PATCH 243/582] Remove forgotten merge conflict marker. apple-llvm-split-commit: 53cfa6a1b1e5d08551ed1166fc6434a602a5b44a apple-llvm-split-dir: clang/ --- clang/unittests/libclang/LibclangTest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp index 53f612b3fb853..1cba1915fa9d3 100644 --- a/clang/unittests/libclang/LibclangTest.cpp +++ b/clang/unittests/libclang/LibclangTest.cpp @@ -574,7 +574,6 @@ TEST_F(LibclangReparseTest, clang_parseTranslationUnit2FullArgv) { DisplayDiagnostics(); } -<<<<<<< HEAD TEST(libclang, RefactoringAction) { std::string Name = clang_getCString(clang_RefactoringActionType_getName(CXRefactor_Rename)); From 4324a04085720f19a12ced67728fa900e93e396f Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <abdulras@fb.com> Date: Mon, 29 Jan 2018 12:06:34 -0800 Subject: [PATCH 244/582] Tooling: silence -Wreturn-type warning The switch should be covered. However, older versions of gcc and clang as well as MSVC do not handle the covered switch and emit a warning. Silence the warning by adding an unreachable. NFC. apple-llvm-split-commit: 61002e79882ef43887da1c0deda6002ec6d55805 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/RefactoringActions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Tooling/Refactor/RefactoringActions.cpp b/clang/lib/Tooling/Refactor/RefactoringActions.cpp index 4b2b735b3d54c..d6592d9b71cdf 100644 --- a/clang/lib/Tooling/Refactor/RefactoringActions.cpp +++ b/clang/lib/Tooling/Refactor/RefactoringActions.cpp @@ -24,6 +24,7 @@ StringRef getRefactoringActionTypeName(RefactoringActionType Action) { return Spelling; #include "clang/Tooling/Refactor/RefactoringActions.def" } + llvm_unreachable("unexpected RefactoringActionType value"); } } // end namespace tooling From 825f3f1489ca5156078ed2601c9426477e1d221d Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Mon, 29 Jan 2018 16:47:23 -0800 Subject: [PATCH 245/582] [index/store] Make sure to ignore any decl occurrences from the predefines buffers that are serialized into module files Fixes crash of rdar://36162712 apple-llvm-split-commit: 891f00ffeb8c6c9e9f5c786e225e8631c1eaa234 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexingAction.cpp | 3 ++- clang/test/Index/Store/using-libstdcpp-arc.mm | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 28dedea6a703b..3ef7ba1880da4 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -245,7 +245,8 @@ class IndexDataRecorder : public IndexDataConsumer { FileID FID, unsigned Offset, ASTNodeInfo ASTNode) override { // Ignore the predefines buffer. - if (FID == PP->getPredefinesFileID()) + const FileEntry *FE = PP->getSourceManager().getFileEntryForID(FID); + if (!FE) return true; FileIndexRecord &Rec = getFileIndexRecord(FID); diff --git a/clang/test/Index/Store/using-libstdcpp-arc.mm b/clang/test/Index/Store/using-libstdcpp-arc.mm index 9738c869838e5..9f2935ac91b38 100644 --- a/clang/test/Index/Store/using-libstdcpp-arc.mm +++ b/clang/test/Index/Store/using-libstdcpp-arc.mm @@ -1,10 +1,21 @@ -// Test to make sure we don't crash, rdar://30816887. +// Test to make sure we don't crash, rdar://30816887&36162712. -// RUN: rm -rf %t.idx -// RUN: %clang_cc1 %s -index-store-path %t.idx -fobjc-arc -fobjc-arc-cxxlib=libstdc++ +// RUN: mkdir -p %t/include +// RUN: echo 'module Foo { header "test.h" }' > %t/include/module.modulemap +// RUN: echo '' > %t/include/test.h + +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang_cc1 %s -index-store-path %t.idx -I %t/include -fobjc-arc -fobjc-arc-cxxlib=libstdc++ -fmodules -fmodules-cache-path=%t.mcp -fimplicit-module-maps // RUN: c-index-test core -print-record %t.idx | FileCheck %s +// RUN: rm -rf %t.idx2 +// RUN: %clang_cc1 %s -index-store-path %t.idx2 -I %t/include -fobjc-arc -fobjc-arc-cxxlib=libstdc++ -fmodules -fmodules-cache-path=%t.mcp -fimplicit-module-maps +// RUN: c-index-test core -print-record %t.idx2 | FileCheck %s + + // XFAIL: linux +@import Foo; + // CHECK: [[@LINE+1]]:6 | function/C void test1(void); From 9b3490c80701ae92f0915e909e0cae73d0992922 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <abdulras@fb.com> Date: Mon, 29 Jan 2018 13:41:29 -0800 Subject: [PATCH 246/582] clang-refactor-test: silence -Wreturn-type warning The switch should be covered. However, older versions of gcc and clang as well as MSVC do not handle the covered switch and emit a warning. Silence the warning by adding an unreachable. NFC. apple-llvm-split-commit: 6e5cf1f4f8b62487c7fa6c827bc9fcb0ff964c1d apple-llvm-split-dir: clang/ --- clang/tools/clang-refactor-test/ClangRefactorTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index 3bca876ce4126..d5de340d25603 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -262,6 +262,7 @@ static const char *renameOccurrenceKindString(CXSymbolOccurrenceKind Kind, case CXSymbolOccurrence_ExtractedDeclaration_Reference: return "extracted-decl-ref"; } + llvm_unreachable("unexpected CXSymbolOccurrenceKind value"); } static int apply(ArrayRef<CXRefactoringReplacement> Replacements, From 8a28b8423f274920a0e65cd654dac3fb85765c35 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Tue, 30 Jan 2018 11:36:15 -0800 Subject: [PATCH 247/582] Tooling: fix -fpermissive warning from gcc Explicitly wrap the template specialization into the namespace where it is declared (in LLVM). This fixes a `-fpermissive` error emitted by GCC when building clang for swift. NFC. apple-llvm-split-commit: eff90344c1a54a79a22572955ea12844d2c82819 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/RefactoringOptions.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/lib/Tooling/Refactor/RefactoringOptions.cpp b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp index ca62890edb638..35389f5bb8fde 100644 --- a/clang/lib/Tooling/Refactor/RefactoringOptions.cpp +++ b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp @@ -26,6 +26,8 @@ void RefactoringOptionSet::print(llvm::raw_ostream &OS) const { } } +namespace llvm { +namespace yaml { template <> struct CustomMappingTraits<RefactoringOptionSet> { static void inputOne(IO &YamlIn, StringRef Key, RefactoringOptionSet &Result) { @@ -44,6 +46,8 @@ template <> struct CustomMappingTraits<RefactoringOptionSet> { llvm_unreachable("Output is done without mapping traits"); } }; +} +} llvm::Expected<RefactoringOptionSet> RefactoringOptionSet::parse(StringRef Source) { From aebcd8be0d70badd8fdd8931b128472226920586 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Fri, 2 Feb 2018 10:30:55 -0800 Subject: [PATCH 248/582] clang-c: indicate correct linkage The `clang_RefactoringReplacement_getAssociatedSymbolOccurrences` declaration was missing the linkage annotation resulting in a build failure on Windows. Correct the linkage annotations. apple-llvm-split-commit: d1f199e0e07634a3c1889fb54bf8475a492acb60 apple-llvm-split-dir: clang/ --- clang/include/clang-c/Refactor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h index d6e8d84eb64d1..a196403cb09f6 100644 --- a/clang/include/clang-c/Refactor.h +++ b/clang/include/clang-c/Refactor.h @@ -1132,6 +1132,7 @@ typedef struct { * \brief Return the set of symbol occurrences that are associated with the * given \p Replacement. */ +CINDEX_LINKAGE CXRefactoringReplacementAssociatedSymbolOccurrences clang_RefactoringReplacement_getAssociatedSymbolOccurrences( CXRefactoringReplacement Replacement); From a9b7cfa55d84f31405216d86ff4a35a3deb2c6cb Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Fri, 2 Feb 2018 10:41:47 -0800 Subject: [PATCH 249/582] libclang: silence -Wreturn-type warning The switch should be covered. However, older versions of gcc and clang as well as MSVC do not handle the covered switch and emit a warning. Silence the warning by adding an unreachable. NFC. apple-llvm-split-commit: c18b599d189e43e1f14df07887bdefdd77558c55 apple-llvm-split-dir: clang/ --- clang/tools/libclang/CRefactor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index aa9667f51b834..e14a05fcfd1f7 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -52,6 +52,7 @@ translateRefactoringActionType(CXRefactoringActionType Action) { return RefactoringActionType::Name; #include "clang/Tooling/Refactor/RefactoringActions.def" } + llvm_unreachable("unknown CXRefactoringActionType value"); } static CXRefactoringActionType @@ -62,6 +63,7 @@ translateRefactoringActionType(RefactoringActionType Action) { return CXRefactor_##Name; #include "clang/Tooling/Refactor/RefactoringActions.def" } + llvm_unreachable("unknown RefactoringActionType value"); } static CXSymbolOccurrenceKind @@ -82,6 +84,7 @@ translateOccurrenceKind(rename::OldSymbolOccurrence::OccurrenceKind Kind) { case rename::OldSymbolOccurrence::MatchingStringLiteral: return CXSymbolOccurrence_MatchingStringLiteral; } + llvm_unreachable("unknown OccurrenceKind value"); } namespace { From 2c3f9b2b20105f1a592fdd8fae6e315d410b0767 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Thu, 8 Feb 2018 16:47:23 -0500 Subject: [PATCH 250/582] Update for optimizer changes. rdar://37352868 apple-llvm-split-commit: 38ddbccde99d1b249765e8ce762b738805ed42fe apple-llvm-split-dir: llvm/ --- llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll index 749abb2718428..b0c77f874aae9 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll @@ -197,8 +197,8 @@ loop2: ; CHECK-NEXT: [[ALLOC:%.*]] = alloca i8, i64 [[T0]], align 8 ; CHECK-NEXT: call void @use(i8* nonnull [[ALLOC]]) ; CHECK-NEXT: call void @llvm.stackrestore(i8* [[SAVE]]) -; CHECK-NEXT: %cmp = icmp ugt i32 %k, 128 ; CHECK-NEXT: %k2 = lshr i32 %k, 1 +; CHECK-NEXT: %cmp = icmp ugt i32 %k, 128 ; CHECK-NEXT: br i1 %cmp, label %loop2, ; CHECK-NEXT: } From 43a019049394d035edfde88d79ca59035c86ec5f Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Thu, 21 Dec 2017 20:14:59 -0800 Subject: [PATCH 251/582] Fix internal testcases for r321337 Conflicts: test/Modules/modulemap-collision.m apple-llvm-split-commit: bcd7f463e13818856a1590fb329a1ce4eb7bf08d apple-llvm-split-dir: clang/ --- clang/test/APINotes/availability.m | 2 +- clang/test/APINotes/nullability.m | 4 ++-- clang/test/APINotes/objc_designated_inits.m | 2 +- clang/test/APINotes/types.m | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/test/APINotes/availability.m b/clang/test/APINotes/availability.m index 7231eb480b883..2ddc2a73da804 100644 --- a/clang/test/APINotes/availability.m +++ b/clang/test/APINotes/availability.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -Wno-private-module -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m index 65c9c2c9bc3d9..a684a4bbde743 100644 --- a/clang/test/APINotes/nullability.m +++ b/clang/test/APINotes/nullability.m @@ -1,9 +1,9 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -Wno-private-module -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables. -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 -fmodules-ignore-macro=SWIFT_VERSION_3_0 +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -Wno-private-module -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 -fmodules-ignore-macro=SWIFT_VERSION_3_0 #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/objc_designated_inits.m b/clang/test/APINotes/objc_designated_inits.m index 24b317c159c38..1f2b8ed534b7a 100644 --- a/clang/test/APINotes/objc_designated_inits.m +++ b/clang/test/APINotes/objc_designated_inits.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -Wno-private-module -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import <SomeKit/SomeKit.h> diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m index a0f728b554729..133d504713d76 100644 --- a/clang/test/APINotes/types.m +++ b/clang/test/APINotes/types.m @@ -1,5 +1,5 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -Wno-private-module -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s #import <SomeKit/SomeKit.h> From c236f6f08b6d5c4fc6f5d3ebd49ae35341b1c156 Mon Sep 17 00:00:00 2001 From: Adrian Prantl <aprantl@apple.com> Date: Fri, 9 Feb 2018 18:43:10 +0000 Subject: [PATCH 252/582] Introduce an API for LLDB to compute the default module cache path LLDB creates Clang modules and had an incomplete copy of the clang Driver code that compute the -fmodule-cache-path. This patch makes the clang driver code accessible to LLDB. Differential Revision: https://reviews.llvm.org/D43128 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@324761 91177308-0d34-0410-b5e6-96231b3b80d8 apple-llvm-split-commit: 2a010b4a0209a0417b632ee8e2fdf27b102b131b apple-llvm-split-dir: clang/ --- clang/include/clang/Driver/Driver.h | 2 ++ clang/lib/Driver/ToolChains/Clang.cpp | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 07134309835eb..53eb64c5dce28 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -572,6 +572,8 @@ class Driver { /// no extra characters remaining at the end. static bool GetReleaseVersion(StringRef Str, MutableArrayRef<unsigned> Digits); + /// Compute the default -fmodule-cache-path. + static void getDefaultModuleCachePath(SmallVectorImpl<char> &Result); }; /// \return True if the last defined optimization level is -Ofast. diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 18b8f0bea2246..f219ebdec0835 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -2500,6 +2500,13 @@ static void RenderBuiltinOptions(const ToolChain &TC, const llvm::Triple &T, CmdArgs.push_back("-fno-math-builtin"); } +void Driver::getDefaultModuleCachePath(SmallVectorImpl<char> &Result) { + llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, Result); + llvm::sys::path::append(Result, "org.llvm.clang."); + appendUserToPath(Result); + llvm::sys::path::append(Result, "ModuleCache"); +} + static void RenderModulesOptions(Compilation &C, const Driver &D, const ArgList &Args, const InputInfo &Input, const InputInfo &Output, @@ -2560,10 +2567,7 @@ static void RenderModulesOptions(Compilation &C, const Driver &D, llvm::sys::path::append(Path, "modules"); } else if (Path.empty()) { // No module path was provided: use the default. - llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, Path); - llvm::sys::path::append(Path, "org.llvm.clang."); - appendUserToPath(Path); - llvm::sys::path::append(Path, "ModuleCache"); + Driver::getDefaultModuleCachePath(Path); } const char Arg[] = "-fmodules-cache-path="; From 5ef9bc55ef7f7893d25b55e4568b17f009121c16 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Fri, 9 Feb 2018 15:10:18 -0800 Subject: [PATCH 253/582] Tooling: use /bigobj when building a file This file has enough symbols that it extends beyond the PE/COFF specification. Use `/bigobj` to enable builds on Windows. apple-llvm-split-commit: 89b4dbe223d92b5b0b2b1fb157e089f95d5b04ff apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clang/lib/Tooling/Refactor/CMakeLists.txt b/clang/lib/Tooling/Refactor/CMakeLists.txt index bd93afa13d0a0..8871e1da091a4 100644 --- a/clang/lib/Tooling/Refactor/CMakeLists.txt +++ b/clang/lib/Tooling/Refactor/CMakeLists.txt @@ -43,3 +43,7 @@ add_clang_library(clangToolingRefactor clangToolingCore clangRewrite ) + +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + set_source_files_properties(Extract.cpp PROPERTIES COMPILE_FLAGS /bigobj) +endif() From 317db3844f776763371a7610c3541b2b1970058a Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Tue, 27 Feb 2018 09:44:18 -0800 Subject: [PATCH 254/582] APINotes: adjust for upstream SVN r326091 `HashString` was replaced in favour of `djbHash`. Note that the seed for the hashing has changed. apple-llvm-split-commit: 8075d5d36005657d897e95078a000cf237fdec08 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesReader.cpp | 3 ++- clang/lib/APINotes/APINotesWriter.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 64b36e6a69b57..7db3c4eb7b3b4 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -16,6 +16,7 @@ #include "clang/APINotes/APINotesReader.h" #include "APINotesFormat.h" #include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/DJB.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" #include "llvm/ADT/DenseMap.h" @@ -160,7 +161,7 @@ namespace { } hash_value_type ComputeHash(internal_key_type key) { - return llvm::HashString(key); + return llvm::djbHash(key); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index e0b6fafd6dbdc..76a373afb9eb4 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Support/DJB.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" #include "llvm/Support/raw_ostream.h" @@ -270,7 +271,7 @@ namespace { using offset_type = unsigned; hash_value_type ComputeHash(key_type_ref key) { - return llvm::HashString(key); + return llvm::djbHash(key); } std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out, From 39227645c33678c3a8654ad55a3a0c770ccc4f48 Mon Sep 17 00:00:00 2001 From: Adam Nemet <anemet@apple.com> Date: Wed, 28 Feb 2018 23:32:30 -0800 Subject: [PATCH 255/582] Fix incorrect merge conflict resolution In our version, I don't think we can remove the dyn_cast so just restore the version prior to the merge. apple-llvm-split-commit: 3fd281b9b64a394f07432b059e6d55cc94325eb8 apple-llvm-split-dir: clang/ --- clang/lib/AST/Type.cpp | 71 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 6890191e6d233..f949539c95708 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1123,45 +1123,46 @@ QualType QualType::substObjCTypeArgs( // Replace an Objective-C type parameter reference with the corresponding // type argument. if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) { - ObjCTypeParamDecl *typeParam = OTPTy->getDecl(); - // If we have type arguments, use them. - if (!typeArgs.empty()) { - // FIXME: Introduce SubstObjCTypeParamType ? - QualType argType = typeArgs[typeParam->getIndex()]; - return ctx.getQualifiedType(argType, splitType.Quals); - } + if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) { + // If we have type arguments, use them. + if (!typeArgs.empty()) { + // FIXME: Introduce SubstObjCTypeParamType ? + QualType argType = typeArgs[typeParam->getIndex()]; + return ctx.getQualifiedType(argType, splitType.Quals); + } - switch (context) { - case ObjCSubstitutionContext::Ordinary: - case ObjCSubstitutionContext::Parameter: - case ObjCSubstitutionContext::Superclass: - // Substitute the bound. - return ctx.getQualifiedType(typeParam->getUnderlyingType(), - splitType.Quals); - - case ObjCSubstitutionContext::Result: - case ObjCSubstitutionContext::Property: { - // Substitute the __kindof form of the underlying type. - const auto *objPtr = typeParam->getUnderlyingType() - ->castAs<ObjCObjectPointerType>(); - - // __kindof types, id, and Class don't need an additional - // __kindof. - if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + switch (context) { + case ObjCSubstitutionContext::Ordinary: + case ObjCSubstitutionContext::Parameter: + case ObjCSubstitutionContext::Superclass: + // Substitute the bound. return ctx.getQualifiedType(typeParam->getUnderlyingType(), splitType.Quals); - // Add __kindof. - const auto *obj = objPtr->getObjectType(); - QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(), - obj->getTypeArgsAsWritten(), - obj->getProtocols(), - /*isKindOf=*/true); - - // Rebuild object pointer type. - resultTy = ctx.getObjCObjectPointerType(resultTy); - return ctx.getQualifiedType(resultTy, splitType.Quals); - } + case ObjCSubstitutionContext::Result: + case ObjCSubstitutionContext::Property: { + // Substitute the __kindof form of the underlying type. + const auto *objPtr = typeParam->getUnderlyingType() + ->castAs<ObjCObjectPointerType>(); + + // __kindof types, id, and Class don't need an additional + // __kindof. + if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + return ctx.getQualifiedType(typeParam->getUnderlyingType(), + splitType.Quals); + + // Add __kindof. + const auto *obj = objPtr->getObjectType(); + QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(), + obj->getTypeArgsAsWritten(), + obj->getProtocols(), + /*isKindOf=*/true); + + // Rebuild object pointer type. + resultTy = ctx.getObjCObjectPointerType(resultTy); + return ctx.getQualifiedType(resultTy, splitType.Quals); + } + } } } From 79d9a2985ef75e2afb7dd14bcb41f95c831618f0 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Fri, 2 Mar 2018 14:52:54 -0800 Subject: [PATCH 256/582] master-next: Update RawComment initializer to match Clang r326512. apple-llvm-split-commit: bd2a0ac05730800584543f57e65b3b73d8eca2d2 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/RenameIndexedFile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index 6d976748f9849..433d0311470ab 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -298,7 +298,7 @@ static void findTextualMatchesInComment( LangOpts) .str(); OldSymbolOccurrence::OccurrenceKind Kind = - RawComment(SM, CommentRange, /*Merged=*/false, /*ParseAllComments=*/false) + RawComment(SM, CommentRange, LangOpts.CommentOpts, /*Merged=*/false) .isDocumentation() ? OldSymbolOccurrence::MatchingDocComment : OldSymbolOccurrence::MatchingComment; From 7f817b9878331372f1fb3842df34ac04f5eeda67 Mon Sep 17 00:00:00 2001 From: asowani <sowania@us.ibm.com> Date: Thu, 8 Mar 2018 15:09:13 +0530 Subject: [PATCH 257/582] ppc64le port. apple-llvm-split-commit: b3053aba44dd27f1ee32237421b490275a73ff5e apple-llvm-split-dir: clang/ --- clang/lib/Basic/Targets/PPC.h | 9 +++++++++ clang/lib/CodeGen/TargetInfo.cpp | 13 +++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h index 04bef258e3865..cd7ce34f879d0 100644 --- a/clang/lib/Basic/Targets/PPC.h +++ b/clang/lib/Basic/Targets/PPC.h @@ -334,6 +334,15 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo { } return false; } + + CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { + switch (CC) { + case CC_Swift: + return CCCR_OK; + default: + return CCCR_Warning; + } + } }; class LLVM_LIBRARY_VISIBILITY DarwinPPC32TargetInfo diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index f523096d95cf3..6f38ead043369 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -4283,7 +4283,7 @@ PPC32TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, namespace { /// PPC64_SVR4_ABIInfo - The 64-bit PowerPC ELF (SVR4) ABI information. -class PPC64_SVR4_ABIInfo : public ABIInfo { +class PPC64_SVR4_ABIInfo : public SwiftABIInfo { public: enum ABIKind { ELFv1 = 0, @@ -4327,7 +4327,7 @@ class PPC64_SVR4_ABIInfo : public ABIInfo { public: PPC64_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, ABIKind Kind, bool HasQPX, bool SoftFloatABI) - : ABIInfo(CGT), Kind(Kind), HasQPX(HasQPX), + : SwiftABIInfo(CGT), Kind(Kind), HasQPX(HasQPX), IsSoftFloatABI(SoftFloatABI) {} bool isPromotableTypeForABI(QualType Ty) const; @@ -4370,6 +4370,15 @@ class PPC64_SVR4_ABIInfo : public ABIInfo { Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty) const override; + + bool shouldPassIndirectlyForSwift(ArrayRef<llvm::Type*> scalars, + bool asReturnValue) const override { + return occupiesMoreThan(CGT, scalars, /*total*/ 4); + } + + bool isSwiftErrorInRegister() const override { + return true; + } }; class PPC64_SVR4_TargetCodeGenInfo : public TargetCodeGenInfo { From c28c6a9fd256f7dcee08a41fde5fcbdfa4825935 Mon Sep 17 00:00:00 2001 From: asowani <sowania@us.ibm.com> Date: Fri, 9 Mar 2018 12:18:36 +0530 Subject: [PATCH 258/582] ppc64le port. apple-llvm-split-commit: 6f37c879c4f1b12e7bbd15476ad6f25737dffe2e apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/TargetInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 6f38ead043369..6a0adecfc912c 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -4377,7 +4377,7 @@ class PPC64_SVR4_ABIInfo : public SwiftABIInfo { } bool isSwiftErrorInRegister() const override { - return true; + return false; } }; From 34eb397421c7c1db28a0f2a73d4c9529a6886b28 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Fri, 16 Mar 2018 10:48:43 -0700 Subject: [PATCH 259/582] Include missing header to fix the build after Clang r327573 The "clang/Tooling/CompilationDatabase.h" header is no longer included by "clang/Tooling/Tooling.h". apple-llvm-split-commit: 3d17c48c607441a94ccd18e113731e387fc48148 apple-llvm-split-dir: clang/ --- clang/tools/libclang/CRefactor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index e14a05fcfd1f7..a58c98bd46b29 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -26,6 +26,7 @@ #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/Utils.h" #include "clang/Index/USRGeneration.h" +#include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Refactor/IndexerQuery.h" #include "clang/Tooling/Refactor/RefactoringActionFinder.h" #include "clang/Tooling/Refactor/RefactoringActions.h" From 39ba395026b11bb590accb5bf96ebca60cfc06d9 Mon Sep 17 00:00:00 2001 From: Nathan Hawes <nhawes@apple.com> Date: Tue, 27 Mar 2018 16:57:42 -0700 Subject: [PATCH 260/582] [Frontend] Fix bad merge causing test failures in Index/Store/handle-prebuilt-module.m Bad merge in CompilerInstance.cpp in 142bd7968f4399c1f95e9bc75665c028889390dc. Resolves rdar://problem/38880213 apple-llvm-split-commit: 24472b6cbed4b779c92803a407933ac198876f07 apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInstance.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 73c5d3f47c7c3..7f2a0e4307126 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1197,8 +1197,14 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, llvm::CrashRecoveryContext CRC; CRC.RunSafelyOnThread( [&]() { - GenerateModuleFromModuleMapAction Action; - Instance.ExecuteAction(Action); + // FIXME: I have no idea what the best way to do this is, but it's + // probably not this. Interfaces changed upstream. + std::unique_ptr<FrontendAction> Action( + new GenerateModuleFromModuleMapAction); + if (wrapGenModuleAction) { + Action = wrapGenModuleAction(FrontendOpts, std::move(Action)); + } + Instance.ExecuteAction(*Action); }, ThreadStackSize); From 51a297bfb5d0fd985aa2a2da4e09be773e9311dd Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 3 Apr 2018 16:51:23 -0400 Subject: [PATCH 261/582] Fix merge. rdar://39130164 apple-llvm-split-commit: ee9ad867f743cbc314cee2d1c6e42e1617c275ca apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 484636762f920..d0a84a2ecf8d2 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -392,7 +392,7 @@ static Function *createCloneDeclaration(Function &OrigF, coro::Shape &Shape, Module *M = OrigF.getParent(); auto *FnTy = Shape.getResumeFunctionType(); - auto *F = Function::Create(FnTy, GlobalValue::LinkageTypes::ExternalLinkage, + auto *F = Function::Create(FnTy, GlobalValue::LinkageTypes::InternalLinkage, OrigF.getName() + Suffix); M->getFunctionList().insert(InsertBefore, F); @@ -595,7 +595,6 @@ void CoroCloner::create() { NewF->setLinkage(llvm::GlobalValue::ExternalLinkage); CloneFunctionInto(NewF, &OrigF, VMap, /*ModuleLevelChanges=*/true, Returns); - NewF->setLinkage(GlobalValue::LinkageTypes::InternalLinkage); NewF->setLinkage(savedLinkage); NewF->setVisibility(savedVisibility); From b25b00570cbf48ca5b9eeeddd5800ee7399d962d Mon Sep 17 00:00:00 2001 From: Alex Lorenz <aleksei_lorenz@apple.com> Date: Fri, 6 Apr 2018 11:38:11 -0700 Subject: [PATCH 262/582] Fixup the bad driver merge apple-llvm-split-commit: f7a812ad508248a72ab31ce68f03dd68483cbd41 apple-llvm-split-dir: clang/ --- clang/tools/driver/driver.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index d23db5cfe9f18..4672ad5d88700 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -314,13 +314,10 @@ static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) { return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); if (Tool == "as") return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); -<<<<<<< HEAD if (Tool == "apinotes") return cc1apinotes_main(argv.slice(2), argv[0], GetExecutablePathVP); -======= if (Tool == "gen-reproducer") return cc1gen_reproducer_main(argv.slice(2), argv[0], GetExecutablePathVP); ->>>>>>> llvm.org/master // Reject unknown tools. llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " From c1ef706b64dd412b01724dabe87fcbf88d5fd966 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 6 Apr 2018 18:59:39 -0700 Subject: [PATCH 263/582] Libclang crash reproducers: emit the signature rdar://35320907 apple-llvm-split-commit: e2371c56abddca7225d69476b8cba59cee3737f6 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Version.h | 3 +++ clang/lib/Basic/Version.cpp | 2 ++ clang/test/Index/record-completion-invocation.c | 2 +- clang/test/Index/record-parsing-invocation.c | 4 ++-- clang/tools/driver/cc1gen_reproducer_main.cpp | 2 ++ clang/tools/libclang/CIndex.cpp | 6 +++--- clang/tools/libclang/CIndexCodeCompletion.cpp | 3 ++- clang/tools/libclang/CIndexer.cpp | 10 ++++++++-- clang/tools/libclang/CIndexer.h | 4 ++-- 9 files changed, 25 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/Basic/Version.h b/clang/include/clang/Basic/Version.h index aec5f99087ba3..3905f1e8f6f96 100644 --- a/clang/include/clang/Basic/Version.h +++ b/clang/include/clang/Basic/Version.h @@ -57,6 +57,9 @@ namespace clang { /// for use in the CPP __VERSION__ macro, which includes the clang version /// number, the repository version, and the vendor tag. std::string getClangFullCPPVersion(); + + /// Returns the major version number of clang. + unsigned getClangMajorVersionNumber(); } #endif // LLVM_CLANG_BASIC_VERSION_H diff --git a/clang/lib/Basic/Version.cpp b/clang/lib/Basic/Version.cpp index c2b7753d41288..e6baeaf464048 100644 --- a/clang/lib/Basic/Version.cpp +++ b/clang/lib/Basic/Version.cpp @@ -148,4 +148,6 @@ std::string getClangFullCPPVersion() { return OS.str(); } +unsigned getClangMajorVersionNumber() { return CLANG_VERSION_MAJOR; } + } // end namespace clang diff --git a/clang/test/Index/record-completion-invocation.c b/clang/test/Index/record-completion-invocation.c index c249565a4b681..52daa55f77a68 100644 --- a/clang/test/Index/record-completion-invocation.c +++ b/clang/test/Index/record-completion-invocation.c @@ -8,4 +8,4 @@ // RUN: env LIBCLANG_DISABLE_CRASH_RECOVERY=1 CINDEXTEST_INVOCATION_EMISSION_PATH=%t not --crash c-index-test -code-completion-at=%s:10:1 "-remap-file=%s,%S/Inputs/record-parsing-invocation-remap.c" %s // RUN: cat %t/libclang-* | FileCheck %s -// CHECK: {"toolchain":"{{.*}}","libclang.operation":"complete","libclang.opts":1,"args":["clang","-fno-spell-checking","{{.*}}record-completion-invocation.c","-Xclang","-detailed-preprocessing-record","-fallow-editor-placeholders"],"invocation-args":["-code-completion-at={{.*}}record-completion-invocation.c:10:1"],"unsaved_file_hashes":[{"name":"{{.*}}record-completion-invocation.c","md5":"aee23773de90e665992b48209351d70e"}]} +// CHECK: {"toolchain":"{{.*}}","signature":"{{.*}}","libclang.operation":"complete","libclang.opts":1,"args":["clang","-fno-spell-checking","{{.*}}record-completion-invocation.c","-Xclang","-detailed-preprocessing-record","-fallow-editor-placeholders"],"invocation-args":["-code-completion-at={{.*}}record-completion-invocation.c:10:1"],"unsaved_file_hashes":[{"name":"{{.*}}record-completion-invocation.c","md5":"aee23773de90e665992b48209351d70e"}]} diff --git a/clang/test/Index/record-parsing-invocation.c b/clang/test/Index/record-parsing-invocation.c index 3254e58aef048..4fbe0a25f7889 100644 --- a/clang/test/Index/record-parsing-invocation.c +++ b/clang/test/Index/record-parsing-invocation.c @@ -24,5 +24,5 @@ # pragma clang __debug parser_crash #endif -// CHECK: {"toolchain":"{{.*}}","libclang.operation":"parse","libclang.opts":1,"args":["clang","-fno-spell-checking","{{.*}}record-parsing-invocation.c","-Xclang","-detailed-preprocessing-record","-fallow-editor-placeholders"]} -// CHECK-UNSAVED: {"toolchain":"{{.*}}","libclang.operation":"parse","libclang.opts":1,"args":["clang","-fno-spell-checking","{{.*}}record-parsing-invocation.c","-Xclang","-detailed-preprocessing-record","-fallow-editor-placeholders"],"unsaved_file_hashes":[{"name":"{{.*}}record-parsing-invocation.c","md5":"aee23773de90e665992b48209351d70e"}]} +// CHECK: {"toolchain":"{{.*}}","signature":"{{.*}}","libclang.operation":"parse","libclang.opts":1,"args":["clang","-fno-spell-checking","{{.*}}record-parsing-invocation.c","-Xclang","-detailed-preprocessing-record","-fallow-editor-placeholders"]} +// CHECK-UNSAVED: {"toolchain":"{{.*}}","signature":"{{.*}}","libclang.operation":"parse","libclang.opts":1,"args":["clang","-fno-spell-checking","{{.*}}record-parsing-invocation.c","-Xclang","-detailed-preprocessing-record","-fallow-editor-placeholders"],"unsaved_file_hashes":[{"name":"{{.*}}record-parsing-invocation.c","md5":"aee23773de90e665992b48209351d70e"}]} diff --git a/clang/tools/driver/cc1gen_reproducer_main.cpp b/clang/tools/driver/cc1gen_reproducer_main.cpp index a4c034d8d3572..72479aeebd555 100644 --- a/clang/tools/driver/cc1gen_reproducer_main.cpp +++ b/clang/tools/driver/cc1gen_reproducer_main.cpp @@ -35,6 +35,7 @@ struct UnsavedFileHash { struct ClangInvocationInfo { std::string Toolchain; + std::string Signature; std::string LibclangOperation; std::string LibclangOptions; std::vector<std::string> Arguments; @@ -60,6 +61,7 @@ template <> struct MappingTraits<UnsavedFileHash> { template <> struct MappingTraits<ClangInvocationInfo> { static void mapping(IO &IO, ClangInvocationInfo &Info) { IO.mapRequired("toolchain", Info.Toolchain); + IO.mapOptional("signature", Info.Signature); IO.mapOptional("libclang.operation", Info.LibclangOperation); IO.mapOptional("libclang.opts", Info.LibclangOptions); IO.mapRequired("args", Info.Arguments); diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 743057182962b..58a87e7dd183c 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -3457,9 +3457,9 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, !PrecompilePreamble ? 0 : 2 - CreatePreambleOnFirstParse; LibclangInvocationReporter InvocationReporter( - *CXXIdx, LibclangInvocationReporter::OperationKind::ParseOperation, - options, llvm::makeArrayRef(*Args), /*InvocationArgs=*/None, - unsaved_files); + *CXXIdx, source_filename, + LibclangInvocationReporter::OperationKind::ParseOperation, options, + llvm::makeArrayRef(*Args), /*InvocationArgs=*/None, unsaved_files); std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCommandLine( Args->data(), Args->data() + Args->size(), CXXIdx->getPCHContainerOperations(), Diags, diff --git a/clang/tools/libclang/CIndexCodeCompletion.cpp b/clang/tools/libclang/CIndexCodeCompletion.cpp index ba2c818cca307..ace1f70fcff54 100644 --- a/clang/tools/libclang/CIndexCodeCompletion.cpp +++ b/clang/tools/libclang/CIndexCodeCompletion.cpp @@ -702,7 +702,8 @@ clang_codeCompleteAt_Impl(CXTranslationUnit TU, const char *complete_filename, complete_line, complete_column) .str(); LibclangInvocationReporter InvocationReporter( - *CXXIdx, LibclangInvocationReporter::OperationKind::CompletionOperation, + *CXXIdx, complete_filename, + LibclangInvocationReporter::OperationKind::CompletionOperation, TU->ParsingOptions, CArgs, CompletionInvocation, unsaved_files); AST->CodeComplete(complete_filename, complete_line, complete_column, RemappedFiles, (options & CXCodeComplete_IncludeMacros), diff --git a/clang/tools/libclang/CIndexer.cpp b/clang/tools/libclang/CIndexer.cpp index d902470e6a989..2b2d6c46d901f 100644 --- a/clang/tools/libclang/CIndexer.cpp +++ b/clang/tools/libclang/CIndexer.cpp @@ -92,8 +92,8 @@ StringRef CIndexer::getClangToolchainPath() { } LibclangInvocationReporter::LibclangInvocationReporter( - CIndexer &Idx, OperationKind Op, unsigned ParseOptions, - llvm::ArrayRef<const char *> Args, + CIndexer &Idx, StringRef SourceFilename, OperationKind Op, + unsigned ParseOptions, llvm::ArrayRef<const char *> Args, llvm::ArrayRef<std::string> InvocationArgs, llvm::ArrayRef<CXUnsavedFile> UnsavedFiles) { StringRef Path = Idx.getInvocationEmissionPath(); @@ -118,6 +118,12 @@ LibclangInvocationReporter::LibclangInvocationReporter( OS << '{'; WriteStringKey("toolchain", Idx.getClangToolchainPath()); OS << ','; + std::string Signature; + llvm::raw_string_ostream SignatureOS(Signature); + SignatureOS << "clang-" << getClangMajorVersionNumber() << ';' + << llvm::sys::path::filename(SourceFilename); + WriteStringKey("signature", SignatureOS.str()); + OS << ','; WriteStringKey("libclang.operation", Op == OperationKind::ParseOperation ? "parse" : "complete"); OS << ','; diff --git a/clang/tools/libclang/CIndexer.h b/clang/tools/libclang/CIndexer.h index 6c46eed4fb988..b0553d4799b84 100644 --- a/clang/tools/libclang/CIndexer.h +++ b/clang/tools/libclang/CIndexer.h @@ -92,8 +92,8 @@ class LibclangInvocationReporter { public: enum class OperationKind { ParseOperation, CompletionOperation }; - LibclangInvocationReporter(CIndexer &Idx, OperationKind Op, - unsigned ParseOptions, + LibclangInvocationReporter(CIndexer &Idx, StringRef SourceFilename, + OperationKind Op, unsigned ParseOptions, llvm::ArrayRef<const char *> Args, llvm::ArrayRef<std::string> InvocationArgs, llvm::ArrayRef<CXUnsavedFile> UnsavedFiles); From 6a19e7c8fc03ca75504341ccf092432943421c38 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Mon, 9 Apr 2018 14:50:50 -0700 Subject: [PATCH 264/582] [index] Update in IndexingAction.cpp for upstream change rdar://39287442 apple-llvm-split-commit: 7e049c5c2d094738ec276bb9a44fd1c444673d33 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexingAction.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index e719743a4ddd8..6507f59b54ba9 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -243,8 +243,20 @@ class IndexDataRecorder : public IndexDataConsumer { private: bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations, - FileID FID, unsigned Offset, + SourceLocation Loc, ASTNodeInfo ASTNode) override { + SourceManager &SM = PP->getSourceManager(); + Loc = SM.getFileLoc(Loc); + if (Loc.isInvalid()) + return true; + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + + if (FID.isInvalid()) + return true; + // Ignore the predefines buffer. const FileEntry *FE = PP->getSourceManager().getFileEntryForID(FID); if (!FE) From e843a3fcae9eb62e4da192f1a47fcf2292a8d559 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 20 Apr 2018 10:31:21 -0700 Subject: [PATCH 265/582] Tweak Module to reduce merge conflicts with upstream LLVM Clang No functionality change. apple-llvm-split-commit: e54180dcef46ffd57d4eb17af12ef4ee326170f1 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Module.h | 6 +++--- clang/lib/Basic/Module.cpp | 15 ++++++++++----- clang/lib/Serialization/ASTReader.cpp | 17 ++++++++++++++--- clang/lib/Serialization/ASTWriter.cpp | 13 +++++++++++-- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index 5139711def035..1297719fb64fb 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -233,9 +233,6 @@ class Module { /// \brief Whether this is an inferred submodule (module * { ... }). unsigned IsInferred : 1; - /// \brief Whether this is a module who has its swift_names inferred. - unsigned IsSwiftInferImportAsMember : 1; - /// \brief Whether we should infer submodules for this module based on /// the headers. /// @@ -265,6 +262,9 @@ class Module { /// to a regular (public) module map. unsigned ModuleMapIsPrivate : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// \brief Describes the visibility of the various names within a /// particular module. enum NameVisibilityKind { diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 631cbd056790f..f21fca3d24d19 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -38,14 +38,19 @@ using namespace clang; Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, bool IsFramework, bool IsExplicit, unsigned VisibilityID) : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent), Directory(), - Umbrella(), ASTFile(nullptr), VisibilityID(VisibilityID), - IsMissingRequirement(false), HasIncompatibleModuleFile(false), - IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), - IsExplicit(IsExplicit), IsSystem(false), IsExternC(false), - IsInferred(false), IsSwiftInferImportAsMember(false), + Umbrella(), ASTFile(nullptr), + VisibilityID(VisibilityID), IsMissingRequirement(false), + HasIncompatibleModuleFile(false), IsAvailable(true), + IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit), + IsSystem(false), IsExternC(false), IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), ConfigMacrosExhaustive(false), NoUndeclaredIncludes(false), ModuleMapIsPrivate(false), + + // SWIFT-SPECIFIC FIELDS HERE. Handling them separately helps avoid merge + // conflicts. + IsSwiftInferImportAsMember(false), + NameVisibility(Hidden) { if (Parent) { if (!Parent->isAvailable()) diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 77800967b9af2..acdceb0f3cc8a 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4980,7 +4980,10 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { break; case SUBMODULE_DEFINITION: { - if (Record.size() < 12) { + // Factor this out into a separate constant to make it easier to resolve + // merge conflicts. + static const unsigned NUM_SWIFT_SPECIFIC_FIELDS = 1; + if (Record.size() < 12 + NUM_SWIFT_SPECIFIC_FIELDS) { Error("malformed module definition"); return Failure; } @@ -4990,11 +4993,15 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { SubmoduleID GlobalID = getGlobalSubmoduleID(F, Record[Idx++]); SubmoduleID Parent = getGlobalSubmoduleID(F, Record[Idx++]); Module::ModuleKind Kind = (Module::ModuleKind)Record[Idx++]; + + // SWIFT-SPECIFIC FIELDS HERE. Handling them separately helps avoid merge + // conflicts. See also NUM_SWIFT_SPECIFIC_FIELDS above. + bool IsSwiftInferImportAsMember = Record[Idx++]; + bool IsFramework = Record[Idx++]; bool IsExplicit = Record[Idx++]; bool IsSystem = Record[Idx++]; bool IsExternC = Record[Idx++]; - bool IsSwiftInferImportAsMember = Record[Idx++]; bool InferSubmodules = Record[Idx++]; bool InferExplicitSubmodules = Record[Idx++]; bool InferExportWildcard = Record[Idx++]; @@ -5043,12 +5050,16 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { CurrentModule->IsFromModuleFile = true; CurrentModule->IsSystem = IsSystem || CurrentModule->IsSystem; CurrentModule->IsExternC = IsExternC; - CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember; CurrentModule->InferSubmodules = InferSubmodules; CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules; CurrentModule->InferExportWildcard = InferExportWildcard; CurrentModule->ConfigMacrosExhaustive = ConfigMacrosExhaustive; CurrentModule->ModuleMapIsPrivate = ModuleMapIsPrivate; + + // SWIFT-SPECIFIC FIELDS HERE. Putting them last helps avoid merge + // conflicts. + CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember; + if (DeserializationListener) DeserializationListener->ModuleRead(GlobalID, CurrentModule); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 69eb3316e706f..c5310efb4685e 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -2801,11 +2801,15 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ID Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Parent Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Kind + + // SWIFT-SPECIFIC FIELDS HERE. Handling them separately helps avoid merge + // conflicts. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM... + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsFramework Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExplicit Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExternC - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferSubmodules... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild... @@ -2908,11 +2912,16 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { ID, ParentID, (RecordData::value_type)Mod->Kind, + + // SWIFT-SPECIFIC FIELDS HERE. + // Handling them separately helps + // avoid merge conflicts. + Mod->IsSwiftInferImportAsMember, + Mod->IsFramework, Mod->IsExplicit, Mod->IsSystem, Mod->IsExternC, - Mod->IsSwiftInferImportAsMember, Mod->InferSubmodules, Mod->InferExplicitSubmodules, Mod->InferExportWildcard, From 82e3f53a66e191efdee9651029f30fd8e663f492 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 20 Apr 2018 08:44:11 -0700 Subject: [PATCH 266/582] Tweak search path for API notes for private top-level modules This is most important for frameworks: we want to look for PrivateHeaders/Bar.apinotes rather than Headers/Bar.apinotes and PrivateHeaders/Bar_private.apinotes. (This is especially important because 'Bar' is probably actually something like 'FooKit_Private' in practice.) Note that the naming convention of 'FooKit_Private' for a private top-level module may cause certain API notes files to be loaded twice on case-insensitive filesystems: once as the regular API notes for 'FooKit_Private', and once as the /private/ API notes for 'FooKit'. However, this shouldn't be a problem in practice (other than a bit of extra I/O) because (1) most declarations will only show up in one of the two modules anyway, being ignored in the other, and (2) the API notes we have are /mostly/ idempotent (except for how they record replacements). (There is a Name field in an API notes file, but it's not currently validated.) rdar://problem/39417625 apple-llvm-split-commit: 889037b5e7e983e50db047b675940f1f9968cc4c apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesManager.cpp | 36 ++++++++++++++----- .../Headers/TopLevelPrivateKit.h | 1 + .../TopLevelPrivateKit_Private.apinotes | 1 + .../Modules/module.modulemap | 5 +++ .../Modules/module.private.modulemap | 5 +++ .../TopLevelPrivateKit.apinotes | 1 + .../TopLevelPrivateKit_Private.apinotes | 4 +++ .../TopLevelPrivateKit_Private.h | 1 + ...opLevelPrivateKit_Private_private.apinotes | 1 + .../Inputs/Headers/PrivateLib.apinotes | 4 +++ .../test/APINotes/Inputs/Headers/PrivateLib.h | 1 + .../Headers/PrivateLib_private.apinotes | 1 + .../Inputs/Headers/module.private.modulemap | 3 ++ .../test/APINotes/top-level-private-modules.c | 8 +++++ 14 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit_Private.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.private.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private_private.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/PrivateLib.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/PrivateLib.h create mode 100644 clang/test/APINotes/Inputs/Headers/PrivateLib_private.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/module.private.modulemap create mode 100644 clang/test/APINotes/top-level-private-modules.c diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 6fc0fda0872b7..6325816eb350a 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -255,21 +255,41 @@ bool APINotesManager::loadCurrentModuleAPINotes( if (module->IsFramework) { // For frameworks, we search in the "Headers" or "PrivateHeaders" // subdirectory. + // + // Public modules: + // - Headers/Foo.apinotes + // - PrivateHeaders/Foo_Private.apinotes + // Private modules: + // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has + // the word "Private" in it in practice) llvm::SmallString<128> path; path += module->Directory->getName(); - unsigned pathLen = path.size(); - llvm::sys::path::append(path, "Headers"); - if (auto apinotesDir = fileMgr.getDirectory(path)) - tryAPINotes(apinotesDir, /*wantPublic=*/true); + if (!module->ModuleMapIsPrivate) { + unsigned pathLen = path.size(); + + llvm::sys::path::append(path, "Headers"); + if (auto apinotesDir = fileMgr.getDirectory(path)) + tryAPINotes(apinotesDir, /*wantPublic=*/true); + + path.resize(pathLen); + } - path.resize(pathLen); llvm::sys::path::append(path, "PrivateHeaders"); - if (auto privateAPINotesDir = fileMgr.getDirectory(path)) - tryAPINotes(privateAPINotesDir, /*wantPublic=*/false); + if (auto privateAPINotesDir = fileMgr.getDirectory(path)) { + tryAPINotes(privateAPINotesDir, + /*wantPublic=*/module->ModuleMapIsPrivate); + } } else { + // Public modules: + // - Foo.apinotes + // - Foo_Private.apinotes + // Private modules: + // - Bar.apinotes (except that 'Bar' probably already has the word + // "Private" in it in practice) tryAPINotes(module->Directory, /*wantPublic=*/true); - tryAPINotes(module->Directory, /*wantPublic=*/false); + if (!module->ModuleMapIsPrivate) + tryAPINotes(module->Directory, /*wantPublic=*/false); } if (foundAny) diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit.h b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit.h new file mode 100644 index 0000000000000..e8ece8fb87041 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit.h @@ -0,0 +1 @@ +extern int TopLevelPrivateKit_Public; \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit_Private.apinotes b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit_Private.apinotes new file mode 100644 index 0000000000000..ece1dd220adf5 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Headers/TopLevelPrivateKit_Private.apinotes @@ -0,0 +1 @@ +garbage here because this file shouldn't get read \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..70faa54e83477 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module TopLevelPrivateKit { + umbrella header "TopLevelPrivateKit.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.private.modulemap b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.private.modulemap new file mode 100644 index 0000000000000..0958a14d67108 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/Modules/module.private.modulemap @@ -0,0 +1,5 @@ +framework module TopLevelPrivateKit_Private { + umbrella header "TopLevelPrivateKit_Private.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit.apinotes new file mode 100644 index 0000000000000..ece1dd220adf5 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit.apinotes @@ -0,0 +1 @@ +garbage here because this file shouldn't get read \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.apinotes b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.apinotes new file mode 100644 index 0000000000000..43323621588bb --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.apinotes @@ -0,0 +1,4 @@ +Name: TopLevelPrivateKit_Private +Globals: +- Name: TopLevelPrivateKit_Private + Type: float diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.h b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.h new file mode 100644 index 0000000000000..39cbfe6e9918b --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private.h @@ -0,0 +1 @@ +extern int TopLevelPrivateKit_Private; diff --git a/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private_private.apinotes b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private_private.apinotes new file mode 100644 index 0000000000000..ece1dd220adf5 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/TopLevelPrivateKit.framework/PrivateHeaders/TopLevelPrivateKit_Private_private.apinotes @@ -0,0 +1 @@ +garbage here because this file shouldn't get read \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Headers/PrivateLib.apinotes b/clang/test/APINotes/Inputs/Headers/PrivateLib.apinotes new file mode 100644 index 0000000000000..5f62284aadcaf --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/PrivateLib.apinotes @@ -0,0 +1,4 @@ +Name: HeaderLib +Globals: +- Name: PrivateLib + Type: float diff --git a/clang/test/APINotes/Inputs/Headers/PrivateLib.h b/clang/test/APINotes/Inputs/Headers/PrivateLib.h new file mode 100644 index 0000000000000..3a47dc7d3758c --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/PrivateLib.h @@ -0,0 +1 @@ +extern int PrivateLib; \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Headers/PrivateLib_private.apinotes b/clang/test/APINotes/Inputs/Headers/PrivateLib_private.apinotes new file mode 100644 index 0000000000000..ece1dd220adf5 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/PrivateLib_private.apinotes @@ -0,0 +1 @@ +garbage here because this file shouldn't get read \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Headers/module.private.modulemap b/clang/test/APINotes/Inputs/Headers/module.private.modulemap new file mode 100644 index 0000000000000..82e08aaaa8c67 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/module.private.modulemap @@ -0,0 +1,3 @@ +module PrivateLib { + header "PrivateLib.h" +} diff --git a/clang/test/APINotes/top-level-private-modules.c b/clang/test/APINotes/top-level-private-modules.c new file mode 100644 index 0000000000000..0da72b2e36f4f --- /dev/null +++ b/clang/test/APINotes/top-level-private-modules.c @@ -0,0 +1,8 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -Wno-private-module -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify + +#include <PrivateLib.h> +#include <TopLevelPrivateKit/TopLevelPrivateKit_Private.h> + +void *testPlain = PrivateLib; // expected-error {{initializing 'void *' with an expression of incompatible type 'float'}} +void *testFramework = TopLevelPrivateKit_Private; // expected-error {{initializing 'void *' with an expression of incompatible type 'float'}} From 845f3829b91522920a59c351b9011af01c5c7f87 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Wed, 25 Apr 2018 09:32:57 -0700 Subject: [PATCH 267/582] [Hashing] Forward-declare hash_value for tuples. This allows hash_combine to work on tuples containing no types within the LLVM namespace, because two-phase name lookup is weird. apple-llvm-split-commit: 691f840fa4ddd6b47462e2769da0fb9d4be63c77 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/Hashing.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h index 8b1a305852b64..7a5a34424688a 100644 --- a/llvm/include/llvm/ADT/Hashing.h +++ b/llvm/include/llvm/ADT/Hashing.h @@ -119,6 +119,9 @@ hash_code hash_value(const std::pair<T, U> &arg); template <typename T> hash_code hash_value(const std::basic_string<T> &arg); +/// \brief Compute a hash_code for a tuple. +template <typename ...Ts> +hash_code hash_value(const std::tuple<Ts...> &arg); /// \brief Override the execution seed with a fixed value. /// From bca07f06d339891cfe4dcc474a1c73d4f758d693 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Fri, 27 Apr 2018 15:39:37 -0700 Subject: [PATCH 268/582] Add a new attribute swift_bridged_typedef for typedef bridging. When importing a typedef of a bridged type, Swift will use the typedef-name to describe the unbridging version of the type (e.g., NSString). This attribute signals when Swift should instead import the typedef using the bridged type (e.g., Swift's String). Clang side of rdar://problem/39497900. apple-llvm-split-commit: 4e2ac64d1cbae0903c80f2439d9379b95ffefa17 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 7 +++++++ clang/include/clang/Basic/AttrDocs.td | 7 +++++++ clang/lib/Sema/SemaDeclAttr.cpp | 3 +++ .../Misc/pragma-attribute-supported-attributes-list.test | 5 +++-- clang/test/SemaObjC/attr-swift.m | 8 ++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 37311b3163c6a..7b3db1233b861 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1743,6 +1743,13 @@ def SwiftBridge : Attr { let Documentation = [SwiftBridgeDocs]; } +def SwiftBridgedTypedef : Attr { + let Spellings = [GNU<"swift_bridged_typedef">]; + let Subjects = SubjectList<[TypedefName], ErrorDiag, "typedefs">; + let Args = []; + let Documentation = [SwiftBridgedTypedefDocs]; +} + def SwiftObjCMembers : Attr { let Spellings = [GNU<"swift_objc_members">]; let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 1123b78bd4472..e8842dcd12c52 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2743,6 +2743,13 @@ The ``swift_bridge`` attribute indicates that the type to which the attribute ap }]; } +def SwiftBridgedTypedefDocs : Documentation { + let Category = SwiftDocs; + let Content = [{ +The ``swift_bridged_typedef`` attribute indicates that, when the typedef to which the attribute appertains is imported into Swift, it should refer to the bridged Swift type (e.g., Swift's ``String``) rather than the Objective-C type as written (e.g., ``NSString``). + }]; +} + def SwiftObjCMembersDocs : Documentation { let Category = SwiftDocs; let Content = [{ diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 7a1b6665781db..55ce252717023 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6916,6 +6916,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SwiftBridge: handleSwiftBridgeAttr(S, D, AL); break; + case AttributeList::AT_SwiftBridgedTypedef: + handleSimpleAttribute<SwiftBridgedTypedefAttr>(S, D, Attr); + break; case AttributeList::AT_SwiftObjCMembers: handleSimpleAttribute<SwiftObjCMembersAttr>(S, D, AL); break; diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 8bff49a78d34d..df2df02f58e35 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,8 +2,8 @@ // The number of supported attributes should never go down! -// Swift has 3 additional attributes -// CHECK: #pragma clang attribute supports 71 attributes: +// Swift has 7 additional attributes +// CHECK: #pragma clang attribute supports 72 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -62,6 +62,7 @@ // CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) // CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member) +// CHECK-NEXT: SwiftBridgedTypedef (SubjectMatchRule_type_alias) // CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: SwiftError (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/SemaObjC/attr-swift.m b/clang/test/SemaObjC/attr-swift.m index e87ac913dd804..bc673f98c3f33 100644 --- a/clang/test/SemaObjC/attr-swift.m +++ b/clang/test/SemaObjC/attr-swift.m @@ -215,3 +215,11 @@ - (instancetype) harry4 __attribute__((swift_error(zero_result))); // expected-e extern _Bool suzanne __attribute__((swift_error(none))); // expected-error {{'swift_error' attribute only applies to functions and Objective-C methods}} + +// --- swift_bridged_typedef --- +@interface NSString +@end + +typedef NSString *NSMyAmazingStringAlias __attribute__((swift_bridged_typedef)); + +struct __attribute__((swift_bridged_typedef)) NotATypedef { }; // expected-error{{'swift_bridged_typedef' attribute only applies to typedefs}} From f04d1436b1ef8a4785a84306330178168197e552 Mon Sep 17 00:00:00 2001 From: Doug Gregor <dgregor@apple.com> Date: Fri, 27 Apr 2018 22:35:46 -0700 Subject: [PATCH 269/582] Fix addition of swift_bridged_typedef attribute. apple-llvm-split-commit: 8c4ab6bcc21790b4fd3b4973fa78787ff565e6c2 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclAttr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 55ce252717023..35ab68862a155 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6917,7 +6917,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleSwiftBridgeAttr(S, D, AL); break; case AttributeList::AT_SwiftBridgedTypedef: - handleSimpleAttribute<SwiftBridgedTypedefAttr>(S, D, Attr); + handleSimpleAttribute<SwiftBridgedTypedefAttr>(S, D, AL); break; case AttributeList::AT_SwiftObjCMembers: handleSimpleAttribute<SwiftObjCMembersAttr>(S, D, AL); From 0e070c2743aa6d9d15f008d52f00b68fd064bbc4 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Mon, 30 Apr 2018 14:08:44 -0700 Subject: [PATCH 270/582] upstream-update: adjust for SVN r331155 The `CharSourceRange` now requires the use of `getBegin()` and `getEnd()` instead. apple-llvm-split-commit: 60cdc24519e18cc5e8ab4a4555d794ce8ca0fd0d apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingProtocolStubs.cpp | 4 ++-- clang/lib/Tooling/Refactor/Extract.cpp | 5 +++-- .../Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp | 2 +- clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/clang/lib/Edit/FillInMissingProtocolStubs.cpp b/clang/lib/Edit/FillInMissingProtocolStubs.cpp index 64843e508b380..8aa6d202ae473 100644 --- a/clang/lib/Edit/FillInMissingProtocolStubs.cpp +++ b/clang/lib/Edit/FillInMissingProtocolStubs.cpp @@ -248,12 +248,12 @@ class MethodSet { return SourceLocation(); SourceLocation Loc = MethodsFromProtocolInContainer[0]->getLocEnd(); if (Loc.isMacroID()) - Loc = SM.getExpansionRange(Loc).second; + Loc = SM.getExpansionRange(Loc).getEnd(); for (const ObjCMethodDecl *M : makeArrayRef(MethodsFromProtocolInContainer).drop_front()) { SourceLocation EndLoc = M->getLocEnd(); if (EndLoc.isMacroID()) - EndLoc = SM.getExpansionRange(EndLoc).second; + EndLoc = SM.getExpansionRange(EndLoc).getEnd(); if (SM.isBeforeInTranslationUnit(Loc, EndLoc)) Loc = EndLoc; } diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index 4f6b4de0b594a..f6a488d86b652 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -306,8 +306,9 @@ initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, Context.getSourceManager().isMacroArgExpansion(Range.getEnd())) Range.setEnd(Context.getSourceManager().getSpellingLoc(Range.getEnd())); else - Range.setEnd( - Context.getSourceManager().getExpansionRange(Range.getEnd()).second); + Range.setEnd(Context.getSourceManager() + .getExpansionRange(Range.getEnd()) + .getEnd()); } CandidateExtractionInfo.push_back(ExtractOperation::CandidateInfo(Range)); Result.RefactoringOp = std::move(Operation); diff --git a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp index ba730f68a8a20..f3f743b9c4f18 100644 --- a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp +++ b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp @@ -163,7 +163,7 @@ static SourceLocation findInsertionLocationForMethodsFromAbstractClass( if (OM->getLexicalDeclContext() == AbstractClass) { SourceLocation EndLoc = M->getLocEnd(); if (EndLoc.isMacroID()) - EndLoc = SM.getExpansionRange(EndLoc).second; + EndLoc = SM.getExpansionRange(EndLoc).getEnd(); if (Loc.isInvalid()) Loc = EndLoc; else if (SM.isBeforeInTranslationUnit(Loc, EndLoc)) diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp index c5865e468a20b..5d0675bb64f50 100644 --- a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -254,7 +254,7 @@ ImplementDeclaredCXXMethodsOperation::runInImplementationAST( NestedNameSpecifier *NamePrefix = nullptr; if (DefinedOutOfLineMethods.empty()) { const RecordDecl *OutermostRecord = findOutermostRecord(Class); - InsertionLoc = SM.getExpansionRange(OutermostRecord->getLocEnd()).second; + InsertionLoc = SM.getExpansionRange(OutermostRecord->getLocEnd()).getEnd(); if (SM.getFileID(InsertionLoc) == File) { // We can insert right after the class. Compute the appropriate // qualification. @@ -297,7 +297,7 @@ ImplementDeclaredCXXMethodsOperation::runInImplementationAST( } else { // Insert at the end of the defined methods. for (const CXXMethodDecl *M : DefinedOutOfLineMethods) { - SourceLocation EndLoc = SM.getExpansionRange(M->getLocEnd()).second; + SourceLocation EndLoc = SM.getExpansionRange(M->getLocEnd()).getEnd(); if (InsertionLoc.isInvalid() || SM.isBeforeInTranslationUnit(InsertionLoc, EndLoc)) { InsertionLoc = EndLoc; From 8e85c571f44bae76f3f3e30d2bbbfecced1002c1 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 3 May 2018 14:16:23 -0700 Subject: [PATCH 271/582] [Refactor] Use a lock when invoking ClangTool::run in the indexed file renamer ClangTool::run is not thread-safe, so we should guard it with a lock. apple-llvm-split-commit: 80847bed867db863ac73febdf2b09db7f25adb3b apple-llvm-split-dir: clang/ --- .../Tooling/Refactor/RenameIndexedFile.h | 22 ++++++++++++++++ .../Tooling/Refactor/RenameIndexedFile.cpp | 5 ++-- clang/tools/libclang/CRefactor.cpp | 25 +++++++++++++------ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h index 51508618f46f7..81442222a0f3a 100644 --- a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h +++ b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h @@ -15,6 +15,7 @@ #include "clang/Tooling/Refactor/RenamedSymbol.h" #include "clang/Tooling/Refactor/SymbolName.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Mutex.h" namespace clang { namespace tooling { @@ -65,16 +66,37 @@ class IndexedFileOccurrenceConsumer { const LangOptions &LangOpts) = 0; }; +/// Guards against thread unsafe parts of ClangTool::run. +class IndexedFileRenamerLock { + llvm::sys::Mutex &Lock; + bool IsUnlocked = false; + +public: + IndexedFileRenamerLock(llvm::sys::Mutex &Lock) : Lock(Lock) { Lock.lock(); } + + void unlock() { + Lock.unlock(); + IsUnlocked = true; + } + + ~IndexedFileRenamerLock() { + if (!IsUnlocked) + Lock.unlock(); + } +}; + /// Finds the renamed \c SymbolOccurrences in an already indexed files. class IndexedFileOccurrenceProducer final : public PreprocessorFrontendAction { bool IsMultiPiece; ArrayRef<IndexedSymbol> Symbols; IndexedFileOccurrenceConsumer &Consumer; + IndexedFileRenamerLock &Lock; const RefactoringOptionSet *Options; public: IndexedFileOccurrenceProducer(ArrayRef<IndexedSymbol> Symbols, IndexedFileOccurrenceConsumer &Consumer, + IndexedFileRenamerLock &Lock, const RefactoringOptionSet *Options = nullptr); private: diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index 433d0311470ab..bef6aca1717dc 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -28,8 +28,8 @@ namespace rename { IndexedFileOccurrenceProducer::IndexedFileOccurrenceProducer( ArrayRef<IndexedSymbol> Symbols, IndexedFileOccurrenceConsumer &Consumer, - const RefactoringOptionSet *Options) - : Symbols(Symbols), Consumer(Consumer), Options(Options) { + IndexedFileRenamerLock &Lock, const RefactoringOptionSet *Options) + : Symbols(Symbols), Consumer(Consumer), Lock(Lock), Options(Options) { IsMultiPiece = false; for (const auto &Symbol : Symbols) { if (Symbol.Name.size() > 1) { @@ -435,6 +435,7 @@ static void findInclusionDirectiveOccurrence( } void IndexedFileOccurrenceProducer::ExecuteAction() { + Lock.unlock(); // The execution should now be thread-safe. Preprocessor &PP = getCompilerInstance().getPreprocessor(); PP.EnterMainSourceFile(); diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index a58c98bd46b29..5635096d56c5a 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -515,6 +515,9 @@ translateIndexedOccurrenceKind(CXCursorKind Kind) { } } +/// ClangTool::run is not thread-safe, so we have to guard it. +static llvm::ManagedStatic<llvm::sys::Mutex> ClangToolConstructionMutex; + // TODO: Remove CXErrorCode performIndexedFileRename( ArrayRef<CXRenamedIndexedSymbol> Symbols, StringRef Filename, @@ -577,6 +580,7 @@ CXErrorCode performIndexedFileRename( public rename::IndexedFileOccurrenceConsumer { ArrayRef<CXRenamedIndexedSymbol> Symbols; ArrayRef<rename::IndexedSymbol> IndexedSymbols; + rename::IndexedFileRenamerLock &Lock; const RefactoringOptionSet *Options; public: @@ -585,13 +589,14 @@ CXErrorCode performIndexedFileRename( ToolRunner(ArrayRef<CXRenamedIndexedSymbol> Symbols, ArrayRef<rename::IndexedSymbol> IndexedSymbols, + rename::IndexedFileRenamerLock &Lock, const RefactoringOptionSet *Options) - : Symbols(Symbols), IndexedSymbols(IndexedSymbols), Options(Options), - Result(nullptr), Err(CXError_Success) {} + : Symbols(Symbols), IndexedSymbols(IndexedSymbols), Lock(Lock), + Options(Options), Result(nullptr), Err(CXError_Success) {} clang::FrontendAction *create() override { return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, - Options); + Lock, Options); } void handleOccurrence(const rename::OldSymbolOccurrence &Occurrence, @@ -613,7 +618,9 @@ CXErrorCode performIndexedFileRename( } }; - auto Runner = llvm::make_unique<ToolRunner>(Symbols, IndexedSymbols, Options); + rename::IndexedFileRenamerLock Lock(*ClangToolConstructionMutex); + auto Runner = + llvm::make_unique<ToolRunner>(Symbols, IndexedSymbols, Lock, Options); // Run a clang tool on the input file. std::string Name = Filename.str(); @@ -687,18 +694,21 @@ CXErrorCode performIndexedSymbolSearch( class ToolRunner final : public FrontendActionFactory, public rename::IndexedFileOccurrenceConsumer { ArrayRef<rename::IndexedSymbol> IndexedSymbols; + rename::IndexedFileRenamerLock &Lock; const RefactoringOptionSet *Options; public: SymbolOccurrencesResult *Result; ToolRunner(ArrayRef<rename::IndexedSymbol> IndexedSymbols, + rename::IndexedFileRenamerLock &Lock, const RefactoringOptionSet *Options) - : IndexedSymbols(IndexedSymbols), Options(Options), Result(nullptr) {} + : IndexedSymbols(IndexedSymbols), Lock(Lock), Options(Options), + Result(nullptr) {} clang::FrontendAction *create() override { return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, - Options); + Lock, Options); } void handleOccurrence(const rename::OldSymbolOccurrence &Occurrence, @@ -714,7 +724,8 @@ CXErrorCode performIndexedSymbolSearch( } }; - auto Runner = llvm::make_unique<ToolRunner>(IndexedSymbols, Options); + rename::IndexedFileRenamerLock Lock(*ClangToolConstructionMutex); + auto Runner = llvm::make_unique<ToolRunner>(IndexedSymbols, Lock, Options); // Run a clang tool on the input file. std::string Name = Filename.str(); From 66628f1709045654209fb93a421e192cf8feb37b Mon Sep 17 00:00:00 2001 From: Jessica Paquette <jpaquette@apple.com> Date: Wed, 9 May 2018 13:03:26 -0700 Subject: [PATCH 272/582] Fix link error introduced in previous merge There was a function parameter missing in CheckRelatedResultTypeCompatability that somehow didn't make it through the merge. apple-llvm-split-commit: 0c827400c7b044f7f6620a7fb6f93304393b008c apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclObjC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index c6e6536ed69ff..49231322f2041 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -4749,7 +4749,7 @@ Decl *Sema::ActOnMethodDeclaration( } ResultTypeCompatibilityKind RTC - = checkRelatedResultTypeCompatibility(ObjCMethod, CurrentClass); + = CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass); CheckObjCMethodOverrides(ObjCMethod, CurrentClass, RTC); From 6757930b01ba46913693963cf1b19600ebc0a55e Mon Sep 17 00:00:00 2001 From: Julie Hockett <juliehockett@google.com> Date: Wed, 9 May 2018 18:27:33 +0000 Subject: [PATCH 273/582] [clang] Adding CharacteristicKind to PPCallbacks::InclusionDirective Adding a SrcMgr::CharacteristicKind parameter to the InclusionDirective in PPCallbacks, and updating calls to that function. This will be useful in https://reviews.llvm.org/D43778 to determine which includes are system headers. Differential Revision: https://reviews.llvm.org/D46614 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@331904 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 6a37651bb30339e60ea617d510b4703bcd2e89e8) apple-llvm-split-commit: 0c7dd45cf68147f8074cf47af6c809f2bccb1c88 apple-llvm-split-dir: clang/ --- clang/include/clang/Lex/PPCallbacks.h | 14 +++++-- clang/include/clang/Lex/PreprocessingRecord.h | 4 +- clang/lib/CodeGen/MacroPPCallbacks.cpp | 3 +- clang/lib/CodeGen/MacroPPCallbacks.h | 3 +- clang/lib/Frontend/DependencyFile.cpp | 9 ++-- clang/lib/Frontend/DependencyGraph.cpp | 23 ++++++----- .../Frontend/ModuleDependencyCollector.cpp | 3 +- .../lib/Frontend/PrintPreprocessedOutput.cpp | 23 ++++++----- .../Frontend/Rewrite/InclusionRewriter.cpp | 6 ++- clang/lib/Lex/PPDirectives.cpp | 2 +- clang/lib/Lex/PreprocessingRecord.cpp | 3 +- clang/tools/libclang/Indexing.cpp | 3 +- clang/unittests/Lex/PPCallbacksTest.cpp | 41 ++++++++++++++----- 13 files changed, 89 insertions(+), 48 deletions(-) diff --git a/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index 1814821cdced1..eb85bda840afe 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -117,6 +117,10 @@ class PPCallbacks { /// \param Imported The module, whenever an inclusion directive was /// automatically turned into a module import or null otherwise. /// + /// \param FileType The characteristic kind, indicates whether a file or + /// directory holds normal user code, system code, or system code which is + /// implicitly 'extern "C"' in C++ mode. + /// virtual void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, @@ -125,7 +129,8 @@ class PPCallbacks { const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) { } /// Callback invoked whenever there was an explicit module-import @@ -367,13 +372,14 @@ class PPChainedCallbacks : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { First->InclusionDirective(HashLoc, IncludeTok, FileName, IsAngled, FilenameRange, File, SearchPath, RelativePath, - Imported); + Imported, FileType); Second->InclusionDirective(HashLoc, IncludeTok, FileName, IsAngled, FilenameRange, File, SearchPath, RelativePath, - Imported); + Imported, FileType); } void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path, diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index d9233c5d8da6e..54e28a52f5b36 100644 --- a/clang/include/clang/Lex/PreprocessingRecord.h +++ b/clang/include/clang/Lex/PreprocessingRecord.h @@ -532,8 +532,8 @@ class Token; StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, - StringRef RelativePath, - const Module *Imported) override; + StringRef RelativePath, const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; void Ifdef(SourceLocation Loc, const Token &MacroNameTok, const MacroDefinition &MD) override; void Ifndef(SourceLocation Loc, const Token &MacroNameTok, diff --git a/clang/lib/CodeGen/MacroPPCallbacks.cpp b/clang/lib/CodeGen/MacroPPCallbacks.cpp index a6f21d8ddcfb7..48dea7d54b1e9 100644 --- a/clang/lib/CodeGen/MacroPPCallbacks.cpp +++ b/clang/lib/CodeGen/MacroPPCallbacks.cpp @@ -178,7 +178,8 @@ void MacroPPCallbacks::FileChanged(SourceLocation Loc, FileChangeReason Reason, void MacroPPCallbacks::InclusionDirective( SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, - StringRef SearchPath, StringRef RelativePath, const Module *Imported) { + StringRef SearchPath, StringRef RelativePath, const Module *Imported, + SrcMgr::CharacteristicKind FileType) { // Record the line location of the current included file. LastHashLoc = HashLoc; diff --git a/clang/lib/CodeGen/MacroPPCallbacks.h b/clang/lib/CodeGen/MacroPPCallbacks.h index e117f96f47dfa..48c67e2d36ade 100644 --- a/clang/lib/CodeGen/MacroPPCallbacks.h +++ b/clang/lib/CodeGen/MacroPPCallbacks.h @@ -101,7 +101,8 @@ class MacroPPCallbacks : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override; + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; /// Hook called whenever a macro definition is seen. void MacroDefined(const Token &MacroNameTok, diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index 5ad1e97a8ffe8..4e605523dc6a9 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -63,7 +63,8 @@ struct DepCollectorPPCallbacks : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { if (!File) DepCollector.maybeAddDependency(FileName, /*FromModule*/false, /*IsSystem*/false, /*IsModuleFile*/false, @@ -193,7 +194,8 @@ class DFGImpl : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override; + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; void EndOfMainFile() override { OutputDependencyFile(); @@ -313,7 +315,8 @@ void DFGImpl::InclusionDirective(SourceLocation HashLoc, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) { if (!File) { if (AddMissingHeaderDeps) AddFilename(FileName); diff --git a/clang/lib/Frontend/DependencyGraph.cpp b/clang/lib/Frontend/DependencyGraph.cpp index 67a977e38be2e..660f664447ab1 100644 --- a/clang/lib/Frontend/DependencyGraph.cpp +++ b/clang/lib/Frontend/DependencyGraph.cpp @@ -50,7 +50,8 @@ class DependencyGraphCallback : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override; + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; void EndOfMainFile() override { OutputGraphFile(); @@ -65,15 +66,17 @@ void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, SysRoot)); } -void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc, - const Token &IncludeTok, - StringRef FileName, - bool IsAngled, - CharSourceRange FilenameRange, - const FileEntry *File, - StringRef SearchPath, - StringRef RelativePath, - const Module *Imported) { +void DependencyGraphCallback::InclusionDirective( + SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) { if (!File) return; diff --git a/clang/lib/Frontend/ModuleDependencyCollector.cpp b/clang/lib/Frontend/ModuleDependencyCollector.cpp index d3b2b00bd6798..25cad8be6d008 100644 --- a/clang/lib/Frontend/ModuleDependencyCollector.cpp +++ b/clang/lib/Frontend/ModuleDependencyCollector.cpp @@ -50,7 +50,8 @@ struct ModuleDependencyPPCallbacks : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { if (!File) return; Collector.addFile(File->getName()); diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index 36ecdada0c8b5..1b35b32656e73 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -130,7 +130,8 @@ class PrintPPOutputPPCallbacks : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override; + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; void Ident(SourceLocation Loc, StringRef str) override; void PragmaMessage(SourceLocation Loc, StringRef Namespace, PragmaMessageKind Kind, StringRef Str) override; @@ -320,15 +321,17 @@ void PrintPPOutputPPCallbacks::FileChanged(SourceLocation Loc, } } -void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, - const Token &IncludeTok, - StringRef FileName, - bool IsAngled, - CharSourceRange FilenameRange, - const FileEntry *File, - StringRef SearchPath, - StringRef RelativePath, - const Module *Imported) { +void PrintPPOutputPPCallbacks::InclusionDirective( + SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) { // In -dI mode, dump #include directives prior to dumping their content or // interpretation. if (DumpIncludeDirectives) { diff --git a/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp b/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp index 3b8d792e3af24..1631eccd70131 100644 --- a/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp +++ b/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -77,7 +77,8 @@ class InclusionRewriter : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override; + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; void WriteLineInfo(StringRef Filename, int Line, SrcMgr::CharacteristicKind FileType, StringRef Extra = StringRef()); @@ -192,7 +193,8 @@ void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, const FileEntry * /*File*/, StringRef /*SearchPath*/, StringRef /*RelativePath*/, - const Module *Imported) { + const Module *Imported, + SrcMgr::CharacteristicKind FileType){ if (Imported) { auto P = ModuleIncludes.insert( std::make_pair(HashLoc.getRawEncoding(), Imported)); diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index e8083c721b899..949dbf17563f2 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1968,7 +1968,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, HashLoc, IncludeTok, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, isAngled, FilenameRange, File, SearchPath, RelativePath, - ShouldEnter ? nullptr : SuggestedModule.getModule()); + ShouldEnter ? nullptr : SuggestedModule.getModule(), FileCharacter); if (SkipHeader && !SuggestedModule.getModule()) Callbacks->FileSkipped(*File, FilenameTok, FileCharacter); } diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp index 5537479b49c91..b59820003b56a 100644 --- a/clang/lib/Lex/PreprocessingRecord.cpp +++ b/clang/lib/Lex/PreprocessingRecord.cpp @@ -471,7 +471,8 @@ void PreprocessingRecord::InclusionDirective( const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) { InclusionDirective::InclusionKind Kind = InclusionDirective::Include; switch (IncludeTok.getIdentifierInfo()->getPPKeywordID()) { diff --git a/clang/tools/libclang/Indexing.cpp b/clang/tools/libclang/Indexing.cpp index 51cffbd7f2e7d..545edfbef4b45 100644 --- a/clang/tools/libclang/Indexing.cpp +++ b/clang/tools/libclang/Indexing.cpp @@ -249,7 +249,8 @@ class IndexPPCallbacks : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { bool isImport = (IncludeTok.is(tok::identifier) && IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import); DataConsumer.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, diff --git a/clang/unittests/Lex/PPCallbacksTest.cpp b/clang/unittests/Lex/PPCallbacksTest.cpp index 67b56a601c71a..4f528712aef62 100644 --- a/clang/unittests/Lex/PPCallbacksTest.cpp +++ b/clang/unittests/Lex/PPCallbacksTest.cpp @@ -39,16 +39,18 @@ class InclusionDirectiveCallbacks : public PPCallbacks { StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override { - this->HashLoc = HashLoc; - this->IncludeTok = IncludeTok; - this->FileName = FileName.str(); - this->IsAngled = IsAngled; - this->FilenameRange = FilenameRange; - this->File = File; - this->SearchPath = SearchPath.str(); - this->RelativePath = RelativePath.str(); - this->Imported = Imported; + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { + this->HashLoc = HashLoc; + this->IncludeTok = IncludeTok; + this->FileName = FileName.str(); + this->IsAngled = IsAngled; + this->FilenameRange = FilenameRange; + this->File = File; + this->SearchPath = SearchPath.str(); + this->RelativePath = RelativePath.str(); + this->Imported = Imported; + this->FileType = FileType; } SourceLocation HashLoc; @@ -60,6 +62,7 @@ class InclusionDirectiveCallbacks : public PPCallbacks { SmallString<16> SearchPath; SmallString<16> RelativePath; const Module* Imported; + SrcMgr::CharacteristicKind FileType; }; // Stub to collect data from PragmaOpenCLExtension callbacks. @@ -138,6 +141,13 @@ class PPCallbacksTest : public ::testing::Test { // the InclusionDirective callback. CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText, const char* HeaderPath, bool SystemHeader) { + return InclusionDirectiveCallback(SourceText, HeaderPath, SystemHeader) + ->FilenameRange; + } + + InclusionDirectiveCallbacks * + InclusionDirectiveCallback(const char *SourceText, const char *HeaderPath, + bool SystemHeader) { std::unique_ptr<llvm::MemoryBuffer> Buf = llvm::MemoryBuffer::getMemBuffer(SourceText); SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); @@ -168,7 +178,7 @@ class PPCallbacksTest : public ::testing::Test { } // Callbacks have been executed at this point -- return filename range. - return Callbacks->FilenameRange; + return Callbacks; } PragmaOpenCLExtensionCallbacks::CallbackParameters @@ -222,6 +232,15 @@ class PPCallbacksTest : public ::testing::Test { } }; +TEST_F(PPCallbacksTest, UserFileCharacteristics) { + const char *Source = "#include \"quoted.h\"\n"; + + SrcMgr::CharacteristicKind Kind = + InclusionDirectiveCallback(Source, "/quoted.h", false)->FileType; + + ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind); +} + TEST_F(PPCallbacksTest, QuotedFilename) { const char* Source = "#include \"quoted.h\"\n"; From 51c9c1d23a794fc5d853862246538662560331dd Mon Sep 17 00:00:00 2001 From: Chris Bieneman <chris.bieneman@me.com> Date: Thu, 10 May 2018 21:15:40 -0700 Subject: [PATCH 274/582] Fixing clang build as a result of r332021 r332021 added an extra parameter to the `InclusionDirective` interface. This updates the override in IncludePPCallbacks to have the extra parameter. apple-llvm-split-commit: cb422f359a52d3beda2474bc9929e7b1ec9721fa apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexingAction.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 539d8103a0acd..9df85bc60dbaf 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -325,7 +325,8 @@ class IncludePPCallbacks : public PPCallbacks { const FileEntry *File, StringRef SearchPath, StringRef RelativePath, - const Module *Imported) override { + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { if (HashLoc.isFileID() && File && File->isValid()) addInclude(HashLoc, File); } From 187d43b8fc897e90e77881796a577aab3c543b93 Mon Sep 17 00:00:00 2001 From: Chris Bieneman <chris.bieneman@me.com> Date: Thu, 10 May 2018 21:25:55 -0700 Subject: [PATCH 275/582] NFC. clang-format This file contains a lot of style violations. This cleans that up. apple-llvm-split-commit: aa61f43b08a8ae42e281bd2130cdeb15c8e3642b apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexingAction.cpp | 391 +++++++++++++++-------------- 1 file changed, 196 insertions(+), 195 deletions(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 9df85bc60dbaf..8fa8abe4553b5 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -8,17 +8,17 @@ //===----------------------------------------------------------------------===// #include "clang/Index/IndexingAction.h" -#include "FileIndexRecord.h" -#include "IndexingContext.h" #include "ClangIndexRecordWriter.h" +#include "FileIndexRecord.h" #include "IndexDataStoreUtils.h" -#include "clang/Index/IndexUnitWriter.h" +#include "IndexingContext.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/Utils.h" #include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexUnitWriter.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" @@ -76,8 +76,7 @@ class IndexASTConsumer : public ASTConsumer { IndexCtx.indexDeclGroupRef(DG); } - void HandleTranslationUnit(ASTContext &Ctx) override { - } + void HandleTranslationUnit(ASTContext &Ctx) override {} }; class IndexActionBase { @@ -87,8 +86,7 @@ class IndexActionBase { IndexActionBase(std::shared_ptr<IndexDataConsumer> dataConsumer, IndexingOptions Opts) - : DataConsumer(std::move(dataConsumer)), - IndexCtx(Opts, *DataConsumer) {} + : DataConsumer(std::move(dataConsumer)), IndexCtx(Opts, *DataConsumer) {} std::unique_ptr<IndexASTConsumer> createIndexASTConsumer(CompilerInstance &CI) { @@ -97,16 +95,14 @@ class IndexActionBase { IndexCtx); } - void finish() { - DataConsumer->finish(); - } + void finish() { DataConsumer->finish(); } }; class IndexAction : public ASTFrontendAction, IndexActionBase { public: IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, IndexingOptions Opts) - : IndexActionBase(std::move(DataConsumer), Opts) {} + : IndexActionBase(std::move(DataConsumer), Opts) {} protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, @@ -127,8 +123,8 @@ class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { WrappingIndexAction(std::unique_ptr<FrontendAction> WrappedAction, std::shared_ptr<IndexDataConsumer> DataConsumer, IndexingOptions Opts) - : WrapperFrontendAction(std::move(WrappedAction)), - IndexActionBase(std::move(DataConsumer), Opts) {} + : WrapperFrontendAction(std::move(WrappedAction)), + IndexActionBase(std::move(DataConsumer), Opts) {} protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, @@ -163,15 +159,13 @@ index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, IndexingOptions Opts, std::unique_ptr<FrontendAction> WrappedAction) { if (WrappedAction) - return llvm::make_unique<WrappingIndexAction>(std::move(WrappedAction), - std::move(DataConsumer), - Opts); + return llvm::make_unique<WrappingIndexAction>( + std::move(WrappedAction), std::move(DataConsumer), Opts); return llvm::make_unique<IndexAction>(std::move(DataConsumer), Opts); } - static bool topLevelDeclVisitor(void *context, const Decl *D) { - IndexingContext &IndexCtx = *static_cast<IndexingContext*>(context); + IndexingContext &IndexCtx = *static_cast<IndexingContext *>(context); return IndexCtx.indexTopLevelDecl(D); } @@ -224,7 +218,8 @@ namespace { class IndexDataRecorder : public IndexDataConsumer { IndexingContext *IndexCtx = nullptr; const Preprocessor *PP = nullptr; - typedef llvm::DenseMap<FileID, std::unique_ptr<FileIndexRecord>> RecordByFileTy; + typedef llvm::DenseMap<FileID, std::unique_ptr<FileIndexRecord>> + RecordByFileTy; RecordByFileTy RecordByFile; public: @@ -234,26 +229,29 @@ class IndexDataRecorder : public IndexDataConsumer { initialize(CI.getASTContext()); } - RecordByFileTy::const_iterator record_begin() const { return RecordByFile.begin(); } - RecordByFileTy::const_iterator record_end() const { return RecordByFile.end(); } + RecordByFileTy::const_iterator record_begin() const { + return RecordByFile.begin(); + } + RecordByFileTy::const_iterator record_end() const { + return RecordByFile.end(); + } bool record_empty() const { return RecordByFile.empty(); } private: bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations, - SourceLocation Loc, - ASTNodeInfo ASTNode) override { + SourceLocation Loc, ASTNodeInfo ASTNode) override { SourceManager &SM = PP->getSourceManager(); Loc = SM.getFileLoc(Loc); if (Loc.isInvalid()) return true; - + FileID FID; unsigned Offset; std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); if (FID.isInvalid()) - return true; + return true; // Ignore the predefines buffer. const FileEntry *FE = PP->getSourceManager().getFileEntryForID(FID); @@ -289,26 +287,28 @@ class IncludePPCallbacks : public PPCallbacks { public: IncludePPCallbacks(IndexingContext &indexCtx, RecordingOptions recordOpts, std::vector<IncludeLocation> &IncludesForFile, - SourceManager &SourceMgr) : - IndexCtx(indexCtx), RecordOpts(recordOpts), - Includes(IncludesForFile), SourceMgr(SourceMgr) {} + SourceManager &SourceMgr) + : IndexCtx(indexCtx), RecordOpts(recordOpts), Includes(IncludesForFile), + SourceMgr(SourceMgr) {} private: void addInclude(SourceLocation From, const FileEntry *To) { assert(To); - if (RecordOpts.RecordIncludes == RecordingOptions::IncludesRecordingKind::None) + if (RecordOpts.RecordIncludes == + RecordingOptions::IncludesRecordingKind::None) return; - std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedExpansionLoc(From); + std::pair<FileID, unsigned> LocInfo = + SourceMgr.getDecomposedExpansionLoc(From); switch (RecordOpts.RecordIncludes) { - case RecordingOptions::IncludesRecordingKind::None: - llvm_unreachable("should have already checked in the beginning"); - case RecordingOptions::IncludesRecordingKind::UserOnly: - if (IndexCtx.isSystemFile(LocInfo.first)) - return; // Ignore includes of system headers. - break; - case RecordingOptions::IncludesRecordingKind::All: - break; + case RecordingOptions::IncludesRecordingKind::None: + llvm_unreachable("should have already checked in the beginning"); + case RecordingOptions::IncludesRecordingKind::UserOnly: + if (IndexCtx.isSystemFile(LocInfo.first)) + return; // Ignore includes of system headers. + break; + case RecordingOptions::IncludesRecordingKind::All: + break; } auto *FE = SourceMgr.getFileEntryForID(LocInfo.first); if (!FE) @@ -317,16 +317,11 @@ class IncludePPCallbacks : public PPCallbacks { Includes.push_back({FE, To, lineNo}); } - virtual void InclusionDirective(SourceLocation HashLoc, - const Token &IncludeTok, - StringRef FileName, - bool IsAngled, - CharSourceRange FilenameRange, - const FileEntry *File, - StringRef SearchPath, - StringRef RelativePath, - const Module *Imported, - SrcMgr::CharacteristicKind FileType) override { + virtual void InclusionDirective( + SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, + bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { if (HashLoc.isFileID() && File && File->isValid()) addInclude(HashLoc, File); } @@ -336,17 +331,21 @@ class IndexDependencyProvider { public: virtual ~IndexDependencyProvider() {} - virtual void visitFileDependencies(const CompilerInstance &CI, + virtual void visitFileDependencies( + const CompilerInstance &CI, llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) = 0; - virtual void visitIncludes( - llvm::function_ref<void(const FileEntry *Source, unsigned Line, - const FileEntry *Target)> visitor) = 0; - virtual void visitModuleImports(const CompilerInstance &CI, - llvm::function_ref<void(serialization::ModuleFile &Mod, - bool isSystem)> visitor) = 0; + virtual void + visitIncludes(llvm::function_ref<void(const FileEntry *Source, unsigned Line, + const FileEntry *Target)> + visitor) = 0; + virtual void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref<void(serialization::ModuleFile &Mod, bool isSystem)> + visitor) = 0; }; -class SourceFilesIndexDependencyCollector : public DependencyCollector, public IndexDependencyProvider { +class SourceFilesIndexDependencyCollector : public DependencyCollector, + public IndexDependencyProvider { IndexingContext &IndexCtx; RecordingOptions RecordOpts; llvm::SetVector<const FileEntry *> Entries; @@ -356,15 +355,14 @@ class SourceFilesIndexDependencyCollector : public DependencyCollector, public I std::string SysrootPath; public: - SourceFilesIndexDependencyCollector(IndexingContext &indexCtx, RecordingOptions recordOpts) - : IndexCtx(indexCtx), RecordOpts(recordOpts) {} + SourceFilesIndexDependencyCollector(IndexingContext &indexCtx, + RecordingOptions recordOpts) + : IndexCtx(indexCtx), RecordOpts(recordOpts) {} virtual void attachToPreprocessor(Preprocessor &PP) override { DependencyCollector::attachToPreprocessor(PP); - PP.addPPCallbacks(llvm::make_unique<IncludePPCallbacks>(IndexCtx, - RecordOpts, - Includes, - PP.getSourceManager())); + PP.addPPCallbacks(llvm::make_unique<IncludePPCallbacks>( + IndexCtx, RecordOpts, Includes, PP.getSourceManager())); } void setSourceManager(SourceManager *SourceMgr) { @@ -372,37 +370,43 @@ class SourceFilesIndexDependencyCollector : public DependencyCollector, public I } void setSysrootPath(StringRef sysroot) { SysrootPath = sysroot; } - void visitFileDependencies(const CompilerInstance &CI, - llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) override { + void visitFileDependencies( + const CompilerInstance &CI, + llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) + override { for (auto *FE : getEntries()) { visitor(FE, isSystemFile(FE)); } } - void visitIncludes( - llvm::function_ref<void(const FileEntry *Source, unsigned Line, - const FileEntry *Target)> visitor) override { + void + visitIncludes(llvm::function_ref<void(const FileEntry *Source, unsigned Line, + const FileEntry *Target)> + visitor) override { for (auto &Include : Includes) { visitor(Include.Source, Include.Line, Include.Target); } } - void visitModuleImports(const CompilerInstance &CI, - llvm::function_ref<void(serialization::ModuleFile &Mod, - bool isSystem)> visitor) override { + void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref<void(serialization::ModuleFile &Mod, bool isSystem)> + visitor) override { HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); if (auto Reader = CI.getModuleManager()) { - Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool { - bool isSystemMod = false; - if (Mod.isModule()) { - if (auto *M = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false)) - isSystemMod = M->IsSystem; - } - if (!isSystemMod || needSystemDependencies()) - visitor(Mod, isSystemMod); - return true; // skip module dependencies. - }); + Reader->getModuleManager().visit( + [&](serialization::ModuleFile &Mod) -> bool { + bool isSystemMod = false; + if (Mod.isModule()) { + if (auto *M = + HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false)) + isSystemMod = M->IsSystem; + } + if (!isSystemMod || needSystemDependencies()) + visitor(Mod, isSystemMod); + return true; // skip module dependencies. + }); } } @@ -420,18 +424,17 @@ class SourceFilesIndexDependencyCollector : public DependencyCollector, public I return RecordOpts.RecordSystemDependencies; } - bool sawDependency(StringRef Filename, bool FromModule, - bool IsSystem, bool IsModuleFile, bool IsMissing) override { - bool sawIt = DependencyCollector::sawDependency(Filename, FromModule, - IsSystem, IsModuleFile, - IsMissing); + bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, + bool IsModuleFile, bool IsMissing) override { + bool sawIt = DependencyCollector::sawDependency( + Filename, FromModule, IsSystem, IsModuleFile, IsMissing); if (auto *FE = SourceMgr->getFileManager().getFile(Filename)) { if (sawIt) Entries.insert(FE); // Record system-ness for all files that we pass through. - if (IsSystemByUID.size() < FE->getUID()+1) - IsSystemByUID.resize(FE->getUID()+1); - IsSystemByUID[FE->getUID()] = IsSystem || isInSysroot(Filename); + if (IsSystemByUID.size() < FE->getUID() + 1) + IsSystemByUID.resize(FE->getUID() + 1); + IsSystemByUID[FE->getUID()] = IsSystem || isInSysroot(Filename); } return sawIt; } @@ -449,10 +452,8 @@ class IndexRecordActionBase { SourceFilesIndexDependencyCollector DepCollector; IndexRecordActionBase(IndexingOptions IndexOpts, RecordingOptions recordOpts) - : RecordOpts(std::move(recordOpts)), - IndexCtx(IndexOpts, Recorder), - DepCollector(IndexCtx, RecordOpts) { - } + : RecordOpts(std::move(recordOpts)), IndexCtx(IndexOpts, Recorder), + DepCollector(IndexCtx, RecordOpts) {} std::unique_ptr<IndexASTConsumer> createIndexASTConsumer(CompilerInstance &CI) { @@ -474,7 +475,7 @@ class IndexRecordActionBase { class IndexRecordAction : public ASTFrontendAction, IndexRecordActionBase { public: IndexRecordAction(IndexingOptions IndexOpts, RecordingOptions RecordOpts) - : IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} + : IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, @@ -488,15 +489,16 @@ class IndexRecordAction : public ASTFrontendAction, IndexRecordActionBase { } }; -class WrappingIndexRecordAction : public WrapperFrontendAction, IndexRecordActionBase { +class WrappingIndexRecordAction : public WrapperFrontendAction, + IndexRecordActionBase { bool CreatedASTConsumer = false; public: WrappingIndexRecordAction(std::unique_ptr<FrontendAction> WrappedAction, IndexingOptions IndexOpts, RecordingOptions RecordOpts) - : WrapperFrontendAction(std::move(WrappedAction)), - IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} + : WrapperFrontendAction(std::move(WrappedAction)), + IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, @@ -539,10 +541,8 @@ static void writeUnitData(const CompilerInstance &CI, IndexDataRecorder &Recorder, IndexDependencyProvider &DepProvider, IndexingOptions IndexOpts, - RecordingOptions RecordOpts, - StringRef OutputFile, - const FileEntry *RootFile, - Module *UnitModule, + RecordingOptions RecordOpts, StringRef OutputFile, + const FileEntry *RootFile, Module *UnitModule, StringRef SysrootPath); void IndexRecordActionBase::finish(CompilerInstance &CI) { @@ -550,16 +550,15 @@ void IndexRecordActionBase::finish(CompilerInstance &CI) { // on the diagnostic client. // FIXME: FrontendAction::EndSourceFile() should probably not call // CI.getDiagnosticClient().EndSourceFile()' until after it has called - // 'EndSourceFileAction()', so that code executing during EndSourceFileAction() - // can emit diagnostics. If this is fixed, DiagClientBeginEndRAII can go away. + // 'EndSourceFileAction()', so that code executing during + // EndSourceFileAction() can emit diagnostics. If this is fixed, + // DiagClientBeginEndRAII can go away. struct DiagClientBeginEndRAII { CompilerInstance &CI; DiagClientBeginEndRAII(CompilerInstance &CI) : CI(CI) { CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts()); } - ~DiagClientBeginEndRAII() { - CI.getDiagnosticClient().EndSourceFile(); - } + ~DiagClientBeginEndRAII() { CI.getDiagnosticClient().EndSourceFile(); } } diagClientBeginEndRAII(CI); SourceManager &SM = CI.getSourceManager(); @@ -594,27 +593,23 @@ void IndexRecordActionBase::finish(CompilerInstance &CI) { } writeUnitData(CI, Recorder, DepCollector, IndexCtx.getIndexOpts(), RecordOpts, - OutputFile, RootFile, UnitMod, - IndexCtx.getSysrootPath()); + OutputFile, RootFile, UnitMod, IndexCtx.getSysrootPath()); } /// Checks if the unit file exists for module file, if it doesn't it generates /// index data for it. -static bool produceIndexDataForModuleFile( - serialization::ModuleFile &Mod, - const CompilerInstance &CI, - IndexingOptions IndexOpts, - RecordingOptions RecordOpts, - IndexUnitWriter &ParentUnitWriter); +static bool produceIndexDataForModuleFile(serialization::ModuleFile &Mod, + const CompilerInstance &CI, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + IndexUnitWriter &ParentUnitWriter); static void writeUnitData(const CompilerInstance &CI, IndexDataRecorder &Recorder, IndexDependencyProvider &DepProvider, IndexingOptions IndexOpts, - RecordingOptions RecordOpts, - StringRef OutputFile, - const FileEntry *RootFile, - Module *UnitModule, + RecordingOptions RecordOpts, StringRef OutputFile, + const FileEntry *RootFile, Module *UnitModule, StringRef SysrootPath) { SourceManager &SM = CI.getSourceManager(); @@ -624,15 +619,19 @@ static void writeUnitData(const CompilerInstance &CI, bool IsSystemUnit = UnitModule ? UnitModule->IsSystem : false; bool IsModuleUnit = UnitModule != nullptr; bool IsDebugCompilation = CI.getCodeGenOpts().OptimizationLevel == 0; - std::string ModuleName = UnitModule ? UnitModule->getFullModuleName() : std::string(); + std::string ModuleName = + UnitModule ? UnitModule->getFullModuleName() : std::string(); - auto getModuleInfo = [](writer::OpaqueModule mod, SmallVectorImpl<char> &Scratch) -> writer::ModuleInfo { + auto getModuleInfo = + [](writer::OpaqueModule mod, + SmallVectorImpl<char> &Scratch) -> writer::ModuleInfo { assert(mod); writer::ModuleInfo info; - std::string fullName = static_cast<const Module*>(mod)->getFullModuleName(); + std::string fullName = + static_cast<const Module *>(mod)->getFullModuleName(); unsigned offset = Scratch.size(); Scratch.append(fullName.begin(), fullName.end()); - info.Name = StringRef(Scratch.data()+offset, fullName.size()); + info.Name = StringRef(Scratch.data() + offset, fullName.size()); return info; }; @@ -645,26 +644,21 @@ static void writeUnitData(const CompilerInstance &CI, return nullptr; }; - IndexUnitWriter UnitWriter(CI.getFileManager(), - DataPath, - "clang", getClangVersion(), - OutputFile, - ModuleName, - RootFile, - IsSystemUnit, - IsModuleUnit, - IsDebugCompilation, - CI.getTargetOpts().Triple, - SysrootPath, - getModuleInfo); - - DepProvider.visitFileDependencies(CI, [&](const FileEntry *FE, bool isSystemFile) { - UnitWriter.addFileDependency(FE, isSystemFile, findModuleForHeader(FE)); - }); - DepProvider.visitIncludes([&](const FileEntry *Source, unsigned Line, const FileEntry *Target) { - UnitWriter.addInclude(Source, Line, Target); - }); - DepProvider.visitModuleImports(CI, [&](serialization::ModuleFile &Mod, bool isSystemMod) { + IndexUnitWriter UnitWriter( + CI.getFileManager(), DataPath, "clang", getClangVersion(), OutputFile, + ModuleName, RootFile, IsSystemUnit, IsModuleUnit, IsDebugCompilation, + CI.getTargetOpts().Triple, SysrootPath, getModuleInfo); + + DepProvider.visitFileDependencies( + CI, [&](const FileEntry *FE, bool isSystemFile) { + UnitWriter.addFileDependency(FE, isSystemFile, findModuleForHeader(FE)); + }); + DepProvider.visitIncludes( + [&](const FileEntry *Source, unsigned Line, const FileEntry *Target) { + UnitWriter.addInclude(Source, Line, Target); + }); + DepProvider.visitModuleImports(CI, [&](serialization::ModuleFile &Mod, + bool isSystemMod) { Module *UnitMod = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false); UnitWriter.addASTFileDependency(Mod.File, isSystemMod, UnitMod); if (Mod.isModule()) { @@ -673,7 +667,8 @@ static void writeUnitData(const CompilerInstance &CI, }); ClangIndexRecordWriter RecordWriter(CI.getASTContext(), RecordOpts); - for (auto I = Recorder.record_begin(), E = Recorder.record_end(); I != E; ++I) { + for (auto I = Recorder.record_begin(), E = Recorder.record_end(); I != E; + ++I) { FileID FID = I->first; const FileIndexRecord &Rec = *I->second; const FileEntry *FE = SM.getFileEntryForID(FID); @@ -707,37 +702,43 @@ class ModuleFileIndexDependencyCollector : public IndexDependencyProvider { public: ModuleFileIndexDependencyCollector(serialization::ModuleFile &Mod, RecordingOptions recordOpts) - : ModFile(Mod), RecordOpts(recordOpts) {} + : ModFile(Mod), RecordOpts(recordOpts) {} - void visitFileDependencies(const CompilerInstance &CI, - llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) override { + void visitFileDependencies( + const CompilerInstance &CI, + llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) + override { auto Reader = CI.getModuleManager(); - Reader->visitInputFiles(ModFile, RecordOpts.RecordSystemDependencies, - /*Complain=*/false, - [&](const serialization::InputFile &IF, bool isSystem) { - auto *FE = IF.getFile(); - if (!FE) - return; - // Ignore module map files, they are not as important to track as source - // files and they may be auto-generated which would create an undesirable - // dependency on an intermediate build byproduct. - if (FE->getName().endswith("module.modulemap")) - return; - - visitor(FE, isSystem); - }); - } - - void visitIncludes( - llvm::function_ref<void(const FileEntry *Source, unsigned Line, - const FileEntry *Target)> visitor) override { - // FIXME: Module files without a preprocessing record do not have info about - // include locations. Serialize enough data to be able to retrieve such info. - } - - void visitModuleImports(const CompilerInstance &CI, - llvm::function_ref<void(serialization::ModuleFile &Mod, - bool isSystem)> visitor) override { + Reader->visitInputFiles( + ModFile, RecordOpts.RecordSystemDependencies, + /*Complain=*/false, + [&](const serialization::InputFile &IF, bool isSystem) { + auto *FE = IF.getFile(); + if (!FE) + return; + // Ignore module map files, they are not as important to track as + // source files and they may be auto-generated which would create an + // undesirable dependency on an intermediate build byproduct. + if (FE->getName().endswith("module.modulemap")) + return; + + visitor(FE, isSystem); + }); + } + + void + visitIncludes(llvm::function_ref<void(const FileEntry *Source, unsigned Line, + const FileEntry *Target)> + visitor) override { + // FIXME: Module files without a preprocessing record do not have info about + // include locations. Serialize enough data to be able to retrieve such + // info. + } + + void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref<void(serialization::ModuleFile &Mod, bool isSystem)> + visitor) override { HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); for (auto *Mod : ModFile.Imports) { bool isSystemMod = false; @@ -751,12 +752,11 @@ class ModuleFileIndexDependencyCollector : public IndexDependencyProvider { } // anonymous namespace. static void indexModule(serialization::ModuleFile &Mod, - const CompilerInstance &CI, - IndexingOptions IndexOpts, + const CompilerInstance &CI, IndexingOptions IndexOpts, RecordingOptions RecordOpts) { DiagnosticsEngine &Diag = CI.getDiagnostics(); Diag.Report(Mod.ImportLoc, diag::remark_index_producing_module_file_data) - << Mod.FileName; + << Mod.FileName; StringRef SysrootPath = CI.getHeaderSearchOpts().Sysroot; HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); @@ -775,17 +775,15 @@ static void indexModule(serialization::ModuleFile &Mod, Recorder.finish(); ModuleFileIndexDependencyCollector DepCollector(Mod, RecordOpts); - writeUnitData(CI, Recorder, DepCollector, IndexOpts, RecordOpts, - Mod.FileName, /*RootFile=*/nullptr, UnitMod, SysrootPath); - + writeUnitData(CI, Recorder, DepCollector, IndexOpts, RecordOpts, Mod.FileName, + /*RootFile=*/nullptr, UnitMod, SysrootPath); } -static bool produceIndexDataForModuleFile( - serialization::ModuleFile &Mod, - const CompilerInstance &CI, - IndexingOptions IndexOpts, - RecordingOptions RecordOpts, - IndexUnitWriter &ParentUnitWriter) { +static bool produceIndexDataForModuleFile(serialization::ModuleFile &Mod, + const CompilerInstance &CI, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + IndexUnitWriter &ParentUnitWriter) { DiagnosticsEngine &Diag = CI.getDiagnostics(); std::string Error; // We don't do timestamp check with the PCM file, on purpose. The PCM may get @@ -793,7 +791,8 @@ static bool produceIndexDataForModuleFile( // index data. User modules normally will get rebuilt and their index data // re-emitted, and system modules are generally stable (and they can also can // get rebuilt along with their index data). - auto IsUptodateOpt = ParentUnitWriter.isUnitUpToDateForOutputFile(Mod.FileName, None, Error); + auto IsUptodateOpt = + ParentUnitWriter.isUnitUpToDateForOutputFile(Mod.FileName, None, Error); if (!IsUptodateOpt.hasValue()) { unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, "failed file status check: %0"); @@ -812,9 +811,8 @@ createIndexDataRecordingAction(IndexingOptions IndexOpts, RecordingOptions RecordOpts, std::unique_ptr<FrontendAction> WrappedAction) { if (WrappedAction) - return llvm::make_unique<WrappingIndexRecordAction>(std::move(WrappedAction), - std::move(IndexOpts), - std::move(RecordOpts)); + return llvm::make_unique<WrappingIndexRecordAction>( + std::move(WrappedAction), std::move(IndexOpts), std::move(RecordOpts)); return llvm::make_unique<IndexRecordAction>(std::move(IndexOpts), std::move(RecordOpts)); } @@ -826,15 +824,15 @@ getIndexOptionsFromFrontendOptions(const FrontendOptions &FEOpts) { RecordOpts.DataDirPath = FEOpts.IndexStorePath; if (FEOpts.IndexIgnoreSystemSymbols) { IndexOpts.SystemSymbolFilter = - index::IndexingOptions::SystemSymbolFilterKind::None; + index::IndexingOptions::SystemSymbolFilterKind::None; } RecordOpts.RecordSymbolCodeGenName = FEOpts.IndexRecordCodegenName; - return { IndexOpts, RecordOpts }; + return {IndexOpts, RecordOpts}; } -std::unique_ptr<FrontendAction> -index::createIndexDataRecordingAction(const FrontendOptions &FEOpts, - std::unique_ptr<FrontendAction> WrappedAction) { +std::unique_ptr<FrontendAction> index::createIndexDataRecordingAction( + const FrontendOptions &FEOpts, + std::unique_ptr<FrontendAction> WrappedAction) { index::IndexingOptions IndexOpts; index::RecordingOptions RecordOpts; std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(FEOpts); @@ -847,10 +845,13 @@ bool index::emitIndexDataForModuleFile(const Module *Mod, IndexUnitWriter &ParentUnitWriter) { index::IndexingOptions IndexOpts; index::RecordingOptions RecordOpts; - std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(CI.getFrontendOpts()); + std::tie(IndexOpts, RecordOpts) = + getIndexOptionsFromFrontendOptions(CI.getFrontendOpts()); auto astReader = CI.getModuleManager(); - serialization::ModuleFile *ModFile = astReader->getModuleManager().lookup(Mod->getASTFile()); + serialization::ModuleFile *ModFile = + astReader->getModuleManager().lookup(Mod->getASTFile()); assert(ModFile && "no module file loaded for module ?"); - return produceIndexDataForModuleFile(*ModFile, CI, IndexOpts, RecordOpts, ParentUnitWriter); + return produceIndexDataForModuleFile(*ModFile, CI, IndexOpts, RecordOpts, + ParentUnitWriter); } From 695e9a7cdde9db321e83afd2450c8b4e8ecc16e4 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 16 May 2018 11:45:04 -0700 Subject: [PATCH 276/582] [test] Disable some API notes tests broken by upstream changes (#188) These test rely on -ast-print including implicit attributes, but it no longer does that as of https://reviews.llvm.org/D46894. Tracked by rdar://problem/40296113. apple-llvm-split-commit: 8ab16dbca916c4b7f418c6d82c9b407f092cee02 apple-llvm-split-dir: clang/ --- clang/test/APINotes/retain-count-convention.m | 4 ++++ clang/test/APINotes/types.m | 4 ++++ clang/test/APINotes/versioned-multi.c | 4 ++++ clang/test/APINotes/versioned.m | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/clang/test/APINotes/retain-count-convention.m b/clang/test/APINotes/retain-count-convention.m index 46fb90caa6932..8c830f5be7023 100644 --- a/clang/test/APINotes/retain-count-convention.m +++ b/clang/test/APINotes/retain-count-convention.m @@ -2,6 +2,10 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fdisable-module-hash -fsyntax-only -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s +// REQUIRES: rdar40296113 +// This test relies on -ast-print including implicit attributes, but it no +// longer does that as of https://reviews.llvm.org/D46894. + #import <SimpleKit/SimpleKit.h> // CHECK: void *getCFOwnedToUnowned() __attribute__((cf_returns_not_retained)); diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m index 133d504713d76..5f9ef4a039c3c 100644 --- a/clang/test/APINotes/types.m +++ b/clang/test/APINotes/types.m @@ -2,6 +2,10 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -Wno-private-module -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s +// REQUIRES: rdar40296113 +// This test relies on -ast-print including implicit attributes, but it no +// longer does that as of https://reviews.llvm.org/D46894. + #import <SomeKit/SomeKit.h> #import <SimpleKit/SimpleKit.h> diff --git a/clang/test/APINotes/versioned-multi.c b/clang/test/APINotes/versioned-multi.c index 48c51fd932e17..8b20160dfd6a6 100644 --- a/clang/test/APINotes/versioned-multi.c +++ b/clang/test/APINotes/versioned-multi.c @@ -14,6 +14,10 @@ // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned5 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=5 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned5/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-5 %s +// REQUIRES: rdar40296113 +// This test relies on -ast-print including implicit attributes, but it no +// longer does that as of https://reviews.llvm.org/D46894. + #import <VersionedKit/VersionedKit.h> // CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4; diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index e7ec2eb43378e..069cc03680bca 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -10,6 +10,10 @@ // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s +// REQUIRES: rdar40296113 +// This test relies on -ast-print including implicit attributes, but it no +// longer does that as of https://reviews.llvm.org/D46894. + #import <VersionedKit/VersionedKit.h> // CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); From c78ae0e9441859f230fbd95124687c5e89b81d2c Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 17 May 2018 10:03:10 -0700 Subject: [PATCH 277/582] [APINotes] Warn when a private API notes file uses the wrong case (#190) It's "FooKit_private.apinotes", not "FooKit_Private.apinotes". rdar://problem/39914779 apple-llvm-split-commit: 84fdb559d12b589eab067b8a11a558041ea6d738 apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticCommonKinds.td | 8 +++++ clang/lib/APINotes/APINotesManager.cpp | 31 ++++++++++++++++--- .../Headers/FrameworkWithWrongCasePrivate.h | 1 + .../Modules/module.modulemap | 5 +++ ...eworkWithWrongCasePrivate_Private.apinotes | 1 + .../Headers/ModuleWithWrongCasePrivate.h | 1 + ...oduleWithWrongCasePrivate_Private.apinotes | 1 + .../APINotes/Inputs/Headers/module.modulemap | 4 +++ .../APINotes/case-for-private-apinotes-file.c | 16 ++++++++++ 9 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Headers/FrameworkWithWrongCasePrivate.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/PrivateHeaders/FrameworkWithWrongCasePrivate_Private.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h create mode 100644 clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate_Private.apinotes create mode 100644 clang/test/APINotes/case-for-private-apinotes-file.c diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 3d0957c3ad974..ffc2bd8c21512 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -237,6 +237,14 @@ def err_apinotes_message : Error<"%0">; def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>; def note_apinotes_message : Note<"%0">; +class NonportablePrivateAPINotesPath : Warning< + "private API notes file for module '%0' should be named " + "'%0_private.apinotes', not '%1'">; +def warn_apinotes_private_case : NonportablePrivateAPINotesPath, + InGroup<DiagGroup<"nonportable-private-apinotes-path">>; +def warn_apinotes_private_case_system : NonportablePrivateAPINotesPath, + DefaultIgnore, InGroup<DiagGroup<"nonportable-private-system-apinotes-path">>; + // OpenCL C++. def err_openclcxx_not_supported : Error< "'%0' is not supported in OpenCL C++">; diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 6325816eb350a..65a2082a60a2c 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -15,6 +15,7 @@ #include "clang/APINotes/APINotesOptions.h" #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/APINotesYAMLCompiler.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" @@ -163,7 +164,7 @@ const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directo // Look for a binary API notes file. llvm::sys::path::append(path, llvm::Twine(basename) + basenameSuffix + "." + BINARY_APINOTES_EXTENSION); - if (const FileEntry *binaryFile = fileMgr.getFile(path)) + if (const FileEntry *binaryFile = fileMgr.getFile(path, /*Open*/true)) return binaryFile; // Go back to the original path. @@ -172,7 +173,7 @@ const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directo // Look for the source API notes file. llvm::sys::path::append(path, llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION); - return fileMgr.getFile(path); + return fileMgr.getFile(path, /*Open*/true); } const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( @@ -225,6 +226,25 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( return HeaderDir; } +static void checkPrivateAPINotesName(DiagnosticsEngine &diags, + const FileEntry *file, + const Module *module) { + if (file->tryGetRealPathName().empty()) + return; + + StringRef realFilename = + llvm::sys::path::filename(file->tryGetRealPathName()); + StringRef realStem = llvm::sys::path::stem(realFilename); + if (realStem.endswith("_private")) + return; + + unsigned diagID = diag::warn_apinotes_private_case; + if (module->IsSystem) + diagID = diag::warn_apinotes_private_case_system; + + diags.Report(SourceLocation(), diagID) << module->Name << realFilename; +} + bool APINotesManager::loadCurrentModuleAPINotes( const Module *module, bool lookInModule, @@ -245,6 +265,9 @@ bool APINotesManager::loadCurrentModuleAPINotes( if (auto file = findAPINotesFile(dir, moduleName, wantPublic)) { foundAny = true; + if (!wantPublic) + checkPrivateAPINotesName(SourceMgr.getDiagnostics(), file, module); + // Try to load the API notes file. CurrentModuleReaders[numReaders] = loadAPINotes(file).release(); if (CurrentModuleReaders[numReaders]) @@ -258,7 +281,7 @@ bool APINotesManager::loadCurrentModuleAPINotes( // // Public modules: // - Headers/Foo.apinotes - // - PrivateHeaders/Foo_Private.apinotes + // - PrivateHeaders/Foo_private.apinotes // Private modules: // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has // the word "Private" in it in practice) @@ -283,7 +306,7 @@ bool APINotesManager::loadCurrentModuleAPINotes( } else { // Public modules: // - Foo.apinotes - // - Foo_Private.apinotes + // - Foo_private.apinotes // Private modules: // - Bar.apinotes (except that 'Bar' probably already has the word // "Private" in it in practice) diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Headers/FrameworkWithWrongCasePrivate.h b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Headers/FrameworkWithWrongCasePrivate.h new file mode 100644 index 0000000000000..a9e3271cf3ecf --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Headers/FrameworkWithWrongCasePrivate.h @@ -0,0 +1 @@ +extern int FrameworkWithWrongCasePrivate; \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..04b96adbbfeb9 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module FrameworkWithWrongCasePrivate { + umbrella header "FrameworkWithWrongCasePrivate.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/PrivateHeaders/FrameworkWithWrongCasePrivate_Private.apinotes b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/PrivateHeaders/FrameworkWithWrongCasePrivate_Private.apinotes new file mode 100644 index 0000000000000..d7af293e8125f --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/PrivateHeaders/FrameworkWithWrongCasePrivate_Private.apinotes @@ -0,0 +1 @@ +Name: FrameworkWithWrongCasePrivate diff --git a/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h new file mode 100644 index 0000000000000..867a15cae9a66 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h @@ -0,0 +1 @@ +extern int ModuleWithWrongCase; diff --git a/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate_Private.apinotes b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate_Private.apinotes new file mode 100644 index 0000000000000..dc6dc50bab6e6 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate_Private.apinotes @@ -0,0 +1 @@ +Name: ModuleWithWrongCasePrivate diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index 54af0f5e76c74..db825537ce5d5 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -5,3 +5,7 @@ module HeaderLib { module BrokenTypes { header "BrokenTypes.h" } + +module ModuleWithWrongCasePrivate { + header "ModuleWithWrongCasePrivate.h" +} diff --git a/clang/test/APINotes/case-for-private-apinotes-file.c b/clang/test/APINotes/case-for-private-apinotes-file.c new file mode 100644 index 0000000000000..e674251935e90 --- /dev/null +++ b/clang/test/APINotes/case-for-private-apinotes-file.c @@ -0,0 +1,16 @@ +// REQUIRES: case-insensitive-filesystem + +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fmodules -fapinotes-modules -fimplicit-module-maps -fmodules-cache-path=%t -F %S/Inputs/Frameworks -I %S/Inputs/Headers %s 2>&1 | FileCheck %s + +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fmodules -fapinotes-modules -fimplicit-module-maps -fmodules-cache-path=%t -iframework %S/Inputs/Frameworks -isystem %S/Inputs/Headers %s -Werror + +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fmodules -fapinotes-modules -fimplicit-module-maps -fmodules-cache-path=%t -iframework %S/Inputs/Frameworks -isystem %S/Inputs/Headers %s -Wnonportable-private-system-apinotes-path 2>&1 | FileCheck %s + +#include <ModuleWithWrongCasePrivate.h> +#include <FrameworkWithWrongCasePrivate/FrameworkWithWrongCasePrivate.h> + +// CHECK: warning: private API notes file for module 'ModuleWithWrongCasePrivate' should be named 'ModuleWithWrongCasePrivate_private.apinotes', not 'ModuleWithWrongCasePrivate_Private.apinotes' +// CHECK: warning: private API notes file for module 'FrameworkWithWrongCasePrivate' should be named 'FrameworkWithWrongCasePrivate_private.apinotes', not 'FrameworkWithWrongCasePrivate_Private.apinotes' From 7527e772f8e410846a960e158df1107e9665b69e Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Fri, 18 May 2018 10:35:39 -0700 Subject: [PATCH 278/582] Adjust for Clang r332598: VersionTuple always uses dots now Remove references to the usesUnderscores() and UseDotAsSeparator() functions that are no longer needed. apple-llvm-split-commit: 74892bd678bb19b8eeb100b4b1323094480e91ae apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesWriter.cpp | 1 - clang/lib/APINotes/APINotesYAMLCompiler.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 76a373afb9eb4..9a909534b58c2 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -437,7 +437,6 @@ namespace { else if (version.getSubminor()) descriptor = 2; else if (version.getMinor()) descriptor = 1; else descriptor = 0; - assert(!version.usesUnderscores() && "Not a serializable version"); writer.write<uint8_t>(descriptor); // Write the components. diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index bccb72f4de2a4..5b1449e181e2f 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -439,8 +439,6 @@ namespace llvm { if (value.tryParse(scalar)) return "not a version number in the form XX.YY"; - // Canonicalize on '.' as a separator. - value.UseDotAsSeparator(); return StringRef(); } From 36c5d76e262ec9ae1d66fba83a1ef3686f2a1477 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Sat, 19 May 2018 20:48:44 -0700 Subject: [PATCH 279/582] [APINotes] Update endian stream interface to match Clang r332757 apple-llvm-split-commit: 2ca2f42c90ca473d9c86eb382c4f7eb1830e0083 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesWriter.cpp | 74 +++++++++++++-------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 9a909534b58c2..18c55b59259ad 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -279,7 +279,7 @@ namespace { data_type_ref data) { uint32_t keyLength = key.size(); uint32_t dataLength = sizeof(uint32_t); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); return { keyLength, dataLength }; @@ -291,7 +291,7 @@ namespace { void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(data); } }; @@ -313,7 +313,7 @@ void APINotesWriter::Implementation::writeIdentifierBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -331,7 +331,7 @@ namespace { /// Emit a serialized representation of the common entity information. static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); uint8_t payload = 0; if (auto swiftPrivate = info.isSwiftPrivate()) { payload |= 0x01; @@ -361,7 +361,7 @@ namespace { /// Emit a serialized representation of the common type information. static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) { emitCommonEntityInfo(out, info); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); if (auto swiftBridge = info.getSwiftBridge()) { writer.write<uint16_t>(swiftBridge->size() + 1); out.write(swiftBridge->c_str(), swiftBridge->size()); @@ -395,21 +395,21 @@ namespace { data_type_ref data) { uint32_t keyLength = sizeof(uint32_t) + 1; uint32_t dataLength = sizeof(uint32_t); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); return { keyLength, dataLength }; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(key.first); writer.write<uint8_t>(key.second); } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(data); } }; @@ -428,7 +428,7 @@ namespace { /// Emit a serialized representation of a version tuple. void emitVersionTuple(raw_ostream &out, const VersionTuple &version) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); // First byte contains the number of components beyond the 'major' // component. @@ -485,7 +485,7 @@ namespace { assert(left.first != right.first && "two entries for the same version"); return left.first < right.first; }); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint16_t>(infoArray.size()); for (const auto &element : infoArray) { emitVersionTuple(out, element.first); @@ -513,7 +513,7 @@ namespace { out.write(reinterpret_cast<const char *>(bytes), 2); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint16_t>(info.getType().size()); out.write(info.getType().data(), info.getType().size()); } @@ -551,7 +551,7 @@ namespace { return asDerived().getUnversionedInfoSize(unversionedInfo); }); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); return { keyLength, dataLength }; @@ -578,7 +578,7 @@ namespace { } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(key); } @@ -617,7 +617,7 @@ namespace { } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(std::get<0>(key)); writer.write<uint32_t>(std::get<1>(key)); writer.write<uint8_t>(std::get<2>(key)); @@ -656,7 +656,7 @@ void APINotesWriter::Implementation::writeObjCContextBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -675,7 +675,7 @@ void APINotesWriter::Implementation::writeObjCContextBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -700,7 +700,7 @@ void APINotesWriter::Implementation::writeObjCPropertyBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -716,7 +716,7 @@ namespace { static void emitParamInfo(raw_ostream &out, const ParamInfo &info) { emitVariableInfo(out, info); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); uint8_t payload = 0; if (auto noescape = info.isNoEscape()) { @@ -747,7 +747,7 @@ namespace { static void emitFunctionInfo(raw_ostream &out, const FunctionInfo &info) { emitCommonEntityInfo(out, info); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); uint8_t payload = 0; payload |= info.NullabilityAudited; @@ -781,7 +781,7 @@ namespace { } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(std::get<0>(key)); writer.write<uint32_t>(std::get<1>(key)); writer.write<uint8_t>(std::get<2>(key)); @@ -795,7 +795,7 @@ namespace { uint8_t payload = 0; payload = (payload << 1) | info.DesignatedInit; payload = (payload << 1) | info.Required; - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint8_t>(payload); emitFunctionInfo(out, info); @@ -820,7 +820,7 @@ void APINotesWriter::Implementation::writeObjCMethodBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -849,14 +849,14 @@ namespace { uint32_t keyLength = sizeof(uint16_t) + sizeof(uint32_t) * key.Identifiers.size(); uint32_t dataLength = sizeof(uint32_t); - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint16_t>(keyLength); writer.write<uint16_t>(dataLength); return { keyLength, dataLength }; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint16_t>(key.NumPieces); for (auto piece : key.Identifiers) { writer.write<uint32_t>(piece); @@ -865,7 +865,7 @@ namespace { void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(data); } }; @@ -887,7 +887,7 @@ void APINotesWriter::Implementation::writeObjCSelectorBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -907,7 +907,7 @@ namespace { } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(key); } @@ -938,7 +938,7 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -958,7 +958,7 @@ namespace { } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(key); } @@ -990,7 +990,7 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -1010,7 +1010,7 @@ namespace { } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<uint32_t>(key); } @@ -1040,7 +1040,7 @@ void APINotesWriter::Implementation::writeEnumConstantBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -1059,7 +1059,7 @@ namespace { return sizeof(IdentifierID); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); writer.write<IdentifierID>(key); } @@ -1081,7 +1081,7 @@ namespace { } void emitUnversionedInfo(raw_ostream &out, const TagInfo &info) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); uint8_t payload = 0; if (auto enumExtensibility = info.EnumExtensibility) { @@ -1119,7 +1119,7 @@ void APINotesWriter::Implementation::writeTagBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } @@ -1138,7 +1138,7 @@ namespace { } void emitUnversionedInfo(raw_ostream &out, const TypedefInfo &info) { - endian::Writer<little> writer(out); + endian::Writer writer(out, little); uint8_t payload = 0; if (auto swiftWrapper = info.SwiftWrapper) { @@ -1169,7 +1169,7 @@ void APINotesWriter::Implementation::writeTypedefBlock( llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 - endian::Writer<little>(blobStream).write<uint32_t>(0); + endian::write<uint32_t>(blobStream, 0, little); tableOffset = generator.Emit(blobStream); } From 25618f7aa25d24a184e65f7ef955ab9cb15a2fb4 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <aleksei_lorenz@apple.com> Date: Wed, 30 May 2018 15:03:59 -0700 Subject: [PATCH 280/582] Remove unused getRequiredQualification function rdar://40397697 apple-llvm-split-commit: e038b5f9156204d013896de2defa3eeae70776d9 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaCodeComplete.cpp | 47 ----------------------------- 1 file changed, 47 deletions(-) diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 14f0de6c8bfa9..0ee321acdc64a 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -439,53 +439,6 @@ ResultBuilder::ShadowMapEntry::end() const { return iterator(DeclOrVector.get<DeclIndexPairVector *>()->end()); } -/// Compute the qualification required to get from the current context -/// (\p CurContext) to the target context (\p TargetContext). -/// -/// \param Context the AST context in which the qualification will be used. -/// -/// \param CurContext the context where an entity is being named, which is -/// typically based on the current scope. -/// -/// \param TargetContext the context in which the named entity actually -/// resides. -/// -/// \returns a nested name specifier that refers into the target context, or -/// NULL if no qualification is needed. -static NestedNameSpecifier * -getRequiredQualification(ASTContext &Context, - const DeclContext *CurContext, - const DeclContext *TargetContext) { - SmallVector<const DeclContext *, 4> TargetParents; - - for (const DeclContext *CommonAncestor = TargetContext; - CommonAncestor && !CommonAncestor->Encloses(CurContext); - CommonAncestor = CommonAncestor->getLookupParent()) { - if (CommonAncestor->isTransparentContext() || - CommonAncestor->isFunctionOrMethod()) - continue; - - TargetParents.push_back(CommonAncestor); - } - - NestedNameSpecifier *Result = nullptr; - while (!TargetParents.empty()) { - const DeclContext *Parent = TargetParents.pop_back_val(); - - if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent)) { - if (!Namespace->getIdentifier()) - continue; - - Result = NestedNameSpecifier::Create(Context, Result, Namespace); - } - else if (const TagDecl *TD = dyn_cast<TagDecl>(Parent)) - Result = NestedNameSpecifier::Create(Context, Result, - false, - Context.getTypeDeclType(TD).getTypePtr()); - } - return Result; -} - /// Determine whether \p Id is a name reserved for the implementation (C99 /// 7.1.3, C++ [lib.global.names]). static bool isReservedName(const IdentifierInfo *Id, From 25e3a989d656b80cd4113de2172e074477c55a1b Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Mon, 11 Jun 2018 12:31:13 -0700 Subject: [PATCH 281/582] [Support] Add swift-specific changes to VersionTuple.h. This used to be in clang, but moved to LLVM. The clang version had two swift-clang extensions: a bool conversion operator, and DenseMapInfo. Add those to swift-llvm. apple-llvm-split-commit: 3042ea1b28830a39d8d649a2f5086e12cd7e92f8 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Support/VersionTuple.h | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h index e85a188e54b45..972b2e49634fd 100644 --- a/llvm/include/llvm/Support/VersionTuple.h +++ b/llvm/include/llvm/Support/VersionTuple.h @@ -15,6 +15,7 @@ #ifndef LLVM_SUPPORT_VERSIONTUPLE_H #define LLVM_SUPPORT_VERSIONTUPLE_H +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -64,6 +65,9 @@ class VersionTuple { return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0; } + /// Whether this is a non-empty version tuple. + explicit operator bool () const { return !empty(); } + /// Retrieve the major version number. unsigned getMajor() const { return Major; } @@ -150,5 +154,32 @@ class VersionTuple { /// Print a version number. raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V); + // Provide DenseMapInfo for version tuples. + template<> + struct DenseMapInfo<VersionTuple> { + static inline VersionTuple getEmptyKey() { + return VersionTuple(0x7FFFFFFF); + } + static inline VersionTuple getTombstoneKey() { + return VersionTuple(0x7FFFFFFE); + } + static unsigned getHashValue(const VersionTuple &value) { + unsigned result = value.getMajor(); + if (auto minor = value.getMinor()) + result = combineHashValue(result, *minor); + if (auto subminor = value.getSubminor()) + result = combineHashValue(result, *subminor); + if (auto build = value.getBuild()) + result = combineHashValue(result, *build); + + return result; + } + + static bool isEqual(const VersionTuple &lhs, + const VersionTuple &rhs) { + return lhs == rhs; + } + }; + } // end namespace llvm #endif // LLVM_SUPPORT_VERSIONTUPLE_H From cf42b8b94af24c7563360de73242697518ce274f Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Mon, 11 Jun 2018 12:58:50 -0700 Subject: [PATCH 282/582] [APINotes] Update for upstream VersionTuple clang->llvm move. Also, replace some ad-hoc 'using' and forward declarations in APINotes/Types.h with an include of Basic/LLVM.h. apple-llvm-split-commit: cf4bd51c4574ad41ebf6e143f7e7a24cb7c6d06f apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesManager.h | 6 ++-- .../include/clang/APINotes/APINotesOptions.h | 4 +-- clang/include/clang/APINotes/APINotesReader.h | 36 +++++++++---------- clang/include/clang/APINotes/APINotesWriter.h | 18 +++++----- clang/include/clang/APINotes/Types.h | 10 +----- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 2 +- 6 files changed, 34 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h index 6eb0534060039..faedf2d44db23 100644 --- a/clang/include/clang/APINotes/APINotesManager.h +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -16,11 +16,11 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Module.h" -#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/VersionTuple.h" #include <memory> #include <string> @@ -57,7 +57,7 @@ class APINotesManager { bool ImplicitAPINotes; /// The Swift version to use when interpreting versioned API notes. - VersionTuple SwiftVersion; + llvm::VersionTuple SwiftVersion; /// API notes readers for the current module. /// @@ -111,7 +111,7 @@ class APINotesManager { ~APINotesManager(); /// Set the Swift version to use when filtering API notes. - void setSwiftVersion(VersionTuple swiftVersion) { + void setSwiftVersion(llvm::VersionTuple swiftVersion) { SwiftVersion = swiftVersion; } diff --git a/clang/include/clang/APINotes/APINotesOptions.h b/clang/include/clang/APINotes/APINotesOptions.h index 24bb9134b21b2..d1cb16dacfc53 100644 --- a/clang/include/clang/APINotes/APINotesOptions.h +++ b/clang/include/clang/APINotes/APINotesOptions.h @@ -14,9 +14,9 @@ #ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H #define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H -#include "clang/Basic/VersionTuple.h" #include <string> #include <vector> +#include "llvm/Support/VersionTuple.h" namespace clang { @@ -25,7 +25,7 @@ namespace clang { class APINotesOptions { public: /// The Swift version which should be used for API notes. - VersionTuple SwiftVersion; + llvm::VersionTuple SwiftVersion; /// The set of search paths where we API notes can be found for /// particular modules. diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index a71c74da7b8a8..06572102a95dc 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -17,9 +17,9 @@ #define LLVM_CLANG_API_NOTES_READER_H #include "clang/APINotes/Types.h" -#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VersionTuple.h" #include <memory> namespace clang { @@ -33,7 +33,7 @@ class APINotesReader { Implementation &Impl; APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, - VersionTuple swiftVersion, bool &failed); + llvm::VersionTuple swiftVersion, bool &failed); public: /// Create a new API notes reader from the given member buffer, which @@ -42,7 +42,7 @@ class APINotesReader { /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr<APINotesReader> get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Create a new API notes reader from the given member buffer, which /// contains the contents of a binary API notes file. @@ -50,7 +50,7 @@ class APINotesReader { /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr<APINotesReader> getUnmanaged(llvm::MemoryBuffer *inputBuffer, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); ~APINotesReader(); @@ -86,8 +86,8 @@ class APINotesReader { /// Form a versioned info set given the desired version and a set of /// results. - VersionedInfo(VersionTuple version, - SmallVector<std::pair<VersionTuple, T>, 1> results); + VersionedInfo(llvm::VersionTuple version, + SmallVector<std::pair<llvm::VersionTuple, T>, 1> results); /// Determine whether there is a result that should be applied directly /// to the AST. @@ -109,11 +109,11 @@ class APINotesReader { unsigned size() const { return Results.size(); } /// Access all versioned results. - const std::pair<VersionTuple, T> *begin() const { return Results.begin(); } - const std::pair<VersionTuple, T> *end() const { return Results.end(); } + const std::pair<llvm::VersionTuple, T> *begin() const { return Results.begin(); } + const std::pair<llvm::VersionTuple, T> *end() const { return Results.end(); } /// Access a specific versioned result. - const std::pair<VersionTuple, T> &operator[](unsigned index) const { + const std::pair<llvm::VersionTuple, T> &operator[](unsigned index) const { return Results[index]; } }; @@ -215,47 +215,47 @@ class APINotesReader { /// Visit an Objective-C class. virtual void visitObjCClass(ContextID contextID, StringRef name, const ObjCContextInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit an Objective-C protocol. virtual void visitObjCProtocol(ContextID contextID, StringRef name, const ObjCContextInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit an Objective-C method. virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, const ObjCMethodInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit an Objective-C property. virtual void visitObjCProperty(ContextID contextID, StringRef name, bool isInstance, const ObjCPropertyInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit a global variable. virtual void visitGlobalVariable(StringRef name, const GlobalVariableInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit a global function. virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit an enumerator. virtual void visitEnumConstant(StringRef name, const EnumConstantInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit a tag. virtual void visitTag(StringRef name, const TagInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Visit a typedef. virtual void visitTypedef(StringRef name, const TypedefInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); }; /// Visit the contents of the API notes file, passing each entity to the diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index 62defc1f944f7..222f33b728a13 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -16,8 +16,8 @@ #ifndef LLVM_CLANG_API_NOTES_WRITER_H #define LLVM_CLANG_API_NOTES_WRITER_H -#include "clang/Basic/VersionTuple.h" #include "clang/APINotes/Types.h" +#include "llvm/Support/VersionTuple.h" namespace llvm { class raw_ostream; @@ -57,7 +57,7 @@ class APINotesWriter { /// properties and methods to the class/protocol. ContextID addObjCContext(StringRef name, bool isClass, const ObjCContextInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add information about a specific Objective-C property. /// @@ -67,7 +67,7 @@ class APINotesWriter { void addObjCProperty(ContextID contextID, StringRef name, bool isInstanceProperty, const ObjCPropertyInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add information about a specific Objective-C method. /// @@ -78,42 +78,42 @@ class APINotesWriter { /// \param info Information about this method. void addObjCMethod(ContextID contextID, ObjCSelectorRef selector, bool isInstanceMethod, const ObjCMethodInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add information about a global variable. /// /// \param name The name of this global variable. /// \param info Information about this global variable. void addGlobalVariable(StringRef name, const GlobalVariableInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add information about a global function. /// /// \param name The name of this global function. /// \param info Information about this global function. void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add information about an enumerator. /// /// \param name The name of this enumerator. /// \param info Information about this enumerator. void addEnumConstant(StringRef name, const EnumConstantInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add information about a tag (struct/union/enum/C++ class). /// /// \param name The name of this tag. /// \param info Information about this tag. void addTag(StringRef name, const TagInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add information about a typedef. /// /// \param name The name of this typedef. /// \param info Information about this typedef. void addTypedef(StringRef name, const TypedefInfo &info, - VersionTuple swiftVersion); + llvm::VersionTuple swiftVersion); /// Add module options void addModuleOptions(ModuleOptions opts); diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 0438ab6f0a3b1..04591621a792e 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_API_NOTES_TYPES_H #define LLVM_CLANG_API_NOTES_TYPES_H +#include "clang/Basic/LLVM.h" #include "clang/Basic/Specifiers.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" @@ -20,10 +21,6 @@ #include <cassert> #include <climits> -namespace llvm { - class raw_ostream; -} - namespace clang { namespace api_notes { @@ -33,11 +30,6 @@ static const char SOURCE_APINOTES_EXTENSION[] = "apinotes"; /// The file extension used for the binary representation of API notes. static const char BINARY_APINOTES_EXTENSION[] = "apinotesc"; -using llvm::ArrayRef; -using llvm::StringRef; -using llvm::Optional; -using llvm::None; - /// Opaque context ID used to refer to an Objective-C class or protocol. class ContextID { public: diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 5b1449e181e2f..1c992bf85ee68 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -14,10 +14,10 @@ #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/Types.h" #include "clang/APINotes/APINotesWriter.h" -#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" #include <algorithm> From d0304dff6137a18fdb638705be8898b6f4aa2124 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 12 Jun 2018 17:06:35 -0700 Subject: [PATCH 283/582] [Refactor][Extract] Don't create an extractedStmtRange if the first or last statement isn't found Couldn't come up with a reasonable test as I have no reproducer for the crash. rdar://34677020 apple-llvm-split-commit: fa11dc91dc6331e591270cb6dda11999ff6fd0f1 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/Extract.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index f6a488d86b652..cc8d000748943 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -187,10 +187,12 @@ Optional<CompoundStatementRange> getExtractedStatements(const CompoundStmt *CS, assert(Begin && End); CompoundStatementRange Result; Result.First = findSelectedStmt(CS->body(), Begin); - assert(Result.First != CS->body_end()); + if (Result.First == CS->body_end()) + return None; Result.Last = findSelectedStmt( CompoundStmt::body_const_range(Result.First, CS->body_end()), End); - assert(Result.Last != CS->body_end()); + if (Result.Last == CS->body_end()) + return None; return Result; } From ef3db22752a7aca591e172417944d5aa4d0307b3 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Wed, 13 Jun 2018 12:10:28 -0700 Subject: [PATCH 284/582] [upstream-with-swift] Temporary workaround for LLVM r334283 LLVM r334283 changed StringRef.h so that split(char) and rsplit(char) are implemented using [r]split(StringRef), which does not have an inlinable definition in the header file. Because the Swift runtime and other target libraries use StringRef and other ADT headers without actually linking libSupport, this causes link failures. Changing to build libSupport for every target is complicated, so we may just need to tweak Swift to avoid using these StringRef functions. This change just puts back the old inline implementations until we can sort that out. apple-llvm-split-commit: cfd4e41eaedd0405b191d423337542f8f3ee3c86 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/StringRef.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/ADT/StringRef.h b/llvm/include/llvm/ADT/StringRef.h index a5ba5b59b5a36..566e8f71f20a0 100644 --- a/llvm/include/llvm/ADT/StringRef.h +++ b/llvm/include/llvm/ADT/StringRef.h @@ -725,7 +725,12 @@ namespace llvm { /// \returns The split substrings. LLVM_NODISCARD std::pair<StringRef, StringRef> split(char Separator) const { - return split(StringRef(&Separator, 1)); + // FIXME: temporary workaround for LLVM r334283 (rdar://problem/41029268) + // return split(StringRef(&Separator, 1)); + size_t Idx = find(Separator); + if (Idx == npos) + return std::make_pair(*this, StringRef()); + return std::make_pair(slice(0, Idx), slice(Idx+1, npos)); } /// Split into two substrings around the first occurrence of a separator @@ -811,7 +816,12 @@ namespace llvm { /// \return - The split substrings. LLVM_NODISCARD std::pair<StringRef, StringRef> rsplit(char Separator) const { - return rsplit(StringRef(&Separator, 1)); + // FIXME: temporary workaround for LLVM r334283 (rdar://problem/41029268) + // return rsplit(StringRef(&Separator, 1)); + size_t Idx = rfind(Separator); + if (Idx == npos) + return std::make_pair(*this, StringRef()); + return std::make_pair(slice(0, Idx), slice(Idx+1, npos)); } /// Return string with consecutive \p Char characters starting from the From 12e0322fdcebaf29a99487d8b646d773c66ce4b3 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Fri, 15 Jun 2018 23:12:20 -0700 Subject: [PATCH 285/582] [upstream-with-swift] Fix bad merge in 4bee7c556e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The “generalized_swift_name” feature was inadvertently dropped when resolving a merge conflict. rdar://problem/40779573 apple-llvm-split-commit: a110f67fb25be94908676dff05fe8f0d3b588836 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Features.def | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 48f4c51f2f282..aa18093a0c449 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -71,6 +71,7 @@ FEATURE(c_thread_safety_attributes, true) FEATURE(cxx_exceptions, LangOpts.CXXExceptions) FEATURE(cxx_rtti, LangOpts.RTTI &&LangOpts.RTTIData) FEATURE(enumerator_attributes, true) +FEATURE(generalized_swift_name, true) FEATURE(nullability, true) FEATURE(nullability_on_arrays, true) FEATURE(memory_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Memory)) From 638778fbc8be6d2c77aadc3d4ae79fda10f81de2 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Sun, 17 Jun 2018 17:08:28 -0700 Subject: [PATCH 286/582] Revert "[upstream-with-swift] Temporary workaround for LLVM r334283" This reverts commit cfd4e41eaedd0405b191d423337542f8f3ee3c86. apple-llvm-split-commit: 22e21994d0274f34ef19656737438edd0003d1c5 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/StringRef.h | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/llvm/include/llvm/ADT/StringRef.h b/llvm/include/llvm/ADT/StringRef.h index 566e8f71f20a0..a5ba5b59b5a36 100644 --- a/llvm/include/llvm/ADT/StringRef.h +++ b/llvm/include/llvm/ADT/StringRef.h @@ -725,12 +725,7 @@ namespace llvm { /// \returns The split substrings. LLVM_NODISCARD std::pair<StringRef, StringRef> split(char Separator) const { - // FIXME: temporary workaround for LLVM r334283 (rdar://problem/41029268) - // return split(StringRef(&Separator, 1)); - size_t Idx = find(Separator); - if (Idx == npos) - return std::make_pair(*this, StringRef()); - return std::make_pair(slice(0, Idx), slice(Idx+1, npos)); + return split(StringRef(&Separator, 1)); } /// Split into two substrings around the first occurrence of a separator @@ -816,12 +811,7 @@ namespace llvm { /// \return - The split substrings. LLVM_NODISCARD std::pair<StringRef, StringRef> rsplit(char Separator) const { - // FIXME: temporary workaround for LLVM r334283 (rdar://problem/41029268) - // return rsplit(StringRef(&Separator, 1)); - size_t Idx = rfind(Separator); - if (Idx == npos) - return std::make_pair(*this, StringRef()); - return std::make_pair(slice(0, Idx), slice(Idx+1, npos)); + return rsplit(StringRef(&Separator, 1)); } /// Return string with consecutive \p Char characters starting from the From d93a4e8945312330537431ee12429f6ce1679b35 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Wed, 20 Jun 2018 16:03:04 -0700 Subject: [PATCH 287/582] [upstream-with-swift] Increase DIAG_SIZE_FRONTEND to 101 The swift-clang/upstream-with-swift branch currently has 1 extra diag compared to upstream Clang. It looks like upstream is now right at that limit of 100, so the swift-clang version is failing. I'm expecting the upstream version to bump up the limit soon (i.e., the next time someone tries to add a frontend diagnostic) but it would be good to keep this version higher by 1. apple-llvm-split-commit: 20a8d19ab854c005e7ea4b669bd6a99249001535 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index b610af953fba1..67ee3939bf990 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -30,7 +30,7 @@ namespace clang { enum { DIAG_SIZE_COMMON = 300, DIAG_SIZE_DRIVER = 200, - DIAG_SIZE_FRONTEND = 100, + DIAG_SIZE_FRONTEND = 101, // swift-clang has 1 extra diag DIAG_SIZE_SERIALIZATION = 120, DIAG_SIZE_LEX = 400, DIAG_SIZE_PARSE = 500, From d7dcb2d42cf0f34e219b8910d052160aa5dfe298 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 12 Jun 2018 14:48:13 -0700 Subject: [PATCH 288/582] [Refactor][Extract] Correctly emit the return type for functions to account for block extractions and copy blocks when extracting without ARC apple-llvm-split-commit: 0405317acbc87c4335ef4463baf220513232ed24 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/Extract.cpp | 29 ++++++++++++++----- .../Refactor/Extract/extract-objc-property.m | 4 +-- clang/test/Refactor/Extract/return-block.m | 24 +++++++++++++++ .../Extract/return-correct-stl-type.cpp | 2 +- .../return-objc-generic-argument-type.m | 10 +++---- 5 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 clang/test/Refactor/Extract/return-block.m diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index cc8d000748943..357abc94a4eec 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -1855,20 +1855,22 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( OS << "static "; else if (!isMethodExtraction()) OS << (isInHeader(FunctionExtractionLoc, SM) ? "inline " : "static "); - ReturnType.print(OS, PP); - OS << ' '; + std::string QualifiedName; + llvm::raw_string_ostream NameOS(QualifiedName); if (isMethodExtraction() && IsDefinition) - printEnclosingMethodScope(FunctionLikeParentDecl, OS, PP); - unsigned NameOffset = OS.str().size(); - OS << ExtractedName << '('; + printEnclosingMethodScope(FunctionLikeParentDecl, NameOS, PP); + NameOS << ExtractedName; + NameOS << '('; bool IsFirst = true; for (const auto &Var : CapturedVariables) { if (!IsFirst) - OS << ", "; + NameOS << ", "; IsFirst = false; - Var.ParameterType.print(OS, PP, /*PlaceHolder=*/Var.getName()); + Var.ParameterType.print(NameOS, PP, /*PlaceHolder=*/Var.getName()); } - OS << ')'; + NameOS << ')'; + ReturnType.print(OS, PP, NameOS.str()); + unsigned NameOffset = OS.str().find(ExtractedName); if (isMethodExtraction() && isEnclosingMethodConst(FunctionLikeParentDecl)) OS << " const"; return RefactoringReplacement::AssociatedSymbolLocation( @@ -1926,7 +1928,18 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( auto Semicolons = computeSemicolonExtractionPolicy( ExtractedStmtRange ? *(ExtractedStmtRange->Last) : S, ExtractedTokenRange, SM, LangOpts); + bool ShouldCopyBlock = false; + if (IsExpr && !LangOpts.ObjCAutoRefCount && + ReturnType->isBlockPointerType()) { + // We can't return local blocks directly without ARC; they should be copied. + // FIXME: This is overly pessimistic, as we only need the copy for local + // blocks. + ExtractedOS << "[("; + ShouldCopyBlock = true; + } ExtractedOS << SourceRewriter.getRewrittenText(ExtractedTokenRange); + if (ShouldCopyBlock) + ExtractedOS << ") copy]"; if (Semicolons.IsNeededInExtractedFunction) ExtractedOS << ';'; if (CanUseReturnForVariablesUsedAfterwards) diff --git a/clang/test/Refactor/Extract/extract-objc-property.m b/clang/test/Refactor/Extract/extract-objc-property.m index 7fc79193c18d6..ee9e74922c28b 100644 --- a/clang/test/Refactor/Extract/extract-objc-property.m +++ b/clang/test/Refactor/Extract/extract-objc-property.m @@ -16,14 +16,14 @@ - (void)test { self.item; // property-end: -1:12 // property-name-end: -2:12 -// CHECK: "static HasProperty * extracted(HasProperty *object) {\nreturn object.item;\n}\n\n" +// CHECK: "static HasProperty *extracted(HasProperty *object) {\nreturn object.item;\n}\n\n" // implicit-name-begin: +2:8 // implicit-begin: +1:3 self.implicitProp; // implicit-end: -1:20 // implicit-name-end: -2:20 -// CHECK: "static HasProperty * extracted(HasProperty *object) {\nreturn object.implicitProp;\n}\n\n" +// CHECK: "static HasProperty *extracted(HasProperty *object) {\nreturn object.implicitProp;\n}\n\n" } // RUN: clang-refactor-test perform -action extract -selected=property -selected=implicit %s -fobjc-arc | FileCheck %s diff --git a/clang/test/Refactor/Extract/return-block.m b/clang/test/Refactor/Extract/return-block.m new file mode 100644 index 0000000000000..2f76cc7ce981e --- /dev/null +++ b/clang/test/Refactor/Extract/return-block.m @@ -0,0 +1,24 @@ +// RUN: clang-refactor-test perform -action extract -selected=%s:16:27-18:4 %s -fobjc-arc | FileCheck %s +// RUN: clang-refactor-test perform -action extract -selected=%s:16:27-18:4 %s | FileCheck --check-prefix=NOARC %s +@interface I + +@end + +@implementation I + +- (void) doStuff: (int)x block: (void (^)(int))block { + +} + +- (void)foo {} + +- (void)viewDidLoad { + [self doStuff: 2 block: ^(int returnCode) { + [self foo]; + }]; +} +// CHECK: "static void (^extracted(I *object))(int) {\nreturn ^(int returnCode) {\n [object foo];\n };\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1 +// CHECK: "extracted(self)" [[@LINE-5]]:27 -> [[@LINE-3]]:4 +// NOARC: "static void (^extracted(I *object))(int) {\nreturn [(^(int returnCode) {\n [object foo];\n }) copy];\n}\n\n" [[@LINE-7]]:1 -> [[@LINE-7]]:1 + +@end diff --git a/clang/test/Refactor/Extract/return-correct-stl-type.cpp b/clang/test/Refactor/Extract/return-correct-stl-type.cpp index f7b9444997106..3544eda9d4639 100644 --- a/clang/test/Refactor/Extract/return-correct-stl-type.cpp +++ b/clang/test/Refactor/Extract/return-correct-stl-type.cpp @@ -28,7 +28,7 @@ typedef BasicString<Traits> String; void returnCharTypeNotUselessValueType() { // CHECK1: "static char extracted(const std::String &x) {\nreturn x.value();\n}\n\n" [[@LINE-1]]:1 -// CHECK1: "static const char * extracted(const std::String &x) {\nreturn x.data();\n}\n\n" [[@LINE-2]]:1 +// CHECK1: "static const char *extracted(const std::String &x) {\nreturn x.data();\n}\n\n" [[@LINE-2]]:1 std::String x; // return-char-begin: +1:9 (void)x.value(); diff --git a/clang/test/Refactor/Extract/return-objc-generic-argument-type.m b/clang/test/Refactor/Extract/return-objc-generic-argument-type.m index 42d97705c4b22..a985ba15b48b6 100644 --- a/clang/test/Refactor/Extract/return-objc-generic-argument-type.m +++ b/clang/test/Refactor/Extract/return-objc-generic-argument-type.m @@ -19,22 +19,22 @@ void foo(Array<NSObject *> *objects) { // prop-begin: +1:3 objects.prop; // prop-end: -1:15 -// CHECK: "static NSObject * extracted(Array<NSObject *> *objects) {\nreturn objects.prop;\n}\n\n" +// CHECK: "static NSObject *extracted(Array<NSObject *> *objects) {\nreturn objects.prop;\n}\n\n" // imp-prop-begin: +1:3 objects.get; // imp-prop-end: -1:14 -// CHECK: "static NSObject * extracted(Array<NSObject *> *objects) {\nreturn objects.get;\n}\n\n" +// CHECK: "static NSObject *extracted(Array<NSObject *> *objects) {\nreturn objects.get;\n}\n\n" // class-prop-begin: +1:3 Array.classProp; // class-prop-end: -1:30 -// CHECK: "static Array * extracted() {\nreturn Array.classProp;\n}\n\n" +// CHECK: "static Array *extracted() {\nreturn Array.classProp;\n}\n\n" typedef Array<NSObject *> ObjectArray; // class-prop2-begin: +1:3 [ObjectArray classProp]; // class-prop2-end: -1:26 -// CHECK: "static Array<NSObject *> * extracted() {\nreturn [ObjectArray classProp];\n}\n\n" +// CHECK: "static Array<NSObject *> *extracted() {\nreturn [ObjectArray classProp];\n}\n\n" // class-method-begin: +1:3 [ObjectArray classGet]; // class-method-end: -1:25 -// CHECK: "static NSObject ** extracted() {\nreturn [ObjectArray classGet];\n}\n\n" +// CHECK: "static NSObject **extracted() {\nreturn [ObjectArray classGet];\n}\n\n" } From f7f52122da3cb0e8b1120d1ef05173d69512cb52 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Sat, 7 Jul 2018 14:08:10 -0700 Subject: [PATCH 289/582] Fix a bad merge conflict resolution The upstream-with-swift branch has refactored some code into a separate function where the "method" parameter is named "M", but the conflict was resolved without adjusting the name. apple-llvm-split-commit: f7d5f3be14fa3cb4bf92992573e56cd177324585 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclObjC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 7eadd7dbef3b5..ba3a7eaa14ce4 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -2191,7 +2191,7 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, static bool shouldWarnUndefinedMethod(const ObjCMethodDecl *M) { // No point warning no definition of method which is 'unavailable'. - return method->getAvailability() != AR_Unavailable; + return M->getAvailability() != AR_Unavailable; } static void WarnUndefinedMethod(Sema &S, SourceLocation ImpLoc, From 370882629e954f2305b27b698967778e357eb4ae Mon Sep 17 00:00:00 2001 From: Nathan Hawes <nhawes@apple.com> Date: Mon, 9 Jul 2018 18:46:11 -0700 Subject: [PATCH 290/582] [AST|index] Fixups following upstream changes apple-llvm-split-commit: ed860890aaac4ae2561d3e1d4c7c3569015e1a9a apple-llvm-split-dir: clang/ --- clang/include/clang/AST/Decl.h | 4 ++-- clang/include/indexstore/indexstore.h | 21 +++++++++++---------- clang/lib/Index/IndexDataStoreUtils.cpp | 3 +++ clang/lib/Index/IndexingAction.cpp | 19 ++++++++++--------- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index c10ca9e9670b7..ed0725d036028 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4260,7 +4260,7 @@ template<typename decl_type> void Redeclarable<decl_type>::setPreviousDecl(decl_type *PrevDecl) { // Note: This routine is implemented here because we need both NamedDecl // and Redeclarable to be defined. - assert(RedeclLink.NextIsLatest() && + assert(RedeclLink.isFirst() && "setPreviousDecl on a decl already in a redeclaration chain"); if (PrevDecl) { @@ -4268,7 +4268,7 @@ void Redeclarable<decl_type>::setPreviousDecl(decl_type *PrevDecl) { // redeclaration, or we can build invalid chains. If the most recent // redeclaration is invalid, it won't be PrevDecl, but we want it anyway. First = PrevDecl->getFirstDecl(); - assert(First->RedeclLink.NextIsLatest() && "Expected first"); + assert(First->RedeclLink.isFirst() && "Expected first"); decl_type *MostRecent = First->getNextRedeclaration(); RedeclLink = PreviousDeclLink(cast<decl_type>(MostRecent)); diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index f4f41db002321..73b93d685985e 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -268,18 +268,19 @@ typedef enum { INDEXSTORE_SYMBOL_ROLE_DYNAMIC = 1 << 6, INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7, INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8, + INDEXSTORE_SYMBOL_ROLE_UNDEFINED = 1 << 9, // Relation roles. - INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 9, - INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 10, - INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 11, - INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 12, - INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 13, - INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 14, - INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 15, - INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 16, - INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 17, - INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 18, + INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 10, + INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 11, + INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 12, + INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 13, + INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 14, + INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 15, + INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 16, + INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 17, + INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 18, + INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 19, } indexstore_symbol_role_t; INDEXSTORE_PUBLIC indexstore_symbol_language_t diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 3efaec95e23e5..45fe721382af4 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -374,6 +374,9 @@ uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) { case SymbolRole::Implicit: storeRoles |= INDEXSTORE_SYMBOL_ROLE_IMPLICIT; break; + case SymbolRole::Undefinition: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_UNDEFINED; + break; case SymbolRole::RelationChildOf: storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF; break; diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 3c618aad3d2be..925815cf12a65 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -123,7 +123,7 @@ class IndexActionBase { std::unique_ptr<IndexASTConsumer> createIndexASTConsumer(CompilerInstance &CI) { - IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot); + IndexCtx->setSysrootPath(CI.getHeaderSearchOpts().Sysroot); return llvm::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), IndexCtx); } @@ -500,21 +500,22 @@ class IndexRecordActionBase { protected: RecordingOptions RecordOpts; IndexDataRecorder Recorder; - IndexingContext IndexCtx; + std::shared_ptr<IndexingContext> IndexCtx; SourceFilesIndexDependencyCollector DepCollector; IndexRecordActionBase(IndexingOptions IndexOpts, RecordingOptions recordOpts) - : RecordOpts(std::move(recordOpts)), IndexCtx(IndexOpts, Recorder), - DepCollector(IndexCtx, RecordOpts) {} + : RecordOpts(std::move(recordOpts)), + IndexCtx(new IndexingContext(IndexOpts, Recorder)), + DepCollector(*IndexCtx, RecordOpts) {} std::unique_ptr<IndexASTConsumer> createIndexASTConsumer(CompilerInstance &CI) { - IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot); - Recorder.init(&IndexCtx, CI); + IndexCtx->setSysrootPath(CI.getHeaderSearchOpts().Sysroot); + Recorder.init(IndexCtx.get(), CI); Preprocessor &PP = CI.getPreprocessor(); DepCollector.setSourceManager(&CI.getSourceManager()); - DepCollector.setSysrootPath(IndexCtx.getSysrootPath()); + DepCollector.setSysrootPath(IndexCtx->getSysrootPath()); DepCollector.attachToPreprocessor(PP); return llvm::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), @@ -644,8 +645,8 @@ void IndexRecordActionBase::finish(CompilerInstance &CI) { /*AllowSearch=*/false); } - writeUnitData(CI, Recorder, DepCollector, IndexCtx.getIndexOpts(), RecordOpts, - OutputFile, RootFile, UnitMod, IndexCtx.getSysrootPath()); + writeUnitData(CI, Recorder, DepCollector, IndexCtx->getIndexOpts(), RecordOpts, + OutputFile, RootFile, UnitMod, IndexCtx->getSysrootPath()); } /// Checks if the unit file exists for module file, if it doesn't it generates From 0c677d037ae0d4aa55371433963cb14a428a0808 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 13 Jul 2018 10:46:31 -0700 Subject: [PATCH 291/582] fixup reference to AttributeList after upstream rename apple-llvm-split-commit: 7a9f6732bfe50a0bbf062755598cb5316aa84db6 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclAttr.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 3ec097be8a3bb..42eb32008a3d5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4788,7 +4788,7 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, Attrs.getRange(), S.Context, Attrs.getAttributeSpellingListIndex())); } -static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) { +static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &Attr) { if (!isa<TagDecl>(D)) { S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl) << S.getLangOpts().CPlusPlus; @@ -5229,7 +5229,7 @@ bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name, return true; } -static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { +static void handleSwiftName(Sema &S, Decl *D, const ParsedAttr &Attr) { StringRef Name; SourceLocation ArgLoc; if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc)) @@ -5266,7 +5266,7 @@ static bool isErrorParameter(Sema &S, QualType paramType) { return false; } -static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) { +static void handleSwiftError(Sema &S, Decl *D, const ParsedAttr &attr) { SwiftErrorAttr::ConventionKind convention; IdentifierLoc *conventionLoc = attr.getArgAsIdent(0); StringRef conventionStr = conventionLoc->Ident->getName(); @@ -5343,7 +5343,7 @@ static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) { attr.getAttributeSpellingListIndex())); } -static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) { +static void handleSwiftBridgeAttr(Sema &S, Decl *D, const ParsedAttr &Attr) { // Make sure that there is a string literal as the annotation's single // argument. StringRef Str; @@ -5361,7 +5361,7 @@ static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } -static void handleSwiftNewtypeAttr(Sema &S, Decl *D, const AttributeList &Attr) { +static void handleSwiftNewtypeAttr(Sema &S, Decl *D, const ParsedAttr &Attr) { // Make sure that there is an identifier as the annotation's single // argument. if (Attr.getNumArgs() != 1) { @@ -6553,7 +6553,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_ObjCBoxable: handleObjCBoxable(S, D, AL); break; - case AttributeList::AT_NSErrorDomain: + case ParsedAttr::AT_NSErrorDomain: handleNSErrorDomain(S, D, AL); break; case ParsedAttr::AT_CFAuditedTransfer: @@ -6620,7 +6620,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_ObjCSubclassingRestricted: handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, AL); break; - case AttributeList::AT_ObjCCompleteDefinition: + case ParsedAttr::AT_ObjCCompleteDefinition: handleSimpleAttribute<ObjCCompleteDefinitionAttr>(S, D, AL); break; case ParsedAttr::AT_ObjCExplicitProtocolImpl: @@ -6905,25 +6905,25 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleSimpleAttribute<RenderScriptKernelAttr>(S, D, AL); break; // Swift attributes. - case AttributeList::AT_SwiftPrivate: + case ParsedAttr::AT_SwiftPrivate: handleSimpleAttribute<SwiftPrivateAttr>(S, D, AL); break; - case AttributeList::AT_SwiftName: + case ParsedAttr::AT_SwiftName: handleSwiftName(S, D, AL); break; - case AttributeList::AT_SwiftError: + case ParsedAttr::AT_SwiftError: handleSwiftError(S, D, AL); break; - case AttributeList::AT_SwiftBridge: + case ParsedAttr::AT_SwiftBridge: handleSwiftBridgeAttr(S, D, AL); break; - case AttributeList::AT_SwiftBridgedTypedef: + case ParsedAttr::AT_SwiftBridgedTypedef: handleSimpleAttribute<SwiftBridgedTypedefAttr>(S, D, AL); break; - case AttributeList::AT_SwiftObjCMembers: + case ParsedAttr::AT_SwiftObjCMembers: handleSimpleAttribute<SwiftObjCMembersAttr>(S, D, AL); break; - case AttributeList::AT_SwiftNewtype: + case ParsedAttr::AT_SwiftNewtype: handleSwiftNewtypeAttr(S, D, AL); break; // XRay attributes. From 7b922e37353570b548958d3c9c8f572afbd6dd2f Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Mon, 23 Jul 2018 02:01:50 -0400 Subject: [PATCH 292/582] Add indexing infrastructure for Swift's read and modify accessors. apple-llvm-split-commit: acdb7319bc58db45f39e1ddea31c015232f860ac apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexSymbol.h | 2 ++ clang/include/indexstore/indexstore.h | 2 ++ clang/lib/Index/IndexDataStoreUtils.cpp | 8 ++++++++ clang/lib/Index/IndexSymbol.cpp | 2 ++ 4 files changed, 14 insertions(+) diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index ddb509217e48d..11c43de459062 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -82,6 +82,8 @@ enum class SymbolSubKind : uint8_t { SwiftAccessorDidSet, SwiftAccessorAddressor, SwiftAccessorMutableAddressor, + SwiftAccessorRead, + SwiftAccessorModify, SwiftExtensionOfStruct, SwiftExtensionOfClass, diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 73b93d685985e..8d0c4f217a9bb 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -237,6 +237,8 @@ typedef enum { INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT = 1011, INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE = 1012, INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM = 1013, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORREAD = 1014, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY = 1015, } indexstore_symbol_subkind_t; typedef enum { diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 45fe721382af4..799c9199b9c45 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -129,6 +129,10 @@ SymbolSubKind index::getSymbolSubKind(indexstore_symbol_subkind_t K) { return SymbolSubKind::SwiftAccessorAddressor; case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR: return SymbolSubKind::SwiftAccessorMutableAddressor; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORREAD: + return SymbolSubKind::SwiftAccessorRead; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY: + return SymbolSubKind::SwiftAccessorModify; case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT: return SymbolSubKind::SwiftExtensionOfStruct; case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS: @@ -269,6 +273,10 @@ indexstore_symbol_subkind_t index::getIndexStoreSubKind(SymbolSubKind K) { return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR; case SymbolSubKind::SwiftAccessorMutableAddressor: return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR; + case SymbolSubKind::SwiftAccessorRead: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORREAD; + case SymbolSubKind::SwiftAccessorModify: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY; case SymbolSubKind::SwiftExtensionOfStruct: return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT; case SymbolSubKind::SwiftExtensionOfClass: diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index 315a3f4930fc6..ad7ef3d104c54 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -496,6 +496,8 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) { case SymbolSubKind::SwiftAccessorDidSet: return "acc-didset"; case SymbolSubKind::SwiftAccessorAddressor: return "acc-addr"; case SymbolSubKind::SwiftAccessorMutableAddressor: return "acc-mutaddr"; + case SymbolSubKind::SwiftAccessorRead: return "acc-read"; + case SymbolSubKind::SwiftAccessorModify: return "acc-modify"; case SymbolSubKind::SwiftExtensionOfStruct: return "ext-struct"; case SymbolSubKind::SwiftExtensionOfClass: return "ext-class"; case SymbolSubKind::SwiftExtensionOfEnum: return "ext-enum"; From dab5207354ee2fd83920e447ba29f7a50726c65c Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Tue, 24 Jul 2018 16:27:06 -0700 Subject: [PATCH 293/582] [index/store] Improvements to make index store reading/writing more robust * Install fatal error handler for libIndexStore to do abort, isntead of exist, the same way that libclang installs it. * Fail with error instead of crashing when there is an empty unit of record file * Check that the temporary file (for a unit or record) was written successfully before attempting to move it in place rdar://34342821 apple-llvm-split-commit: 0d75da7543c152013720cadd721b4cee46f224a1 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexRecordReader.cpp | 5 +++++ clang/lib/Index/IndexRecordWriter.cpp | 7 ++++++ clang/lib/Index/IndexUnitReader.cpp | 5 +++++ clang/lib/Index/IndexUnitWriter.cpp | 7 ++++++ clang/test/Index/Store/empty-unit.c | 18 +++++++++++++++ clang/tools/IndexStore/IndexStore.cpp | 32 +++++++++++++++++++++++++++ 6 files changed, 74 insertions(+) create mode 100644 clang/test/Index/Store/empty-unit.c diff --git a/clang/lib/Index/IndexRecordReader.cpp b/clang/lib/Index/IndexRecordReader.cpp index c657d07bea40d..1ba3165c7fdab 100644 --- a/clang/lib/Index/IndexRecordReader.cpp +++ b/clang/lib/Index/IndexRecordReader.cpp @@ -352,6 +352,11 @@ IndexRecordReader::createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer, Impl.Buffer = std::move(Buffer); llvm::BitstreamCursor Stream(*Impl.Buffer); + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return nullptr; + } + // Sniff for the signature. if (Stream.Read(8) != 'I' || Stream.Read(8) != 'D' || diff --git a/clang/lib/Index/IndexRecordWriter.cpp b/clang/lib/Index/IndexRecordWriter.cpp index c4e6d50716a96..d22f4144aba28 100644 --- a/clang/lib/Index/IndexRecordWriter.cpp +++ b/clang/lib/Index/IndexRecordWriter.cpp @@ -320,6 +320,13 @@ IndexRecordWriter::endRecord(std::string &Error, OS.write(State.Buffer.data(), State.Buffer.size()); OS.close(); + if (OS.has_error()) { + llvm::raw_string_ostream Err(Error); + Err << "failed to write '" << TempPath << "': " << OS.error().message(); + OS.clear_error(); + return Result::Failure; + } + // Atomically move the unique file into place. if (std::error_code EC = sys::fs::rename(TempPath.c_str(), State.RecordPath.c_str())) { diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp index fa8e9e924ef35..c53407e493365 100644 --- a/clang/lib/Index/IndexUnitReader.cpp +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -270,6 +270,11 @@ bool IndexUnitReaderImpl::init(std::unique_ptr<MemoryBuffer> Buf, this->MemBuf = std::move(Buf); llvm::BitstreamCursor Stream(*MemBuf); + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return true; + } + // Sniff for the signature. if (Stream.Read(8) != 'I' || Stream.Read(8) != 'D' || diff --git a/clang/lib/Index/IndexUnitWriter.cpp b/clang/lib/Index/IndexUnitWriter.cpp index 7c981ae9750d8..c899cbd5eca1e 100644 --- a/clang/lib/Index/IndexUnitWriter.cpp +++ b/clang/lib/Index/IndexUnitWriter.cpp @@ -367,6 +367,13 @@ bool IndexUnitWriter::write(std::string &Error) { OS.write(Buffer.data(), Buffer.size()); OS.close(); + if (OS.has_error()) { + llvm::raw_string_ostream Err(Error); + Err << "failed to write '" << TempPath << "': " << OS.error().message(); + OS.clear_error(); + return true; + } + std::error_code EC = fs::rename(/*from=*/TempPath.c_str(), /*to=*/UnitPath.c_str()); if (EC) { llvm::raw_string_ostream Err(Error); diff --git a/clang/test/Index/Store/empty-unit.c b/clang/test/Index/Store/empty-unit.c new file mode 100644 index 0000000000000..8ffdf0dd2ea05 --- /dev/null +++ b/clang/test/Index/Store/empty-unit.c @@ -0,0 +1,18 @@ +// XFAIL: linux + +void foo(int i); + +// RUN: %clang_cc1 -index-store-path %t/idx %s -o %t.o +// RUN: touch %t.empty + +// RUN: cp %t.empty $(find %t/idx -name "empty-unit.c*o*") +// RUN: not c-index-test core -print-unit %t/idx 2> %t.err +// RUN: FileCheck %s -input-file %t.err -check-prefix ERR-UNIT +// ERR-UNIT: error loading unit: empty file + +// Also check for empty record files. +// RUN: %clang_cc1 -index-store-path %t/idx2 %s -o %t.o +// RUN: cp %t.empty $(find %t/idx2 -name "empty-unit.c-*") +// RUN: not c-index-test core -print-record %t/idx2 2> %t2.err +// RUN: FileCheck %s -input-file %t2.err -check-prefix ERR-RECORD +// ERR-RECORD: error loading record: empty file diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index b24cde8663a7e..7412d82a70c80 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/ManagedStatic.h" #include <Block.h> using namespace clang; @@ -44,6 +45,32 @@ static timespec toTimeSpec(sys::TimePoint<> tp) { return ts; } +//===----------------------------------------------------------------------===// +// Fatal error handling +//===----------------------------------------------------------------------===// + +static void fatal_error_handler(void *user_data, const std::string& reason, + bool gen_crash_diag) { + // Write the result out to stderr avoiding errs() because raw_ostreams can + // call report_fatal_error. + fprintf(stderr, "INDEXSTORE FATAL ERROR: %s\n", reason.c_str()); + ::abort(); +} + +namespace { +struct RegisterFatalErrorHandler { + RegisterFatalErrorHandler() { + llvm::install_fatal_error_handler(fatal_error_handler, nullptr); + } +}; +} + +static llvm::ManagedStatic<RegisterFatalErrorHandler> RegisterFatalErrorHandlerOnce; + +//===----------------------------------------------------------------------===// +// C API +//===----------------------------------------------------------------------===// + namespace { struct IndexStoreError { @@ -69,6 +96,11 @@ indexstore_format_version(void) { indexstore_t indexstore_store_create(const char *store_path, indexstore_error_t *c_error) { + // Look through the managed static to trigger construction of the managed + // static which registers our fatal error handler. This ensures it is only + // registered once. + (void)*RegisterFatalErrorHandlerOnce; + std::unique_ptr<IndexDataStore> store; std::string error; store = IndexDataStore::create(store_path, error); From a273d71c552776e9a0117a6fd9c2a7b2c56ac34d Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Mon, 13 Aug 2018 13:14:33 -0700 Subject: [PATCH 294/582] Update calls to getEndLoc and getStartLoc Avoids using deprecated interfaces and silence warnings apple-llvm-split-commit: 55d5fd00e1cbf83ac97c9412cdb6c87296113c96 apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingProtocolStubs.cpp | 10 ++-- .../lib/Edit/FillInMissingSwitchEnumCases.cpp | 8 +-- clang/lib/Sema/SemaDeclAttr.cpp | 4 +- clang/lib/Tooling/Refactor/ASTSlice.cpp | 14 ++--- clang/lib/Tooling/Refactor/Extract.cpp | 56 +++++++++---------- .../ExtractRepeatedExpressionIntoVariable.cpp | 4 +- .../lib/Tooling/Refactor/ExtractionUtils.cpp | 2 +- ...nMissingMethodStubsFromAbstractClasses.cpp | 4 +- .../Tooling/Refactor/IfSwitchConversion.cpp | 30 +++++----- .../Refactor/ImplementDeclaredMethods.cpp | 10 ++-- .../Refactor/LocalizeObjCStringLiteral.cpp | 4 +- .../Refactor/SourceLocationUtilities.cpp | 8 +-- clang/lib/Tooling/Refactor/USRFinder.cpp | 14 ++--- clang/tools/libclang/CRefactor.cpp | 4 +- 14 files changed, 86 insertions(+), 86 deletions(-) diff --git a/clang/lib/Edit/FillInMissingProtocolStubs.cpp b/clang/lib/Edit/FillInMissingProtocolStubs.cpp index 8aa6d202ae473..02f11bc008acd 100644 --- a/clang/lib/Edit/FillInMissingProtocolStubs.cpp +++ b/clang/lib/Edit/FillInMissingProtocolStubs.cpp @@ -246,12 +246,12 @@ class MethodSet { // Find the appropriate source locations by looking if (MethodsFromProtocolInContainer.empty()) return SourceLocation(); - SourceLocation Loc = MethodsFromProtocolInContainer[0]->getLocEnd(); + SourceLocation Loc = MethodsFromProtocolInContainer[0]->getEndLoc(); if (Loc.isMacroID()) Loc = SM.getExpansionRange(Loc).getEnd(); for (const ObjCMethodDecl *M : makeArrayRef(MethodsFromProtocolInContainer).drop_front()) { - SourceLocation EndLoc = M->getLocEnd(); + SourceLocation EndLoc = M->getEndLoc(); if (EndLoc.isMacroID()) EndLoc = SM.getExpansionRange(EndLoc).getEnd(); if (SM.isBeforeInTranslationUnit(Loc, EndLoc)) @@ -390,12 +390,12 @@ static void perform(MethodSet &Methods, const ObjCContainerDecl *Container, SourceLocation InsertionLoc = isa<ObjCImplDecl>(Container) - ? Container->getLocEnd() - : getLocationOfPrecedingToken(Container->getLocEnd(), + ? Container->getEndLoc() + : getLocationOfPrecedingToken(Container->getEndLoc(), Context.getSourceManager(), Context.getLangOpts()); if (InsertionLoc.isInvalid()) - InsertionLoc = Container->getLocEnd(); + InsertionLoc = Container->getEndLoc(); PrintingPolicy PP = Context.getPrintingPolicy(); PP.PolishForDeclaration = true; diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp index c6d59340d30ad..46ef3c8915219 100644 --- a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -134,9 +134,9 @@ void edit::fillInMissingSwitchEnumCases( // Note: Switch cases are ordered back to front, so the last default // case would be the first case in the switch statement. if (DefaultCase && DefaultCase == Switch->getSwitchCaseList()) - InsertionLoc = DefaultCase->getLocStart(); + InsertionLoc = DefaultCase->getBeginLoc(); else - InsertionLoc = Switch->getBody()->getLocEnd(); + InsertionLoc = Switch->getBody()->getEndLoc(); } Consumer(FixItHint::CreateInsertion( Context.getSourceManager().getSpellingLoc(InsertionLoc), OS.str())); @@ -160,7 +160,7 @@ void edit::fillInMissingSwitchEnumCases( // order of enum case values defined in the enum. if (useCaseBasedOrdering(CoveredEnumCaseValues, CoveredEnumCases)) { // Start inserting before the first covered case. - SourceLocation InsertionLoc = FirstCoveredEnumCase->getLocStart(); + SourceLocation InsertionLoc = FirstCoveredEnumCase->getBeginLoc(); for (const auto &EnumCase : EnumCases) { if (!CoveredEnumCases.count(EnumCase.second)) { @@ -175,7 +175,7 @@ void edit::fillInMissingSwitchEnumCases( auto It = CoveredEnumCases.find(EnumCase.second); assert(It != CoveredEnumCases.end() && "Missing enum case"); const CaseInfo &Case = It->second; - InsertionLoc = Case.NextCase ? Case.NextCase->getLocStart() + InsertionLoc = Case.NextCase ? Case.NextCase->getBeginLoc() : /*Insert before end*/ SourceLocation(); } CreateReplacementForMissingCaseGroup(UncoveredEnumCases, InsertionLoc); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index a638b67359361..dddb59d1a9dce 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4885,7 +4885,7 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &Attr) { if (!isa<TagDecl>(D)) { - S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl) + S.Diag(D->getBeginLoc(), diag::err_nserrordomain_not_tagdecl) << S.getLangOpts().CPlusPlus; return; } @@ -4895,7 +4895,7 @@ static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &Attr) { // Try to locate the argument directly SourceLocation loc = Attr.getLoc(); if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) - loc = Attr.getArgAsExpr(0)->getLocStart(); + loc = Attr.getArgAsExpr(0)->getBeginLoc(); S.Diag(loc, diag::err_nserrordomain_requires_identifier); return; diff --git a/clang/lib/Tooling/Refactor/ASTSlice.cpp b/clang/lib/Tooling/Refactor/ASTSlice.cpp index ebf6abc286b81..de6de11485c82 100644 --- a/clang/lib/Tooling/Refactor/ASTSlice.cpp +++ b/clang/lib/Tooling/Refactor/ASTSlice.cpp @@ -184,10 +184,10 @@ ASTSlice::ASTSlice(SourceLocation Location, SourceRange SelectionRange, for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { if (EndLoc.isValid() && !Context.getSourceManager().isBeforeInTranslationUnit( - CurrDecl->getLocStart(), EndLoc)) + CurrDecl->getBeginLoc(), EndLoc)) break; const SourceLocation FileLoc = - Context.getSourceManager().getSpellingLoc(CurrDecl->getLocStart()); + Context.getSourceManager().getSpellingLoc(CurrDecl->getBeginLoc()); if (Context.getSourceManager().getFileID(FileLoc) == SearchFile) Visitor.TraverseDecl(CurrDecl); // We are only interested in looking at a single top level declaration @@ -197,7 +197,7 @@ ASTSlice::ASTSlice(SourceLocation Location, SourceRange SelectionRange, // that are declared outside of the @implementation, so continue looking // through them. if (isa<ObjCImplDecl>(CurrDecl)) { - EndLoc = CurrDecl->getLocEnd(); + EndLoc = CurrDecl->getEndLoc(); continue; } break; @@ -404,7 +404,7 @@ computeSelectionRangeOverlapKinds(MutableArrayRef<ASTSlice::Node> NodeTree, const Stmt *findFirstStatementAfter(const CompoundStmt *CS, SourceLocation Loc, const SourceManager &SM) { for (const Stmt *S : CS->body()) { - if (!SM.isBeforeInTranslationUnit(S->getLocStart(), Loc)) + if (!SM.isBeforeInTranslationUnit(S->getBeginLoc(), Loc)) return S; } return nullptr; @@ -418,7 +418,7 @@ const Stmt *findLastStatementBefore(const CompoundStmt *CS, SourceLocation Loc, const Stmt *Last = StartAt; for (auto E = CS->body_end(); It != E; ++It) { const Stmt *S = *It; - if (!SM.isBeforeInTranslationUnit(S->getLocStart(), Loc)) + if (!SM.isBeforeInTranslationUnit(S->getBeginLoc(), Loc)) return Last; Last = S; } @@ -498,7 +498,7 @@ static bool isCaseSelected(const SwitchStmt *S, SourceRange SelectionRange, const SourceManager &SM) { for (const SwitchCase *Case = S->getSwitchCaseList(); Case; Case = Case->getNextSwitchCase()) { - SourceRange Range(Case->getLocStart(), Case->getColonLoc()); + SourceRange Range(Case->getBeginLoc(), Case->getColonLoc()); if (areRangesOverlapping(Range, SelectionRange, SM)) return true; } @@ -584,7 +584,7 @@ Optional<SelectedStmtSet> ASTSlice::computeSelectedStmtSet() { // doesn't overlap with any actual statements. if (!Result.containsSelectionRangeStart || !Context.getSourceManager().isBeforeInTranslationUnit( - Result.containsSelectionRangeStart->getLocStart(), + Result.containsSelectionRangeStart->getBeginLoc(), SelectionRange.getEnd())) return None; diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index 357abc94a4eec..11ec350e65bcb 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -252,11 +252,11 @@ initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, if (isMultipleCandidateBinOp(BinOp->getOpcode()) && (!Stmts.containsSelectionRangeStart || getPreciseTokenLocEnd( - BinOp->getLHS()->getLocEnd(), Context.getSourceManager(), + BinOp->getLHS()->getEndLoc(), Context.getSourceManager(), Context.getLangOpts()) == SelectionRange.getBegin()) && Stmts.containsSelectionRangeEnd) { SourceRange FirstCandidateRange = - SourceRange(SelectionRange.getBegin(), BinOp->getLocEnd()); + SourceRange(SelectionRange.getBegin(), BinOp->getEndLoc()); if (FirstCandidateRange.getEnd().isMacroID()) FirstCandidateRange.setEnd(Context.getSourceManager().getExpansionLoc( FirstCandidateRange.getEnd())); @@ -288,8 +288,8 @@ initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo; SourceRange Range; if (ExtractedStmtRange) - Range = SourceRange(ExtractedStmtRange->getFirst()->getLocStart(), - ExtractedStmtRange->getLast()->getLocEnd()); + Range = SourceRange(ExtractedStmtRange->getFirst()->getBeginLoc(), + ExtractedStmtRange->getLast()->getEndLoc()); else Range = Selected->getSourceRange(); bool IsBeginMacroArgument = false; @@ -474,7 +474,7 @@ static bool isImplicitInitializer(const VarDecl *VD) { return false; const auto *Construct = dyn_cast<CXXConstructExpr>(E); if (!Construct) - return E->getLocStart() == VD->getLocation(); + return E->getBeginLoc() == VD->getLocation(); return Construct->getParenOrBraceRange().isInvalid(); } @@ -828,7 +828,7 @@ class ReturnRewriter : public RecursiveASTVisitor<ReturnRewriter> { bool VisitReturnStmt(const ReturnStmt *S) { SourceRewriter.InsertText( - getPreciseTokenLocEnd(S->getLocEnd(), SourceRewriter.getSourceMgr(), + getPreciseTokenLocEnd(S->getEndLoc(), SourceRewriter.getSourceMgr(), SourceRewriter.getLangOpts()), Text); return true; @@ -891,7 +891,7 @@ class DefinedInExtractedCodeDeclStmtRewriter /// while still preserving the original initializer expressions when we /// can. void rewriteAllVariableDeclarationsToAssignments(const DeclStmt *S) { - SourceLocation StartLoc = S->getLocStart(); + SourceLocation StartLoc = S->getBeginLoc(); for (const Decl *D : S->decls()) { const auto *VD = dyn_cast<VarDecl>(D); if (!VD || !VariablesUsedAfterExtraction.count(VD)) @@ -901,13 +901,13 @@ class DefinedInExtractedCodeDeclStmtRewriter // This can affect the semantics of the program if the implicit // initialization expression has side effects. SourceRange Range = SourceRange( - StartLoc, S->isSingleDecl() ? S->getLocEnd() : VD->getLocation()); + StartLoc, S->isSingleDecl() ? S->getEndLoc() : VD->getLocation()); SourceRewriter.RemoveText(Range); continue; } std::string Str; llvm::raw_string_ostream OS(Str); - if (StartLoc != S->getLocStart()) + if (StartLoc != S->getBeginLoc()) OS << "; "; const ASTContext &Ctx = D->getASTContext(); // Dereference the variable unless the source uses C++. @@ -915,7 +915,7 @@ class DefinedInExtractedCodeDeclStmtRewriter OS << '*'; OS << VD->getName() << " = "; const Expr *Init = getInitializerExprWithLexicalRange(VD->getInit()); - SourceLocation End = Init->getLocStart(); + SourceLocation End = Init->getBeginLoc(); if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) { SourceRange SubRange = Construct->getParenOrBraceRange(); if (SubRange.isValid()) { @@ -928,7 +928,7 @@ class DefinedInExtractedCodeDeclStmtRewriter auto Range = CharSourceRange::getCharRange(StartLoc, End); SourceRewriter.ReplaceText(StartLoc, SourceRewriter.getRangeSize(Range), OS.str()); - StartLoc = getPreciseTokenLocEnd(D->getLocEnd(), Ctx.getSourceManager(), + StartLoc = getPreciseTokenLocEnd(D->getEndLoc(), Ctx.getSourceManager(), Ctx.getLangOpts()); } } @@ -1057,10 +1057,10 @@ class CapturedVariableCaptureByPointerRewriter } void dereferenceTargetVar(const Expr *E, bool WrapInParens = false) { - SourceRewriter.InsertTextBefore(E->getLocStart(), + SourceRewriter.InsertTextBefore(E->getBeginLoc(), WrapInParens ? "(*" : "*"); if (WrapInParens) - SourceRewriter.InsertTextAfterToken(E->getLocEnd(), ")"); + SourceRewriter.InsertTextAfterToken(E->getEndLoc(), ")"); } bool VisitDeclRefExpr(const DeclRefExpr *E) { @@ -1078,7 +1078,7 @@ class CapturedVariableCaptureByPointerRewriter if (VD == TargetVD) { // Remove the '&' as the variable is now a pointer. SourceRewriter.RemoveText( - CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart())); + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getBeginLoc())); return true; } } @@ -1120,9 +1120,9 @@ class CapturedThisReferenceRewriter void rewriteThis(const CXXThisExpr *E) { RewrittenExpressions.insert(E); if (!E->isImplicit()) - SourceRewriter.ReplaceText(E->getLocStart(), 4, "object"); + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, "object"); else - SourceRewriter.InsertText(E->getLocStart(), "object"); + SourceRewriter.InsertText(E->getBeginLoc(), "object"); } bool VisitMemberExpr(const MemberExpr *E) { @@ -1133,7 +1133,7 @@ class CapturedThisReferenceRewriter if (!This->isImplicit() && E->isArrow()) SourceRewriter.ReplaceText(E->getOperatorLoc(), 2, "."); else - SourceRewriter.InsertText(E->getBase()->getLocEnd(), "."); + SourceRewriter.InsertText(E->getBase()->getEndLoc(), "."); } return true; } @@ -1153,7 +1153,7 @@ class CapturedThisPointerRewriter RewrittenExpressions(RewrittenExpressions) {} void replace(const CXXThisExpr *E, StringRef Text) { - SourceRewriter.ReplaceText(E->getLocStart(), 4, Text); + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, Text); } bool VisitCXXThisExpr(const CXXThisExpr *E) { @@ -1170,7 +1170,7 @@ class CapturedThisPointerRewriter if (!This->isImplicit()) { // Remove the '*' as the variable is now a reference. SourceRewriter.RemoveText( - CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart())); + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getBeginLoc())); replace(This, "object"); return true; } @@ -1195,9 +1195,9 @@ class CapturedSelfRewriter : public PseudoObjectRewriter<CapturedSelfRewriter> { const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); if (!VD || VD != SelfDecl) return true; - if (E->getLocStart().isInvalid()) + if (E->getBeginLoc().isInvalid()) return true; - SourceRewriter.ReplaceText(E->getLocStart(), 4, "object"); + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, "object"); return true; } @@ -1207,14 +1207,14 @@ class CapturedSelfRewriter : public PseudoObjectRewriter<CapturedSelfRewriter> { if (!DRE) return; const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD || VD != SelfDecl || DRE->getLocStart().isValid()) + if (!VD || VD != SelfDecl || DRE->getBeginLoc().isValid()) return; SourceRewriter.InsertText(Loc, Text); } bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { insertObjectForImplicitSelf(E->getBase()->IgnoreImpCasts(), - E->getLocStart(), "object->"); + E->getBeginLoc(), "object->"); return true; } }; @@ -1238,9 +1238,9 @@ class CapturedClassSelfRewriter bool VisitDeclRefExpr(const DeclRefExpr *E) { const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()); - if (!VD || VD != SelfDecl || E->getLocStart().isInvalid()) + if (!VD || VD != SelfDecl || E->getBeginLoc().isInvalid()) return true; - SourceRewriter.ReplaceText(E->getLocStart(), 4, ClassName); + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, ClassName); return true; } }; @@ -1380,7 +1380,7 @@ computeFunctionExtractionLocation(const Decl *D, bool IsMethodExtraction) { while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext())) D = RD; } - return D->getLocStart(); + return D->getBeginLoc(); } namespace { @@ -1453,10 +1453,10 @@ computeAppropriateExtractionLocationForMethodDeclaration( for (const CXXMethodDecl *M : RD->methods()) { if (M->isImplicit()) continue; - Loc = M->getLocEnd(); + Loc = M->getEndLoc(); } return Loc.isValid() ? std::make_pair(Loc, MethodDeclarationPlacement::After) - : std::make_pair(RD->getLocEnd(), + : std::make_pair(RD->getEndLoc(), MethodDeclarationPlacement::Before); } diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index 4641e0e9238b1..42b67c31dc212 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -288,8 +288,8 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( // Replace the duplicates with a reference to the variable. for (const Expr *E : DuplicateExpressions) { Replacements.push_back(RefactoringReplacement( - SourceRange(SM.getSpellingLoc(E->getLocStart()), - getPreciseTokenLocEnd(SM.getSpellingLoc(E->getLocEnd()), SM, + SourceRange(SM.getSpellingLoc(E->getBeginLoc()), + getPreciseTokenLocEnd(SM.getSpellingLoc(E->getEndLoc()), SM, Context.getLangOpts())), Name, CreatedSymbol, /*NameOffset=*/llvm::makeArrayRef(unsigned(0)))); diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp index a733add982ed6..5a8c2dc39a432 100644 --- a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp @@ -113,7 +113,7 @@ class ExtractedVariableInsertionLocFinder if (StmtReachabilityChecker::areAllExpressionsReachableFrom( I->first, Expressions) && I->second) { - Loc = I->second->getLocStart(); + Loc = I->second->getBeginLoc(); break; } } diff --git a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp index f3f743b9c4f18..2ddd5f95922d7 100644 --- a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp +++ b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp @@ -161,7 +161,7 @@ static SourceLocation findInsertionLocationForMethodsFromAbstractClass( for (const CXXMethodDecl *OM : M->overridden_methods()) { OM = OM->getCanonicalDecl(); if (OM->getLexicalDeclContext() == AbstractClass) { - SourceLocation EndLoc = M->getLocEnd(); + SourceLocation EndLoc = M->getEndLoc(); if (EndLoc.isMacroID()) EndLoc = SM.getExpansionRange(EndLoc).getEnd(); if (Loc.isInvalid()) @@ -226,7 +226,7 @@ FillInMissingMethodStubsFromAbstractClassesOperation::perform( std::string InsertionGroupOSStr; llvm::raw_string_ostream InsertionGroupOS(InsertionGroupOSStr); - SourceLocation InsertionLoc = Class->getLocEnd(); + SourceLocation InsertionLoc = Class->getEndLoc(); const CXXRecordDecl *CurrentAbstractClass = nullptr; SourceLocation CurrentGroupInsertionLoc; for (const auto &I : llvm::enumerate(MissingMethods)) { diff --git a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp index 5ce39552876ab..cea4b3c6bc4c5 100644 --- a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -169,14 +169,14 @@ RefactoringOperationResult clang::tooling::initiateIfSwitchConversionOperation( // Find the ranges in which initiation can be performed and verify that the // ifs don't have any initialization expressions or condition variables. SmallVector<SourceRange, 4> Ranges; - SourceLocation RangeStart = If->getLocStart(); + SourceLocation RangeStart = If->getBeginLoc(); const IfStmt *CurrentIf = If; const SourceManager &SM = Context.getSourceManager(); while (true) { const Stmt *Then = CurrentIf->getThen(); Ranges.emplace_back(RangeStart, findLastLocationOfSourceConstruct( - CurrentIf->getCond()->getLocEnd(), Then, SM)); + CurrentIf->getCond()->getEndLoc(), Then, SM)); const auto *Else = CurrentIf->getElse(); if (!Else) break; @@ -303,7 +303,7 @@ struct CasePlacement { CasePlacement(const IfStmt *If, const SourceManager &SM, bool AreBracesNeeded) { CaseStartLoc = SM.getSpellingLoc(isa<CompoundStmt>(If->getThen()) - ? If->getThen()->getLocEnd() + ? If->getThen()->getEndLoc() : If->getElseLoc()); SourceLocation BodyEndLoc = findLastNonCompoundLocation(If->getThen()); NeedsNewLine = BodyEndLoc.isValid() @@ -345,17 +345,17 @@ addCaseReplacements(const IfStmt *If, const CasePlacement &CaseInfo, assert(!CaseValues.empty()); Replacements.emplace_back( SourceRange(CaseInfo.CaseStartLoc, - SM.getSpellingLoc(CaseValues[0]->getLocStart())), + SM.getSpellingLoc(CaseValues[0]->getBeginLoc())), CaseInfo.getCaseReplacementString()); SourceLocation PrevCaseEnd = getPreciseTokenLocEnd( - SM.getSpellingLoc(CaseValues[0]->getLocEnd()), SM, LangOpts); + SM.getSpellingLoc(CaseValues[0]->getEndLoc()), SM, LangOpts); for (const Expr *CaseValue : llvm::makeArrayRef(CaseValues).drop_front()) { Replacements.emplace_back( - SourceRange(PrevCaseEnd, SM.getSpellingLoc(CaseValue->getLocStart())), + SourceRange(PrevCaseEnd, SM.getSpellingLoc(CaseValue->getBeginLoc())), StringRef(":\ncase ")); PrevCaseEnd = getPreciseTokenLocEnd( - SM.getSpellingLoc(CaseValue->getLocEnd()), SM, LangOpts); + SM.getSpellingLoc(CaseValue->getEndLoc()), SM, LangOpts); } AreBracesNeeded = areBracesNeeded(If->getThen()); @@ -365,12 +365,12 @@ addCaseReplacements(const IfStmt *If, const CasePlacement &CaseInfo, SourceRange( PrevCaseEnd, getPreciseTokenLocEnd( - SM.getSpellingLoc(If->getThen()->getLocStart()), SM, LangOpts)), + SM.getSpellingLoc(If->getThen()->getBeginLoc()), SM, LangOpts)), ColonReplacement); } else { // Find the location of the if's ')' SourceLocation End = findClosingParenLocEnd( - SM.getSpellingLoc(If->getCond()->getLocEnd()), SM, LangOpts); + SM.getSpellingLoc(If->getCond()->getEndLoc()), SM, LangOpts); if (!End.isValid()) return llvm::make_error<RefactoringOperationError>( "couldn't find the location of ')'"); @@ -392,14 +392,14 @@ IfSwitchConversionOperation::perform(ASTContext &Context, // should be preserved. const Expr *LHS = getConditionFirstLHS(If->getCond()); assert(LHS && "Missing == expression"); - Replacements.emplace_back(SourceRange(SM.getSpellingLoc(If->getLocStart()), - SM.getSpellingLoc(LHS->getLocStart())), + Replacements.emplace_back(SourceRange(SM.getSpellingLoc(If->getBeginLoc()), + SM.getSpellingLoc(LHS->getBeginLoc())), StringRef("switch (")); bool AreBracesNeeded = false; if (auto Error = addCaseReplacements( If, CasePlacement(getPreciseTokenLocEnd( - SM.getSpellingLoc(LHS->getLocEnd()), SM, LangOpts)), + SM.getSpellingLoc(LHS->getEndLoc()), SM, LangOpts)), AreBracesNeeded, Replacements, SM, LangOpts)) return std::move(Error); @@ -422,7 +422,7 @@ IfSwitchConversionOperation::perform(ASTContext &Context, AreBracesNeeded = areBracesNeeded(Else); SourceLocation EndLoc = getPreciseTokenLocEnd( - SM.getSpellingLoc(isa<CompoundStmt>(Else) ? Else->getLocStart() + SM.getSpellingLoc(isa<CompoundStmt>(Else) ? Else->getBeginLoc() : CurrentIf->getElseLoc()), SM, LangOpts); Replacements.emplace_back(SourceRange(DefaultInfo.CaseStartLoc, EndLoc), @@ -438,7 +438,7 @@ IfSwitchConversionOperation::perform(ASTContext &Context, std::string TerminatingReplacement; llvm::raw_string_ostream OS(TerminatingReplacement); if (!isa<CompoundStmt>(LastBody)) { - TerminatingReplacementLoc = LastBody->getLocEnd(); + TerminatingReplacementLoc = LastBody->getEndLoc(); // Try to adjust the location in order to preserve any trailing comments on // the last line of the last body. if (!TerminatingReplacementLoc.isMacroID()) @@ -450,7 +450,7 @@ IfSwitchConversionOperation::perform(ASTContext &Context, if (AreBracesNeeded) OS << "\n}"; } else { - TerminatingReplacementLoc = LastBody->getLocEnd(); + TerminatingReplacementLoc = LastBody->getEndLoc(); if (IsLastBreakNeeded) OS << "break;\n"; if (AreBracesNeeded) diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp index 5d0675bb64f50..a3bbbfa42a036 100644 --- a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -187,7 +187,7 @@ ImplementDeclaredCXXMethodsOperation::perform( void ImplementDeclaredCXXMethodsOperation::addInlineBody( const CXXMethodDecl *MD, const ASTContext &Context, std::vector<RefactoringReplacement> &Replacements) { - SourceLocation EndLoc = MD->getLocEnd(); + SourceLocation EndLoc = MD->getEndLoc(); SourceRange SemiRange = getRangeOfNextToken( EndLoc, tok::semi, Context.getSourceManager(), Context.getLangOpts()); if (SemiRange.isValid()) { @@ -238,7 +238,7 @@ ImplementDeclaredCXXMethodsOperation::runInImplementationAST( if (const FunctionDecl *MD = M->getDefinition()) { if (!MD->isOutOfLine()) continue; - SourceLocation Loc = SM.getExpansionLoc(MD->getLocStart()); + SourceLocation Loc = SM.getExpansionLoc(MD->getBeginLoc()); if (SM.getFileID(Loc) == File) DefinedOutOfLineMethods.push_back(cast<CXXMethodDecl>(MD)); } @@ -254,7 +254,7 @@ ImplementDeclaredCXXMethodsOperation::runInImplementationAST( NestedNameSpecifier *NamePrefix = nullptr; if (DefinedOutOfLineMethods.empty()) { const RecordDecl *OutermostRecord = findOutermostRecord(Class); - InsertionLoc = SM.getExpansionRange(OutermostRecord->getLocEnd()).getEnd(); + InsertionLoc = SM.getExpansionRange(OutermostRecord->getEndLoc()).getEnd(); if (SM.getFileID(InsertionLoc) == File) { // We can insert right after the class. Compute the appropriate // qualification. @@ -297,7 +297,7 @@ ImplementDeclaredCXXMethodsOperation::runInImplementationAST( } else { // Insert at the end of the defined methods. for (const CXXMethodDecl *M : DefinedOutOfLineMethods) { - SourceLocation EndLoc = SM.getExpansionRange(M->getLocEnd()).getEnd(); + SourceLocation EndLoc = SM.getExpansionRange(M->getEndLoc()).getEnd(); if (InsertionLoc.isInvalid() || SM.isBeforeInTranslationUnit(InsertionLoc, EndLoc)) { InsertionLoc = EndLoc; @@ -437,7 +437,7 @@ ImplementDeclaredObjCMethodsOperation::runInImplementationAST( OS << StringRef(MethodDeclarations[I.index()]).drop_back(); // Drop the ';' OS << " { \n <#code#>;\n}\n\n"; } - SourceLocation InsertionLoc = ImplementationContainer->getLocEnd(); + SourceLocation InsertionLoc = ImplementationContainer->getEndLoc(); Replacements.push_back(RefactoringReplacement( SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str()))); diff --git a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp index 08cf41dbbb1c1..3bbffefea7ad7 100644 --- a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp +++ b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp @@ -72,11 +72,11 @@ LocalizeObjCStringLiteralOperation::perform(ASTContext &Context, // TODO: New API: Replace by something like Node.wrap("NSLocalizedString(", ", // @""") SourceLocation LocStart = - Context.getSourceManager().getSpellingLoc(E->getLocStart()); + Context.getSourceManager().getSpellingLoc(E->getBeginLoc()); Replacements.emplace_back(SourceRange(LocStart, LocStart), StringRef("NSLocalizedString(")); SourceLocation LocEnd = getPreciseTokenLocEnd( - Context.getSourceManager().getSpellingLoc(E->getLocEnd()), + Context.getSourceManager().getSpellingLoc(E->getEndLoc()), Context.getSourceManager(), Context.getLangOpts()); Replacements.emplace_back(SourceRange(LocEnd, LocEnd), StringRef(", @\"\")")); return std::move(Replacements); diff --git a/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp b/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp index 0a0c4cdc9d3b5..7c55e653e9afb 100644 --- a/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp +++ b/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp @@ -18,7 +18,7 @@ namespace tooling { SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd, const Stmt *Body, const SourceManager &SM) { - SourceLocation BodyStart = SM.getSpellingLoc(Body->getLocStart()); + SourceLocation BodyStart = SM.getSpellingLoc(Body->getBeginLoc()); unsigned BodyLine = SM.getSpellingLineNumber(BodyStart); unsigned HeaderLine = SM.getSpellingLineNumber(HeaderEnd); @@ -40,7 +40,7 @@ SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart, const SourceManager &SM) { if (!isa<CompoundStmt>(PreviousBody)) return HeaderStart; - SourceLocation BodyEnd = SM.getSpellingLoc(PreviousBody->getLocEnd()); + SourceLocation BodyEnd = SM.getSpellingLoc(PreviousBody->getEndLoc()); unsigned BodyLine = SM.getSpellingLineNumber(BodyEnd); unsigned HeaderLine = SM.getSpellingLineNumber(HeaderStart); if (BodyLine >= HeaderLine) @@ -88,8 +88,8 @@ SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind, SourceLocation findLastNonCompoundLocation(const Stmt *S) { const auto *CS = dyn_cast<CompoundStmt>(S); if (!CS) - return S->getLocEnd(); - return CS->body_back() ? CS->body_back()->getLocEnd() : SourceLocation(); + return S->getEndLoc(); + return CS->body_back() ? CS->body_back()->getEndLoc() : SourceLocation(); } bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, diff --git a/clang/lib/Tooling/Refactor/USRFinder.cpp b/clang/lib/Tooling/Refactor/USRFinder.cpp index 92f16d3141bec..3768dcbaaef96 100644 --- a/clang/lib/Tooling/Refactor/USRFinder.cpp +++ b/clang/lib/Tooling/Refactor/USRFinder.cpp @@ -467,7 +467,7 @@ static bool overridesSystemMethod(const ObjCMethodDecl *MD, SmallVector<const ObjCMethodDecl *, 8> Overrides; MD->getOverriddenMethods(Overrides); for (const auto *Override : Overrides) { - SourceLocation Loc = Override->getLocStart(); + SourceLocation Loc = Override->getBeginLoc(); if (Loc.isValid()) { if (SM.getFileCharacteristic(Loc) != SrcMgr::C_User) return true; @@ -622,7 +622,7 @@ const NamedDecl *getNamedDeclAt(const ASTContext &Context, } } // Declarations with invalid locations are probably implicit. - if (ND->getLocStart().isInvalid()) + if (ND->getBeginLoc().isInvalid()) return nullptr; // Declarations in system headers can't be renamed. auto CheckSystemLoc = [&](SourceLocation Loc) -> bool { @@ -633,26 +633,26 @@ const NamedDecl *getNamedDeclAt(const ASTContext &Context, } return false; }; - if (CheckSystemLoc(ND->getLocStart())) + if (CheckSystemLoc(ND->getBeginLoc())) return nullptr; if (const auto *TD = dyn_cast<TypedefNameDecl>(ND)) { if (const TypedefNameDecl *CTD = TD->getCanonicalDecl()) { - if (CheckSystemLoc(CTD->getLocStart())) + if (CheckSystemLoc(CTD->getBeginLoc())) return nullptr; } } else if (const auto *TD = dyn_cast<TagDecl>(ND)) { if (const TagDecl *CTD = TD->getCanonicalDecl()) { - if (CheckSystemLoc(CTD->getLocStart())) + if (CheckSystemLoc(CTD->getBeginLoc())) return nullptr; } } else if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { if (const FunctionDecl *CFD = FD->getCanonicalDecl()) { - if (CheckSystemLoc(CFD->getLocStart())) + if (CheckSystemLoc(CFD->getBeginLoc())) return nullptr; } } else if (const auto *VD = dyn_cast<VarDecl>(ND)) { if (const VarDecl *CVD = VD->getCanonicalDecl()) { - if (CheckSystemLoc(CVD->getLocStart())) + if (CheckSystemLoc(CVD->getBeginLoc())) return nullptr; } } diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index 5635096d56c5a..bf60509801990 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -1294,12 +1294,12 @@ clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action) { if (const Stmt *S = Operation->getTransformedStmt()) { SourceRange Range = S->getSourceRange(); if (const Stmt *Last = Operation->getLastTransformedStmt()) - Range.setEnd(Last->getLocEnd()); + Range.setEnd(Last->getEndLoc()); return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range); } else if (const Decl *D = Operation->getTransformedDecl()) { SourceRange Range = D->getSourceRange(); if (const Decl *Last = Operation->getLastTransformedDecl()) - Range.setEnd(Last->getLocEnd()); + Range.setEnd(Last->getEndLoc()); return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range); } } From 3248e3233aa10f04f5f655f890acb81aed2b1952 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 16 May 2018 14:14:38 -0700 Subject: [PATCH 295/582] [APINotes] API-noted attributes should not be implicit Even though they don't have valid source locations, they /are/ a developer-specified part of the AST, and so it makes sense to include them in the printed representation. This also brings back the tests that were disabled in 8ab16db. Note that the SwiftVersionedAttr and SwiftVersionedRemovalAttr attributes are still considered implicit, since they really are part of the compiler infrastructure and not something the developer chose to put in themselves. (They also don't have a printed representation anyway.) rdar://problem/40296113 apple-llvm-split-commit: 22b3806ecc80955294a5f992a63f341a399ad0a7 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 76 +++++++----- clang/test/APINotes/properties.m | 20 +-- clang/test/APINotes/retain-count-convention.m | 4 - clang/test/APINotes/types.m | 4 - clang/test/APINotes/versioned-multi.c | 4 - clang/test/APINotes/versioned.m | 114 +++++++++--------- 6 files changed, 109 insertions(+), 113 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index d472f3509592a..b8fd01461fa49 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -209,7 +209,7 @@ static void handleAPINotedRetainCountAttribute(Sema &S, Decl *D, // The template argument has a default to make the "removal" case more // concise; it doesn't matter /which/ attribute is being removed. handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, [&] { - return A::CreateImplicit(S.Context); + return new (S.Context) A(SourceRange(), S.Context, /*SpellingIndex*/0); }, [](const Decl *D) -> Decl::attr_iterator { return llvm::find_if(D->attrs(), [](const Attr *next) -> bool { return isa<CFReturnsRetainedAttr>(next) || @@ -256,24 +256,25 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (info.Unavailable) { handleAPINotedAttribute<UnavailableAttr>(S, D, true, metadata, [&] { - return UnavailableAttr::CreateImplicit(S.Context, + return new (S.Context) UnavailableAttr(SourceRange(), S.Context, CopyString(S.Context, - info.UnavailableMsg)); + info.UnavailableMsg), + /*SpellingIndex*/0); }); } if (info.UnavailableInSwift) { handleAPINotedAttribute<AvailabilityAttr>(S, D, true, metadata, [&] { - return AvailabilityAttr::CreateImplicit( - S.Context, - &S.Context.Idents.get("swift"), - VersionTuple(), - VersionTuple(), - VersionTuple(), - /*Unavailable=*/true, - CopyString(S.Context, info.UnavailableMsg), - /*Strict=*/false, - /*Replacement=*/StringRef()); + return new (S.Context) AvailabilityAttr(SourceRange(), S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + CopyString(S.Context, + info.UnavailableMsg), + /*Strict=*/false, + /*Replacement=*/StringRef(), + /*SpellingIndex*/0); }, [](const Decl *decl) { return llvm::find_if(decl->attrs(), [](const Attr *next) -> bool { @@ -292,7 +293,8 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (auto swiftPrivate = info.isSwiftPrivate()) { handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, metadata, [&] { - return SwiftPrivateAttr::CreateImplicit(S.Context); + return new (S.Context) SwiftPrivateAttr(SourceRange(), S.Context, + /*SpellingIndex*/0); }); } @@ -307,9 +309,10 @@ static void ProcessAPINotes(Sema &S, Decl *D, return nullptr; } - return SwiftNameAttr::CreateImplicit(S.Context, + return new (S.Context) SwiftNameAttr(SourceRange(), S.Context, CopyString(S.Context, - info.SwiftName)); + info.SwiftName), + /*SpellingIndex*/0); }); } } @@ -321,9 +324,10 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (auto swiftBridge = info.getSwiftBridge()) { handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(), metadata, [&] { - return SwiftBridgeAttr::CreateImplicit(S.Context, + return new (S.Context) SwiftBridgeAttr(SourceRange(), S.Context, CopyString(S.Context, - *swiftBridge)); + *swiftBridge), + /*SpellingIndex*/0); }); } @@ -331,9 +335,9 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (auto nsErrorDomain = info.getNSErrorDomain()) { handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(), metadata, [&] { - return NSErrorDomainAttr::CreateImplicit( - S.Context, - &S.Context.Idents.get(*nsErrorDomain)); + return new (S.Context) NSErrorDomainAttr( + SourceRange(), S.Context, &S.Context.Idents.get(*nsErrorDomain), + /*SpellingIndex*/0); }); } @@ -414,7 +418,8 @@ static void ProcessAPINotes(Sema &S, ParmVarDecl *D, // noescape if (auto noescape = info.isNoEscape()) { handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, metadata, [&] { - return NoEscapeAttr::CreateImplicit(S.Context); + return new (S.Context) NoEscapeAttr(SourceRange(), S.Context, + /*SpellingIndex*/0); }); } @@ -447,7 +452,8 @@ static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D, *asAccessors, metadata, [&] { - return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context); + return new (S.Context) SwiftImportPropertyAsAccessorsAttr( + SourceRange(), S.Context, /*SpellingIndex*/0); }); } } @@ -593,7 +599,9 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { IFace->setHasDesignatedInitializers(); } - return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context); + return new (S.Context) ObjCDesignatedInitializerAttr(SourceRange(), + S.Context, + /*SpellingIndex*/0); }); } @@ -622,14 +630,16 @@ static void ProcessAPINotes(Sema &S, TagDecl *D, kind = EnumExtensibilityAttr::Closed; break; } - return EnumExtensibilityAttr::CreateImplicit(S.Context, kind); + return new (S.Context) EnumExtensibilityAttr(SourceRange(), S.Context, + kind, /*SpellingIndex*/0); }); } if (auto flagEnum = info.isFlagEnum()) { handleAPINotedAttribute<FlagEnumAttr>(S, D, flagEnum.getValue(), metadata, [&] { - return FlagEnumAttr::CreateImplicit(S.Context); + return new (S.Context) FlagEnumAttr(SourceRange(), S.Context, + /*SpellingIndex*/0); }); } @@ -662,10 +672,9 @@ static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, kind = SwiftNewtypeAttr::NK_Enum; break; } - return SwiftNewtypeAttr::CreateImplicit( - S.Context, - SwiftNewtypeAttr::GNU_swift_wrapper, - kind); + return new (S.Context) SwiftNewtypeAttr( + SourceRange(), S.Context, kind, + SwiftNewtypeAttr::GNU_swift_wrapper); }); } @@ -691,14 +700,17 @@ static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) { handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(S, D, *asNonGeneric, metadata, [&] { - return SwiftImportAsNonGenericAttr::CreateImplicit(S.Context); + return new (S.Context) SwiftImportAsNonGenericAttr(SourceRange(), + S.Context, + /*SpellingIndex*/0); }); } if (auto objcMembers = info.getSwiftObjCMembers()) { handleAPINotedAttribute<SwiftObjCMembersAttr>(S, D, *objcMembers, metadata, [&] { - return SwiftObjCMembersAttr::CreateImplicit(S.Context); + return new (S.Context) SwiftObjCMembersAttr(SourceRange(), S.Context, + /*SpellingIndex*/0); }); } diff --git a/clang/test/APINotes/properties.m b/clang/test/APINotes/properties.m index 0b3d24482c073..60423633e9a64 100644 --- a/clang/test/APINotes/properties.m +++ b/clang/test/APINotes/properties.m @@ -9,36 +9,36 @@ @import VersionedKit; // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnly 'id' -// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClass 'id' -// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id' -// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}} -// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id' -// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}} -// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id' // CHECK-3-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} -// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} // CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 'id' // CHECK-3-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} -// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <<invalid sloc>> // CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} // CHECK-NOT: Attr diff --git a/clang/test/APINotes/retain-count-convention.m b/clang/test/APINotes/retain-count-convention.m index 8c830f5be7023..46fb90caa6932 100644 --- a/clang/test/APINotes/retain-count-convention.m +++ b/clang/test/APINotes/retain-count-convention.m @@ -2,10 +2,6 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fdisable-module-hash -fsyntax-only -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s -// REQUIRES: rdar40296113 -// This test relies on -ast-print including implicit attributes, but it no -// longer does that as of https://reviews.llvm.org/D46894. - #import <SimpleKit/SimpleKit.h> // CHECK: void *getCFOwnedToUnowned() __attribute__((cf_returns_not_retained)); diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m index 5f9ef4a039c3c..133d504713d76 100644 --- a/clang/test/APINotes/types.m +++ b/clang/test/APINotes/types.m @@ -2,10 +2,6 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -Wno-private-module -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify // RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s -// REQUIRES: rdar40296113 -// This test relies on -ast-print including implicit attributes, but it no -// longer does that as of https://reviews.llvm.org/D46894. - #import <SomeKit/SomeKit.h> #import <SimpleKit/SimpleKit.h> diff --git a/clang/test/APINotes/versioned-multi.c b/clang/test/APINotes/versioned-multi.c index 8b20160dfd6a6..48c51fd932e17 100644 --- a/clang/test/APINotes/versioned-multi.c +++ b/clang/test/APINotes/versioned-multi.c @@ -14,10 +14,6 @@ // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned5 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=5 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned5/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-5 %s -// REQUIRES: rdar40296113 -// This test relies on -ast-print including implicit attributes, but it no -// longer does that as of https://reviews.llvm.org/D46894. - #import <VersionedKit/VersionedKit.h> // CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4; diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 069cc03680bca..db5a9d3b5f1e3 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -10,10 +10,6 @@ // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s -// REQUIRES: rdar40296113 -// This test relies on -ast-print including implicit attributes, but it no -// longer does that as of https://reviews.llvm.org/D46894. - #import <VersionedKit/VersionedKit.h> // CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); @@ -22,10 +18,10 @@ // CHECK-DUMP-LABEL: Dumping moveToPointDUMP // CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)" -// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> "moveTo(a:b:)" // CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> "moveTo(a:b:)" // CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping unversionedRenameDUMP @@ -36,9 +32,9 @@ // CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping TestGenericDUMP -// CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} Implicit +// CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} <<invalid sloc>> // CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} Implicit +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} <<invalid sloc>> // CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping Swift3RenamedOnlyDUMP @@ -46,7 +42,7 @@ // CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} IsReplacedByActive{{$}} // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Name" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Name" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> "SpecialSwift3Name" // CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping Swift3RenamedAlsoDUMP @@ -56,7 +52,7 @@ // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> "Swift4Name" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Also" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> "SpecialSwift3Also" // CHECK-DUMP-NOT: Attr // CHECK-DUMP-LABEL: Dumping Swift4RenamedDUMP @@ -64,7 +60,7 @@ // CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 4 {{[0-9]+}} IsReplacedByActive{{$}} // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift4Name" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 4{{$}} -// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift4Name" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <<invalid sloc>> "SpecialSwift4Name" // CHECK-DUMP-NOT: Attr // CHECK-DUMP-NOT: Dumping @@ -76,9 +72,9 @@ // CHECK-UNVERSIONED: typedef double MyDoubleWrapper __attribute__((swift_wrapper("struct"))); -// CHECK-UNVERSIONED: enum MyErrorCode { +// CHECK-UNVERSIONED: enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorCode { // CHECK-UNVERSIONED-NEXT: MyErrorCodeFailed = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((ns_error_domain(MyErrorDomain))); +// CHECK-UNVERSIONED-NEXT: }; // CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType"))) // CHECK-UNVERSIONED: @interface MyReferenceType @@ -99,93 +95,93 @@ // CHECK-VERSIONED-NOT: __attribute__((swift_objc_members) // CHECK-VERSIONED: @interface TestProperties -// CHECK-UNVERSIONED-LABEL: enum FlagEnum { +// CHECK-UNVERSIONED-LABEL: enum __attribute__((flag_enum)) FlagEnum { // CHECK-UNVERSIONED-NEXT: FlagEnumA = 1, // CHECK-UNVERSIONED-NEXT: FlagEnumB = 2 -// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum)); -// CHECK-UNVERSIONED-LABEL: enum NewlyFlagEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((flag_enum)) NewlyFlagEnum { // CHECK-UNVERSIONED-NEXT: NewlyFlagEnumA = 1, // CHECK-UNVERSIONED-NEXT: NewlyFlagEnumB = 2 -// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum)); -// CHECK-UNVERSIONED-LABEL: enum APINotedFlagEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((flag_enum)) APINotedFlagEnum { // CHECK-UNVERSIONED-NEXT: APINotedFlagEnumA = 1, // CHECK-UNVERSIONED-NEXT: APINotedFlagEnumB = 2 -// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum)); -// CHECK-UNVERSIONED-LABEL: enum OpenEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) OpenEnum { // CHECK-UNVERSIONED-NEXT: OpenEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-UNVERSIONED-LABEL: enum NewlyOpenEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) NewlyOpenEnum { // CHECK-UNVERSIONED-NEXT: NewlyOpenEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-UNVERSIONED-LABEL: enum NewlyClosedEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) NewlyClosedEnum { // CHECK-UNVERSIONED-NEXT: NewlyClosedEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); -// CHECK-UNVERSIONED-LABEL: enum ClosedToOpenEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) ClosedToOpenEnum { // CHECK-UNVERSIONED-NEXT: ClosedToOpenEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-UNVERSIONED-LABEL: enum OpenToClosedEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) OpenToClosedEnum { // CHECK-UNVERSIONED-NEXT: OpenToClosedEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); -// CHECK-UNVERSIONED-LABEL: enum APINotedOpenEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) APINotedOpenEnum { // CHECK-UNVERSIONED-NEXT: APINotedOpenEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-UNVERSIONED-LABEL: enum APINotedClosedEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) APINotedClosedEnum { // CHECK-UNVERSIONED-NEXT: APINotedClosedEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-UNVERSIONED-NEXT: }; -// CHECK-VERSIONED-LABEL: enum FlagEnum { +// CHECK-VERSIONED-LABEL: enum __attribute__((flag_enum)) FlagEnum { // CHECK-VERSIONED-NEXT: FlagEnumA = 1, // CHECK-VERSIONED-NEXT: FlagEnumB = 2 -// CHECK-VERSIONED-NEXT: } __attribute__((flag_enum)); +// CHECK-VERSIONED-NEXT: }; // CHECK-VERSIONED-LABEL: enum NewlyFlagEnum { // CHECK-VERSIONED-NEXT: NewlyFlagEnumA = 1, // CHECK-VERSIONED-NEXT: NewlyFlagEnumB = 2 // CHECK-VERSIONED-NEXT: }; -// CHECK-VERSIONED-LABEL: enum APINotedFlagEnum { +// CHECK-VERSIONED-LABEL: enum __attribute__((flag_enum)) APINotedFlagEnum { // CHECK-VERSIONED-NEXT: APINotedFlagEnumA = 1, // CHECK-VERSIONED-NEXT: APINotedFlagEnumB = 2 -// CHECK-VERSIONED-NEXT: } __attribute__((flag_enum)); -// CHECK-VERSIONED-LABEL: enum OpenEnum { +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) OpenEnum { // CHECK-VERSIONED-NEXT: OpenEnumA = 1 -// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); +// CHECK-VERSIONED-NEXT: }; // CHECK-VERSIONED-LABEL: enum NewlyOpenEnum { // CHECK-VERSIONED-NEXT: NewlyOpenEnumA = 1 // CHECK-VERSIONED-NEXT: }; // CHECK-VERSIONED-LABEL: enum NewlyClosedEnum { // CHECK-VERSIONED-NEXT: NewlyClosedEnumA = 1 // CHECK-VERSIONED-NEXT: }; -// CHECK-VERSIONED-LABEL: enum ClosedToOpenEnum { +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) ClosedToOpenEnum { // CHECK-VERSIONED-NEXT: ClosedToOpenEnumA = 1 -// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); -// CHECK-VERSIONED-LABEL: enum OpenToClosedEnum { +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) OpenToClosedEnum { // CHECK-VERSIONED-NEXT: OpenToClosedEnumA = 1 -// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-VERSIONED-LABEL: enum APINotedOpenEnum { +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) APINotedOpenEnum { // CHECK-VERSIONED-NEXT: APINotedOpenEnumA = 1 -// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-VERSIONED-LABEL: enum APINotedClosedEnum { +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) APINotedClosedEnum { // CHECK-VERSIONED-NEXT: APINotedClosedEnumA = 1 -// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-VERSIONED-NEXT: }; // These don't actually have versioned information, so we just check them once. -// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFEnum { +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) SoonToBeCFEnum { // CHECK-UNVERSIONED-NEXT: SoonToBeCFEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) SoonToBeNSEnum { // CHECK-UNVERSIONED-NEXT: SoonToBeNSEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))); -// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFOptions { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)) SoonToBeCFOptions { // CHECK-UNVERSIONED-NEXT: SoonToBeCFOptionsA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)); -// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSOptions { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)) SoonToBeNSOptions { // CHECK-UNVERSIONED-NEXT: SoonToBeNSOptionsA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)); -// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFClosedEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) SoonToBeCFClosedEnum { // CHECK-UNVERSIONED-NEXT: SoonToBeCFClosedEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); -// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSClosedEnum { +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) SoonToBeNSClosedEnum { // CHECK-UNVERSIONED-NEXT: SoonToBeNSClosedEnumA = 1 -// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed"))); +// CHECK-UNVERSIONED-NEXT: }; // CHECK-UNVERSIONED-LABEL: enum UndoAllThatHasBeenDoneToMe { // CHECK-UNVERSIONED-NEXT: UndoAllThatHasBeenDoneToMeA = 1 // CHECK-UNVERSIONED-NEXT: }; From 477b05d2efd495145aa0ab90e696482b1a17ebb5 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 14 Mar 2018 15:26:17 -0700 Subject: [PATCH 296/582] Sink PrettyDeclStackTrace down to the AST library ...and add some very basic stack trace entries for module building. This would have helped track down rdar://problem/38434694 sooner. apple-llvm-split-commit: 28ebedd9512d2fb44fb3b73545022420ba4516fe apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInstance.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 5964807d2d7d6..7b43947a2f124 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1168,6 +1168,11 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, llvm::CrashRecoveryContext CRC; CRC.RunSafelyOnThread( [&]() { + SmallString<64> CrashInfoMessage("While building module for '"); + CrashInfoMessage += ModuleName; + CrashInfoMessage += "'"; + llvm::PrettyStackTraceString CrashInfo(CrashInfoMessage.c_str()); + // FIXME: I have no idea what the best way to do this is, but it's // probably not this. Interfaces changed upstream. std::unique_ptr<FrontendAction> Action( From 671ad50789d4484a73532f1c07a6d2dbaa9977fa Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 15 Aug 2018 11:13:13 -0700 Subject: [PATCH 297/582] Re-remove problematic PrettyStackTrace entry added in r328276 (#203) This got lost in a merge at some point. Re-cherry-picked from r328286. apple-llvm-split-commit: 57bc4d9fb416fe3e877621e1ea68ed680a2e0d4c apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInstance.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 7b43947a2f124..5964807d2d7d6 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1168,11 +1168,6 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, llvm::CrashRecoveryContext CRC; CRC.RunSafelyOnThread( [&]() { - SmallString<64> CrashInfoMessage("While building module for '"); - CrashInfoMessage += ModuleName; - CrashInfoMessage += "'"; - llvm::PrettyStackTraceString CrashInfo(CrashInfoMessage.c_str()); - // FIXME: I have no idea what the best way to do this is, but it's // probably not this. Interfaces changed upstream. std::unique_ptr<FrontendAction> Action( From 292e9ac4f39756855184d9586b98516b05eb7dd0 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Fri, 17 Aug 2018 04:49:33 -0400 Subject: [PATCH 298/582] Support swifterror in coroutine lowering. The support for swifterror allocas should work in all lowerings. The support for swifterror arguments only really works in a lowering with prototypes where you can ensure that the prototype also has a swifterror argument; I'm not really sure how it could possibly be made to work in the switch lowering. apple-llvm-split-commit: 83980d7d6933feffced1b9bcbde63cf32ebcb3f9 apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 166 ++++++++++++++++++ llvm/lib/Transforms/Coroutines/CoroInternal.h | 1 + llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 71 ++++++++ .../Transforms/Coroutines/coro-swifterror.ll | 143 +++++++++++++++ 4 files changed, 381 insertions(+) create mode 100644 llvm/test/Transforms/Coroutines/coro-swifterror.ll diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp index 55fb9d051c4e9..30843989c6d6a 100644 --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/circular_raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/PromoteMemToReg.h" using namespace llvm; @@ -1075,11 +1076,176 @@ static Instruction *lowerNonLocalAlloca(CoroAllocaAllocInst *AI, return cast<Instruction>(Alloc); } +/// Get the current swifterror value. +static Value *emitGetSwiftErrorValue(IRBuilder<> &Builder, Type *ValueTy, + coro::Shape &Shape) { + // Make a fake function pointer as a sort of intrinsic. + auto FnTy = FunctionType::get(ValueTy, {}, false); + auto Fn = ConstantPointerNull::get(FnTy->getPointerTo()); + + auto Call = Builder.CreateCall(Fn, {}); + Shape.SwiftErrorOps.push_back(Call); + + return Call; +} + +/// Set the given value as the current swifterror value. +/// +/// Returns a slot that can be used as a swifterror slot. +static Value *emitSetSwiftErrorValue(IRBuilder<> &Builder, Value *V, + coro::Shape &Shape) { + // Make a fake function pointer as a sort of intrinsic. + auto FnTy = FunctionType::get(V->getType()->getPointerTo(), + {V->getType()}, false); + auto Fn = ConstantPointerNull::get(FnTy->getPointerTo()); + + auto Call = Builder.CreateCall(Fn, { V }); + Shape.SwiftErrorOps.push_back(Call); + + return Call; +} + +/// Set the swifterror value from the given alloca before a call, +/// then put in back in the alloca afterwards. +/// +/// Returns an address that will stand in for the swifterror slot +/// until splitting. +static Value *emitSetAndGetSwiftErrorValueAround(Instruction *Call, + AllocaInst *Alloca, + coro::Shape &Shape) { + auto ValueTy = Alloca->getAllocatedType(); + IRBuilder<> Builder(Call); + + // Load the current value from the alloca and set it as the + // swifterror value. + auto ValueBeforeCall = Builder.CreateLoad(ValueTy, Alloca); + auto Addr = emitSetSwiftErrorValue(Builder, ValueBeforeCall, Shape); + + // Move to after the call. Since swifterror only has a guaranteed + // value on normal exits, we can ignore implicit and explicit unwind + // edges. + if (isa<CallInst>(Call)) { + Builder.SetInsertPoint(Call->getNextNode()); + } else { + auto Invoke = cast<InvokeInst>(Call); + Builder.SetInsertPoint(Invoke->getNormalDest()->getFirstNonPHIOrDbg()); + } + + // Get the current swifterror value and store it to the alloca. + auto ValueAfterCall = emitGetSwiftErrorValue(Builder, ValueTy, Shape); + Builder.CreateStore(ValueAfterCall, Alloca); + + return Addr; +} + +/// Eliminate a formerly-swifterror alloca by inserting the get/set +/// intrinsics and attempting to MemToReg the alloca away. +static void eliminateSwiftErrorAlloca(Function &F, AllocaInst *Alloca, + coro::Shape &Shape) { + for (auto UI = Alloca->use_begin(), UE = Alloca->use_end(); UI != UE; ) { + // We're likely changing the use list, so use a mutation-safe + // iteration pattern. + auto &Use = *UI; + ++UI; + + // swifterror values can only be used in very specific ways. + // We take advantage of that here. + auto User = Use.getUser(); + if (isa<LoadInst>(User) || isa<StoreInst>(User)) + continue; + + assert(isa<CallInst>(User) || isa<InvokeInst>(User)); + auto Call = cast<Instruction>(User); + + auto Addr = emitSetAndGetSwiftErrorValueAround(Call, Alloca, Shape); + + // Use the returned slot address as the call argument. + Use.set(Addr); + } + + // All the uses should be loads and stores now. + assert(isAllocaPromotable(Alloca)); +} + +/// "Eliminate" a swifterror argument by reducing it to the alloca case +/// and then loading and storing in the prologue and epilog. +/// +/// The argument keeps the swifterror flag. +static void eliminateSwiftErrorArgument(Function &F, Argument &Arg, + coro::Shape &Shape, + SmallVectorImpl<AllocaInst*> &AllocasToPromote) { + IRBuilder<> Builder(F.getEntryBlock().getFirstNonPHIOrDbg()); + + auto ArgTy = cast<PointerType>(Arg.getType()); + auto ValueTy = ArgTy->getElementType(); + + // Reduce to the alloca case: + + // Create an alloca and replace all uses of the arg with it. + auto Alloca = Builder.CreateAlloca(ValueTy, ArgTy->getAddressSpace()); + Arg.replaceAllUsesWith(Alloca); + + // Set an initial value in the alloca. swifterror is always null on entry. + auto InitialValue = Constant::getNullValue(ValueTy); + Builder.CreateStore(InitialValue, Alloca); + + // Find all the suspends in the function and save and restore around them. + for (auto Suspend : Shape.CoroSuspends) { + (void) emitSetAndGetSwiftErrorValueAround(Suspend, Alloca, Shape); + } + + // Find all the coro.ends in the function and restore the error value. + for (auto End : Shape.CoroEnds) { + Builder.SetInsertPoint(End); + auto FinalValue = Builder.CreateLoad(ValueTy, Alloca); + (void) emitSetSwiftErrorValue(Builder, FinalValue, Shape); + } + + // Now we can use the alloca logic. + AllocasToPromote.push_back(Alloca); + eliminateSwiftErrorAlloca(F, Alloca, Shape); +} + +/// Eliminate all problematic uses of swifterror arguments and allocas +/// from the function. We'll fix them up later when splitting the function. +static void eliminateSwiftError(Function &F, coro::Shape &Shape) { + SmallVector<AllocaInst*, 4> AllocasToPromote; + + // Look for a swifterror argument. + for (auto &Arg : F.args()) { + if (!Arg.hasSwiftErrorAttr()) continue; + + eliminateSwiftErrorArgument(F, Arg, Shape, AllocasToPromote); + break; + } + + // Look for swifterror allocas. + for (auto &Inst : F.getEntryBlock()) { + auto Alloca = dyn_cast<AllocaInst>(&Inst); + if (!Alloca || !Alloca->isSwiftError()) continue; + + // Clear the swifterror flag. + Alloca->setSwiftError(false); + + AllocasToPromote.push_back(Alloca); + eliminateSwiftErrorAlloca(F, Alloca, Shape); + } + + // If we have any allocas to promote, compute a dominator tree and + // promote them en masse. + if (!AllocasToPromote.empty()) { + DominatorTree DT(F); + PromoteMemToReg(AllocasToPromote, DT); + } +} + void coro::buildCoroutineFrame(Function &F, Shape &Shape) { // Lower coro.dbg.declare to coro.dbg.value, since we are going to rewrite // access to local variables. LowerDbgDeclare(F); + eliminateSwiftError(F, Shape); + if (Shape.ABI == coro::ABI::Switch && Shape.SwitchLowering.PromiseAlloca) { Shape.getSwitchCoroId()->clearPromise(); diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h index f1741f2fad2a3..9b2f03181b4fb 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -90,6 +90,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape { SmallVector<CoroEndInst *, 4> CoroEnds; SmallVector<CoroSizeInst *, 2> CoroSizes; SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends; + SmallVector<CallInst*, 2> SwiftErrorOps; // Field indexes for special fields in the switch lowering. struct SwitchFieldIndex { diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index da90d8477a450..e1f4a4d49d6d7 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -98,6 +98,7 @@ class CoroCloner { ValueToValueMapTy VMap; IRBuilder<> Builder; Value *NewFramePtr = nullptr; + Value *SwiftErrorSlot = nullptr; /// The active suspend instruction; meaningful only for continuation ABIs. AnyCoroSuspendInst *ActiveSuspend = nullptr; @@ -148,6 +149,7 @@ class CoroCloner { void replaceRetconSuspendUses(); void replaceCoroSuspends(); void replaceCoroEnds(); + void replaceSwiftErrorOps(); void handleFinalSuspend(); void maybeFreeContinuationStorage(); }; @@ -487,6 +489,68 @@ void CoroCloner::replaceCoroEnds() { } } +static void replaceSwiftErrorOps(Function &F, coro::Shape &Shape, + ValueToValueMapTy *VMap) { + Value *CachedSlot = nullptr; + auto getSwiftErrorSlot = [&](Type *ValueTy) -> Value * { + if (CachedSlot) { + assert(CachedSlot->getType()->getPointerElementType() == ValueTy && + "multiple swifterror slots in function with different types"); + return CachedSlot; + } + + // Check if the function has a swifterror argument. + for (auto &Arg : F.args()) { + if (Arg.isSwiftError()) { + CachedSlot = &Arg; + assert(Arg.getType()->getPointerElementType() == ValueTy && + "swifterror argument does not have expected type"); + return &Arg; + } + } + + // Create a swifterror alloca. + IRBuilder<> Builder(F.getEntryBlock().getFirstNonPHIOrDbg()); + auto Alloca = Builder.CreateAlloca(ValueTy); + Alloca->setSwiftError(true); + + CachedSlot = Alloca; + return Alloca; + }; + + for (CallInst *Op : Shape.SwiftErrorOps) { + auto MappedOp = VMap ? cast<CallInst>((*VMap)[Op]) : Op; + IRBuilder<> Builder(MappedOp); + + // If there are no arguments, this is a 'get' operation. + Value *MappedResult; + if (Op->getNumArgOperands() == 0) { + auto ValueTy = Op->getType(); + auto Slot = getSwiftErrorSlot(ValueTy); + MappedResult = Builder.CreateLoad(ValueTy, Slot); + } else { + assert(Op->getNumArgOperands() == 1); + auto Value = MappedOp->getArgOperand(0); + auto ValueTy = Value->getType(); + auto Slot = getSwiftErrorSlot(ValueTy); + Builder.CreateStore(Value, Slot); + MappedResult = Slot; + } + + MappedOp->replaceAllUsesWith(MappedResult); + MappedOp->eraseFromParent(); + } + + // If we're updating the original function, we've invalidated SwiftErrorOps. + if (VMap == nullptr) { + Shape.SwiftErrorOps.clear(); + } +} + +void CoroCloner::replaceSwiftErrorOps() { + ::replaceSwiftErrorOps(*NewF, Shape, &VMap); +} + void CoroCloner::replaceEntryBlock() { // In the original function, the AllocaSpillBlock is a block immediately // following the allocation of the frame object which defines GEPs for @@ -688,6 +752,9 @@ void CoroCloner::create() { // Handle suspends. replaceCoroSuspends(); + // Handle swifterror. + replaceSwiftErrorOps(); + // Remove coro.end intrinsics. replaceCoroEnds(); @@ -1291,6 +1358,10 @@ static void splitCoroutine(Function &F, CallGraph &CG, CallGraphSCC &SCC) { splitCoroutine(F, Shape, Clones); } + // Replace all the swifterror operations in the original function. + // This invalidates SwiftErrorOps in the Shape. + replaceSwiftErrorOps(F, Shape, nullptr); + removeCoroEnds(Shape, &CG); postSplitCleanup(F); diff --git a/llvm/test/Transforms/Coroutines/coro-swifterror.ll b/llvm/test/Transforms/Coroutines/coro-swifterror.ll new file mode 100644 index 0000000000000..c330137964692 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-swifterror.ll @@ -0,0 +1,143 @@ +; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s +target datalayout = "E-p:32:32" + +define i8* @f(i8* %buffer, i32 %n, i8** swifterror %errorslot) { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i1, i8**)* @f_prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + call void @print(i32 %n.val) + call void @maybeThrow(i8** swifterror %errorslot) + %errorload1 = load i8*, i8** %errorslot + call void @logError(i8* %errorload1) + %suspend_result = call { i1, i8** } (...) @llvm.coro.suspend.retcon.i1p0p0i8() + %unwind0 = extractvalue { i1, i8** } %suspend_result, 0 + br i1 %unwind0, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define i8* @f(i8* %buffer, i32 %n, i8** swifterror %errorslot) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: call void @print(i32 %n) +; TODO: figure out a way to eliminate this +; CHECK-NEXT: store i8* null, i8** %errorslot +; CHECK-NEXT: call void @maybeThrow(i8** swifterror %errorslot) +; CHECK-NEXT: [[T1:%.*]] = load i8*, i8** %errorslot +; CHECK-NEXT: call void @logError(i8* [[T1]]) +; CHECK-NEXT: store i8* [[T1]], i8** %errorslot +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1, i8**)* @f.resume.0 to i8*) +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i1 zeroext, i8** swifterror) +; CHECK-NEXT: : +; CHECK-NEXT: br i1 %1, +; CHECK: : +; CHECK-NEXT: [[ERROR:%.*]] = load i8*, i8** %2, align 4 +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: %inc = add i32 [[T1]], 1 +; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 +; CHECK-NEXT: call void @print(i32 %inc) +; CHECK-NEXT: store i8* [[ERROR]], i8** %2 +; CHECK-NEXT: call void @maybeThrow(i8** swifterror %2) +; CHECK-NEXT: [[T2:%.*]] = load i8*, i8** %2 +; CHECK-NEXT: call void @logError(i8* [[T2]]) +; CHECK-NEXT: store i8* [[T2]], i8** %2 +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1, i8**)* @f.resume.0 to i8*) +; CHECK: : +; CHECK-NEXT: ret i8* null +; CHECK-NEXT: } + +define i8* @g(i8* %buffer, i32 %n) { +entry: + %errorslot = alloca swifterror i8*, align 4 + store i8* null, i8** %errorslot + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i1)* @g_prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + call void @print(i32 %n.val) + call void @maybeThrow(i8** swifterror %errorslot) + %errorload1 = load i8*, i8** %errorslot + call void @logError(i8* %errorload1) + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1() + br i1 %unwind0, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define i8* @g(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ERRORSLOT:%.*]] = alloca swifterror i8*, align 4 +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: call void @print(i32 %n) +; CHECK-NEXT: store i8* null, i8** [[ERRORSLOT]], align 4 +; CHECK-NEXT: call void @maybeThrow(i8** nonnull swifterror [[ERRORSLOT]]) +; CHECK-NEXT: [[T1:%.*]] = load i8*, i8** [[ERRORSLOT]], align 4 +; CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i8, i8* %buffer, i32 4 +; CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to i8** +; CHECK-NEXT: store i8* [[T1]], i8** [[T3]], align 4 +; CHECK-NEXT: call void @logError(i8* [[T1]]) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @g.resume.0 to i8*) +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @g.resume.0(i8* noalias nonnull, i1 zeroext) +; CHECK-NEXT: : +; CHECK-NEXT: [[ERRORSLOT:%.*]] = alloca swifterror i8*, align 4 +; CHECK-NEXT: br i1 %1, +; CHECK: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: %inc = add i32 [[T1]], 1 +; CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i8, i8* %0, i32 4 +; CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to i8** +; CHECK-NEXT: [[T4:%.*]] = load i8*, i8** [[T3]] +; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 +; CHECK-NEXT: call void @print(i32 %inc) +; CHECK-NEXT: store i8* [[T4]], i8** [[ERRORSLOT]] +; CHECK-NEXT: call void @maybeThrow(i8** nonnull swifterror [[ERRORSLOT]]) +; CHECK-NEXT: [[T5:%.*]] = load i8*, i8** [[ERRORSLOT]] +; CHECK-NEXT: store i8* [[T5]], i8** [[T3]], align 4 +; CHECK-NEXT: call void @logError(i8* [[T5]]) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @g.resume.0 to i8*) +; CHECK: : +; CHECK-NEXT: ret i8* null +; CHECK-NEXT: } + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare { i1, i8** } @llvm.coro.suspend.retcon.i1p0p0i8(...) +declare i1 @llvm.coro.suspend.retcon.i1(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare i8* @f_prototype(i8*, i1 zeroext, i8** swifterror) +declare i8* @g_prototype(i8*, i1 zeroext) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare void @print(i32) +declare void @maybeThrow(i8** swifterror) +declare void @logError(i8*) From 28e6fe081db52056428c72bc7281852b5e6ee700 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 20 Aug 2018 10:05:05 -0700 Subject: [PATCH 299/582] Revert "[ObjC] Error out when using forward-declared protocol in a @protocol" This reverts commit b14b766b1c2e712e1b2ccca1418fb0fbb68d0230. Breaks master-next. apple-llvm-split-commit: 6cb785d8ff13d8be9ee4e790d02c716fbf9bef6d apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticGroups.td | 3 +-- .../clang/Basic/DiagnosticSemaKinds.td | 4 ++-- clang/lib/CodeGen/CGObjCMac.cpp | 5 ++--- clang/lib/Sema/SemaExpr.cpp | 10 ++++++++++ clang/lib/Sema/SemaExprObjC.cpp | 6 +----- .../forward-declare-protocol-gnu.m | 10 ++++------ .../forward-protocol-metadata-symbols.m | 6 +++--- clang/test/CodeGenObjC/hidden-visibility.m | 2 +- clang/test/CodeGenObjC/link-errors.m | 2 +- clang/test/CodeGenObjC/protocol-comdat.m | 4 ++-- clang/test/CodeGenObjC/protocols-lazy.m | 19 +++++-------------- clang/test/CodeGenObjC/protocols.m | 3 +-- clang/test/PCH/objc_exprs.h | 4 +--- .../Parser/objc-cxx-keyword-identifiers.mm | 4 +--- clang/test/SemaObjC/protocol-expr-neg-1.m | 6 +++--- 15 files changed, 38 insertions(+), 50 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 40b215278937f..eb0fb277403d6 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -622,8 +622,7 @@ def DeallocInCategory:DiagGroup<"dealloc-in-category">; def SelectorTypeMismatch : DiagGroup<"selector-type-mismatch">; def Selector : DiagGroup<"selector", [SelectorTypeMismatch]>; def Protocol : DiagGroup<"protocol">; -// No longer in use, preserve for backwards compatibility. -def : DiagGroup<"at-protocol">; +def AtProtocol : DiagGroup<"at-protocol">; def PropertyAccessDotSyntax: DiagGroup<"property-access-dot-syntax">; def PropertyAttr : DiagGroup<"property-attribute-mismatch">; def SuperSubClassMismatch : DiagGroup<"super-class-method-mismatch">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2202a5c4ab3e5..b24f94602a8a3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -867,8 +867,8 @@ def err_protocol_has_circular_dependency : Error< "protocol has circular dependency">; def err_undeclared_protocol : Error<"cannot find protocol declaration for %0">; def warn_undef_protocolref : Warning<"cannot find protocol definition for %0">; -def err_atprotocol_protocol : Error< - "@protocol is using a forward protocol declaration of %0">; +def warn_atprotocol_protocol : Warning< + "@protocol is using a forward protocol declaration of %0">, InGroup<AtProtocol>; def warn_readonly_property : Warning< "attribute 'readonly' of property %0 restricts attribute " "'readwrite' of property inherited from %1">, diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 4167522aad77f..6498a625e177f 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -6838,9 +6838,8 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocol( return Entry; // Use the protocol definition, if there is one. - assert(PD->hasDefinition() && - "emitting protocol metadata without definition"); - PD = PD->getDefinition(); + if (const ObjCProtocolDecl *Def = PD->getDefinition()) + PD = Def; auto methodLists = ProtocolMethodLists::get(PD); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 73e717f605fd7..1259d25629b23 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8095,6 +8095,16 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (RHS.isInvalid()) return Incompatible; } + + Expr *PRE = RHS.get()->IgnoreParenCasts(); + if (Diagnose && isa<ObjCProtocolExpr>(PRE)) { + ObjCProtocolDecl *PDecl = cast<ObjCProtocolExpr>(PRE)->getProtocol(); + if (PDecl && !PDecl->hasDefinition()) { + Diag(PRE->getExprLoc(), diag::warn_atprotocol_protocol) << PDecl; + Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; + } + } + CastKind Kind; Sema::AssignConvertType result = CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS); diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 13a009090f9ee..bbbe343db0ac0 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -1227,12 +1227,8 @@ ExprResult Sema::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId, Diag(ProtoLoc, diag::err_undeclared_protocol) << ProtocolId; return true; } - if (!PDecl->hasDefinition()) { - Diag(ProtoLoc, diag::err_atprotocol_protocol) << PDecl; - Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; - } else { + if (PDecl->hasDefinition()) PDecl = PDecl->getDefinition(); - } QualType Ty = Context.getObjCProtoType(); if (Ty.isNull()) diff --git a/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m b/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m index 3731fb078eea9..b57a4a48d4a00 100644 --- a/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m +++ b/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m @@ -3,11 +3,9 @@ // Regression test: check that we don't crash when referencing a forward-declared protocol. @protocol P; -@interface I <P> -@end - -@implementation I - -@end +Protocol *getProtocol(void) +{ + return @protocol(P); +} // CHECK: @.objc_protocol diff --git a/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m b/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m index 76afc9c6c7609..16d33ec15d82c 100644 --- a/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m +++ b/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m @@ -3,7 +3,7 @@ @interface NSObject @end -@protocol P0 @end +@protocol P0; @interface A : NSObject <P0> +(Class) getClass; @@ -19,8 +19,8 @@ int main() { } // CHECK: @"\01l_OBJC_PROTOCOL_$_P0" = weak hidden global -// CHECK: @"\01l_OBJC_LABEL_PROTOCOL_$_P0" = weak hidden global // CHECK: @"\01l_OBJC_CLASS_PROTOCOLS_$_A" = private global +// CHECK: @"\01l_OBJC_LABEL_PROTOCOL_$_P0" = weak hidden global // CHECK: @"\01l_OBJC_PROTOCOL_REFERENCE_$_P0" = weak hidden global // CHECK: llvm.used = appending global [3 x i8*] @@ -33,7 +33,7 @@ int main() { // CHECK-SAME: OBJC_METH_VAR_NAME_ // CHECK-SAME: OBJC_METH_VAR_TYPE_ // CHECK-SAME: "\01l_OBJC_$_CLASS_METHODS_A" -// CHECK-SAME: OBJC_CLASS_NAME_.1 // CHECK-SAME: "\01l_OBJC_CLASS_PROTOCOLS_$_A" +// CHECK-SAME: OBJC_CLASS_NAME_.1 // CHECK-SAME: "OBJC_LABEL_CLASS_$" // CHECK-SAME: section "llvm.metadata" diff --git a/clang/test/CodeGenObjC/hidden-visibility.m b/clang/test/CodeGenObjC/hidden-visibility.m index 03290c29bbdb1..cb23ca18f81fc 100644 --- a/clang/test/CodeGenObjC/hidden-visibility.m +++ b/clang/test/CodeGenObjC/hidden-visibility.m @@ -16,7 +16,7 @@ @implementation I @end -@protocol Prot0 @end +@protocol Prot0; id f0() { return @protocol(Prot0); diff --git a/clang/test/CodeGenObjC/link-errors.m b/clang/test/CodeGenObjC/link-errors.m index 44797d9e8d1dc..f2d9ddba883ba 100644 --- a/clang/test/CodeGenObjC/link-errors.m +++ b/clang/test/CodeGenObjC/link-errors.m @@ -10,7 +10,7 @@ -(id) alloc; -(id) init; @end -@protocol P @end +@protocol P; @interface A : Root @end diff --git a/clang/test/CodeGenObjC/protocol-comdat.m b/clang/test/CodeGenObjC/protocol-comdat.m index ddf8e5a77d4c4..a19ba8cf35dcf 100644 --- a/clang/test/CodeGenObjC/protocol-comdat.m +++ b/clang/test/CodeGenObjC/protocol-comdat.m @@ -4,8 +4,8 @@ @protocol P - (void) method; @end -@protocol Q @end -@protocol R @end +@protocol Q; +@protocol R; @interface I<P> @end diff --git a/clang/test/CodeGenObjC/protocols-lazy.m b/clang/test/CodeGenObjC/protocols-lazy.m index a2dfc106d6433..fba7454b95498 100644 --- a/clang/test/CodeGenObjC/protocols-lazy.m +++ b/clang/test/CodeGenObjC/protocols-lazy.m @@ -18,10 +18,7 @@ @protocol P2 -im1; @end // RUN: grep OBJC_PROTOCOL_P3 %t | count 3 // RUN: not grep OBJC_PROTOCOL_INSTANCE_METHODS_P3 %t @protocol P3; -@interface UserP3<P3> -@end -@implementation UserP3 -@end +void f1() { id x = @protocol(P3); } // Definition triggered by class reference. // RUN: grep OBJC_PROTOCOL_P4 %t | count 3 @@ -34,16 +31,10 @@ @implementation I0 -im1 { return 0; }; @end // RUN: grep OBJC_PROTOCOL_P5 %t | count 3 // RUN: grep OBJC_PROTOCOL_INSTANCE_METHODS_P5 %t | count 3 @protocol P5; -@interface UserP5<P5> // This generates a forward - // reference, which has to be - // updated on the next line. -@end -@protocol P5 -im1; @end -@implementation UserP5 - -- im1 { } - -@end +void f2() { id x = @protocol(P5); } // This generates a forward + // reference, which has to be + // updated on the next line. +@protocol P5 -im1; @end // Protocol reference following definition. // RUN: grep OBJC_PROTOCOL_P6 %t | count 4 diff --git a/clang/test/CodeGenObjC/protocols.m b/clang/test/CodeGenObjC/protocols.m index 31965e1711899..6dadb11273dca 100644 --- a/clang/test/CodeGenObjC/protocols.m +++ b/clang/test/CodeGenObjC/protocols.m @@ -7,8 +7,7 @@ +(int) maxValue; -(int) conformsTo: (id) x; @end -@protocol P0 -@end +@protocol P0; @protocol P1 +(void) classMethodReq0; diff --git a/clang/test/PCH/objc_exprs.h b/clang/test/PCH/objc_exprs.h index e4952f5929bf4..807304c20f866 100644 --- a/clang/test/PCH/objc_exprs.h +++ b/clang/test/PCH/objc_exprs.h @@ -1,13 +1,11 @@ @protocol foo; -@protocol foo2 -@end @class itf; // Expressions typedef typeof(@"foo" "bar") objc_string; typedef typeof(@encode(int)) objc_encode; -typedef typeof(@protocol(foo2)) objc_protocol; +typedef typeof(@protocol(foo)) objc_protocol; typedef typeof(@selector(noArgs)) objc_selector_noArgs; typedef typeof(@selector(oneArg:)) objc_selector_oneArg; typedef typeof(@selector(foo:bar:)) objc_selector_twoArg; diff --git a/clang/test/Parser/objc-cxx-keyword-identifiers.mm b/clang/test/Parser/objc-cxx-keyword-identifiers.mm index cff38c5543713..6791f0d3732a2 100644 --- a/clang/test/Parser/objc-cxx-keyword-identifiers.mm +++ b/clang/test/Parser/objc-cxx-keyword-identifiers.mm @@ -18,9 +18,7 @@ @protocol P // ok @protocol new // expected-error {{expected identifier; 'new' is a keyword in Objective-C++}} @end -@protocol P2; -@protocol delete // expected-error {{expected identifier; 'delete' is a keyword in Objective-C++}} -@end +@protocol P2, delete; // expected-error {{expected identifier; 'delete' is a keyword in Objective-C++}} @class Foo, try; // expected-error {{expected identifier; 'try' is a keyword in Objective-C++}} diff --git a/clang/test/SemaObjC/protocol-expr-neg-1.m b/clang/test/SemaObjC/protocol-expr-neg-1.m index f659b30046e13..26cdac70ae8db 100644 --- a/clang/test/SemaObjC/protocol-expr-neg-1.m +++ b/clang/test/SemaObjC/protocol-expr-neg-1.m @@ -12,7 +12,7 @@ @protocol p1 int main() { Protocol *proto = @protocol(p1); - Protocol *fproto = @protocol(fproto); // expected-error {{@protocol is using a forward protocol declaration of 'fproto'}} + Protocol *fproto = @protocol(fproto); // expected-warning {{@protocol is using a forward protocol declaration of 'fproto'}} Protocol *pp = @protocol(i); // expected-error {{cannot find protocol declaration for 'i'}} Protocol *p1p = @protocol(cl); // expected-error {{cannot find protocol declaration for 'cl'}} } @@ -26,9 +26,9 @@ - (int) conformsToProtocol : (Protocol *)protocl; @end int doesConform(id foo) { - return [foo conformsToProtocol:@protocol(TestProtocol)]; // expected-error {{@protocol is using a forward protocol declaration of 'TestProtocol'}} + return [foo conformsToProtocol:@protocol(TestProtocol)]; // expected-warning {{@protocol is using a forward protocol declaration of 'TestProtocol'}} } int doesConformSuper(id foo) { - return [foo conformsToProtocol:@protocol(SuperProtocol)]; // expected-error {{@protocol is using a forward protocol declaration of 'SuperProtocol'}} + return [foo conformsToProtocol:@protocol(SuperProtocol)]; // expected-warning {{@protocol is using a forward protocol declaration of 'SuperProtocol'}} } From 533c3005228594dc03c306eae6c96e1fa7069be1 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Mon, 13 Aug 2018 19:47:34 -0700 Subject: [PATCH 300/582] Some changes got lost in some bad merge conflict a while ago. Re-introduce them back. rdar://problem/43237155 apple-llvm-split-commit: 4e8de222601262de295a4b5d91e4bc9c3c9081a5 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTContext.cpp | 4 + clang/lib/AST/DeclObjC.cpp | 10 ++- clang/lib/AST/Type.cpp | 82 +++++++++++-------- clang/lib/Sema/SemaType.cpp | 21 +++++ .../Checkers/DynamicTypePropagation.cpp | 2 +- 5 files changed, 80 insertions(+), 39 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 26d0e0e121a2c..0a40ee5de9d50 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4467,6 +4467,10 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, bool allowOnPointerType) const { hasError = false; + if (const auto *objT = dyn_cast<ObjCTypeParamType>(type.getTypePtr())) { + return getObjCTypeParamType(objT->getDecl(), protocols); + } + // Apply protocol qualifiers to ObjCObjectPointerType. if (allowOnPointerType) { if (const auto *objPtr = diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index beea402f250bc..b0b27427949d0 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1382,8 +1382,14 @@ ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc, IdentifierInfo *name, SourceLocation colonLoc, TypeSourceInfo *boundInfo) { - return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, - nameLoc, name, colonLoc, boundInfo); + auto *TPDecl = + new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, + nameLoc, name, colonLoc, boundInfo); + QualType TPType = ctx.getObjCTypeParamType(TPDecl, {}); + TPDecl->setTypeForDecl(TPType.getTypePtr()); + return TPDecl; +// return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, +// nameLoc, name, colonLoc, boundInfo); } ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 60d3393ef5944..7dc01b2f2f3ca 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1160,47 +1160,57 @@ QualType QualType::substObjCTypeArgs( // Replace an Objective-C type parameter reference with the corresponding // type argument. - if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) { - if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) { - // If we have type arguments, use them. - if (!typeArgs.empty()) { - // FIXME: Introduce SubstObjCTypeParamType ? - QualType argType = typeArgs[typeParam->getIndex()]; + if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) { + ObjCTypeParamDecl *typeParam = OTPTy->getDecl(); + // If we have type arguments, use them. + if (!typeArgs.empty()) { + QualType argType = typeArgs[typeParam->getIndex()]; + if (OTPTy->qual_empty()) return ctx.getQualifiedType(argType, splitType.Quals); - } - switch (context) { - case ObjCSubstitutionContext::Ordinary: - case ObjCSubstitutionContext::Parameter: - case ObjCSubstitutionContext::Superclass: - // Substitute the bound. + // Apply protocol lists if exists. + bool hasError; + SmallVector<ObjCProtocolDecl*, 8> protocolsVec; + protocolsVec.append(OTPTy->qual_begin(), + OTPTy->qual_end()); + ArrayRef<ObjCProtocolDecl *> protocolsToApply = protocolsVec; + QualType resultTy = ctx.applyObjCProtocolQualifiers(argType, + protocolsToApply, hasError, true/*allowOnPointerType*/); + + return ctx.getQualifiedType(resultTy, splitType.Quals); + } + + switch (context) { + case ObjCSubstitutionContext::Ordinary: + case ObjCSubstitutionContext::Parameter: + case ObjCSubstitutionContext::Superclass: + // Substitute the bound. + return ctx.getQualifiedType(typeParam->getUnderlyingType(), + splitType.Quals); + + case ObjCSubstitutionContext::Result: + case ObjCSubstitutionContext::Property: { + // Substitute the __kindof form of the underlying type. + const auto *objPtr = typeParam->getUnderlyingType() + ->castAs<ObjCObjectPointerType>(); + + // __kindof types, id, and Class don't need an additional + // __kindof. + if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) return ctx.getQualifiedType(typeParam->getUnderlyingType(), splitType.Quals); - case ObjCSubstitutionContext::Result: - case ObjCSubstitutionContext::Property: { - // Substitute the __kindof form of the underlying type. - const auto *objPtr = typeParam->getUnderlyingType() - ->castAs<ObjCObjectPointerType>(); - - // __kindof types, id, and Class don't need an additional - // __kindof. - if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) - return ctx.getQualifiedType(typeParam->getUnderlyingType(), - splitType.Quals); - - // Add __kindof. - const auto *obj = objPtr->getObjectType(); - QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(), - obj->getTypeArgsAsWritten(), - obj->getProtocols(), - /*isKindOf=*/true); - - // Rebuild object pointer type. - resultTy = ctx.getObjCObjectPointerType(resultTy); - return ctx.getQualifiedType(resultTy, splitType.Quals); - } - } + // Add __kindof. + const auto *obj = objPtr->getObjectType(); + QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(), + obj->getTypeArgsAsWritten(), + obj->getProtocols(), + /*isKindOf=*/true); + + // Rebuild object pointer type. + resultTy = ctx.getObjCObjectPointerType(resultTy); + return ctx.getQualifiedType(resultTy, splitType.Quals); + } } } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 29776cb4d45c0..bc697e2b74d0d 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1111,6 +1111,20 @@ TypeResult Sema::actOnObjCTypeArgsAndProtocolQualifiers( ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } + if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) { + // Protocol qualifier information. + if (OTPTL.getNumProtocols() > 0) { + assert(OTPTL.getNumProtocols() == Protocols.size()); + OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc); + OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc); + for (unsigned i = 0, n = Protocols.size(); i != n; ++i) + OTPTL.setProtocolLoc(i, ProtocolLocs[i]); + } + + // We're done. Return the completed type to the parser. + return CreateParsedType(Result, ResultTInfo); + } + auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>(); // Type argument information. @@ -6482,6 +6496,13 @@ bool Sema::checkNullabilityTypeSpecifier(QualType &type, bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) { // Find out if it's an Objective-C object or object pointer type; + if (isa<ObjCTypeParamType>(type)) { + // Build the attributed type to record where __kindof occurred. + type = + Context.getAttributedType(AttributedType::attr_objc_kindof, type, type); + return false; + } + const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>(); const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() : type->getAs<ObjCObjectType>(); diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index b896c3773d1f1..126e57645a43b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -638,7 +638,7 @@ static bool isObjCTypeParamDependent(QualType Type) { : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { public: IsObjCTypeParamDependentTypeVisitor() : Result(false) {} - bool VisitTypedefType(const TypedefType *Type) { + bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { if (isa<ObjCTypeParamDecl>(Type->getDecl())) { Result = true; return false; From eb201ed78ff4eb260fcec94ef7bf1132964b0cca Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 22 Aug 2018 13:21:11 -0700 Subject: [PATCH 301/582] Revert "Some changes got lost in some bad merge conflict a while ago." This reverts commit 4e8de222601262de295a4b5d91e4bc9c3c9081a5. apple-llvm-split-commit: 407c4aa29682e7b8f5be2874898e44421627bec2 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTContext.cpp | 4 - clang/lib/AST/DeclObjC.cpp | 10 +-- clang/lib/AST/Type.cpp | 82 ++++++++----------- clang/lib/Sema/SemaType.cpp | 14 ---- .../Checkers/DynamicTypePropagation.cpp | 2 +- 5 files changed, 39 insertions(+), 73 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 098abf2063967..ef1ab0ba26de6 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4467,10 +4467,6 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, bool allowOnPointerType) const { hasError = false; - if (const auto *objT = dyn_cast<ObjCTypeParamType>(type.getTypePtr())) { - return getObjCTypeParamType(objT->getDecl(), protocols); - } - // Apply protocol qualifiers to ObjCObjectPointerType. if (allowOnPointerType) { if (const auto *objPtr = diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index b0b27427949d0..beea402f250bc 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1382,14 +1382,8 @@ ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc, IdentifierInfo *name, SourceLocation colonLoc, TypeSourceInfo *boundInfo) { - auto *TPDecl = - new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, - nameLoc, name, colonLoc, boundInfo); - QualType TPType = ctx.getObjCTypeParamType(TPDecl, {}); - TPDecl->setTypeForDecl(TPType.getTypePtr()); - return TPDecl; -// return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, -// nameLoc, name, colonLoc, boundInfo); + return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, + nameLoc, name, colonLoc, boundInfo); } ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 2e1aed26fd460..04b4193cee913 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1138,57 +1138,47 @@ QualType QualType::substObjCTypeArgs( // Replace an Objective-C type parameter reference with the corresponding // type argument. - if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) { - ObjCTypeParamDecl *typeParam = OTPTy->getDecl(); - // If we have type arguments, use them. - if (!typeArgs.empty()) { - QualType argType = typeArgs[typeParam->getIndex()]; - if (OTPTy->qual_empty()) + if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) { + if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) { + // If we have type arguments, use them. + if (!typeArgs.empty()) { + // FIXME: Introduce SubstObjCTypeParamType ? + QualType argType = typeArgs[typeParam->getIndex()]; return ctx.getQualifiedType(argType, splitType.Quals); + } - // Apply protocol lists if exists. - bool hasError; - SmallVector<ObjCProtocolDecl*, 8> protocolsVec; - protocolsVec.append(OTPTy->qual_begin(), - OTPTy->qual_end()); - ArrayRef<ObjCProtocolDecl *> protocolsToApply = protocolsVec; - QualType resultTy = ctx.applyObjCProtocolQualifiers(argType, - protocolsToApply, hasError, true/*allowOnPointerType*/); - - return ctx.getQualifiedType(resultTy, splitType.Quals); - } - - switch (context) { - case ObjCSubstitutionContext::Ordinary: - case ObjCSubstitutionContext::Parameter: - case ObjCSubstitutionContext::Superclass: - // Substitute the bound. - return ctx.getQualifiedType(typeParam->getUnderlyingType(), - splitType.Quals); - - case ObjCSubstitutionContext::Result: - case ObjCSubstitutionContext::Property: { - // Substitute the __kindof form of the underlying type. - const auto *objPtr = typeParam->getUnderlyingType() - ->castAs<ObjCObjectPointerType>(); - - // __kindof types, id, and Class don't need an additional - // __kindof. - if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + switch (context) { + case ObjCSubstitutionContext::Ordinary: + case ObjCSubstitutionContext::Parameter: + case ObjCSubstitutionContext::Superclass: + // Substitute the bound. return ctx.getQualifiedType(typeParam->getUnderlyingType(), splitType.Quals); - // Add __kindof. - const auto *obj = objPtr->getObjectType(); - QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(), - obj->getTypeArgsAsWritten(), - obj->getProtocols(), - /*isKindOf=*/true); - - // Rebuild object pointer type. - resultTy = ctx.getObjCObjectPointerType(resultTy); - return ctx.getQualifiedType(resultTy, splitType.Quals); - } + case ObjCSubstitutionContext::Result: + case ObjCSubstitutionContext::Property: { + // Substitute the __kindof form of the underlying type. + const auto *objPtr = typeParam->getUnderlyingType() + ->castAs<ObjCObjectPointerType>(); + + // __kindof types, id, and Class don't need an additional + // __kindof. + if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + return ctx.getQualifiedType(typeParam->getUnderlyingType(), + splitType.Quals); + + // Add __kindof. + const auto *obj = objPtr->getObjectType(); + QualType resultTy = ctx.getObjCObjectType(obj->getBaseType(), + obj->getTypeArgsAsWritten(), + obj->getProtocols(), + /*isKindOf=*/true); + + // Rebuild object pointer type. + resultTy = ctx.getObjCObjectPointerType(resultTy); + return ctx.getQualifiedType(resultTy, splitType.Quals); + } + } } } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 635fa110bd4c2..3c9b7fa797974 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1158,20 +1158,6 @@ TypeResult Sema::actOnObjCTypeArgsAndProtocolQualifiers( ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } - if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) { - // Protocol qualifier information. - if (OTPTL.getNumProtocols() > 0) { - assert(OTPTL.getNumProtocols() == Protocols.size()); - OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc); - OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc); - for (unsigned i = 0, n = Protocols.size(); i != n; ++i) - OTPTL.setProtocolLoc(i, ProtocolLocs[i]); - } - - // We're done. Return the completed type to the parser. - return CreateParsedType(Result, ResultTInfo); - } - auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>(); // Type argument information. diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 126e57645a43b..b896c3773d1f1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -638,7 +638,7 @@ static bool isObjCTypeParamDependent(QualType Type) { : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { public: IsObjCTypeParamDependentTypeVisitor() : Result(false) {} - bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { + bool VisitTypedefType(const TypedefType *Type) { if (isa<ObjCTypeParamDecl>(Type->getDecl())) { Result = true; return false; From 02235f55a1c6b99135e3351a654a967446791527 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 22 Aug 2018 13:24:19 -0700 Subject: [PATCH 302/582] [GitHub] workaround test failure in test/Index/print-type.m This change is required to account for divergent behavior in upstream and GitHub downstream Clang. rdar://24619481 isn't fixed. This fix should be reapplied when rdar://28824900 is fixed. rdar://43584043 apple-llvm-split-commit: 3ae72b27572c0e18607f305c03fa7f60e8f5d2b6 apple-llvm-split-dir: clang/ --- clang/test/Index/print-type.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Index/print-type.m b/clang/test/Index/print-type.m index 2afdfc0ff842c..fda4af85d4982 100644 --- a/clang/test/Index/print-type.m +++ b/clang/test/Index/print-type.m @@ -21,4 +21,4 @@ -(SomeType)generic; // CHECK: ParmDecl=j:5:49 (Definition) [Out,] [type=short *] [typekind=Pointer] [isPOD=1] [pointeetype=short] [pointeekind=Short] // CHECK: ParmDecl=p:6:36 (Definition) [type=__kindof Foo *] [typekind=ObjCObjectPointer] [canonicaltype=__kindof Foo *] [canonicaltypekind=ObjCObjectPointer] [basetype=Foo] [basekind=ObjCInterface] [isPOD=1] [pointeetype=Foo] [pointeekind=ObjCInterface] // CHECK: ObjCPropertyDecl=classProp:7:23 [class,] [type=int] [typekind=Int] [isPOD=1] -// CHECK: ObjCInstanceMethodDecl=generic:11:12 [type=] [typekind=Invalid] [resulttype=SomeType] [resulttypekind=ObjCTypeParam] [isPOD=0] +// CHECK: ObjCInstanceMethodDecl=generic:11:12 [type=] [typekind=Invalid] [resulttype=SomeType] [resulttypekind=Typedef] [isPOD=0] From b53fa8bdb834e98fdc6d94bc3b045862e28ee200 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 22 Aug 2018 19:39:51 -0400 Subject: [PATCH 303/582] Remove unreachable blocks before splitting a coroutine. The suspend-crossing algorithm is not correct in the presence of uses that cannot be reached on some successor path from their defs. apple-llvm-split-commit: 96e746dd02d7dc184c40c9f567a4b3c1370420e1 apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 20 ++++++++ .../Coroutines/coro-retcon-unreachable.ll | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-unreachable.ll diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index e1f4a4d49d6d7..ccacfbcf3325c 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -56,6 +56,7 @@ #include "llvm/Pass.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" @@ -1338,7 +1339,26 @@ static void splitCoroutine(Function &F, coro::Shape &Shape, llvm_unreachable("bad ABI kind"); } +namespace { + class PrettyStackTraceFunction : public PrettyStackTraceEntry { + Function &F; + public: + PrettyStackTraceFunction(Function &F) : F(F) {} + void print(raw_ostream &OS) const override { + OS << "While splitting coroutine "; + F.printAsOperand(OS, /*print type*/ false, F.getParent()); + OS << "\n"; + } + }; +} + static void splitCoroutine(Function &F, CallGraph &CG, CallGraphSCC &SCC) { + PrettyStackTraceFunction prettyStackTrace(F); + + // The suspend-crossing algorithm in buildCoroutineFrame get tripped + // up by uses in unreachable blocks, so remove them as a first pass. + removeUnreachableBlocks(F); + coro::Shape Shape(F); if (!Shape.CoroBegin) return; diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-unreachable.ll b/llvm/test/Transforms/Coroutines/coro-retcon-unreachable.ll new file mode 100644 index 0000000000000..27ee2fd540a41 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-unreachable.ll @@ -0,0 +1,46 @@ +; RUN: opt < %s -coro-early -coro-split -S | FileCheck %s +target datalayout = "E-p:64:64" + +%swift.type = type { i64 } +%swift.opaque = type opaque +%T4red215EmptyCollectionV = type opaque +%TSi = type <{ i64 }> + +define hidden swiftcc { i8*, %swift.opaque* } @no_suspends(i8* %buffer, i64 %arg) #1 { + %id = call token @llvm.coro.id.retcon.once(i32 32, i32 8, i8* %buffer, i8* bitcast (void (i8*, i1)* @prototype to i8*), i8* bitcast (i8* (i64)* @malloc to i8*), i8* bitcast (void (i8*)* @free to i8*)) + %begin = call i8* @llvm.coro.begin(token %id, i8* null) + call void @print(i64 %arg) + call void @llvm.trap() + unreachable + +bb1: + call void @print(i64 %arg) + call i1 @llvm.coro.end(i8* %begin, i1 false) + unreachable +} +; CHECK-LABEL: define hidden swiftcc { i8*, %swift.opaque* } @no_suspends( +; CHECK: call token @llvm.coro.id.retcon.once +; CHECK-NEXT: call void @print(i64 %arg) +; CHECK-NEXT: call void @llvm.trap() +; CHECK-NEXT: unreachable + +declare swiftcc void @prototype(i8* noalias dereferenceable(32), i1) +declare void @print(i64) + +declare noalias i8* @malloc(i64) #5 +declare void @free(i8* nocapture) #5 + +declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*) #5 +declare i8* @llvm.coro.begin(token, i8* writeonly) #5 +declare token @llvm.coro.alloca.alloc.i64(i64, i32) #5 +declare i8* @llvm.coro.alloca.get(token) #5 +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #6 +declare i1 @llvm.coro.suspend.retcon.i1(...) #5 +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #6 +declare void @llvm.coro.alloca.free(token) #5 +declare i1 @llvm.coro.end(i8*, i1) #5 + +declare void @llvm.trap() + +attributes #1 = { noreturn nounwind } +attributes #5 = { nounwind } From 8147e42b04ac0b63078b07c412445454e0a5fd75 Mon Sep 17 00:00:00 2001 From: Nathan Hawes <nhawes@apple.com> Date: Thu, 23 Aug 2018 14:27:25 -0700 Subject: [PATCH 304/582] [IndexStore] Keep indexstore_symbol_role_t enum bits fixed They were previously required to exactly match the SymbolRole enum, and shifted when a new Undefined role was added there. The indexstore values were intended to be stable though, so this patch implements mapping functions between the two, and restores the indexstore bits to their original values, assigning 'undefined' the next available bit instead of matching its value in SymbolRole. Also fixes a few places where the conversion functions weren't being called. apple-llvm-split-commit: e60d01310ee04a3858f4ffff21a72ee816c4df50 apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 40 ++++++------ clang/lib/Index/IndexDataStoreUtils.cpp | 68 ++++++++++++++++++-- clang/tools/IndexStore/IndexStore.cpp | 2 +- clang/tools/c-index-test/JSONAggregation.cpp | 8 +-- clang/tools/c-index-test/core_main.cpp | 8 +-- 5 files changed, 92 insertions(+), 34 deletions(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 8d0c4f217a9bb..4edf2a66af567 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -261,28 +261,28 @@ typedef enum { } indexstore_symbol_language_t; typedef enum { - INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, - INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, - INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, - INDEXSTORE_SYMBOL_ROLE_READ = 1 << 3, - INDEXSTORE_SYMBOL_ROLE_WRITE = 1 << 4, - INDEXSTORE_SYMBOL_ROLE_CALL = 1 << 5, - INDEXSTORE_SYMBOL_ROLE_DYNAMIC = 1 << 6, - INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7, - INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8, - INDEXSTORE_SYMBOL_ROLE_UNDEFINED = 1 << 9, + INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, + INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, + INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, + INDEXSTORE_SYMBOL_ROLE_READ = 1 << 3, + INDEXSTORE_SYMBOL_ROLE_WRITE = 1 << 4, + INDEXSTORE_SYMBOL_ROLE_CALL = 1 << 5, + INDEXSTORE_SYMBOL_ROLE_DYNAMIC = 1 << 6, + INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7, + INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8, + INDEXSTORE_SYMBOL_ROLE_UNDEFINITION = 1 << 19, // Relation roles. - INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 10, - INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 11, - INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 12, - INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 13, - INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 14, - INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 15, - INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 16, - INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 17, - INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 18, - INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 19, + INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 9, + INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 10, + INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 11, + INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 12, + INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 13, + INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 14, + INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 15, + INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 16, + INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 17, + INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 18, } indexstore_symbol_role_t; INDEXSTORE_PUBLIC indexstore_symbol_language_t diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 799c9199b9c45..37fccf706946d 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -175,15 +175,73 @@ SymbolLanguage index::getSymbolLanguage(indexstore_symbol_language_t L) { /// Map an indexstore representation to a SymbolPropertySet, handling /// unknown values. SymbolPropertySet index::getSymbolProperties(uint64_t Props) { - // FIXME: currently these enums must be kept in sync. - return (uint64_t)Props; + SymbolPropertySet SymbolProperties = 0; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_GENERIC) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::Generic; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::TemplateSpecialization; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_UNITTEST) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::UnitTest; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::IBAnnotated; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::IBOutletCollection; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::GKInspectable; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_LOCAL) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::Local; + + return SymbolProperties; } /// Map an indexstore representation to a SymbolRoleSet, handling unknown /// values. SymbolRoleSet index::getSymbolRoles(uint64_t Roles) { - // FIXME: currently these enums must be kept in sync. - return (uint64_t)Roles; + SymbolRoleSet SymbolRoles = 0; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DECLARATION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Declaration; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DEFINITION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Definition; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REFERENCE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Reference; + if (Roles & INDEXSTORE_SYMBOL_ROLE_READ) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Read; + if (Roles & INDEXSTORE_SYMBOL_ROLE_WRITE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Write; + if (Roles & INDEXSTORE_SYMBOL_ROLE_CALL) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Call; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DYNAMIC) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Dynamic; + if (Roles & INDEXSTORE_SYMBOL_ROLE_ADDRESSOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::AddressOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_IMPLICIT) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Implicit; + if (Roles & INDEXSTORE_SYMBOL_ROLE_UNDEFINITION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Undefinition; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationChildOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_BASEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationBaseOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationOverrideOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationReceivedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationCalledBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationExtendedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationAccessorOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationContainedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationIBTypeOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationSpecializationOf; + + return SymbolRoles; } /// Map a SymbolLanguage to a indexstore_symbol_language_t. @@ -383,7 +441,7 @@ uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) { storeRoles |= INDEXSTORE_SYMBOL_ROLE_IMPLICIT; break; case SymbolRole::Undefinition: - storeRoles |= INDEXSTORE_SYMBOL_ROLE_UNDEFINED; + storeRoles |= INDEXSTORE_SYMBOL_ROLE_UNDEFINITION; break; case SymbolRole::RelationChildOf: storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF; diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index 7412d82a70c80..a80f687fbcd79 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -334,7 +334,7 @@ indexstore_occurrence_relations_apply(indexstore_occurrence_t occur, uint64_t indexstore_occurrence_get_roles(indexstore_occurrence_t occur) { - return static_cast<IndexRecordOccurrence*>(occur)->Roles; + return getIndexStoreRoles(static_cast<IndexRecordOccurrence*>(occur)->Roles); } void diff --git a/clang/tools/c-index-test/JSONAggregation.cpp b/clang/tools/c-index-test/JSONAggregation.cpp index 83118fcc2385b..0df1e8dc5d0f8 100644 --- a/clang/tools/c-index-test/JSONAggregation.cpp +++ b/clang/tools/c-index-test/JSONAggregation.cpp @@ -223,17 +223,17 @@ std::unique_ptr<RecordInfo> Aggregator::processRecord(StringRef recordFile) { recordReader.foreachOccurrence([&](IndexRecordOccurrence idxOccur) -> bool { SymbolIndex symIdx = getSymbolIndex(idxOccur.getSymbol()); SymbolInfo &symInfo = Symbols[symIdx]; - symInfo.Roles |= idxOccur.getRoles(); + symInfo.Roles |= getSymbolRoles(idxOccur.getRoles()); SymbolOccurrenceInfo occurInfo; occurInfo.Symbol = symIdx; idxOccur.foreachRelation([&](IndexSymbolRelation rel) -> bool { SymbolIndex relsymIdx = getSymbolIndex(rel.getSymbol()); SymbolInfo &relsymInfo = Symbols[relsymIdx]; - relsymInfo.RelatedRoles |= rel.getRoles(); - occurInfo.Relations.emplace_back(relsymIdx, rel.getRoles()); + relsymInfo.RelatedRoles |= getSymbolRoles(rel.getRoles()); + occurInfo.Relations.emplace_back(relsymIdx, getSymbolRoles(rel.getRoles())); return true; }); - occurInfo.Roles = idxOccur.getRoles(); + occurInfo.Roles = getSymbolRoles(idxOccur.getRoles()); std::tie(occurInfo.Line, occurInfo.Column) = idxOccur.getLineCol(); record->Occurrences.push_back(std::move(occurInfo)); return true; diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index a21d53f2ca9fe..49a058fc952e5 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -735,9 +735,9 @@ static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS) { OS << Sym.getCodegenName(); OS << " | "; - printSymbolRoles(Sym.getRoles(), OS); + printSymbolRoles(getSymbolRoles(Sym.getRoles()), OS); OS << " - "; - printSymbolRoles(Sym.getRelatedRoles(), OS); + printSymbolRoles(getSymbolRoles(Sym.getRelatedRoles()), OS); OS << '\n'; } @@ -764,12 +764,12 @@ static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS return true; }); - printSymbolRoles(Occur.getRoles(), OS); + printSymbolRoles(getSymbolRoles(Occur.getRoles()), OS); OS << " | "; OS << "rel: " << NumRelations << '\n'; Occur.foreachRelation([&](indexstore::IndexSymbolRelation Rel) { OS << '\t'; - printSymbolRoles(Rel.getRoles(), OS); + printSymbolRoles(getSymbolRoles(Rel.getRoles()), OS); OS << " | "; auto Sym = Rel.getSymbol(); if (Sym.getUSR().empty()) From daedbe5a073bf7ed80c26f541a31e625e759c11d Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sun, 26 Aug 2018 09:53:51 -0700 Subject: [PATCH 305/582] [index/store] Update to handle introduction of SymbolProperty::ProtocolInterface apple-llvm-split-commit: ad7469faccc394f8bc170bcfbd073ebcf440f2dd apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 3 ++- clang/lib/Index/IndexDataStoreUtils.cpp | 5 +++++ clang/test/Index/Store/external-source-symbol-hash.m | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 4edf2a66af567..7e582eb2aab9c 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -24,7 +24,7 @@ * INDEXSTORE_VERSION_MAJOR is intended for "major" source/ABI breaking changes. */ #define INDEXSTORE_VERSION_MAJOR 0 -#define INDEXSTORE_VERSION_MINOR 9 +#define INDEXSTORE_VERSION_MINOR 10 #define INDEXSTORE_VERSION_ENCODE(major, minor) ( \ ((major) * 10000) \ @@ -250,6 +250,7 @@ typedef enum { INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION = 1 << 5, INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE = 1 << 6, INDEXSTORE_SYMBOL_PROPERTY_LOCAL = 1 << 7, + INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE = 1 << 8, } indexstore_symbol_property_t; typedef enum { diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 37fccf706946d..5d185b5c41409 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -192,6 +192,8 @@ SymbolPropertySet index::getSymbolProperties(uint64_t Props) { SymbolProperties |= (SymbolPropertySet)SymbolProperty::GKInspectable; if (Props & INDEXSTORE_SYMBOL_PROPERTY_LOCAL) SymbolProperties |= (SymbolPropertySet)SymbolProperty::Local; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface; return SymbolProperties; } @@ -403,6 +405,9 @@ uint64_t index::getIndexStoreProperties(SymbolPropertySet Props) { case SymbolProperty::Local: storeProp |= INDEXSTORE_SYMBOL_PROPERTY_LOCAL; break; + case SymbolProperty::ProtocolInterface: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE; + break; } }); return storeProp; diff --git a/clang/test/Index/Store/external-source-symbol-hash.m b/clang/test/Index/Store/external-source-symbol-hash.m index 20dca7cf8ed77..1b4f89d9251f3 100644 --- a/clang/test/Index/Store/external-source-symbol-hash.m +++ b/clang/test/Index/Store/external-source-symbol-hash.m @@ -29,7 +29,7 @@ void test(id<P1> first, I2 *second, enum E3 third) {} @protocol P1 // CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Decl | rel: 0 -(void)method; -// CHECK: [[@LINE-1]]:8 | instance-method/Swift | c:@M@some_module@objc(pl)P1(im)method | Decl,Dyn,RelChild | rel: 1 +// CHECK: [[@LINE-1]]:8 | instance-method(protocol)/Swift | c:@M@some_module@objc(pl)P1(im)method | Decl,Dyn,RelChild | rel: 1 @end EXT_DECL("other_module") From b4b9b63c1cd20a80c449c62cf17a04c3934a2f86 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sun, 26 Aug 2018 11:31:22 -0700 Subject: [PATCH 306/582] [index/store] Remove storing file size+mod time for dependencies We decided against writing these in order to get reproducible build products (index data output being the same with the same inputs). Keep these reserved for the future, for coming up with a better scheme to track state of dependencies without using modification time. apple-llvm-split-commit: 2f6b097cc7c75550be8b734a98a48815211d5223 apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexUnitReader.h | 2 -- clang/include/indexstore/IndexStoreCXX.h | 2 -- clang/include/indexstore/indexstore.h | 6 ----- clang/lib/Index/IndexUnitReader.cpp | 6 ++--- clang/lib/Index/IndexUnitWriter.cpp | 22 +++++++++---------- clang/test/Index/Store/print-unit.c | 2 +- .../Index/Store/print-units-with-modules.m | 6 ++--- clang/tools/IndexStore/IndexStore.cpp | 12 ---------- clang/tools/IndexStore/IndexStore.exports | 2 -- clang/tools/c-index-test/core_main.cpp | 12 ++++++---- 10 files changed, 26 insertions(+), 46 deletions(-) diff --git a/clang/include/clang/Index/IndexUnitReader.h b/clang/include/clang/Index/IndexUnitReader.h index ccd2dceb21bce..4c40edcbe960f 100644 --- a/clang/include/clang/Index/IndexUnitReader.h +++ b/clang/include/clang/Index/IndexUnitReader.h @@ -59,8 +59,6 @@ class IndexUnitReader { StringRef UnitOrRecordName; StringRef FilePath; StringRef ModuleName; - size_t FileSize; - time_t ModTime; }; struct IncludeInfo { StringRef SourcePath; diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h index addaa86f130c6..06d6977c86b99 100644 --- a/clang/include/indexstore/IndexStoreCXX.h +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -387,8 +387,6 @@ class IndexUnitDependency { StringRef getName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_name(obj)); } StringRef getFilePath() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_filepath(obj)); } StringRef getModuleName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_modulename(obj)); } - time_t getModificationTime() { return indexstore_unit_dependency_get_modification_time(obj); } - size_t getFileSize() { return indexstore_unit_dependency_get_file_size(obj); } }; diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 7e582eb2aab9c..a0a811556ca58 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -463,12 +463,6 @@ indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t); INDEXSTORE_PUBLIC indexstore_string_ref_t indexstore_unit_dependency_get_name(indexstore_unit_dependency_t); -INDEXSTORE_PUBLIC time_t -indexstore_unit_dependency_get_modification_time(indexstore_unit_dependency_t); - -INDEXSTORE_PUBLIC size_t -indexstore_unit_dependency_get_file_size(indexstore_unit_dependency_t); - INDEXSTORE_PUBLIC indexstore_string_ref_t indexstore_unit_include_get_source_path(indexstore_unit_include_t); diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp index c53407e493365..5559d91bacc96 100644 --- a/clang/lib/Index/IndexUnitReader.cpp +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -299,8 +299,8 @@ bool IndexUnitReaderImpl::foreachDependency(DependencyReceiver Receiver) { bool IsSystem = Record[I++]; int PathIndex = (int)Record[I++] - 1; int ModuleIndex = (int)Record[I++] - 1; - time_t ModTime = (time_t)Record[I++]; - size_t FileSize = Record[I++]; + I++; // Reserved field. + I++; // Reserved field. StringRef Name = Blob; IndexUnitReader::DependencyKind DepKind; @@ -318,7 +318,7 @@ bool IndexUnitReaderImpl::foreachDependency(DependencyReceiver Receiver) { StringRef ModuleName = this->getModuleName(ModuleIndex); return Receiver(IndexUnitReader::DependencyInfo{DepKind, IsSystem, Name, - PathBuf.str(), ModuleName, FileSize, ModTime}); + PathBuf.str(), ModuleName}); }); std::string Error; diff --git a/clang/lib/Index/IndexUnitWriter.cpp b/clang/lib/Index/IndexUnitWriter.cpp index c899cbd5eca1e..c608982c3eeb0 100644 --- a/clang/lib/Index/IndexUnitWriter.cpp +++ b/clang/lib/Index/IndexUnitWriter.cpp @@ -450,8 +450,13 @@ void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream, Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // PathIndex (1-based, 0 = none) Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // ModuleIndex (1-based, 0 = none) - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 32)); // time_t - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // file size + // Reserved. These used to be time_t & file size but we decided against + // writing these in order to get reproducible build products (index data + // output being the same with the same inputs). Keep these reserved for the + // future, for coming up with a better scheme to track state of dependencies + // without using modification time. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 0)); // Reserved + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 0)); // Reserved Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); @@ -472,13 +477,8 @@ void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream, } else { Record.push_back(0); } - if (Data.FileIndex != -1) { - Record.push_back(Files[Data.FileIndex].File->getModificationTime()); - Record.push_back(Files[Data.FileIndex].File->getSize()); - } else { - Record.push_back(0); - Record.push_back(0); - } + Record.push_back(0); // Reserved. + Record.push_back(0); // Reserved. Stream.EmitRecordWithBlob(AbbrevCode, Record, Data.Name); }; @@ -504,8 +504,8 @@ void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream, } else { Record.push_back(0); } - Record.push_back(File.File->getModificationTime()); - Record.push_back(File.File->getSize()); + Record.push_back(0); // Reserved. + Record.push_back(0); // Reserved. Stream.EmitRecordWithBlob(AbbrevCode, Record, StringRef()); } diff --git a/clang/test/Index/Store/print-unit.c b/clang/test/Index/Store/print-unit.c index 19254a1665d4e..3be02b778035d 100644 --- a/clang/test/Index/Store/print-unit.c +++ b/clang/test/Index/Store/print-unit.c @@ -27,7 +27,7 @@ void foo(int i); // CHECK: Record | user | {{.*}}/Inputs/using-overlay.h | using-overlay.h- // CHECK: Record | system | {{.*}}/Inputs/sys/syshead.h | syshead.h- // CHECK: Record | system | {{.*}}/Inputs/sys/another.h | another.h- -// CHECK: File | user | {{.*}}/Inputs/print-unit.h | | {{[0-9]*$}} +// CHECK: File | user | {{.*}}/Inputs/print-unit.h{{$}} // CHECK: DEPEND END (6) // CHECK: INCLUDE START // CHECK: {{.*}}/print-unit.c:3 | {{.*}}/Inputs/print-unit.h diff --git a/clang/test/Index/Store/print-units-with-modules.m b/clang/test/Index/Store/print-units-with-modules.m index cedfe2ceb41e5..0cb9a404a0532 100644 --- a/clang/test/Index/Store/print-units-with-modules.m +++ b/clang/test/Index/Store/print-units-with-modules.m @@ -41,7 +41,7 @@ // CHECK: DEPEND START // CHECK: Record | user | ModTop | {{.*}}/Inputs/module/ModTop.h | ModTop.h // CHECK: Record | user | ModTop.Sub1 | {{.*}}/Inputs/module/ModTopSub1.h | ModTopSub1.h -// CHECK: File | user | ModTop.Sub2 | {{.*}}/Inputs/module/ModTopSub2.h | | {{[0-9]*$}} +// CHECK: File | user | ModTop.Sub2 | {{.*}}/Inputs/module/ModTopSub2.h{{$}} // CHECK: DEPEND END (3) // CHECK: print-units-with-modules.m.tmp.o @@ -54,6 +54,6 @@ // CHECK: DEPEND START // CHECK: Unit | user | ModDep | {{.*}}/ModDep.pcm | ModDep.pcm // CHECK: Unit | system | ModSystem | {{.*}}/ModSystem.pcm | ModSystem.pcm -// CHECK: File | user | {{.*}}/print-units-with-modules.m | | {{[0-9]*$}} -// CHECK: File | user | {{.*}}/Inputs/module/module.modulemap | | {{[0-9]*$}} +// CHECK: File | user | {{.*}}/print-units-with-modules.m{{$}} +// CHECK: File | user | {{.*}}/Inputs/module/module.modulemap{{$}} // CHECK: DEPEND END (4) diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index a80f687fbcd79..4da7ebfcef85a 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -628,18 +628,6 @@ indexstore_unit_dependency_get_name(indexstore_unit_dependency_t c_dep) { return toIndexStoreString(dep->UnitOrRecordName); } -time_t -indexstore_unit_dependency_get_modification_time(indexstore_unit_dependency_t c_dep) { - auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); - return dep->ModTime; -} - -size_t -indexstore_unit_dependency_get_file_size(indexstore_unit_dependency_t c_dep) { - auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep); - return dep->FileSize; -} - indexstore_string_ref_t indexstore_unit_include_get_source_path(indexstore_unit_include_t c_inc) { auto inc = static_cast<const IndexUnitReader::IncludeInfo*>(c_inc); diff --git a/clang/tools/IndexStore/IndexStore.exports b/clang/tools/IndexStore/IndexStore.exports index 70c174f360315..437cee918b98a 100644 --- a/clang/tools/IndexStore/IndexStore.exports +++ b/clang/tools/IndexStore/IndexStore.exports @@ -36,8 +36,6 @@ indexstore_record_reader_occurrences_in_line_range_apply indexstore_record_reader_occurrences_of_symbols_apply indexstore_unit_dependency_get_kind indexstore_unit_dependency_get_filepath -indexstore_unit_dependency_get_file_size -indexstore_unit_dependency_get_modification_time indexstore_unit_dependency_get_modulename indexstore_unit_dependency_get_name indexstore_unit_dependency_is_system diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index 49a058fc952e5..b7cf7d1b03c30 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -507,8 +507,10 @@ static int printUnit(StringRef Filename, raw_ostream &OS) { OS << " | "; if (!Dep.ModuleName.empty()) OS << Dep.ModuleName << " | "; - OS << Dep.FilePath << " | " << Dep.UnitOrRecordName << " | "; - OS << Dep.ModTime << " | " << Dep.FileSize << '\n'; + OS << Dep.FilePath; + if (!Dep.UnitOrRecordName.empty()) + OS << " | " << Dep.UnitOrRecordName; + OS << '\n'; ++NumDepends; return true; }); @@ -564,8 +566,10 @@ static bool printStoreUnit(indexstore::IndexStore &Store, StringRef UnitName, OS << " | "; if (!Dep.getModuleName().empty()) OS << Dep.getModuleName() << " | "; - OS << Dep.getFilePath() << " | " << Dep.getName() << " | "; - OS << Dep.getModificationTime() << '\n'; + OS << Dep.getFilePath(); + if (!Dep.getName().empty()) + OS << " | " << Dep.getName(); + OS << '\n'; ++NumDepends; return true; }); From 4885e729c8fe137249673fdafec68d6f1f9a7357 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sun, 26 Aug 2018 18:49:06 -0700 Subject: [PATCH 307/582] [DirectoryWatcher] Some enhancements to the DirectoryWatcher Reduce the latency value and make it more robust. apple-llvm-split-commit: c21a0ffccd48359fd06d2a2b6cfd09542f552059 apple-llvm-split-dir: clang/ --- .../lib/DirectoryWatcher/DirectoryWatcher.cpp | 158 +++++++++++++----- clang/tools/c-index-test/core_main.cpp | 1 + 2 files changed, 120 insertions(+), 39 deletions(-) diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp index b5434476504f3..e5900de9663fb 100644 --- a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp +++ b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp @@ -12,6 +12,7 @@ #include "clang/DirectoryWatcher/DirectoryWatcher.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" @@ -31,12 +32,70 @@ using namespace clang; using namespace llvm; -static Optional<llvm::sys::TimePoint<>> getModTime(StringRef path) { +static Optional<sys::fs::file_status> getFileStatus(StringRef path) { sys::fs::file_status Status; std::error_code EC = status(path, Status); if (EC) return None; - return Status.getLastModificationTime(); + return Status; +} + +namespace llvm { +// Specialize DenseMapInfo for sys::fs::UniqueID. +template <> struct DenseMapInfo<sys::fs::UniqueID> { + static sys::fs::UniqueID getEmptyKey() { + return sys::fs::UniqueID{DenseMapInfo<uint64_t>::getEmptyKey(), + DenseMapInfo<uint64_t>::getEmptyKey()}; + } + + static sys::fs::UniqueID getTombstoneKey() { + return sys::fs::UniqueID{DenseMapInfo<uint64_t>::getTombstoneKey(), + DenseMapInfo<uint64_t>::getEmptyKey()}; + } + + static unsigned getHashValue(const sys::fs::UniqueID &val) { + return DenseMapInfo<std::pair<uint64_t, uint64_t>>::getHashValue( + std::make_pair(val.getDevice(), val.getFile())); + } + + static bool isEqual(const sys::fs::UniqueID &LHS, const sys::fs::UniqueID &RHS) { + return LHS == RHS; + } +}; +} + +namespace { +/// Used for initial directory scan. +/// +/// Note that it is only accessed while inside the serial queue so it is thread +/// safe to access it without additional protection. +struct DirectoryScan { + DenseSet<sys::fs::UniqueID> FileIDSet; + std::vector<std::tuple<std::string, sys::TimePoint<>>> Files; + + void scanDirectory(StringRef Path) { + using namespace llvm::sys; + + std::error_code EC; + for (auto It = fs::directory_iterator(Path, EC), End = fs::directory_iterator(); + !EC && It != End; It.increment(EC)) { + auto status = getFileStatus(It->path()); + if (!status.hasValue()) + continue; + Files.push_back(std::make_tuple(It->path(), status->getLastModificationTime())); + FileIDSet.insert(status->getUniqueID()); + } + } + + std::vector<DirectoryWatcher::Event> getAsFileEvents() const { + std::vector<DirectoryWatcher::Event> Events; + for (const auto &info : Files) { + DirectoryWatcher::Event Event{DirectoryWatcher::EventKind::Added, std::get<0>(info), std::get<1>(info)}; + Events.push_back(std::move(Event)); + } + return Events; + } +}; } struct DirectoryWatcher::Implementation { @@ -44,7 +103,8 @@ struct DirectoryWatcher::Implementation { FSEventStreamRef EventStream = nullptr; bool setupFSEventStream(StringRef path, EventReceiver receiver, - dispatch_queue_t queue); + dispatch_queue_t queue, + std::shared_ptr<DirectoryScan> initialScanPtr); void stopFSEventStream(); ~Implementation() { @@ -58,9 +118,13 @@ namespace { struct EventStreamContextData { std::string WatchedPath; DirectoryWatcher::EventReceiver Receiver; + std::shared_ptr<DirectoryScan> InitialScan; - EventStreamContextData(std::string watchedPath, DirectoryWatcher::EventReceiver receiver) - : WatchedPath(std::move(watchedPath)), Receiver(std::move(receiver)) { + EventStreamContextData(std::string watchedPath, DirectoryWatcher::EventReceiver receiver, + std::shared_ptr<DirectoryScan> initialScanPtr) + : WatchedPath(std::move(watchedPath)), + Receiver(std::move(receiver)), + InitialScan(std::move(initialScanPtr)) { } static void dispose(const void *ctx) { @@ -91,28 +155,58 @@ static void eventStreamCallback( continue; } DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Modified; - if ((flags & kFSEventStreamEventFlagItemCreated) || - (flags & kFSEventStreamEventFlagItemRenamed)) - K = DirectoryWatcher::EventKind::Added; - if (flags & kFSEventStreamEventFlagItemRemoved) + bool hasAddedFlag = flags & (kFSEventStreamEventFlagItemCreated | + kFSEventStreamEventFlagItemRenamed); + bool hasRemovedFlag = flags & kFSEventStreamEventFlagItemRemoved; + Optional<sys::fs::file_status> statusOpt; + // NOTE: With low latency sometimes for a file that is moved inside the + // directory, or for a file that is removed from the directory, the flags + // have both 'renamed' and 'removed'. We use getting the file status as a + // way to distinguish between the two. + if (hasAddedFlag) { + statusOpt = getFileStatus(path); + if (statusOpt.hasValue()) { + K = DirectoryWatcher::EventKind::Added; + } else { + K = DirectoryWatcher::EventKind::Removed; + } + } else if (hasRemovedFlag) { K = DirectoryWatcher::EventKind::Removed; - llvm::sys::TimePoint<> modTime{}; - if (K != DirectoryWatcher::EventKind::Removed) { - auto modTimeOpt = getModTime(path); - if (!modTimeOpt.hasValue()) + } else { + statusOpt = getFileStatus(path); + if (!statusOpt.hasValue()) { + K = DirectoryWatcher::EventKind::Removed; + } + } + + if (ctx->InitialScan && K == DirectoryWatcher::EventKind::Added) { + // For the first time we get the events, check that we haven't already + // sent the 'added' event at the initial scan. + if (ctx->InitialScan->FileIDSet.count(statusOpt->getUniqueID())) { + // Already reported this event at the initial directory scan. continue; - modTime = modTimeOpt.getValue(); + } } + + llvm::sys::TimePoint<> modTime{}; + if (statusOpt.hasValue()) + modTime = statusOpt->getLastModificationTime(); DirectoryWatcher::Event Evt{K, path, modTime}; Events.push_back(Evt); } - ctx->Receiver(Events, /*isInitial=*/false); + // We won't need to check again later on. + ctx->InitialScan.reset(); + + if (!Events.empty()) { + ctx->Receiver(Events, /*isInitial=*/false); + } } bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path, EventReceiver receiver, - dispatch_queue_t queue) { + dispatch_queue_t queue, + std::shared_ptr<DirectoryScan> initialScanPtr) { if (path.empty()) return true; @@ -120,7 +214,7 @@ bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path, CFStringRef cfPathStr = CFStringCreateWithBytes(nullptr, (const UInt8 *)path.data(), path.size(), kCFStringEncodingUTF8, false); CFArrayAppendValue(pathsToWatch, cfPathStr); CFRelease(cfPathStr); - CFAbsoluteTime latency = 0.2; // Latency in seconds. + CFAbsoluteTime latency = 0.0; // Latency in seconds. std::string realPath; { @@ -134,7 +228,9 @@ bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path, realPath = path; } - EventStreamContextData *ctxData = new EventStreamContextData(std::move(realPath), std::move(receiver)); + EventStreamContextData *ctxData = + new EventStreamContextData(std::move(realPath), std::move(receiver), + std::move(initialScanPtr)); FSEventStreamContext context; context.version = 0; context.info = ctxData; @@ -176,24 +272,6 @@ DirectoryWatcher::~DirectoryWatcher() { delete &Impl; } -#if HAVE_CORESERVICES -static std::vector<DirectoryWatcher::Event> scanDirectory(StringRef Path) { - using namespace llvm::sys; - - std::vector<DirectoryWatcher::Event> Events; - std::error_code EC; - for (auto It = fs::directory_iterator(Path, EC), End = fs::directory_iterator(); - !EC && It != End; It.increment(EC)) { - auto modTime = getModTime(It->path()); - if (!modTime.hasValue()) - continue; - DirectoryWatcher::Event Event{DirectoryWatcher::EventKind::Added, It->path(), modTime.getValue()}; - Events.push_back(std::move(Event)); - } - return Events; -} -#endif - std::unique_ptr<DirectoryWatcher> DirectoryWatcher::create(StringRef Path, EventReceiver Receiver, bool waitInitialSync, std::string &Error) { #if HAVE_CORESERVICES @@ -224,6 +302,8 @@ std::unique_ptr<DirectoryWatcher> DirectoryWatcher::create(StringRef Path, DirWatch.reset(new DirectoryWatcher()); auto &Impl = DirWatch->Impl; + auto initialScan = std::make_shared<DirectoryScan>(); + dispatch_queue_t queue = dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL); dispatch_semaphore_t initScanSema = dispatch_semaphore_create(0); dispatch_semaphore_t setupFSEventsSema = dispatch_semaphore_create(0); @@ -235,13 +315,13 @@ std::unique_ptr<DirectoryWatcher> DirectoryWatcher::create(StringRef Path, // Wait for the event stream to be setup before doing the initial scan, // to make sure we won't miss any events. dispatch_semaphore_wait(setupFSEventsSema, DISPATCH_TIME_FOREVER); - auto events = scanDirectory(copiedPath); - Receiver(events, /*isInitial=*/true); + initialScan->scanDirectory(copiedPath); + Receiver(initialScan->getAsFileEvents(), /*isInitial=*/true); dispatch_semaphore_signal(initScanSema); dispatch_release(setupFSEventsSema); dispatch_release(initScanSema); }); - bool fsErr = Impl.setupFSEventStream(Path, Receiver, queue); + bool fsErr = Impl.setupFSEventStream(Path, Receiver, queue, initialScan); dispatch_semaphore_signal(setupFSEventsSema); if (waitInitialSync) { diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index b7cf7d1b03c30..f665a98df04f2 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -799,6 +799,7 @@ static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { static int watchDirectory(StringRef dirPath) { raw_ostream &OS = outs(); auto receiver = [&](ArrayRef<DirectoryWatcher::Event> Events, bool isInitial) { + OS << "-- " << Events.size() << " :\n"; for (auto evt : Events) { switch (evt.Kind) { case DirectoryWatcher::EventKind::Added: From 178f4caddbb2cd1b2e7e9f66f3e6f4d3900814f8 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 6 Sep 2018 02:31:53 -0700 Subject: [PATCH 308/582] Revert "[DebugInfo] Normalize common kinds of DWARF sub-expressions." This reverts commit 846d4ba8936132ff7774c0bc7a50caa849596f69 while we investigate why this breaks some swift-lldb tests. apple-llvm-split-commit: 83ff6830d801c65fa5ed43095df2db5c822d871d apple-llvm-split-dir: llvm/ --- .../CodeGen/AsmPrinter/DwarfExpression.cpp | 26 +++++--------- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h | 3 -- .../DebugInfo/AMDGPU/variable-locations.ll | 6 ++-- llvm/test/DebugInfo/ARM/PR26163.ll | 4 +-- llvm/test/DebugInfo/ARM/split-complex.ll | 2 +- .../Generic/incorrect-variable-debugloc1.ll | 11 +++--- llvm/test/DebugInfo/X86/PR26148.ll | 4 +-- llvm/test/DebugInfo/X86/constant-loclist.ll | 2 +- llvm/test/DebugInfo/X86/dw_op_minus_direct.ll | 2 +- llvm/test/DebugInfo/X86/partial-constant.ll | 2 +- llvm/test/DebugInfo/X86/pieces-4.ll | 2 +- llvm/test/DebugInfo/X86/split-global.ll | 4 +-- llvm/test/DebugInfo/X86/stack-value-dwarf4.ll | 34 ++++++++++--------- llvm/test/DebugInfo/X86/stack-value-piece.ll | 8 ++--- 14 files changed, 49 insertions(+), 61 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp index af51d27663449..d8d1a5e8f8411 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -24,20 +24,6 @@ using namespace llvm; -void DwarfExpression::emitConstu(uint64_t Value) { - if (Value < 32) - emitOp(dwarf::DW_OP_lit0 + Value); - else if (Value == std::numeric_limits<uint64_t>::max()) { - // Only do this for 64-bit values as the DWARF expression stack uses - // target-address-size values. - emitOp(dwarf::DW_OP_lit0); - emitOp(dwarf::DW_OP_not); - } else { - emitOp(dwarf::DW_OP_constu); - emitUnsigned(Value); - } -} - void DwarfExpression::addReg(int DwarfReg, const char *Comment) { assert(DwarfReg >= 0 && "invalid negative dwarf register number"); assert((LocationKind == Unknown || LocationKind == Register) && @@ -86,12 +72,14 @@ void DwarfExpression::addOpPiece(unsigned SizeInBits, unsigned OffsetInBits) { } void DwarfExpression::addShr(unsigned ShiftBy) { - emitConstu(ShiftBy); + emitOp(dwarf::DW_OP_constu); + emitUnsigned(ShiftBy); emitOp(dwarf::DW_OP_shr); } void DwarfExpression::addAnd(unsigned Mask) { - emitConstu(Mask); + emitOp(dwarf::DW_OP_constu); + emitUnsigned(Mask); emitOp(dwarf::DW_OP_and); } @@ -193,7 +181,8 @@ void DwarfExpression::addSignedConstant(int64_t Value) { void DwarfExpression::addUnsignedConstant(uint64_t Value) { assert(LocationKind == Implicit || LocationKind == Unknown); LocationKind = Implicit; - emitConstu(Value); + emitOp(dwarf::DW_OP_constu); + emitUnsigned(Value); } void DwarfExpression::addUnsignedConstant(const APInt &Value) { @@ -384,7 +373,8 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor, break; case dwarf::DW_OP_constu: assert(LocationKind != Register); - emitConstu(Op->getArg(0)); + emitOp(dwarf::DW_OP_constu); + emitUnsigned(Op->getArg(0)); break; case dwarf::DW_OP_stack_value: LocationKind = Implicit; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h index d47c4d1d29696..0637d952eba4d 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -138,9 +138,6 @@ class DwarfExpression { /// Emit a raw unsigned value. virtual void emitUnsigned(uint64_t Value) = 0; - /// Emit a normalized unsigned constant. - void emitConstu(uint64_t Value); - /// Return whether the given machine register is the frame register in the /// current function. virtual bool isFrameRegister(const TargetRegisterInfo &TRI, unsigned MachineReg) = 0; diff --git a/llvm/test/DebugInfo/AMDGPU/variable-locations.ll b/llvm/test/DebugInfo/AMDGPU/variable-locations.ll index df0628a2ca169..75859edb6aff8 100644 --- a/llvm/test/DebugInfo/AMDGPU/variable-locations.ll +++ b/llvm/test/DebugInfo/AMDGPU/variable-locations.ll @@ -36,15 +36,15 @@ declare void @llvm.dbg.declare(metadata, metadata, metadata) define amdgpu_kernel void @kernel1( ; CHECK: {{.*}}DW_TAG_formal_parameter -; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +4, DW_OP_lit1, DW_OP_swap, DW_OP_xderef) +; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +4, DW_OP_constu 0x1, DW_OP_swap, DW_OP_xderef) ; CHECK-NEXT: DW_AT_name {{.*}}"ArgN" i32 %ArgN, ; CHECK: {{.*}}DW_TAG_formal_parameter -; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +8, DW_OP_lit1, DW_OP_swap, DW_OP_xderef) +; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +8, DW_OP_constu 0x1, DW_OP_swap, DW_OP_xderef) ; CHECK-NEXT: DW_AT_name {{.*}}"ArgA" i32 addrspace(1)* %ArgA, ; CHECK: {{.*}}DW_TAG_formal_parameter -; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +16, DW_OP_lit1, DW_OP_swap, DW_OP_xderef) +; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +16, DW_OP_constu 0x1, DW_OP_swap, DW_OP_xderef) ; CHECK-NEXT: DW_AT_name {{.*}}"ArgB" i32 addrspace(1)* %ArgB) !dbg !13 { entry: diff --git a/llvm/test/DebugInfo/ARM/PR26163.ll b/llvm/test/DebugInfo/ARM/PR26163.ll index 3b3ec9c3f4f86..5b7bdc19513c2 100644 --- a/llvm/test/DebugInfo/ARM/PR26163.ll +++ b/llvm/test/DebugInfo/ARM/PR26163.ll @@ -9,8 +9,8 @@ ; CHECK: DW_TAG_inlined_subroutine ; CHECK: DW_TAG_variable ; CHECK: DW_AT_location [DW_FORM_sec_offset] ({{.*}} -; CHECK: [0x00000004, 0x00000004): DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x8 -; CHECK: [0x00000004, 0x00000014): DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK: [0x00000004, 0x00000004): DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x8 +; CHECK: [0x00000004, 0x00000014): DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4) ; Created form the following test case (PR26163) with ; clang -cc1 -triple armv4t--freebsd11.0-gnueabi -emit-obj -debug-info-kind=standalone -O2 -x c test.c diff --git a/llvm/test/DebugInfo/ARM/split-complex.ll b/llvm/test/DebugInfo/ARM/split-complex.ll index 280569311fcba..fcda92d114b02 100644 --- a/llvm/test/DebugInfo/ARM/split-complex.ll +++ b/llvm/test/DebugInfo/ARM/split-complex.ll @@ -14,7 +14,7 @@ entry: ; The target has no native double type. ; SROA split the complex value into two i64 values. ; CHECK: DW_TAG_formal_parameter - ; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_lit0, DW_OP_piece 0x8) + ; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_constu 0x0, DW_OP_piece 0x8) ; CHECK-NEXT: DW_AT_name {{.*}} "c" tail call void @llvm.dbg.value(metadata i64 0, metadata !14, metadata !17), !dbg !16 ; Manually removed to disable location list emission: diff --git a/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll b/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll index df7fb643e6edb..76c7e560b87c1 100644 --- a/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll +++ b/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll @@ -1,6 +1,6 @@ ; REQUIRES: object-emission ; This test is failing for powerpc64, because a location list for the -; variable 'c' is not generated at all. Temporary marking this test as XFAIL +; variable 'c' is not generated at all. Temporary marking this test as XFAIL ; for powerpc, until PR21881 is fixed. ; XFAIL: powerpc64 @@ -9,14 +9,13 @@ ; RUN: %llc_dwarf -O2 -dwarf-version 4 -filetype=obj < %s | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF4 ; This is a test for PR21176. -; DW_OP_const <const> doesn't describe a constant value, but a value at a constant address. +; DW_OP_const <const> doesn't describe a constant value, but a value at a constant address. ; The proper way to describe a constant value is DW_OP_constu <const>, DW_OP_stack_value. -; For values < 32 we emit the canonical DW_OP_lit<const>. ; Generated with clang -S -emit-llvm -g -O2 test.cpp ; extern int func(); -; +; ; int main() ; { ; volatile int c = 13; @@ -27,8 +26,8 @@ ; CHECK: DW_TAG_variable ; CHECK: DW_AT_location ; CHECK-NOT: DW_AT -; DWARF23: DW_OP_lit13{{$}} -; DWARF4: DW_OP_lit13, DW_OP_stack_value{{$}} +; DWARF23: DW_OP_constu 0xd{{$}} +; DWARF4: DW_OP_constu 0xd, DW_OP_stack_value{{$}} ; Function Attrs: uwtable define i32 @main() #0 !dbg !4 { diff --git a/llvm/test/DebugInfo/X86/PR26148.ll b/llvm/test/DebugInfo/X86/PR26148.ll index 685f2d503717e..198776ef9535e 100644 --- a/llvm/test/DebugInfo/X86/PR26148.ll +++ b/llvm/test/DebugInfo/X86/PR26148.ll @@ -19,8 +19,8 @@ ; AS in 26163, we expect two ranges (as opposed to one), the first one being zero sized ; ; -; CHECK: [0x0000000000000004, 0x0000000000000004): DW_OP_lit3, DW_OP_piece 0x4, DW_OP_reg5 RDI, DW_OP_piece 0x2 -; CHECK: [0x0000000000000004, 0x0000000000000014): DW_OP_lit3, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_piece 0x4 +; CHECK: [0x0000000000000004, 0x0000000000000004): DW_OP_constu 0x3, DW_OP_piece 0x4, DW_OP_reg5 RDI, DW_OP_piece 0x2 +; CHECK: [0x0000000000000004, 0x0000000000000014): DW_OP_constu 0x3, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_piece 0x4 source_filename = "test/DebugInfo/X86/PR26148.ll" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/DebugInfo/X86/constant-loclist.ll b/llvm/test/DebugInfo/X86/constant-loclist.ll index 17643d1eab724..981068317d771 100644 --- a/llvm/test/DebugInfo/X86/constant-loclist.ll +++ b/llvm/test/DebugInfo/X86/constant-loclist.ll @@ -14,7 +14,7 @@ ; CHECK-NEXT: DW_AT_name {{.*}}"i" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_location [DW_FORM_data4] ( -; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_lit0 +; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_constu 0x0 ; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_constu 0x4000000000000000) ; CHECK-NEXT: DW_AT_name {{.*}}"u" diff --git a/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll b/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll index bc4241c1cc1be..a801347c2f027 100644 --- a/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll +++ b/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll @@ -17,7 +17,7 @@ ; CHECK: .debug_loc contents: ; CHECK: 0x00000000: -; CHECK-NEXT: [0x0000000000000000, 0x0000000000000004): DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_lit1, DW_OP_minus, DW_OP_stack_value +; CHECK-NEXT: [0x0000000000000000, 0x0000000000000004): DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_constu 0x1, DW_OP_minus, DW_OP_stack_value ; rax+0, constu 0xffffffff, and, constu 0x00000001, minus, stack-value source_filename = "minus.c" diff --git a/llvm/test/DebugInfo/X86/partial-constant.ll b/llvm/test/DebugInfo/X86/partial-constant.ll index 831d6165c0b3a..0c02cf125211c 100644 --- a/llvm/test/DebugInfo/X86/partial-constant.ll +++ b/llvm/test/DebugInfo/X86/partial-constant.ll @@ -18,7 +18,7 @@ ; CHECK-NOT: DW_AT_const_value ; CHECK: .debug_loc contents: ; CHECK-NEXT: 0x00000000: -; CHECK-NEXT: {{.*}}: DW_OP_lit1, DW_OP_stack_value +; CHECK-NEXT: {{.*}}: DW_OP_constu 0x1, DW_OP_stack_value source_filename = "test.ii" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/DebugInfo/X86/pieces-4.ll b/llvm/test/DebugInfo/X86/pieces-4.ll index 6ae8597e424cf..0e8c5e1185d0f 100644 --- a/llvm/test/DebugInfo/X86/pieces-4.ll +++ b/llvm/test/DebugInfo/X86/pieces-4.ll @@ -25,7 +25,7 @@ ; DWARF: .debug_loc contents: ; DWARF-NEXT: 0x00000000: -; DWARF-NEXT: {{.*}}: DW_OP_breg7 RSP+{{[0-9]+}}, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4 +; DWARF-NEXT: {{.*}}: DW_OP_breg7 RSP+{{[0-9]+}}, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4 ; ModuleID = 't.c' source_filename = "t.c" diff --git a/llvm/test/DebugInfo/X86/split-global.ll b/llvm/test/DebugInfo/X86/split-global.ll index abe0a820cfc37..a47dd532807cd 100644 --- a/llvm/test/DebugInfo/X86/split-global.ll +++ b/llvm/test/DebugInfo/X86/split-global.ll @@ -17,13 +17,13 @@ target triple = "x86_64-apple-macosx10.12.0" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_name {{.*}}"part_const" ; CHECK-NOT: DW_TAG -; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x8, DW_OP_piece 0x4, DW_OP_lit2, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x8, DW_OP_piece 0x4, DW_OP_constu 0x2, DW_OP_stack_value, DW_OP_piece 0x4) ; [0x0000000000000008], piece 0x00000004, constu 0x00000002, stack-value, piece 0x00000004 ; CHECK-NOT: DW_TAG ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_name {{.*}}"full_const" ; CHECK-NOT: DW_TAG -; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_lit1, DW_OP_stack_value, DW_OP_piece 0x4, DW_OP_lit2, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_constu 0x1, DW_OP_stack_value, DW_OP_piece 0x4, DW_OP_constu 0x2, DW_OP_stack_value, DW_OP_piece 0x4) ; CHECK-NOT: DW_TAG @point.y = global i32 2, align 4, !dbg !13 @point.x = global i32 1, align 4, !dbg !12 diff --git a/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll b/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll index 479625b6df8fe..7ad7cceb7ff0d 100644 --- a/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll +++ b/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll @@ -6,23 +6,25 @@ target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" -; CHECK-DWARF2: .byte 6 # DW_AT_location -; CHECK-DWARF2-NEXT: .byte 52 -; CHECK-DWARF2-NEXT: .byte 147 -; CHECK-DWARF2-NEXT: .byte 2 -; CHECK-DWARF2-NEXT: .byte 48 -; CHECK-DWARF2-NEXT: .byte 147 -; CHECK-DWARF2-NEXT: .byte 2 +; CHECK-DWARF2: .byte 8 # DW_AT_location +; CHECK-DWARF2 .byte 16 +; CHECK-DWARF2 .byte 4 +; CHECK-DWARF2 .byte 147 +; CHECK-DWARF2 .byte 2 +; CHECK-DWARF2 .byte 16 +; CHECK-DWARF2 .byte 0 +; CHECK-DWARF2 .byte 147 +; CHECK-DWARF2 .byte 2 -; CHECK-DWARF4: .byte 8 # DW_AT_location -; CHECK-DWARF4-NEXT:.byte 52 -; CHECK-DWARF4-NEXT:.byte 159 -; CHECK-DWARF4-NEXT:.byte 147 -; CHECK-DWARF4-NEXT:.byte 2 -; CHECK-DWARF4-NEXT:.byte 48 -; CHECK-DWARF4-NEXT:.byte 159 -; CHECK-DWARF4-NEXT:.byte 147 -; CHECK-DWARF4-NEXT:.byte 2 +; CHECK-DWARF4: .byte 10 # DW_AT_location +; CHECK-DWARF4-NEXT: .byte 16 +; CHECK-DWARF4-NEXT: .byte 4 +; CHECK-DWARF4-NEXT: .byte 159 +; CHECK-DWARF4-NEXT: .byte 147 +; CHECK-DWARF4-NEXT: .byte 2 +; CHECK-DWARF4-NEXT: .byte 16 +; CHECK-DWARF4-NEXT: .byte 0 +; CHECK-DWARF4-NEXT: .byte 159 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang", file: !4, globals: !1, emissionKind: FullDebug) !1 = !{!2, !10} diff --git a/llvm/test/DebugInfo/X86/stack-value-piece.ll b/llvm/test/DebugInfo/X86/stack-value-piece.ll index b6a03c3aa7755..995504f614cde 100644 --- a/llvm/test/DebugInfo/X86/stack-value-piece.ll +++ b/llvm/test/DebugInfo/X86/stack-value-piece.ll @@ -19,21 +19,21 @@ ; CHECK: DW_AT_name {{.*}} "i" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_location {{.*}} ([[I:.*]] -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4) ; CHECK-NEXT: DW_AT_name {{.*}} "r" ; ; CHECK: DW_TAG_subprogram ; CHECK: DW_AT_name {{.*}} "f" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_location {{.*}} ([[F:.*]] -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4) ; CHECK-NEXT: DW_AT_name {{.*}} "r" ; ; CHECK: .debug_loc contents: ; CHECK: [[I]]: -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4 +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4 ; CHECK: [[F]]: -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4 +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4 source_filename = "stack-value-piece.c" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" From 6e57de226f0637e1dde705f418f3d54984ec0b21 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sun, 9 Sep 2018 10:43:36 -0700 Subject: [PATCH 309/582] [index] Some refactoring to simplify how DirectoryWatcher is used for index-store watching apple-llvm-split-commit: 0c9459986cb985869939282eff368767bf9f3701 apple-llvm-split-dir: clang/ --- .../clang/DirectoryWatcher/DirectoryWatcher.h | 36 +++++++--- clang/include/clang/Index/IndexDataStore.h | 30 +-------- clang/lib/CMakeLists.txt | 3 +- clang/lib/DirectoryWatcher/CMakeLists.txt | 3 - clang/lib/Index/CMakeLists.txt | 1 - clang/lib/Index/IndexDataStoreUtils.cpp | 33 ++++++++++ clang/lib/IndexDataStore/CMakeLists.txt | 11 ++++ .../IndexDataStore.cpp | 65 +++++-------------- clang/tools/IndexStore/CMakeLists.txt | 3 +- clang/tools/IndexStore/IndexStore.cpp | 6 +- 10 files changed, 92 insertions(+), 99 deletions(-) create mode 100644 clang/lib/IndexDataStore/CMakeLists.txt rename clang/lib/{Index => IndexDataStore}/IndexDataStore.cpp (70%) diff --git a/clang/include/clang/DirectoryWatcher/DirectoryWatcher.h b/clang/include/clang/DirectoryWatcher/DirectoryWatcher.h index 09d17a996126c..2739955a11307 100644 --- a/clang/include/clang/DirectoryWatcher/DirectoryWatcher.h +++ b/clang/include/clang/DirectoryWatcher/DirectoryWatcher.h @@ -14,7 +14,7 @@ #define LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H #include "clang/Basic/LLVM.h" -#include "clang/Index/IndexDataStore.h" +#include "llvm/Support/Chrono.h" #include <functional> #include <memory> #include <string> @@ -25,21 +25,41 @@ namespace clang { /// /// Guarantees that the first time the directory is processed, the receiver will /// be invoked even if the directory is empty. -class DirectoryWatcher : public index::AbstractDirectoryWatcher { - struct Implementation; - Implementation &Impl; +class DirectoryWatcher { +public: + enum class EventKind { + /// A file was added. + Added, + /// A file was removed. + Removed, + /// A file was modified. + Modified, + /// The watched directory got deleted. No more events will follow. + DirectoryDeleted, + }; - DirectoryWatcher(); + struct Event { + EventKind Kind; + std::string Filename; + llvm::sys::TimePoint<> ModTime; + }; - DirectoryWatcher(const DirectoryWatcher&) = delete; - DirectoryWatcher &operator =(const DirectoryWatcher&) = delete; + typedef std::function<void(ArrayRef<Event> Events, bool isInitial)> EventReceiver; -public: ~DirectoryWatcher(); static std::unique_ptr<DirectoryWatcher> create(StringRef Path, EventReceiver Receiver, bool waitInitialSync, std::string &Error); + +private: + struct Implementation; + Implementation &Impl; + + DirectoryWatcher(); + + DirectoryWatcher(const DirectoryWatcher&) = delete; + DirectoryWatcher &operator =(const DirectoryWatcher&) = delete; }; } // namespace clang diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h index c6b3e40e39650..145a83dbcb7c8 100644 --- a/clang/include/clang/Index/IndexDataStore.h +++ b/clang/include/clang/Index/IndexDataStore.h @@ -18,37 +18,10 @@ #include <functional> #include <memory> #include <string> -#include <vector> namespace clang { namespace index { -class AbstractDirectoryWatcher { -public: - enum class EventKind { - /// A file was added. - Added, - /// A file was removed. - Removed, - /// A file was modified. - Modified, - /// The watched directory got deleted. No more events will follow. - DirectoryDeleted, - }; - - struct Event { - EventKind Kind; - std::string Filename; - llvm::sys::TimePoint<> ModTime; - }; - - typedef std::function<void(ArrayRef<Event> Events, bool isInitial)> EventReceiver; - typedef std::unique_ptr<AbstractDirectoryWatcher>(CreateFnTy) - (StringRef Path, EventReceiver Receiver, bool waitInitialSync, std::string &Error); - - virtual ~AbstractDirectoryWatcher() {} -}; - class IndexDataStore { public: ~IndexDataStore(); @@ -82,8 +55,7 @@ class IndexDataStore { void setUnitEventHandler(UnitEventHandler Handler); /// \returns true if an error occurred. - bool startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, - bool waitInitialSync, std::string &Error); + bool startEventListening(bool waitInitialSync, std::string &Error); void stopEventListening(); void discardUnit(StringRef UnitName); diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index 6fccc2dac6df1..adcfee8a16649 100644 --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -19,8 +19,9 @@ add_subdirectory(Serialization) add_subdirectory(Frontend) add_subdirectory(FrontendTool) add_subdirectory(Tooling) -add_subdirectory(Index) add_subdirectory(DirectoryWatcher) +add_subdirectory(Index) +add_subdirectory(IndexDataStore) if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) endif() diff --git a/clang/lib/DirectoryWatcher/CMakeLists.txt b/clang/lib/DirectoryWatcher/CMakeLists.txt index b70e92526592e..5b506166b7aea 100644 --- a/clang/lib/DirectoryWatcher/CMakeLists.txt +++ b/clang/lib/DirectoryWatcher/CMakeLists.txt @@ -4,9 +4,6 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangDirectoryWatcher DirectoryWatcher.cpp - - LINK_LIBS - clangBasic ) if(BUILD_SHARED_LIBS) diff --git a/clang/lib/Index/CMakeLists.txt b/clang/lib/Index/CMakeLists.txt index bb17d15b55269..ff30c74e803e3 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -10,7 +10,6 @@ add_clang_library(clangIndex CommentToXML.cpp FileIndexRecord.cpp IndexBody.cpp - IndexDataStore.cpp IndexDataStoreUtils.cpp IndexDecl.cpp IndexingAction.cpp diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 5d185b5c41409..39dea609c83eb 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -9,13 +9,46 @@ #include "clang/Index/IndexDataStoreSymbolUtils.h" #include "IndexDataStoreUtils.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace clang::index; using namespace clang::index::store; using namespace llvm; +static void appendSubDir(StringRef subdir, SmallVectorImpl<char> &StorePathBuf) { + SmallString<10> VersionPath; + raw_svector_ostream(VersionPath) << 'v' << STORE_FORMAT_VERSION; + + sys::path::append(StorePathBuf, VersionPath); + sys::path::append(StorePathBuf, subdir); +} + +void store::appendInteriorUnitPath(StringRef UnitName, + SmallVectorImpl<char> &PathBuf) { + sys::path::append(PathBuf, UnitName); +} + +void store::appendUnitSubDir(SmallVectorImpl<char> &StorePathBuf) { + return appendSubDir("units", StorePathBuf); +} + +void store::appendRecordSubDir(SmallVectorImpl<char> &StorePathBuf) { + return appendSubDir("records", StorePathBuf); +} + +void store::appendInteriorRecordPath(StringRef RecordName, + SmallVectorImpl<char> &PathBuf) { + // To avoid putting a huge number of files into the records directory, create + // subdirectories based on the last 2 characters from the hash. + StringRef hash2chars = RecordName.substr(RecordName.size()-2); + sys::path::append(PathBuf, hash2chars); + sys::path::append(PathBuf, RecordName); +} + void store::emitBlockID(unsigned ID, const char *Name, BitstreamWriter &Stream, RecordDataImpl &Record) { Record.clear(); diff --git a/clang/lib/IndexDataStore/CMakeLists.txt b/clang/lib/IndexDataStore/CMakeLists.txt new file mode 100644 index 0000000000000..35c8dc05631fd --- /dev/null +++ b/clang/lib/IndexDataStore/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_library(clangIndexDataStore + IndexDataStore.cpp + + LINK_LIBS + clangDirectoryWatcher + clangIndex + ) diff --git a/clang/lib/Index/IndexDataStore.cpp b/clang/lib/IndexDataStore/IndexDataStore.cpp similarity index 70% rename from clang/lib/Index/IndexDataStore.cpp rename to clang/lib/IndexDataStore/IndexDataStore.cpp index 4140392dfc565..ec7a346935cc4 100644 --- a/clang/lib/Index/IndexDataStore.cpp +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -8,7 +8,8 @@ //===----------------------------------------------------------------------===// #include "clang/Index/IndexDataStore.h" -#include "IndexDataStoreUtils.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "../lib/Index/IndexDataStoreUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/FileSystem.h" @@ -21,40 +22,6 @@ using namespace clang::index; using namespace clang::index::store; using namespace llvm; -static void appendSubDir(StringRef subdir, SmallVectorImpl<char> &StorePathBuf) { - SmallString<10> VersionPath; - raw_svector_ostream(VersionPath) << 'v' << STORE_FORMAT_VERSION; - - sys::path::append(StorePathBuf, VersionPath); - sys::path::append(StorePathBuf, subdir); -} - -void store::appendInteriorUnitPath(StringRef UnitName, - SmallVectorImpl<char> &PathBuf) { - sys::path::append(PathBuf, UnitName); -} - -void store::appendUnitSubDir(SmallVectorImpl<char> &StorePathBuf) { - return appendSubDir("units", StorePathBuf); -} - -void store::appendRecordSubDir(SmallVectorImpl<char> &StorePathBuf) { - return appendSubDir("records", StorePathBuf); -} - -void store::appendInteriorRecordPath(StringRef RecordName, - SmallVectorImpl<char> &PathBuf) { - // To avoid putting a huge number of files into the records directory, create - // subdirectories based on the last 2 characters from the hash. - StringRef hash2chars = RecordName.substr(RecordName.size()-2); - sys::path::append(PathBuf, hash2chars); - sys::path::append(PathBuf, RecordName); -} - -//===----------------------------------------------------------------------===// -// IndexDataStore -//===----------------------------------------------------------------------===// - namespace { class UnitEventHandlerData { @@ -75,7 +42,7 @@ class UnitEventHandlerData { class IndexDataStoreImpl { std::string FilePath; std::shared_ptr<UnitEventHandlerData> TheUnitEventHandlerData; - std::unique_ptr<AbstractDirectoryWatcher> DirWatcher; + std::unique_ptr<DirectoryWatcher> DirWatcher; public: explicit IndexDataStoreImpl(StringRef indexStorePath) @@ -87,8 +54,7 @@ class IndexDataStoreImpl { bool foreachUnitName(bool sorted, llvm::function_ref<bool(StringRef unitName)> receiver); void setUnitEventHandler(IndexDataStore::UnitEventHandler Handler); - bool startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, - bool waitInitialSync, std::string &Error); + bool startEventListening(bool waitInitialSync, std::string &Error); void stopEventListening(); void discardUnit(StringRef UnitName); void discardRecord(StringRef RecordName); @@ -131,8 +97,7 @@ void IndexDataStoreImpl::setUnitEventHandler(IndexDataStore::UnitEventHandler ha TheUnitEventHandlerData->setHandler(std::move(handler)); } -bool IndexDataStoreImpl::startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, - bool waitInitialSync, std::string &Error) { +bool IndexDataStoreImpl::startEventListening(bool waitInitialSync, std::string &Error) { if (DirWatcher) { Error = "event listener already active"; return true; @@ -143,20 +108,20 @@ bool IndexDataStoreImpl::startEventListening(llvm::function_ref<AbstractDirector appendUnitSubDir(UnitPath); auto localUnitEventHandlerData = TheUnitEventHandlerData; - auto OnUnitsChange = [localUnitEventHandlerData](ArrayRef<AbstractDirectoryWatcher::Event> Events, bool isInitial) { + auto OnUnitsChange = [localUnitEventHandlerData](ArrayRef<DirectoryWatcher::Event> Events, bool isInitial) { SmallVector<IndexDataStore::UnitEvent, 16> UnitEvents; UnitEvents.reserve(Events.size()); - for (const AbstractDirectoryWatcher::Event &evt : Events) { + for (const DirectoryWatcher::Event &evt : Events) { IndexDataStore::UnitEventKind K; StringRef UnitName = sys::path::filename(evt.Filename); switch (evt.Kind) { - case AbstractDirectoryWatcher::EventKind::Added: + case DirectoryWatcher::EventKind::Added: K = IndexDataStore::UnitEventKind::Added; break; - case AbstractDirectoryWatcher::EventKind::Removed: + case DirectoryWatcher::EventKind::Removed: K = IndexDataStore::UnitEventKind::Removed; break; - case AbstractDirectoryWatcher::EventKind::Modified: + case DirectoryWatcher::EventKind::Modified: K = IndexDataStore::UnitEventKind::Modified; break; - case AbstractDirectoryWatcher::EventKind::DirectoryDeleted: + case DirectoryWatcher::EventKind::DirectoryDeleted: K = IndexDataStore::UnitEventKind::DirectoryDeleted; UnitName = StringRef(); break; @@ -170,7 +135,8 @@ bool IndexDataStoreImpl::startEventListening(llvm::function_ref<AbstractDirector } }; - DirWatcher = createFn(UnitPath.str(), OnUnitsChange, waitInitialSync, Error); + DirWatcher = DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, + waitInitialSync, Error); if (!DirWatcher) return true; @@ -237,9 +203,8 @@ void IndexDataStore::setUnitEventHandler(UnitEventHandler Handler) { return IMPL->setUnitEventHandler(std::move(Handler)); } -bool IndexDataStore::startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn, - bool waitInitialSync, std::string &Error) { - return IMPL->startEventListening(std::move(createFn), waitInitialSync, Error); +bool IndexDataStore::startEventListening(bool waitInitialSync, std::string &Error) { + return IMPL->startEventListening(waitInitialSync, Error); } void IndexDataStore::stopEventListening() { diff --git a/clang/tools/IndexStore/CMakeLists.txt b/clang/tools/IndexStore/CMakeLists.txt index 616c402c145a0..e1680296e06d5 100644 --- a/clang/tools/IndexStore/CMakeLists.txt +++ b/clang/tools/IndexStore/CMakeLists.txt @@ -9,8 +9,7 @@ set(SOURCES ) set(LIBS - clangDirectoryWatcher - clangIndex + clangIndexDataStore ) set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/IndexStore.exports) diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index 4da7ebfcef85a..9db61441d1259 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -222,11 +222,7 @@ indexstore_store_start_unit_event_listening(indexstore_t c_store, memcpy(&listen_opts, client_opts, clientOptSize); std::string error; - auto createFn = [](StringRef Path, AbstractDirectoryWatcher::EventReceiver Receiver, bool waitInitialSync, std::string &Error) - -> std::unique_ptr<AbstractDirectoryWatcher> { - return DirectoryWatcher::create(Path, std::move(Receiver), waitInitialSync, Error); - }; - bool err = store->startEventListening(createFn, listen_opts.wait_initial_sync, error); + bool err = store->startEventListening(listen_opts.wait_initial_sync, error); if (err && c_error) *c_error = new IndexStoreError{ error }; return err; From 3d14643f372a0ec882be3f571ecda8303a579a33 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sun, 9 Sep 2018 11:37:47 -0700 Subject: [PATCH 310/582] [test/index/store] Remove or put 'XFAIL: linux' instances appropriately apple-llvm-split-commit: c4e7690d4ec78282366b63b87edfcf3e09a8d28f apple-llvm-split-dir: clang/ --- clang/test/Index/Store/external-source-symbol-hash.m | 2 ++ clang/test/Index/Store/record-hash-using.cpp | 2 -- clang/test/Index/Store/record-hash.cpp | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/clang/test/Index/Store/external-source-symbol-hash.m b/clang/test/Index/Store/external-source-symbol-hash.m index 1b4f89d9251f3..b9792eadb81e5 100644 --- a/clang/test/Index/Store/external-source-symbol-hash.m +++ b/clang/test/Index/Store/external-source-symbol-hash.m @@ -1,3 +1,5 @@ +// XFAIL: linux + // RUN: rm -rf %t.idx // RUN: %clang_cc1 %s -index-store-path %t.idx -D USE_EXTERNAL // RUN: c-index-test core -print-record %t.idx | FileCheck %s diff --git a/clang/test/Index/Store/record-hash-using.cpp b/clang/test/Index/Store/record-hash-using.cpp index c8285d5e96c96..5c81eda574656 100644 --- a/clang/test/Index/Store/record-hash-using.cpp +++ b/clang/test/Index/Store/record-hash-using.cpp @@ -1,5 +1,3 @@ -// XFAIL: linux - // RUN: rm -rf %t // RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=T // RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=B -DTYPE2=A -DTYPE3=T -DTYPE4=T diff --git a/clang/test/Index/Store/record-hash.cpp b/clang/test/Index/Store/record-hash.cpp index 21a4dc4d974ad..1aa17816d6e1e 100644 --- a/clang/test/Index/Store/record-hash.cpp +++ b/clang/test/Index/Store/record-hash.cpp @@ -1,5 +1,3 @@ -// XFAIL: linux - // RUN: rm -rf %t // RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=long // RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=char From 4fe1f93f74dc400d0e72fe28f785677fca1616ff Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Tue, 11 Sep 2018 13:57:40 -0700 Subject: [PATCH 311/582] [index/store] Changes to make libIndexStore available for linux * Add additional functions to libIndexStore that accept function pointers instead of blocks * Remove all the INDEXSTORE_HAS_BLOCKS checks from c-index-test * Make libIndexStore buildable on linux * Adjust index/Store tests to make sure they pass on linux apple-llvm-split-commit: ee98f3b282a8c85f86c14d452e92939113c4f462 apple-llvm-split-dir: clang/ --- clang/include/indexstore/IndexStoreCXX.h | 53 ++++-- clang/include/indexstore/indexstore.h | 59 ++++++- clang/test/Index/Store/empty-unit.c | 4 +- .../Index/Store/external-source-symbol-hash.m | 2 - .../test/Index/Store/handle-prebuilt-module.m | 2 - clang/test/Index/Store/json-with-module.m | 4 +- clang/test/Index/Store/json-with-pch.c | 6 +- clang/test/Index/Store/json.c | 2 - clang/test/Index/Store/print-record.mm | 2 +- clang/test/Index/Store/print-unit.c | 2 +- .../Index/Store/print-units-with-modules.m | 2 - clang/test/Index/Store/print-units-with-pch.c | 6 +- .../Store/record-hash-crash-invalid-name.cpp | 2 - clang/test/Index/Store/record-hash-crash.cpp | 2 - clang/test/Index/Store/syntax-only.c | 2 - clang/test/Index/Store/unit-with-vfs.c | 2 - clang/test/Index/Store/unit-workdir-prefix.c | 2 - clang/test/Index/Store/using-libstdcpp-arc.mm | 3 - clang/tools/IndexStore/CMakeLists.txt | 4 - clang/tools/IndexStore/IndexStore.cpp | 160 +++++++++++++++++- clang/tools/IndexStore/IndexStore.exports | 10 ++ clang/tools/c-index-test/CMakeLists.txt | 4 +- clang/tools/c-index-test/JSONAggregation.cpp | 9 - clang/tools/c-index-test/core_main.cpp | 36 ---- 24 files changed, 280 insertions(+), 100 deletions(-) diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h index 06d6977c86b99..3a3ba388e82aa 100644 --- a/clang/include/indexstore/IndexStoreCXX.h +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -30,6 +30,12 @@ static inline StringRef stringFromIndexStoreStringRef(indexstore_string_ref_t st return StringRef(str.data, str.length); } +template<typename Ret, typename ...Params> +static inline Ret functionPtrFromFunctionRef(void *ctx, Params ...params) { + auto fn = (llvm::function_ref<Ret(Params...)> *)ctx; + return (*fn)(std::forward<Params>(params)...); +} + class IndexRecordSymbol { indexstore_symbol_t obj; friend class IndexRecordReader; @@ -77,7 +83,7 @@ class IndexRecordOccurrence { return receiver(sym_rel); }); #else - return false; + return indexstore_occurrence_relations_apply_f(obj, &receiver, functionPtrFromFunctionRef); #endif } @@ -136,7 +142,7 @@ class IndexStore { return receiver(stringFromIndexStoreStringRef(unit_name)); }); #else - return false; + return indexstore_store_units_apply_f(obj, sorted, &receiver, functionPtrFromFunctionRef); #endif } @@ -192,9 +198,28 @@ class IndexStore { indexstore_store_set_unit_event_handler(obj, ^(indexstore_unit_event_notification_t evt_note) { handler(UnitEventNotification(evt_note)); }); +#else + if (!handler) { + indexstore_store_set_unit_event_handler_f(obj, nullptr, nullptr, nullptr); + return; + } + + auto fnPtr = new UnitEventHandler(handler); + indexstore_store_set_unit_event_handler_f(obj, fnPtr, event_handler, event_handler_finalizer); #endif } +private: + static void event_handler(void *ctx, indexstore_unit_event_notification_t evt) { + auto fnPtr = (UnitEventHandler*)ctx; + (*fnPtr)(evt); + } + static void event_handler_finalizer(void *ctx) { + auto fnPtr = (UnitEventHandler*)ctx; + delete fnPtr; + } + +public: bool startEventListening(bool waitInitialSync, std::string &error) { indexstore_unit_event_listen_options_t opts; opts.wait_initial_sync = waitInitialSync; @@ -294,7 +319,8 @@ class IndexRecordReader { receiver(symbol); }); #else - return false; + return indexstore_record_reader_search_symbols_f(obj, &filter, functionPtrFromFunctionRef, + &receiver, functionPtrFromFunctionRef); #endif } @@ -304,7 +330,7 @@ class IndexRecordReader { return receiver(sym); }); #else - return false; + return indexstore_record_reader_symbols_apply_f(obj, noCache, &receiver, functionPtrFromFunctionRef); #endif } @@ -315,7 +341,6 @@ class IndexRecordReader { bool foreachOccurrence(ArrayRef<IndexRecordSymbol> symbolsFilter, ArrayRef<IndexRecordSymbol> relatedSymbolsFilter, llvm::function_ref<bool(IndexRecordOccurrence)> receiver) { -#if INDEXSTORE_HAS_BLOCKS llvm::SmallVector<indexstore_symbol_t, 16> c_symbolsFilter; c_symbolsFilter.reserve(symbolsFilter.size()); for (IndexRecordSymbol sym : symbolsFilter) { @@ -326,6 +351,7 @@ class IndexRecordReader { for (IndexRecordSymbol sym : relatedSymbolsFilter) { c_relatedSymbolsFilter.push_back(sym.obj); } +#if INDEXSTORE_HAS_BLOCKS return indexstore_record_reader_occurrences_of_symbols_apply(obj, c_symbolsFilter.data(), c_symbolsFilter.size(), c_relatedSymbolsFilter.data(), @@ -334,7 +360,11 @@ class IndexRecordReader { return receiver(occur); }); #else - return false; + return indexstore_record_reader_occurrences_of_symbols_apply_f(obj, + c_symbolsFilter.data(), c_symbolsFilter.size(), + c_relatedSymbolsFilter.data(), + c_relatedSymbolsFilter.size(), + &receiver, functionPtrFromFunctionRef); #endif } @@ -345,7 +375,7 @@ class IndexRecordReader { return receiver(occur); }); #else - return false; + return indexstore_record_reader_occurrences_apply_f(obj, &receiver, functionPtrFromFunctionRef); #endif } @@ -359,7 +389,10 @@ class IndexRecordReader { return receiver(occur); }); #else - return false; + return indexstore_record_reader_occurrences_in_line_range_apply_f(obj, + lineStart, + lineEnd, + &receiver, functionPtrFromFunctionRef); #endif } }; @@ -480,7 +513,7 @@ class IndexUnitReader { return receiver(dep); }); #else - return false; + return indexstore_unit_reader_dependencies_apply_f(obj, &receiver, functionPtrFromFunctionRef); #endif } @@ -490,7 +523,7 @@ class IndexUnitReader { return receiver(inc); }); #else - return false; + return indexstore_unit_reader_includes_apply_f(obj, &receiver, functionPtrFromFunctionRef); #endif } }; diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index a0a811556ca58..4bfd210dd2d25 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -24,7 +24,7 @@ * INDEXSTORE_VERSION_MAJOR is intended for "major" source/ABI breaking changes. */ #define INDEXSTORE_VERSION_MAJOR 0 -#define INDEXSTORE_VERSION_MINOR 10 +#define INDEXSTORE_VERSION_MINOR 11 #define INDEXSTORE_VERSION_ENCODE(major, minor) ( \ ((major) * 10000) \ @@ -101,6 +101,11 @@ indexstore_store_units_apply(indexstore_t, unsigned sorted, bool(^applier)(indexstore_string_ref_t unit_name)); #endif +INDEXSTORE_PUBLIC bool +indexstore_store_units_apply_f(indexstore_t, unsigned sorted, + void *context, + bool(*applier)(void *context, indexstore_string_ref_t unit_name)); + typedef void *indexstore_unit_event_notification_t; typedef void *indexstore_unit_event_t; @@ -137,6 +142,11 @@ indexstore_store_set_unit_event_handler(indexstore_t, indexstore_unit_event_handler_t handler); #endif +INDEXSTORE_PUBLIC void +indexstore_store_set_unit_event_handler_f(indexstore_t, void *context, + void(*handler)(void *context, indexstore_unit_event_notification_t), + void(*finalizer)(void *context)); + typedef struct { /// If true, \c indexstore_store_start_unit_event_listening will block until /// the initial set of units is passed to the unit event handler, otherwise @@ -332,6 +342,11 @@ indexstore_occurrence_relations_apply(indexstore_occurrence_t, bool(^applier)(indexstore_symbol_relation_t symbol_rel)); #endif +INDEXSTORE_PUBLIC bool +indexstore_occurrence_relations_apply_f(indexstore_occurrence_t, + void *context, + bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)); + INDEXSTORE_PUBLIC uint64_t indexstore_occurrence_get_roles(indexstore_occurrence_t); @@ -388,6 +403,37 @@ indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t bool(^applier)(indexstore_occurrence_t occur)); #endif +INDEXSTORE_PUBLIC bool +indexstore_record_reader_search_symbols_f(indexstore_record_reader_t, + void *filter_ctx, + bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), + void *receiver_ctx, + void(*receiver)(void *receiver_ctx, indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t, + bool nocache, + void *context, + bool(*applier)(void *context, indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_apply_f(indexstore_record_reader_t, + void *context, + bool(*applier)(void *context, indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_in_line_range_apply_f(indexstore_record_reader_t, + unsigned line_start, + unsigned line_count, + void *context, + bool(*applier)(void *context, indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_of_symbols_apply_f(indexstore_record_reader_t, + indexstore_symbol_t *symbols, size_t symbols_count, + indexstore_symbol_t *related_symbols, size_t related_symbols_count, + void *context, + bool(*applier)(void *context, indexstore_occurrence_t occur)); typedef void *indexstore_unit_reader_t; @@ -480,9 +526,18 @@ indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t, INDEXSTORE_PUBLIC bool indexstore_unit_reader_includes_apply(indexstore_unit_reader_t, bool(^applier)(indexstore_unit_include_t)); - #endif +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_dependencies_apply_f(indexstore_unit_reader_t, + void *context, + bool(*applier)(void *context, indexstore_unit_dependency_t)); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_includes_apply_f(indexstore_unit_reader_t, + void *context, + bool(*applier)(void *context, indexstore_unit_include_t)); + INDEXSTORE_END_DECLS #endif diff --git a/clang/test/Index/Store/empty-unit.c b/clang/test/Index/Store/empty-unit.c index 8ffdf0dd2ea05..701f9030f78ad 100644 --- a/clang/test/Index/Store/empty-unit.c +++ b/clang/test/Index/Store/empty-unit.c @@ -1,7 +1,6 @@ -// XFAIL: linux - void foo(int i); +// RUN: rm -rf %t/idx // RUN: %clang_cc1 -index-store-path %t/idx %s -o %t.o // RUN: touch %t.empty @@ -11,6 +10,7 @@ void foo(int i); // ERR-UNIT: error loading unit: empty file // Also check for empty record files. +// RUN: rm -rf %t/idx2 // RUN: %clang_cc1 -index-store-path %t/idx2 %s -o %t.o // RUN: cp %t.empty $(find %t/idx2 -name "empty-unit.c-*") // RUN: not c-index-test core -print-record %t/idx2 2> %t2.err diff --git a/clang/test/Index/Store/external-source-symbol-hash.m b/clang/test/Index/Store/external-source-symbol-hash.m index b9792eadb81e5..1b4f89d9251f3 100644 --- a/clang/test/Index/Store/external-source-symbol-hash.m +++ b/clang/test/Index/Store/external-source-symbol-hash.m @@ -1,5 +1,3 @@ -// XFAIL: linux - // RUN: rm -rf %t.idx // RUN: %clang_cc1 %s -index-store-path %t.idx -D USE_EXTERNAL // RUN: c-index-test core -print-record %t.idx | FileCheck %s diff --git a/clang/test/Index/Store/handle-prebuilt-module.m b/clang/test/Index/Store/handle-prebuilt-module.m index f6a0c42a8e8be..0136a9c9f86a4 100644 --- a/clang/test/Index/Store/handle-prebuilt-module.m +++ b/clang/test/Index/Store/handle-prebuilt-module.m @@ -1,5 +1,3 @@ -// XFAIL: linux - // RUN: rm -rf %t // RUN: mkdir %t // RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx1 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err1 diff --git a/clang/test/Index/Store/json-with-module.m b/clang/test/Index/Store/json-with-module.m index 1ef69697749a5..a02210aaee50b 100644 --- a/clang/test/Index/Store/json-with-module.m +++ b/clang/test/Index/Store/json-with-module.m @@ -1,9 +1,7 @@ // RUN: rm -rf %t.idx %t.mcp -// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module // RUN: c-index-test core -aggregate-json %t.idx -o %t.json // RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json // RUN: diff -u %s.json %t.final.json -// XFAIL: linux - @import ModDep; diff --git a/clang/test/Index/Store/json-with-pch.c b/clang/test/Index/Store/json-with-pch.c index 9ffe80f02c340..c8bffa035ed0c 100644 --- a/clang/test/Index/Store/json-with-pch.c +++ b/clang/test/Index/Store/json-with-pch.c @@ -1,10 +1,10 @@ // RUN: rm -rf %t.idx -// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx -// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror // RUN: c-index-test core -aggregate-json %t.idx -o %t.json // RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json // RUN: diff -u %s.json %t.final.json -// XFAIL: linux + int main() { test1_func(); } diff --git a/clang/test/Index/Store/json.c b/clang/test/Index/Store/json.c index c4ea965d36b1c..c9be85f9112a4 100644 --- a/clang/test/Index/Store/json.c +++ b/clang/test/Index/Store/json.c @@ -6,5 +6,3 @@ // RUN: c-index-test core -aggregate-json %t.idx -o %t.json // RUN: sed -e "s:%S::g" -e "s:%t.o::g" %t.json > %t.final.json // RUN: diff -u %S/Inputs/json.c.json %t.final.json - -// XFAIL: linux diff --git a/clang/test/Index/Store/print-record.mm b/clang/test/Index/Store/print-record.mm index ce24983503a6d..cbca273481968 100644 --- a/clang/test/Index/Store/print-record.mm +++ b/clang/test/Index/Store/print-record.mm @@ -2,7 +2,7 @@ // RUN: %clang_cc1 %s -index-store-path %t.idx // RUN: c-index-test core -print-record %t.idx | FileCheck %s -// XFAIL: linux + @class MyCls; diff --git a/clang/test/Index/Store/print-unit.c b/clang/test/Index/Store/print-unit.c index 3be02b778035d..31de79667d729 100644 --- a/clang/test/Index/Store/print-unit.c +++ b/clang/test/Index/Store/print-unit.c @@ -1,4 +1,4 @@ -// XFAIL: linux + #include "print-unit.h" #include "syshead.h" diff --git a/clang/test/Index/Store/print-units-with-modules.m b/clang/test/Index/Store/print-units-with-modules.m index 0cb9a404a0532..3e2fea4206da0 100644 --- a/clang/test/Index/Store/print-units-with-modules.m +++ b/clang/test/Index/Store/print-units-with-modules.m @@ -2,8 +2,6 @@ // RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module // RUN: c-index-test core -print-unit %t.idx | FileCheck %s -// XFAIL: linux - @import ModDep; @import ModSystem; diff --git a/clang/test/Index/Store/print-units-with-pch.c b/clang/test/Index/Store/print-units-with-pch.c index 1e533a2eab7b1..8bf0082e3a45c 100644 --- a/clang/test/Index/Store/print-units-with-pch.c +++ b/clang/test/Index/Store/print-units-with-pch.c @@ -1,10 +1,8 @@ // RUN: rm -rf %t.idx -// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx -// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror // RUN: c-index-test core -print-unit %t.idx | FileCheck %s -// XFAIL: linux - int main() { test1_func(); } diff --git a/clang/test/Index/Store/record-hash-crash-invalid-name.cpp b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp index 4479789e9a236..5c049567de024 100644 --- a/clang/test/Index/Store/record-hash-crash-invalid-name.cpp +++ b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp @@ -1,7 +1,5 @@ // Makes sure it doesn't crash. -// XFAIL: linux - // RUN: rm -rf %t // RUN: not %clang_cc1 %s -index-store-path %t/idx -std=c++14 // RUN: c-index-test core -print-record %t/idx | FileCheck %s diff --git a/clang/test/Index/Store/record-hash-crash.cpp b/clang/test/Index/Store/record-hash-crash.cpp index c0f79120c66e4..68501edfef9d4 100644 --- a/clang/test/Index/Store/record-hash-crash.cpp +++ b/clang/test/Index/Store/record-hash-crash.cpp @@ -1,7 +1,5 @@ // Makes sure it doesn't crash. -// XFAIL: linux - // RUN: rm -rf %t // RUN: %clang_cc1 %s -index-store-path %t/idx -std=c++14 // RUN: c-index-test core -print-record %t/idx | FileCheck %s diff --git a/clang/test/Index/Store/syntax-only.c b/clang/test/Index/Store/syntax-only.c index 53d22bc142464..7c97ec64d42cc 100644 --- a/clang/test/Index/Store/syntax-only.c +++ b/clang/test/Index/Store/syntax-only.c @@ -3,8 +3,6 @@ // RUN: c-index-test core -print-unit %t.idx | FileCheck %s -check-prefix=CHECK-UNIT // RUN: c-index-test core -print-record %t.idx | FileCheck %s -check-prefix=CHECK-RECORD -// XFAIL: linux - // CHECK-UNIT: out-file: {{.*}}/syntax-only.c.myoutfile // CHECK-RECORD: function/C | foo | c:@F@foo diff --git a/clang/test/Index/Store/unit-with-vfs.c b/clang/test/Index/Store/unit-with-vfs.c index cbed6261246e0..840b2ae3da30a 100644 --- a/clang/test/Index/Store/unit-with-vfs.c +++ b/clang/test/Index/Store/unit-with-vfs.c @@ -7,6 +7,4 @@ // RUN: %clang_cc1 %s -index-store-path %t.idx -I %t -ivfsoverlay %t.yaml // RUN: c-index-test core -print-unit %t.idx | FileCheck %s -// XFAIL: linux - // CHECK: Record | user | {{.*}}test/Index/Store/Inputs/using-overlay.h diff --git a/clang/test/Index/Store/unit-workdir-prefix.c b/clang/test/Index/Store/unit-workdir-prefix.c index e7a3a7102f33b..8228dcb12d5c2 100644 --- a/clang/test/Index/Store/unit-workdir-prefix.c +++ b/clang/test/Index/Store/unit-workdir-prefix.c @@ -1,5 +1,3 @@ -// XFAIL: linux - #include "header.h" void foo(void) { diff --git a/clang/test/Index/Store/using-libstdcpp-arc.mm b/clang/test/Index/Store/using-libstdcpp-arc.mm index 9f2935ac91b38..5ac2b6351d3eb 100644 --- a/clang/test/Index/Store/using-libstdcpp-arc.mm +++ b/clang/test/Index/Store/using-libstdcpp-arc.mm @@ -12,9 +12,6 @@ // RUN: %clang_cc1 %s -index-store-path %t.idx2 -I %t/include -fobjc-arc -fobjc-arc-cxxlib=libstdc++ -fmodules -fmodules-cache-path=%t.mcp -fimplicit-module-maps // RUN: c-index-test core -print-record %t.idx2 | FileCheck %s - -// XFAIL: linux - @import Foo; // CHECK: [[@LINE+1]]:6 | function/C diff --git a/clang/tools/IndexStore/CMakeLists.txt b/clang/tools/IndexStore/CMakeLists.txt index e1680296e06d5..f6974e676c4f2 100644 --- a/clang/tools/IndexStore/CMakeLists.txt +++ b/clang/tools/IndexStore/CMakeLists.txt @@ -22,9 +22,6 @@ else() set(output_name "IndexStore") endif() -# FIXME: needs to be ported to non-Apple platforms. -if(APPLE) - add_clang_library(IndexStore ${ENABLE_SHARED} ${ENABLE_STATIC} OUTPUT_NAME ${output_name} ${SOURCES} @@ -88,4 +85,3 @@ install(DIRECTORY ../../include/indexstore PATTERN "*.h" PATTERN ".svn" EXCLUDE ) -endif() diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index 9db61441d1259..8f873164721ad 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -23,7 +23,10 @@ #include "llvm/ADT/SmallString.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/ManagedStatic.h" + +#if INDEXSTORE_HAS_BLOCKS #include <Block.h> +#endif using namespace clang; using namespace clang::index; @@ -126,6 +129,17 @@ indexstore_store_units_apply(indexstore_t c_store, unsigned sorted, return applier(toIndexStoreString(unitName)); }); } +#endif + +bool +indexstore_store_units_apply_f(indexstore_t c_store, unsigned sorted, + void *context, + bool(*applier)(void *context, indexstore_string_ref_t unit_name)) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool { + return applier(context, toIndexStoreString(unitName)); + }); +} size_t indexstore_unit_event_notification_get_events_count(indexstore_unit_event_notification_t c_evtnote) { @@ -174,6 +188,7 @@ indexstore_unit_event_get_modification_time(indexstore_unit_event_t c_evt) { return toTimeSpec(evt->ModTime); } +#if INDEXSTORE_HAS_BLOCKS void indexstore_store_set_unit_event_handler(indexstore_t c_store, indexstore_unit_event_handler_t blk_handler) { @@ -209,6 +224,45 @@ indexstore_store_set_unit_event_handler(indexstore_t c_store, } #endif +void +indexstore_store_set_unit_event_handler_f(indexstore_t c_store, void *context, + void(*fn_handler)(void *context, indexstore_unit_event_notification_t), + void(*finalizer)(void *context)) { + IndexDataStore *store = static_cast<IndexDataStore*>(c_store); + if (!fn_handler) { + store->setUnitEventHandler(nullptr); + return; + } + + class BlockWrapper { + void *context; + void(*fn_handler)(void *context, indexstore_unit_event_notification_t); + void(*finalizer)(void *context); + + public: + BlockWrapper(void *_context, + void(*_fn_handler)(void *context, indexstore_unit_event_notification_t), + void(*_finalizer)(void *context)) + : context(_context), fn_handler(_fn_handler), finalizer(_finalizer) {} + + ~BlockWrapper() { + if (finalizer) { + finalizer(context); + } + } + + void operator()(indexstore_unit_event_notification_t evt_note) const { + fn_handler(context, evt_note); + } + }; + + auto handler = std::make_shared<BlockWrapper>(context, fn_handler, finalizer); + + store->setUnitEventHandler([handler](IndexDataStore::UnitEventNotification evtNote) { + (*handler)(&evtNote); + }); +} + bool indexstore_store_start_unit_event_listening(indexstore_t c_store, indexstore_unit_event_listen_options_t *client_opts, @@ -328,6 +382,18 @@ indexstore_occurrence_relations_apply(indexstore_occurrence_t occur, } #endif +bool +indexstore_occurrence_relations_apply_f(indexstore_occurrence_t occur, + void *context, + bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)) { + auto *recOccur = static_cast<IndexRecordOccurrence*>(occur); + for (auto &rel : recOccur->Relations) { + if (!applier(context, &rel)) + return false; + } + return true; +} + uint64_t indexstore_occurrence_get_roles(indexstore_occurrence_t occur) { return getIndexStoreRoles(static_cast<IndexRecordOccurrence*>(occur)->Roles); @@ -444,6 +510,77 @@ indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t } #endif +bool +indexstore_record_reader_search_symbols_f(indexstore_record_reader_t rdr, + void *filter_ctx, + bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), + void *receiver_ctx, + void(*receiver)(void *receiver_ctx, indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + + auto filterFn = [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn { + bool stop = false; + bool accept = filter(filter_ctx, (indexstore_symbol_t)&D, &stop); + return { accept, !stop }; + }; + auto receiverFn = [&](const IndexRecordDecl *D) { + receiver(receiver_ctx, (indexstore_symbol_t)D); + }; + + return reader->searchDecls(filterFn, receiverFn); +} + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t rdr, + bool nocache, + void *context, + bool(*applier)(void *context, indexstore_symbol_t symbol)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordDecl *D) -> bool { + return applier(context, (indexstore_symbol_t)D); + }; + return reader->foreachDecl(nocache, receiverFn); +} + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_apply_f(indexstore_record_reader_t rdr, + void *context, + bool(*applier)(void *context, indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier(context, (indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence(receiverFn); +} + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_in_line_range_apply_f(indexstore_record_reader_t rdr, + unsigned line_start, + unsigned line_count, + void *context, + bool(*applier)(void *context, indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier(context, (indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrenceInLineRange(line_start, line_count, receiverFn); +} + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_of_symbols_apply_f(indexstore_record_reader_t rdr, + indexstore_symbol_t *symbols, size_t symbols_count, + indexstore_symbol_t *related_symbols, size_t related_symbols_count, + void *context, + bool(*applier)(void *context, indexstore_occurrence_t occur)) { + auto *reader = static_cast<IndexRecordReader *>(rdr); + auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { + return applier(context, (indexstore_occurrence_t)&RO); + }; + return reader->foreachOccurrence({(IndexRecordDecl**)symbols, symbols_count}, + {(IndexRecordDecl**)related_symbols, related_symbols_count}, + receiverFn); +} + size_t indexstore_store_get_unit_name_from_output_path(indexstore_t store, const char *output_path, @@ -452,7 +589,8 @@ indexstore_store_get_unit_name_from_output_path(indexstore_t store, SmallString<256> unitName; IndexUnitWriter::getUnitNameForAbsoluteOutputFile(output_path, unitName); size_t nameLen = unitName.size(); - strlcpy(name_buf, unitName.c_str(), buf_size); + strncpy(name_buf, unitName.c_str(), buf_size-1); + name_buf[buf_size-1] = '\0'; return nameLen; } @@ -661,3 +799,23 @@ indexstore_unit_reader_includes_apply(indexstore_unit_reader_t rdr, }); } #endif + +bool +indexstore_unit_reader_dependencies_apply_f(indexstore_unit_reader_t rdr, + void *context, + bool(*applier)(void *context, indexstore_unit_dependency_t)) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &depInfo) -> bool { + return applier(context, (void*)&depInfo); + }); +} + +bool +indexstore_unit_reader_includes_apply_f(indexstore_unit_reader_t rdr, + void *context, + bool(*applier)(void *context, indexstore_unit_include_t)) { + auto reader = static_cast<IndexUnitReader*>(rdr); + return reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &incInfo) -> bool { + return applier(context, (void*)&incInfo); + }); +} diff --git a/clang/tools/IndexStore/IndexStore.exports b/clang/tools/IndexStore/IndexStore.exports index 437cee918b98a..9196ad60c38a3 100644 --- a/clang/tools/IndexStore/IndexStore.exports +++ b/clang/tools/IndexStore/IndexStore.exports @@ -6,7 +6,9 @@ indexstore_store_dispose indexstore_store_get_unit_modification_time indexstore_store_get_unit_name_from_output_path indexstore_store_units_apply +indexstore_store_units_apply_f indexstore_store_set_unit_event_handler +indexstore_store_set_unit_event_handler_f indexstore_store_start_unit_event_listening indexstore_store_stop_unit_event_listening indexstore_store_discard_unit @@ -27,13 +29,19 @@ indexstore_occurrence_get_symbol indexstore_occurrence_get_roles indexstore_occurrence_get_line_col indexstore_occurrence_relations_apply +indexstore_occurrence_relations_apply_f indexstore_record_reader_create indexstore_record_reader_dispose indexstore_record_reader_search_symbols +indexstore_record_reader_search_symbols_f indexstore_record_reader_symbols_apply +indexstore_record_reader_symbols_apply_f indexstore_record_reader_occurrences_apply +indexstore_record_reader_occurrences_apply_f indexstore_record_reader_occurrences_in_line_range_apply +indexstore_record_reader_occurrences_in_line_range_apply_f indexstore_record_reader_occurrences_of_symbols_apply +indexstore_record_reader_occurrences_of_symbols_apply_f indexstore_unit_dependency_get_kind indexstore_unit_dependency_get_filepath indexstore_unit_dependency_get_modulename @@ -57,7 +65,9 @@ indexstore_unit_reader_get_output_file indexstore_unit_reader_get_sysroot_path indexstore_unit_reader_get_target indexstore_unit_reader_dependencies_apply +indexstore_unit_reader_dependencies_apply_f indexstore_unit_reader_includes_apply +indexstore_unit_reader_includes_apply_f indexstore_unit_reader_has_main_file indexstore_unit_reader_is_debug_compilation indexstore_unit_reader_is_module_unit diff --git a/clang/tools/c-index-test/CMakeLists.txt b/clang/tools/c-index-test/CMakeLists.txt index fbfac6b9abe83..2992da6117d1b 100644 --- a/clang/tools/c-index-test/CMakeLists.txt +++ b/clang/tools/c-index-test/CMakeLists.txt @@ -12,9 +12,7 @@ add_clang_executable(c-index-test set(INDEXSTORE_LIB) set(CINDEXTEST_LIBS) -if(APPLE) - set(INDEXSTORE_LIB IndexStore) -endif() +set(INDEXSTORE_LIB IndexStore) if(NOT MSVC) set_property( diff --git a/clang/tools/c-index-test/JSONAggregation.cpp b/clang/tools/c-index-test/JSONAggregation.cpp index 0df1e8dc5d0f8..6325b00e3e325 100644 --- a/clang/tools/c-index-test/JSONAggregation.cpp +++ b/clang/tools/c-index-test/JSONAggregation.cpp @@ -21,8 +21,6 @@ using namespace clang::index; using namespace indexstore; using namespace llvm; -#if INDEXSTORE_HAS_BLOCKS - namespace { typedef size_t FilePathIndex; @@ -402,10 +400,3 @@ bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { BuryPointer(aggregator); return false; } - -#else - -bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { - return true; -} -#endif diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index f665a98df04f2..9c512451b48b7 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -322,8 +322,6 @@ static bool printSourceSymbolsFromModule(StringRef modulePath, return false; } -#if INDEXSTORE_HAS_BLOCKS - //===----------------------------------------------------------------------===// // Print Record //===----------------------------------------------------------------------===// @@ -606,25 +604,6 @@ static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { return !Success; } - -#else - -static int printUnit(StringRef Filename, raw_ostream &OS) { - return 1; -} - -static int printStoreUnits(StringRef StorePath, raw_ostream &OS) { - return 1; -} - -static int printStoreFileRecord(StringRef storePath, StringRef filePath, - Optional<unsigned> lineStart, unsigned lineCount, - raw_ostream &OS) { - return 1; -} - -#endif - //===----------------------------------------------------------------------===// // Helper Utils //===----------------------------------------------------------------------===// @@ -656,8 +635,6 @@ static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, } } -#if INDEXSTORE_HAS_BLOCKS - static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS) { printSymbolInfo(Rec.SymInfo, OS); OS << " | "; @@ -785,17 +762,6 @@ static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS }); } -#else - -static int printRecord(StringRef Filename, raw_ostream &OS) { - return 1; -} -static int printStoreRecords(StringRef StorePath, raw_ostream &OS) { - return 1; -} - -#endif - static int watchDirectory(StringRef dirPath) { raw_ostream &OS = outs(); auto receiver = [&](ArrayRef<DirectoryWatcher::Event> Events, bool isInitial) { @@ -939,11 +905,9 @@ int indextest_core_main(int argc, const char **argv) { return printUnit(options::InputFiles[0], outs()); } -#if INDEXSTORE_HAS_BLOCKS if (options::Action == ActionType::PrintStoreFormatVersion) { outs() << indexstore::IndexStore::formatVersion() << '\n'; } -#endif if (options::Action == ActionType::AggregateAsJSON) { if (options::InputFiles.empty()) { From 77f577c64cc2f564b76ec51899730a7d2bfc12fd Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 14 Sep 2018 11:16:50 -0700 Subject: [PATCH 312/582] RecordLayout.h: avoid spurious copies of values to be emitted (#108) This was causing significant slowdowns for Swift AST serialization. Oops. apple-llvm-split-commit: c0eb23954d4959b12af0095c4f566c52ff2f0449 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Bitcode/RecordLayout.h | 29 ++++++++------ llvm/unittests/Bitcode/CMakeLists.txt | 1 + llvm/unittests/Bitcode/RecordLayout.cpp | 50 ++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 llvm/unittests/Bitcode/RecordLayout.cpp diff --git a/llvm/include/llvm/Bitcode/RecordLayout.h b/llvm/include/llvm/Bitcode/RecordLayout.h index c59b13199699f..5b199cb408165 100644 --- a/llvm/include/llvm/Bitcode/RecordLayout.h +++ b/llvm/include/llvm/Bitcode/RecordLayout.h @@ -209,12 +209,13 @@ namespace impl { public: template <typename BufferTy, typename FirstData, typename... Data> static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, - unsigned abbrCode, FirstData data, Data... rest) { + unsigned abbrCode, FirstData data, Data &&... rest) { static_assert(!First::IS_COMPOUND, "arrays and blobs may not appear in the middle of a record"); First::assertValid(data); buffer.push_back(data); - BCRecordCoding<Fields...>::emit(out, buffer, abbrCode, rest...); + BCRecordCoding<Fields...>::emit(out, buffer, abbrCode, + std::forward<Data>(rest)...); } template <typename ElementTy, typename FirstData, typename... Data> @@ -247,7 +248,7 @@ namespace impl { public: template <typename BufferTy, typename LastData> static void emit(llvm::BitstreamWriter &out, BufferTy &buffer, - unsigned abbrCode, LastData data) { + unsigned abbrCode, const LastData &data) { static_assert(!Last::IS_COMPOUND, "arrays and blobs need special handling"); Last::assertValid(data); @@ -291,7 +292,8 @@ namespace impl { for (auto &item : arrayData) EleTy::assertValid(item); #endif - buffer.reserve(buffer.size() + arrayData.size()); + buffer.reserve(buffer.size() + std::distance(arrayData.begin(), + arrayData.end())); std::copy(arrayData.begin(), arrayData.end(), std::back_inserter(buffer)); out.EmitRecordWithAbbrev(abbrCode, buffer); @@ -418,8 +420,8 @@ class BCGenericRecordLayout { /// /// Note that even fixed arguments must be specified here. template <typename BufferTy, typename... Data> - void emit(BufferTy &buffer, unsigned recordID, Data... data) const { - emitRecord(Out, buffer, AbbrevCode, recordID, data...); + void emit(BufferTy &buffer, unsigned recordID, Data &&... data) const { + emitRecord(Out, buffer, AbbrevCode, recordID, std::forward<Data>(data)...); } /// Registers this record's layout with the bitstream reader. @@ -440,7 +442,8 @@ class BCGenericRecordLayout { /// the special Nothing value. template <typename BufferTy, typename... Data> static void emitRecord(llvm::BitstreamWriter &out, BufferTy &buffer, - unsigned abbrCode, unsigned recordID, Data... data) { + unsigned abbrCode, unsigned recordID, + Data &&... data) { static_assert(sizeof...(data) <= sizeof...(Fields) || impl::has_array<Fields...>::value, "Too many record elements"); @@ -448,7 +451,8 @@ class BCGenericRecordLayout { "Too few record elements"); buffer.clear(); impl::BCRecordCoding<IDField, Fields...>::emit(out, buffer, abbrCode, - recordID, data...); + recordID, + std::forward<Data>(data)...); } /// Extract record data from \p buffer into the given data fields. @@ -497,8 +501,8 @@ class BCRecordLayout : public BCGenericRecordLayout<BCLiteral<RecordCode>, /// /// Note that even fixed arguments must be specified here. template <typename BufferTy, typename... Data> - void emit(BufferTy &buffer, Data... data) const { - Base::emit(buffer, RecordCode, data...); + void emit(BufferTy &buffer, Data &&... data) const { + Base::emit(buffer, RecordCode, std::forward<Data>(data)...); } /// Emit a record identified by \p abbrCode to bitstream reader \p out, using @@ -508,8 +512,9 @@ class BCRecordLayout : public BCGenericRecordLayout<BCLiteral<RecordCode>, /// and blobs can only be passed as StringRefs. template <typename BufferTy, typename... Data> static void emitRecord(llvm::BitstreamWriter &out, BufferTy &buffer, - unsigned abbrCode, Data... data) { - Base::emitRecord(out, buffer, abbrCode, RecordCode, data...); + unsigned abbrCode, Data &&... data) { + Base::emitRecord(out, buffer, abbrCode, RecordCode, + std::forward<Data>(data)...); } }; diff --git a/llvm/unittests/Bitcode/CMakeLists.txt b/llvm/unittests/Bitcode/CMakeLists.txt index 4d06f8008d389..b68cac7c8a683 100644 --- a/llvm/unittests/Bitcode/CMakeLists.txt +++ b/llvm/unittests/Bitcode/CMakeLists.txt @@ -10,4 +10,5 @@ add_llvm_unittest(BitcodeTests BitReaderTest.cpp BitstreamReaderTest.cpp BitstreamWriterTest.cpp + RecordLayout.cpp ) diff --git a/llvm/unittests/Bitcode/RecordLayout.cpp b/llvm/unittests/Bitcode/RecordLayout.cpp new file mode 100644 index 0000000000000..9a7a099a6a19e --- /dev/null +++ b/llvm/unittests/Bitcode/RecordLayout.cpp @@ -0,0 +1,50 @@ +//===- llvm/unittest/Bitcode/RecordLayout.cpp - Tests for BCRecordLayout --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Support/Compiler.h" + +using namespace llvm; + +namespace { +class NonCopyableArrayLikeType { + int singleElement = 0; +public: + NonCopyableArrayLikeType() = default; + + NonCopyableArrayLikeType(const NonCopyableArrayLikeType &) = delete; + NonCopyableArrayLikeType(NonCopyableArrayLikeType &&) = delete; + void operator=(const NonCopyableArrayLikeType &) = delete; + void operator=(NonCopyableArrayLikeType &&) = delete; + ~NonCopyableArrayLikeType() = default; + + const int *begin() const { + return &singleElement; + } + const int *end() const { + return begin() + 1; + } +}; +} // end anonymous namespace + +// This "test" isn't actually run; we just want to make sure it compiles. +LLVM_ATTRIBUTE_UNUSED +static void testThatArrayIsNotCopied(BitstreamWriter &out, + SmallVectorImpl<uint64_t> &buffer) { + using Layout = BCRecordLayout</*ID*/0, BCFixed<16>, BCArray<BCFixed<16>>>; + Layout layout(out); + + NonCopyableArrayLikeType varArray; + layout.emit(buffer, /*field*/1, varArray); + + const NonCopyableArrayLikeType constVarArray; + layout.emit(buffer, /*field*/1, constVarArray); + + layout.emit(buffer, /*field*/1, NonCopyableArrayLikeType()); +} From 5292db0578d15448f58a08ea175c987108134f71 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 14 Sep 2018 11:17:17 -0700 Subject: [PATCH 313/582] [APINotes] API-noted attributes should not be implicit (#189) Even though they don't have valid source locations, they /are/ a developer-specified part of the AST, and so it makes sense to include them in the printed representation. This also brings back the tests that were disabled in 8ab16db. Note that the SwiftVersionedAttr and SwiftVersionedRemovalAttr attributes are still considered implicit, since they really are part of the compiler infrastructure and not something the developer chose to put in themselves. (They also don't have a printed representation anyway.) rdar://problem/40296113 apple-llvm-split-commit: 2da1f90336704b33dc108c2b8d7d0a799b54ebd8 apple-llvm-split-dir: clang/ From 264bd89978238268fc618132133b0f3ffa389ebd Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 14 Sep 2018 11:17:35 -0700 Subject: [PATCH 314/582] [APINotes] Don't apply API notes to @class / forward @protocol decls (#198) Like 725aa93ac0 last year, this mostly just makes sense anyway (especially when forward declarations cross framework boundaries), but also fixes a bug. In this case it's even less controversial (hopefully) because you already couldn't write attributes in-source on @class declarations; doing so would be rejected in either prefix or suffix position. rdar://problem/40278479 apple-llvm-split-commit: d899f7de9536fc15d41e13e669380fa875fecdc6 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclObjC.cpp | 3 --- .../LayeredKit.framework/Headers/LayeredKit.h | 11 +++++++++++ .../LayeredKit.framework/Modules/module.modulemap | 5 +++++ .../Headers/LayeredKitImpl.apinotes | 9 +++++++++ .../Headers/LayeredKitImpl.h | 7 +++++++ .../Modules/module.modulemap | 5 +++++ clang/test/APINotes/objc-forward-declarations.m | 12 ++++++++++++ 7 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Headers/LayeredKit.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/objc-forward-declarations.m diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index d136231d3ed22..987476ccc39cb 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1776,14 +1776,12 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, = ObjCProtocolDecl::Create(Context, CurContext, Ident, IdentPair.second, AtProtocolLoc, PrevDecl); - ProcessAPINotes(PDecl); PushOnScopeChains(PDecl, TUScope); CheckObjCDeclScope(PDecl); ProcessDeclAttributeList(TUScope, PDecl, attrList); AddPragmaAttributes(TUScope, PDecl); - ProcessAPINotes(PDecl); if (PrevDecl) mergeDeclAttributes(PDecl, PrevDecl); @@ -3148,7 +3146,6 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, ClassName, TypeParams, PrevIDecl, IdentLocs[i]); IDecl->setAtEndRange(IdentLocs[i]); - ProcessAPINotes(IDecl); PushOnScopeChains(IDecl, TUScope); CheckObjCDeclScope(IDecl); diff --git a/clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Headers/LayeredKit.h b/clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Headers/LayeredKit.h new file mode 100644 index 0000000000000..a95d19ecbe9af --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Headers/LayeredKit.h @@ -0,0 +1,11 @@ +@import LayeredKitImpl; + +// @interface declarations already don't inherit attributes from forward +// declarations, so in order to test this properly we have to /not/ define +// UpwardClass anywhere. + +// @interface UpwardClass +// @end + +@protocol UpwardProto +@end diff --git a/clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..04bbe72a2b6e2 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/LayeredKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module LayeredKit { + umbrella header "LayeredKit.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.apinotes b/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.apinotes new file mode 100644 index 0000000000000..bece28cfe6057 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.apinotes @@ -0,0 +1,9 @@ +Name: LayeredKitImpl +Classes: +- Name: PerfectlyNormalClass + Availability: none +- Name: UpwardClass + Availability: none +Protocols: +- Name: UpwardProto + Availability: none diff --git a/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.h b/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.h new file mode 100644 index 0000000000000..99591d35803aa --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Headers/LayeredKitImpl.h @@ -0,0 +1,7 @@ +@protocol UpwardProto; +@class UpwardClass; + +@interface PerfectlyNormalClass +@end + +void doImplementationThings(UpwardClass *first, id <UpwardProto> second) __attribute((unavailable)); diff --git a/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..58a6e55c1067f --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/LayeredKitImpl.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module LayeredKitImpl { + umbrella header "LayeredKitImpl.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/objc-forward-declarations.m b/clang/test/APINotes/objc-forward-declarations.m new file mode 100644 index 0000000000000..e82bed2055504 --- /dev/null +++ b/clang/test/APINotes/objc-forward-declarations.m @@ -0,0 +1,12 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -F %S/Inputs/Frameworks %s -verify + +@import LayeredKit; + +void test( + UpwardClass *okayClass, + id <UpwardProto> okayProto, + PerfectlyNormalClass *badClass // expected-error {{'PerfectlyNormalClass' is unavailable}} +) { + // expected-note@LayeredKitImpl/LayeredKitImpl.h:4 {{'PerfectlyNormalClass' has been explicitly marked unavailable here}} +} From a55e4631465e2cb34d0d51259dc7eab74ec52b10 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <abdulras@fb.com> Date: Mon, 17 Sep 2018 11:18:32 -0700 Subject: [PATCH 315/582] IndexStore: repair Windows build Building IndexStore would fail as `dllimport` would be applied to a non-inlined function definition which is not permitted. Remove the declaration to change the error to a warning (inconsistent dll storage). This allows us to build correctly. We could always apply the `dllexport` attribute to the definitions to avoid the warning. apple-llvm-split-commit: 2801f618d0aef3d0473b8ef207750402552cca87 apple-llvm-split-dir: clang/ --- clang/tools/IndexStore/IndexStore.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index 8f873164721ad..cc517ec481553 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -530,7 +530,7 @@ indexstore_record_reader_search_symbols_f(indexstore_record_reader_t rdr, return reader->searchDecls(filterFn, receiverFn); } -INDEXSTORE_PUBLIC bool +bool indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t rdr, bool nocache, void *context, @@ -542,7 +542,7 @@ indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t rdr, return reader->foreachDecl(nocache, receiverFn); } -INDEXSTORE_PUBLIC bool +bool indexstore_record_reader_occurrences_apply_f(indexstore_record_reader_t rdr, void *context, bool(*applier)(void *context, indexstore_occurrence_t occur)) { @@ -553,7 +553,7 @@ indexstore_record_reader_occurrences_apply_f(indexstore_record_reader_t rdr, return reader->foreachOccurrence(receiverFn); } -INDEXSTORE_PUBLIC bool +bool indexstore_record_reader_occurrences_in_line_range_apply_f(indexstore_record_reader_t rdr, unsigned line_start, unsigned line_count, @@ -566,7 +566,7 @@ indexstore_record_reader_occurrences_in_line_range_apply_f(indexstore_record_rea return reader->foreachOccurrenceInLineRange(line_start, line_count, receiverFn); } -INDEXSTORE_PUBLIC bool +bool indexstore_record_reader_occurrences_of_symbols_apply_f(indexstore_record_reader_t rdr, indexstore_symbol_t *symbols, size_t symbols_count, indexstore_symbol_t *related_symbols, size_t related_symbols_count, From 39e2a7e2c365856fdf84ac3b41ad9338787b96fe Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Tue, 18 Sep 2018 03:01:59 -0700 Subject: [PATCH 316/582] Revert "Revert "[DebugInfo] Normalize common kinds of DWARF sub-expressions."" This reverts commit 83ff6830d801c65fa5ed43095df2db5c822d871d now that the corresponding issue has been fixed on the lldb side. apple-llvm-split-commit: b23dddc7de497f169acd463938ab5248934bfb0e apple-llvm-split-dir: llvm/ --- .../CodeGen/AsmPrinter/DwarfExpression.cpp | 26 +++++++++----- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h | 3 ++ .../DebugInfo/AMDGPU/variable-locations.ll | 6 ++-- llvm/test/DebugInfo/ARM/PR26163.ll | 4 +-- llvm/test/DebugInfo/ARM/split-complex.ll | 2 +- .../Generic/incorrect-variable-debugloc1.ll | 11 +++--- llvm/test/DebugInfo/X86/PR26148.ll | 4 +-- llvm/test/DebugInfo/X86/constant-loclist.ll | 2 +- llvm/test/DebugInfo/X86/dw_op_minus_direct.ll | 2 +- llvm/test/DebugInfo/X86/partial-constant.ll | 2 +- llvm/test/DebugInfo/X86/pieces-4.ll | 2 +- llvm/test/DebugInfo/X86/split-global.ll | 4 +-- llvm/test/DebugInfo/X86/stack-value-dwarf4.ll | 34 +++++++++---------- llvm/test/DebugInfo/X86/stack-value-piece.ll | 8 ++--- 14 files changed, 61 insertions(+), 49 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp index d8d1a5e8f8411..af51d27663449 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -24,6 +24,20 @@ using namespace llvm; +void DwarfExpression::emitConstu(uint64_t Value) { + if (Value < 32) + emitOp(dwarf::DW_OP_lit0 + Value); + else if (Value == std::numeric_limits<uint64_t>::max()) { + // Only do this for 64-bit values as the DWARF expression stack uses + // target-address-size values. + emitOp(dwarf::DW_OP_lit0); + emitOp(dwarf::DW_OP_not); + } else { + emitOp(dwarf::DW_OP_constu); + emitUnsigned(Value); + } +} + void DwarfExpression::addReg(int DwarfReg, const char *Comment) { assert(DwarfReg >= 0 && "invalid negative dwarf register number"); assert((LocationKind == Unknown || LocationKind == Register) && @@ -72,14 +86,12 @@ void DwarfExpression::addOpPiece(unsigned SizeInBits, unsigned OffsetInBits) { } void DwarfExpression::addShr(unsigned ShiftBy) { - emitOp(dwarf::DW_OP_constu); - emitUnsigned(ShiftBy); + emitConstu(ShiftBy); emitOp(dwarf::DW_OP_shr); } void DwarfExpression::addAnd(unsigned Mask) { - emitOp(dwarf::DW_OP_constu); - emitUnsigned(Mask); + emitConstu(Mask); emitOp(dwarf::DW_OP_and); } @@ -181,8 +193,7 @@ void DwarfExpression::addSignedConstant(int64_t Value) { void DwarfExpression::addUnsignedConstant(uint64_t Value) { assert(LocationKind == Implicit || LocationKind == Unknown); LocationKind = Implicit; - emitOp(dwarf::DW_OP_constu); - emitUnsigned(Value); + emitConstu(Value); } void DwarfExpression::addUnsignedConstant(const APInt &Value) { @@ -373,8 +384,7 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor, break; case dwarf::DW_OP_constu: assert(LocationKind != Register); - emitOp(dwarf::DW_OP_constu); - emitUnsigned(Op->getArg(0)); + emitConstu(Op->getArg(0)); break; case dwarf::DW_OP_stack_value: LocationKind = Implicit; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h index 0637d952eba4d..d47c4d1d29696 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -138,6 +138,9 @@ class DwarfExpression { /// Emit a raw unsigned value. virtual void emitUnsigned(uint64_t Value) = 0; + /// Emit a normalized unsigned constant. + void emitConstu(uint64_t Value); + /// Return whether the given machine register is the frame register in the /// current function. virtual bool isFrameRegister(const TargetRegisterInfo &TRI, unsigned MachineReg) = 0; diff --git a/llvm/test/DebugInfo/AMDGPU/variable-locations.ll b/llvm/test/DebugInfo/AMDGPU/variable-locations.ll index 75859edb6aff8..df0628a2ca169 100644 --- a/llvm/test/DebugInfo/AMDGPU/variable-locations.ll +++ b/llvm/test/DebugInfo/AMDGPU/variable-locations.ll @@ -36,15 +36,15 @@ declare void @llvm.dbg.declare(metadata, metadata, metadata) define amdgpu_kernel void @kernel1( ; CHECK: {{.*}}DW_TAG_formal_parameter -; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +4, DW_OP_constu 0x1, DW_OP_swap, DW_OP_xderef) +; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +4, DW_OP_lit1, DW_OP_swap, DW_OP_xderef) ; CHECK-NEXT: DW_AT_name {{.*}}"ArgN" i32 %ArgN, ; CHECK: {{.*}}DW_TAG_formal_parameter -; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +8, DW_OP_constu 0x1, DW_OP_swap, DW_OP_xderef) +; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +8, DW_OP_lit1, DW_OP_swap, DW_OP_xderef) ; CHECK-NEXT: DW_AT_name {{.*}}"ArgA" i32 addrspace(1)* %ArgA, ; CHECK: {{.*}}DW_TAG_formal_parameter -; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +16, DW_OP_constu 0x1, DW_OP_swap, DW_OP_xderef) +; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_fbreg +16, DW_OP_lit1, DW_OP_swap, DW_OP_xderef) ; CHECK-NEXT: DW_AT_name {{.*}}"ArgB" i32 addrspace(1)* %ArgB) !dbg !13 { entry: diff --git a/llvm/test/DebugInfo/ARM/PR26163.ll b/llvm/test/DebugInfo/ARM/PR26163.ll index 5b7bdc19513c2..3b3ec9c3f4f86 100644 --- a/llvm/test/DebugInfo/ARM/PR26163.ll +++ b/llvm/test/DebugInfo/ARM/PR26163.ll @@ -9,8 +9,8 @@ ; CHECK: DW_TAG_inlined_subroutine ; CHECK: DW_TAG_variable ; CHECK: DW_AT_location [DW_FORM_sec_offset] ({{.*}} -; CHECK: [0x00000004, 0x00000004): DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x8 -; CHECK: [0x00000004, 0x00000014): DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK: [0x00000004, 0x00000004): DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x8 +; CHECK: [0x00000004, 0x00000014): DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) ; Created form the following test case (PR26163) with ; clang -cc1 -triple armv4t--freebsd11.0-gnueabi -emit-obj -debug-info-kind=standalone -O2 -x c test.c diff --git a/llvm/test/DebugInfo/ARM/split-complex.ll b/llvm/test/DebugInfo/ARM/split-complex.ll index fcda92d114b02..280569311fcba 100644 --- a/llvm/test/DebugInfo/ARM/split-complex.ll +++ b/llvm/test/DebugInfo/ARM/split-complex.ll @@ -14,7 +14,7 @@ entry: ; The target has no native double type. ; SROA split the complex value into two i64 values. ; CHECK: DW_TAG_formal_parameter - ; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_constu 0x0, DW_OP_piece 0x8) + ; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (DW_OP_lit0, DW_OP_piece 0x8) ; CHECK-NEXT: DW_AT_name {{.*}} "c" tail call void @llvm.dbg.value(metadata i64 0, metadata !14, metadata !17), !dbg !16 ; Manually removed to disable location list emission: diff --git a/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll b/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll index 76c7e560b87c1..df7fb643e6edb 100644 --- a/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll +++ b/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll @@ -1,6 +1,6 @@ ; REQUIRES: object-emission ; This test is failing for powerpc64, because a location list for the -; variable 'c' is not generated at all. Temporary marking this test as XFAIL +; variable 'c' is not generated at all. Temporary marking this test as XFAIL ; for powerpc, until PR21881 is fixed. ; XFAIL: powerpc64 @@ -9,13 +9,14 @@ ; RUN: %llc_dwarf -O2 -dwarf-version 4 -filetype=obj < %s | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF4 ; This is a test for PR21176. -; DW_OP_const <const> doesn't describe a constant value, but a value at a constant address. +; DW_OP_const <const> doesn't describe a constant value, but a value at a constant address. ; The proper way to describe a constant value is DW_OP_constu <const>, DW_OP_stack_value. +; For values < 32 we emit the canonical DW_OP_lit<const>. ; Generated with clang -S -emit-llvm -g -O2 test.cpp ; extern int func(); -; +; ; int main() ; { ; volatile int c = 13; @@ -26,8 +27,8 @@ ; CHECK: DW_TAG_variable ; CHECK: DW_AT_location ; CHECK-NOT: DW_AT -; DWARF23: DW_OP_constu 0xd{{$}} -; DWARF4: DW_OP_constu 0xd, DW_OP_stack_value{{$}} +; DWARF23: DW_OP_lit13{{$}} +; DWARF4: DW_OP_lit13, DW_OP_stack_value{{$}} ; Function Attrs: uwtable define i32 @main() #0 !dbg !4 { diff --git a/llvm/test/DebugInfo/X86/PR26148.ll b/llvm/test/DebugInfo/X86/PR26148.ll index 198776ef9535e..685f2d503717e 100644 --- a/llvm/test/DebugInfo/X86/PR26148.ll +++ b/llvm/test/DebugInfo/X86/PR26148.ll @@ -19,8 +19,8 @@ ; AS in 26163, we expect two ranges (as opposed to one), the first one being zero sized ; ; -; CHECK: [0x0000000000000004, 0x0000000000000004): DW_OP_constu 0x3, DW_OP_piece 0x4, DW_OP_reg5 RDI, DW_OP_piece 0x2 -; CHECK: [0x0000000000000004, 0x0000000000000014): DW_OP_constu 0x3, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_piece 0x4 +; CHECK: [0x0000000000000004, 0x0000000000000004): DW_OP_lit3, DW_OP_piece 0x4, DW_OP_reg5 RDI, DW_OP_piece 0x2 +; CHECK: [0x0000000000000004, 0x0000000000000014): DW_OP_lit3, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_piece 0x4 source_filename = "test/DebugInfo/X86/PR26148.ll" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/DebugInfo/X86/constant-loclist.ll b/llvm/test/DebugInfo/X86/constant-loclist.ll index 981068317d771..17643d1eab724 100644 --- a/llvm/test/DebugInfo/X86/constant-loclist.ll +++ b/llvm/test/DebugInfo/X86/constant-loclist.ll @@ -14,7 +14,7 @@ ; CHECK-NEXT: DW_AT_name {{.*}}"i" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_location [DW_FORM_data4] ( -; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_constu 0x0 +; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_lit0 ; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_constu 0x4000000000000000) ; CHECK-NEXT: DW_AT_name {{.*}}"u" diff --git a/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll b/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll index a801347c2f027..bc4241c1cc1be 100644 --- a/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll +++ b/llvm/test/DebugInfo/X86/dw_op_minus_direct.ll @@ -17,7 +17,7 @@ ; CHECK: .debug_loc contents: ; CHECK: 0x00000000: -; CHECK-NEXT: [0x0000000000000000, 0x0000000000000004): DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_constu 0x1, DW_OP_minus, DW_OP_stack_value +; CHECK-NEXT: [0x0000000000000000, 0x0000000000000004): DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_lit1, DW_OP_minus, DW_OP_stack_value ; rax+0, constu 0xffffffff, and, constu 0x00000001, minus, stack-value source_filename = "minus.c" diff --git a/llvm/test/DebugInfo/X86/partial-constant.ll b/llvm/test/DebugInfo/X86/partial-constant.ll index 0c02cf125211c..831d6165c0b3a 100644 --- a/llvm/test/DebugInfo/X86/partial-constant.ll +++ b/llvm/test/DebugInfo/X86/partial-constant.ll @@ -18,7 +18,7 @@ ; CHECK-NOT: DW_AT_const_value ; CHECK: .debug_loc contents: ; CHECK-NEXT: 0x00000000: -; CHECK-NEXT: {{.*}}: DW_OP_constu 0x1, DW_OP_stack_value +; CHECK-NEXT: {{.*}}: DW_OP_lit1, DW_OP_stack_value source_filename = "test.ii" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" diff --git a/llvm/test/DebugInfo/X86/pieces-4.ll b/llvm/test/DebugInfo/X86/pieces-4.ll index 0e8c5e1185d0f..6ae8597e424cf 100644 --- a/llvm/test/DebugInfo/X86/pieces-4.ll +++ b/llvm/test/DebugInfo/X86/pieces-4.ll @@ -25,7 +25,7 @@ ; DWARF: .debug_loc contents: ; DWARF-NEXT: 0x00000000: -; DWARF-NEXT: {{.*}}: DW_OP_breg7 RSP+{{[0-9]+}}, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4 +; DWARF-NEXT: {{.*}}: DW_OP_breg7 RSP+{{[0-9]+}}, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4 ; ModuleID = 't.c' source_filename = "t.c" diff --git a/llvm/test/DebugInfo/X86/split-global.ll b/llvm/test/DebugInfo/X86/split-global.ll index a47dd532807cd..abe0a820cfc37 100644 --- a/llvm/test/DebugInfo/X86/split-global.ll +++ b/llvm/test/DebugInfo/X86/split-global.ll @@ -17,13 +17,13 @@ target triple = "x86_64-apple-macosx10.12.0" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_name {{.*}}"part_const" ; CHECK-NOT: DW_TAG -; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x8, DW_OP_piece 0x4, DW_OP_constu 0x2, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x8, DW_OP_piece 0x4, DW_OP_lit2, DW_OP_stack_value, DW_OP_piece 0x4) ; [0x0000000000000008], piece 0x00000004, constu 0x00000002, stack-value, piece 0x00000004 ; CHECK-NOT: DW_TAG ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_name {{.*}}"full_const" ; CHECK-NOT: DW_TAG -; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_constu 0x1, DW_OP_stack_value, DW_OP_piece 0x4, DW_OP_constu 0x2, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_lit1, DW_OP_stack_value, DW_OP_piece 0x4, DW_OP_lit2, DW_OP_stack_value, DW_OP_piece 0x4) ; CHECK-NOT: DW_TAG @point.y = global i32 2, align 4, !dbg !13 @point.x = global i32 1, align 4, !dbg !12 diff --git a/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll b/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll index 7ad7cceb7ff0d..479625b6df8fe 100644 --- a/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll +++ b/llvm/test/DebugInfo/X86/stack-value-dwarf4.ll @@ -6,25 +6,23 @@ target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" -; CHECK-DWARF2: .byte 8 # DW_AT_location -; CHECK-DWARF2 .byte 16 -; CHECK-DWARF2 .byte 4 -; CHECK-DWARF2 .byte 147 -; CHECK-DWARF2 .byte 2 -; CHECK-DWARF2 .byte 16 -; CHECK-DWARF2 .byte 0 -; CHECK-DWARF2 .byte 147 -; CHECK-DWARF2 .byte 2 +; CHECK-DWARF2: .byte 6 # DW_AT_location +; CHECK-DWARF2-NEXT: .byte 52 +; CHECK-DWARF2-NEXT: .byte 147 +; CHECK-DWARF2-NEXT: .byte 2 +; CHECK-DWARF2-NEXT: .byte 48 +; CHECK-DWARF2-NEXT: .byte 147 +; CHECK-DWARF2-NEXT: .byte 2 -; CHECK-DWARF4: .byte 10 # DW_AT_location -; CHECK-DWARF4-NEXT: .byte 16 -; CHECK-DWARF4-NEXT: .byte 4 -; CHECK-DWARF4-NEXT: .byte 159 -; CHECK-DWARF4-NEXT: .byte 147 -; CHECK-DWARF4-NEXT: .byte 2 -; CHECK-DWARF4-NEXT: .byte 16 -; CHECK-DWARF4-NEXT: .byte 0 -; CHECK-DWARF4-NEXT: .byte 159 +; CHECK-DWARF4: .byte 8 # DW_AT_location +; CHECK-DWARF4-NEXT:.byte 52 +; CHECK-DWARF4-NEXT:.byte 159 +; CHECK-DWARF4-NEXT:.byte 147 +; CHECK-DWARF4-NEXT:.byte 2 +; CHECK-DWARF4-NEXT:.byte 48 +; CHECK-DWARF4-NEXT:.byte 159 +; CHECK-DWARF4-NEXT:.byte 147 +; CHECK-DWARF4-NEXT:.byte 2 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang", file: !4, globals: !1, emissionKind: FullDebug) !1 = !{!2, !10} diff --git a/llvm/test/DebugInfo/X86/stack-value-piece.ll b/llvm/test/DebugInfo/X86/stack-value-piece.ll index 995504f614cde..b6a03c3aa7755 100644 --- a/llvm/test/DebugInfo/X86/stack-value-piece.ll +++ b/llvm/test/DebugInfo/X86/stack-value-piece.ll @@ -19,21 +19,21 @@ ; CHECK: DW_AT_name {{.*}} "i" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_location {{.*}} ([[I:.*]] -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) ; CHECK-NEXT: DW_AT_name {{.*}} "r" ; ; CHECK: DW_TAG_subprogram ; CHECK: DW_AT_name {{.*}} "f" ; CHECK: DW_TAG_variable ; CHECK-NEXT: DW_AT_location {{.*}} ([[F:.*]] -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4) ; CHECK-NEXT: DW_AT_name {{.*}} "r" ; ; CHECK: .debug_loc contents: ; CHECK: [[I]]: -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4 +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg5 RDI, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4 ; CHECK: [[F]]: -; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_constu 0x0, DW_OP_stack_value, DW_OP_piece 0x4 +; CHECK-NEXT: [{{.*}}, {{.*}}): DW_OP_reg17 XMM0, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x4 source_filename = "stack-value-piece.c" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" From d7bfe8c6bf6f9543ab1aa611ef31f21030ee62b8 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Fri, 21 Sep 2018 10:06:45 -0700 Subject: [PATCH 317/582] [test] Disable a bitcode test for AppleClang This test just checks that the upstream llvm.org compiler can load bitcode produced by an old version of AppleClang. The upstream compiler discards the debug info from the test file because of a version mismatch. However, the Apple compiler does not. Disable this test for Apple compilers (there already are dedicated debug info bitcode compatibility tests for it). rdar://44645820 apple-llvm-split-commit: 6b97e720c72834115fbf98f39c3995a03f2bd97e apple-llvm-split-dir: llvm/ --- llvm/test/Bitcode/apple-clang-700-compat.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/test/Bitcode/apple-clang-700-compat.test b/llvm/test/Bitcode/apple-clang-700-compat.test index c7ab2f4968713..e31ddefbf0f6f 100644 --- a/llvm/test/Bitcode/apple-clang-700-compat.test +++ b/llvm/test/Bitcode/apple-clang-700-compat.test @@ -1,3 +1,5 @@ +; REQUIRE: upstream-llvm.org-sources + ; AppleClang-700 uses a debug info version that isn't recognized by llvm.org ; compilers. However, it does produce valid bitcode which should parse without ; crashing the metadata loader. From f74d5c5e4b6c6036f6d252528968c9ac4541f72c Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Mon, 1 Oct 2018 10:13:22 -0700 Subject: [PATCH 318/582] [test] Fix typo in a REQUIRES line rdar://44867097 apple-llvm-split-commit: a94837f14b33d9a858fd660e61f894845a393865 apple-llvm-split-dir: llvm/ --- llvm/test/Bitcode/apple-clang-700-compat.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/Bitcode/apple-clang-700-compat.test b/llvm/test/Bitcode/apple-clang-700-compat.test index e31ddefbf0f6f..f89dfdb614890 100644 --- a/llvm/test/Bitcode/apple-clang-700-compat.test +++ b/llvm/test/Bitcode/apple-clang-700-compat.test @@ -1,4 +1,4 @@ -; REQUIRE: upstream-llvm.org-sources +; REQUIRES: upstream-llvm.org-sources ; AppleClang-700 uses a debug info version that isn't recognized by llvm.org ; compilers. However, it does produce valid bitcode which should parse without From f2f4bccf5a903e14667dd58c2ca822743d73e16f Mon Sep 17 00:00:00 2001 From: Ben Langmuir <blangmuir@apple.com> Date: Wed, 3 Oct 2018 11:22:04 -0700 Subject: [PATCH 319/582] [indexstore] Fix indexstore.h to compile in C This header is intended to be useable from C code, but it was using C++isms like ctime and an untaged struct. apple-llvm-split-commit: 68acee37959f1b1fd23fc4e036b7e1e0989ef107 apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 4bfd210dd2d25..9a40ddb4ce65d 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -14,9 +14,10 @@ #ifndef LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H #define LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H -#include <stdint.h> +#include <stdbool.h> #include <stddef.h> -#include <ctime> +#include <stdint.h> +#include <time.h> /** * \brief The version constants for the Index Store C API. @@ -131,7 +132,7 @@ indexstore_unit_event_get_kind(indexstore_unit_event_t); INDEXSTORE_PUBLIC indexstore_string_ref_t indexstore_unit_event_get_unit_name(indexstore_unit_event_t); -INDEXSTORE_PUBLIC timespec +INDEXSTORE_PUBLIC struct timespec indexstore_unit_event_get_modification_time(indexstore_unit_event_t); #if INDEXSTORE_HAS_BLOCKS From 823c0f67f11e68ef0e42db6093e95f87eb8a7a76 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Mon, 1 Oct 2018 12:08:04 -0700 Subject: [PATCH 320/582] IndexStore: correct symbol visibility On ELF targets, use protected visibility (symbols cannot be interpositioned). On MachO, use default visibility. On Windows, we need to define it to either `__declspec(dllexport)` when building the library itself, or `__declspec(dllimport)` when using the library. This fixes a number of warnings when building IndexCore for Windows. Remove the use of the exports list and rely on the annotations instead. This makes the build similar across all the targets (Windows requires all listed symbols to be present, and we would need to preprocess the list otherwise). Fix the accidental usage of `_CINDEX_LIB_` opting for the CMake standard variable `<Name>_EXPORTS`. Simplify the logic in the CMake for Apple targets a bit. apple-llvm-split-commit: ecb54ae3f7900b29629caa14bfe68b5dd0fba86a apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 8 +++-- clang/tools/IndexStore/CMakeLists.txt | 44 ++++++++++----------------- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 9a40ddb4ce65d..b5550b618ab53 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -53,8 +53,12 @@ #endif #ifndef INDEXSTORE_PUBLIC -# if defined (_MSC_VER) -# define INDEXSTORE_PUBLIC __declspec(dllimport) +# ifdef _WIN32 +# ifdef IndexStore_EXPORTS +# define INDEXSTORE_PUBLIC __declspec(dllexport) +# else +# define INDEXSTORE_PUBLIC __declspec(dllimport) +# endif # else # define INDEXSTORE_PUBLIC # endif diff --git a/clang/tools/IndexStore/CMakeLists.txt b/clang/tools/IndexStore/CMakeLists.txt index f6974e676c4f2..b648f54a5a1c1 100644 --- a/clang/tools/IndexStore/CMakeLists.txt +++ b/clang/tools/IndexStore/CMakeLists.txt @@ -12,9 +12,9 @@ set(LIBS clangIndexDataStore ) -set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/IndexStore.exports) - -set(ENABLE_SHARED SHARED) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Windows) + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/IndexStore.exports) +endif() if(WIN32) set(output_name "libIndexStore") @@ -22,7 +22,7 @@ else() set(output_name "IndexStore") endif() -add_clang_library(IndexStore ${ENABLE_SHARED} ${ENABLE_STATIC} +add_clang_library(IndexStore SHARED OUTPUT_NAME ${output_name} ${SOURCES} @@ -35,31 +35,19 @@ add_clang_library(IndexStore ${ENABLE_SHARED} ${ENABLE_STATIC} Support ) -set(INDEXSTORE_LIBRARY_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") - -if(ENABLE_SHARED) - if(WIN32) - set_target_properties(IndexStore - PROPERTIES - VERSION ${INDEXSTORE_LIBRARY_VERSION} - DEFINE_SYMBOL _CINDEX_LIB_) - elseif(APPLE) - set(INDEXSTORE_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") - set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -Wl,-current_version -Wl,${INDEXSTORE_LIBRARY_VERSION}") - - check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) - if(HAVE_CORESERVICES_H) - set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -framework CoreServices") - endif() - - set_property(TARGET IndexStore APPEND_STRING PROPERTY - LINK_FLAGS ${INDEXSTORE_LINK_FLAGS}) - else() - set_target_properties(IndexStore - PROPERTIES - VERSION ${INDEXSTORE_LIBRARY_VERSION} - DEFINE_SYMBOL _CINDEX_LIB_) +if(APPLE) + set(INDEXSTORE_LIBRARY_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + + set(INDEXSTORE_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -Wl,-current_version -Wl,${INDEXSTORE_LIBRARY_VERSION}") + + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -framework CoreServices") endif() + + set_property(TARGET IndexStore APPEND_STRING PROPERTY + LINK_FLAGS ${INDEXSTORE_LINK_FLAGS}) endif() if (LLVM_INSTALL_TOOLCHAIN_ONLY) From 7efef6ede5c71d33a249689801c0868eb2dbf1a6 Mon Sep 17 00:00:00 2001 From: Erik Pilkington <erik.pilkington@gmail.com> Date: Mon, 29 Oct 2018 15:11:43 -0700 Subject: [PATCH 321/582] Fix some failing tests apple-llvm-split-commit: 85c82273ebb8de9f0fe7c270de54de389b67af25 apple-llvm-split-dir: clang/ --- clang/test/Modules/odr_hash.mm | 14 +++++--------- clang/test/Sema/attr-availability-swift.c | 4 ++-- clang/test/Sema/switch-availability.c | 4 ++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/clang/test/Modules/odr_hash.mm b/clang/test/Modules/odr_hash.mm index f04fb7eb5e8e0..8bc5f35f7c754 100644 --- a/clang/test/Modules/odr_hash.mm +++ b/clang/test/Modules/odr_hash.mm @@ -47,7 +47,7 @@ @interface I2 : I1 @interface Interface1 <T : I1 *> { @public - T<P1> x; + T x; // FIXME: align with upstream (rdar://43906928). } @end @@ -228,12 +228,12 @@ void valid() { #if defined(FIRST) @interface Interface4 <T : I1 *> { @public - T<P1> x; + T x; // FIXME: align with upstream (rdar://43906928). } @end @interface Interface5 <T : I1 *> { @public - T<P1> x; + T x; // FIXME: align with upstream (rdar://43906928). } @end @interface Interface6 <T1 : I1 *, T2 : I2 *> { @@ -244,12 +244,12 @@ @interface Interface6 <T1 : I1 *, T2 : I2 *> { #elif defined(SECOND) @interface Interface4 <T : I1 *> { @public - T<P2> x; + T x; // FIXME: align with upstream (rdar://43906928). } @end @interface Interface5 <T : I1 *> { @public - T<P1, P2> x; + T x; // FIXME: align with upstream (rdar://43906928). } @end @interface Interface6 <T1 : I1 *, T2 : I2 *> { @@ -276,11 +276,7 @@ @interface Interface6 <T1 : I1 *, T2 : I2 *> { }; #else Invalid1 i1; -// expected-error@first.h:* {{'Types::ObjCTypeParam::Invalid1::x' from module 'FirstModule' is not present in definition of 'Types::ObjCTypeParam::Invalid1' in module 'SecondModule'}} -// expected-note@second.h:* {{declaration of 'x' does not match}} Invalid2 i2; -// expected-error@first.h:* {{'Types::ObjCTypeParam::Invalid2::x' from module 'FirstModule' is not present in definition of 'Types::ObjCTypeParam::Invalid2' in module 'SecondModule'}} -// expected-note@second.h:* {{declaration of 'x' does not match}} Invalid3 i3; // expected-error@first.h:* {{'Types::ObjCTypeParam::Invalid3::x' from module 'FirstModule' is not present in definition of 'Types::ObjCTypeParam::Invalid3' in module 'SecondModule'}} // expected-note@second.h:* {{declaration of 'x' does not match}} diff --git a/clang/test/Sema/attr-availability-swift.c b/clang/test/Sema/attr-availability-swift.c index 42e75246d332f..d77094cb21630 100644 --- a/clang/test/Sema/attr-availability-swift.c +++ b/clang/test/Sema/attr-availability-swift.c @@ -16,11 +16,11 @@ extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable))); // CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "" "" extern int noSwiftGlobal1 __attribute__((availability(macosx, introduced=10.1))); // okay -// CHECK: AvailabilityAttr {{.*}}macos 10.1 0 0 "" "" // CHECK: AvailabilityAttr {{.*}}Inherited swift 0 0 0 Unavailable "" "" +// CHECK: AvailabilityAttr {{.*}}macos 10.1 0 0 "" "" extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable, message="and this one has a message"))); // okay -// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "and this one has a message" "" // CHECK: AvailabilityAttr {{.*}}Inherited macos 10.1 0 0 "" "" +// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "and this one has a message" "" extern int noSwiftGlobal2 __attribute__((availability(swift, introduced=5))); // expected-warning{{only 'unavailable' and 'deprecated' are supported for Swift availability}} // CHECK: VarDecl // CHECK-NOT: AvailabilityAttr diff --git a/clang/test/Sema/switch-availability.c b/clang/test/Sema/switch-availability.c index 888edddac463d..b150a9e56be09 100644 --- a/clang/test/Sema/switch-availability.c +++ b/clang/test/Sema/switch-availability.c @@ -15,7 +15,7 @@ enum SwitchTwo { }; void testSwitchTwo(enum SwitchTwo st) { - switch (st) {} // expected-warning{{enumeration values 'Vim' and 'Emacs' not handled in switch}} + switch (st) {} // expected-warning{{enumeration values 'Vim' and 'Emacs' not handled in switch}} expected-note{{add missing switch cases}} } enum SwitchThree { @@ -23,5 +23,5 @@ enum SwitchThree { }; void testSwitchThree(enum SwitchThree st) { - switch (st) {} // expected-warning{{enumeration value 'New' not handled in switch}} + switch (st) {} // expected-warning{{enumeration value 'New' not handled in switch}} expected-note{{add missing switch cases}} } From 10cc4b4e52224f982634ca7b460bbd78fc1bd2ee Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Mon, 29 Oct 2018 15:26:58 -0700 Subject: [PATCH 322/582] Turn -Watimport-in-framework-header off by default Swift is not ready for this yet, and after some discussion with Jordan Rose we agreed that it's better to only use this when explicitly asked by the user. rdar://problem/45653633 apple-llvm-split-commit: c6842a1ed41c6ad9e6ec58c3788599e6fa0aa325 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticParseKinds.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index f60d8706b40dc..ef371617d0521 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -257,7 +257,7 @@ def err_atimport : Error< def warn_atimport_in_framework_header : Warning< "use of '@import' in framework header is discouraged, " "including this header requires -fmodules">, - InGroup<FrameworkHdrAtImport>; + InGroup<FrameworkHdrAtImport>, DefaultIgnore; def err_invalid_reference_qualifier_application : Error< "'%0' qualifier may not be applied to a reference">; From 7d936ab817dfb54b66241d4190eed1e3b31e8c90 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Mon, 29 Oct 2018 15:55:46 -0700 Subject: [PATCH 323/582] Turn -Wframework-include-private-from-public off by default Users are not yet ready for this, turn this off by default until we get users to proper fix their headers. rdar://problem/45653419 apple-llvm-split-commit: 8ad33e83e09977f60f42850ba21418de87889200 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticLexKinds.td | 2 +- clang/test/Modules/framework-public-includes-private.m | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 085648c65d1d2..ab7200bebe347 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -740,7 +740,7 @@ def warn_quoted_include_in_framework_header : Warning< >, InGroup<FrameworkHdrQuotedInclude>, DefaultIgnore; def warn_framework_include_private_from_public : Warning< "public framework header includes private framework header '%0'" - >, InGroup<FrameworkIncludePrivateFromPublic>; + >, InGroup<FrameworkIncludePrivateFromPublic>, DefaultIgnore; def warn_auto_module_import : Warning< "treating #%select{include|import|include_next|__include_macros}0 as an " diff --git a/clang/test/Modules/framework-public-includes-private.m b/clang/test/Modules/framework-public-includes-private.m index eb4d2877a1cdc..b8ae085a8fecc 100644 --- a/clang/test/Modules/framework-public-includes-private.m +++ b/clang/test/Modules/framework-public-includes-private.m @@ -13,12 +13,12 @@ // RUN: %clang_cc1 \ // RUN: -iquote %t/z.hmap -iquote %t/a.hmap -ivfsoverlay %t/z.yaml \ // RUN: -F%S/Inputs/framework-public-includes-private \ -// RUN: -fsyntax-only %s -verify +// RUN: -fsyntax-only -Wframework-include-private-from-public %s -verify // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ // RUN: -iquote %t/z.hmap -iquote %t/a.hmap -ivfsoverlay %t/z.yaml \ // RUN: -F%S/Inputs/framework-public-includes-private \ -// RUN: -fsyntax-only %s \ +// RUN: -fsyntax-only -Wframework-include-private-from-public %s \ // RUN: 2>%t/stderr // The same warnings show up when modules is on but -verify doesn't get it From 60102a2ec115302e8375facacf11041edb888b2f Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Wed, 31 Oct 2018 09:21:16 -0700 Subject: [PATCH 324/582] Adjust for SVN r345637 ObjC1 and ObjC2 were removed as ObjC1 has not been supported in a very long time. It has been replaced with `ObjC`. apple-llvm-split-commit: 3b19a7c56a6c5e94c9ca332527188c59016e97cb apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/SymbolName.cpp | 2 +- clang/lib/Tooling/Refactor/TypeUtils.cpp | 4 ++-- clang/tools/clang-refactor-test/ClangRefactorTest.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/lib/Tooling/Refactor/SymbolName.cpp b/clang/lib/Tooling/Refactor/SymbolName.cpp index 2a02d1627eea3..61da1873e38aa 100644 --- a/clang/lib/Tooling/Refactor/SymbolName.cpp +++ b/clang/lib/Tooling/Refactor/SymbolName.cpp @@ -29,7 +29,7 @@ static void initNames(std::vector<std::string> &Strings, StringRef Name, } OldSymbolName::OldSymbolName(StringRef Name, const LangOptions &LangOpts) { - initNames(Strings, Name, LangOpts.ObjC1); + initNames(Strings, Name, LangOpts.ObjC); } OldSymbolName::OldSymbolName(StringRef Name, bool IsObjectiveCSelector) { diff --git a/clang/lib/Tooling/Refactor/TypeUtils.cpp b/clang/lib/Tooling/Refactor/TypeUtils.cpp index c07136bd709ad..03c8a5bc45b39 100644 --- a/clang/lib/Tooling/Refactor/TypeUtils.cpp +++ b/clang/lib/Tooling/Refactor/TypeUtils.cpp @@ -50,7 +50,7 @@ static QualType preferredBoolType(const Decl *FunctionLikeParentDecl, case BuiltinType::Bool: // In Objective-C[++] we want to try to use 'BOOL' when the 'BOOL' typedef // is defined. - if (Ctx.getLangOpts().ObjC1 && Ctx.getBOOLDecl()) { + if (Ctx.getLangOpts().ObjC && Ctx.getBOOLDecl()) { if (Ctx.getLangOpts().CPlusPlus && FunctionLikeParentDecl) { // When extracting expression from a standalone function in // Objective-C++ we should use BOOL when expression uses BOOL, otherwise @@ -183,7 +183,7 @@ QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl, } // The bool type adjustment is required only in C or Objective-C[++]. - if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().ObjC1) + if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().ObjC) return T; E = E->IgnoreParenImpCasts(); if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { diff --git a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp index d5de340d25603..ac2210c024be1 100644 --- a/clang/tools/clang-refactor-test/ClangRefactorTest.cpp +++ b/clang/tools/clang-refactor-test/ClangRefactorTest.cpp @@ -594,7 +594,7 @@ int rename(CXTranslationUnit TU, CXIndex CIdx, ArrayRef<const char *> Args) { // FIXME: This is a hack LangOptions LangOpts; - LangOpts.ObjC1 = true; + LangOpts.ObjC = true; tooling::OldSymbolName NewSymbolName(opts::rename::NewName, LangOpts); if (ExpectedReplacements.empty()) { @@ -729,7 +729,7 @@ int renameIndexedFile(CXIndex CIdx, ArrayRef<const char *> Args) { PrintFilenames = true; LangOptions LangOpts; - LangOpts.ObjC1 = true; + LangOpts.ObjC = true; tooling::OldSymbolName ExpectedReplacementStrings( opts::rename::IndexedNewNames[0], LangOpts); @@ -750,7 +750,7 @@ int renameIndexedFile(CXIndex CIdx, ArrayRef<const char *> Args) { : SymbolIndex] .c_str(); LangOptions LangOpts; - LangOpts.ObjC1 = true; + LangOpts.ObjC = true; tooling::OldSymbolName NewSymbolName(NewName, LangOpts); outs() << occurrenceToString(FileResult.Occurrences[I], /*IsLocal*/ false, From 11b8843679ab698eaa44c1bccd5f629a3676cc71 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Tue, 30 Oct 2018 11:54:56 -0700 Subject: [PATCH 325/582] [unittests] Add a unit test file for DirectoryWatcher library apple-llvm-split-commit: 7470cb13b2772112cf423a87f99d2221f487a49c apple-llvm-split-dir: clang/ --- clang/unittests/CMakeLists.txt | 1 + .../unittests/DirectoryWatcher/CMakeLists.txt | 21 ++ .../DirectoryWatcher/DirectoryWatcherTest.cpp | 328 ++++++++++++++++++ 3 files changed, 350 insertions(+) create mode 100644 clang/unittests/DirectoryWatcher/CMakeLists.txt create mode 100644 clang/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt index 6eff59986cbfa..0d1db0afcb3ec 100644 --- a/clang/unittests/CMakeLists.txt +++ b/clang/unittests/CMakeLists.txt @@ -30,5 +30,6 @@ add_subdirectory(CodeGen) if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD) add_subdirectory(libclang) endif() +add_subdirectory(DirectoryWatcher) add_subdirectory(Rename) add_subdirectory(Index) diff --git a/clang/unittests/DirectoryWatcher/CMakeLists.txt b/clang/unittests/DirectoryWatcher/CMakeLists.txt new file mode 100644 index 0000000000000..45cf70261ad05 --- /dev/null +++ b/clang/unittests/DirectoryWatcher/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_unittest(DirectoryWatcherTests + DirectoryWatcherTest.cpp + ) + +target_link_libraries(DirectoryWatcherTests + PRIVATE + clangDirectoryWatcher + ) + +if(APPLE) + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + set(DWT_LINK_FLAGS "${DWT_LINK_FLAGS} -framework CoreServices") + set_property(TARGET DirectoryWatcherTests APPEND_STRING PROPERTY + LINK_FLAGS ${DWT_LINK_FLAGS}) + endif() +endif() diff --git a/clang/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp b/clang/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp new file mode 100644 index 0000000000000..5fec06a64d629 --- /dev/null +++ b/clang/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp @@ -0,0 +1,328 @@ +//===- unittests/DirectoryWatcher/DirectoryWatcherTest.cpp ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "gtest/gtest.h" +#include <condition_variable> +#include <mutex> +#include <thread> + +using namespace llvm; +using namespace llvm::sys; +using namespace llvm::sys::fs; +using namespace clang; + +namespace { + +class EventCollection { + SmallVector<DirectoryWatcher::Event, 6> Events; +public: + EventCollection() = default; + explicit EventCollection(ArrayRef<DirectoryWatcher::Event> events) { + append(events); + } + + void append(ArrayRef<DirectoryWatcher::Event> events) { + Events.append(events.begin(), events.end()); + } + + bool empty() const { return Events.empty(); } + size_t size() const { return Events.size(); } + void clear() { Events.clear(); } + + bool hasEvents(ArrayRef<StringRef> filenames, + ArrayRef<DirectoryWatcher::EventKind> kinds, + ArrayRef<file_status> stats) const { + assert(filenames.size() == kinds.size()); + assert(filenames.size() == stats.size()); + SmallVector<DirectoryWatcher::Event, 6> evts = Events; + bool hadError = false; + for (unsigned i = 0, e = filenames.size(); i < e; ++i) { + StringRef fname = filenames[i]; + DirectoryWatcher::EventKind kind = kinds[i]; + file_status stat = stats[i]; + auto it = std::find_if(evts.begin(), evts.end(), [&](const DirectoryWatcher::Event &evt)->bool { + return path::filename(evt.Filename) == fname; + }); + if (it == evts.end()) { + hadError = err(Twine("expected filename '"+fname+"' not found")); + continue; + } + if (it->Kind != kind) { + hadError = err(Twine("filename '" + fname + "' has event kind " + + std::to_string((int)it->Kind) + ", expected ") + + std::to_string((int)kind)); + } + if (it->Kind != DirectoryWatcher::EventKind::Removed && + it->ModTime != stat.getLastModificationTime()) + hadError = err(Twine("filename '"+fname+"' has different mod time")); + evts.erase(it); + } + for (const auto &evt : evts) { + hadError = err(Twine("unexpected filename '"+path::filename(evt.Filename)+"' found")); + } + return !hadError; + } + + bool hasAdded(ArrayRef<StringRef> filenames, ArrayRef<file_status> stats) const { + std::vector<DirectoryWatcher::EventKind> kinds{ + filenames.size(), DirectoryWatcher::EventKind::Added }; + return hasEvents(filenames, kinds, stats); + } + + bool hasRemoved(ArrayRef<StringRef> filenames) const { + std::vector<DirectoryWatcher::EventKind> kinds{ + filenames.size(), DirectoryWatcher::EventKind::Removed }; + std::vector<file_status> stats{ filenames.size(), file_status{} }; + return hasEvents(filenames, kinds, stats); + } + +private: + bool err(Twine msg) const { + SmallString<128> buf; + llvm::errs() << msg.toStringRef(buf) << '\n'; + return true; + } +}; + +struct EventOccurrence { + std::vector<DirectoryWatcher::Event> Events; + bool IsInitial; +}; + +class DirectoryWatcherTest: public std::enable_shared_from_this<DirectoryWatcherTest> { + std::string WatchedDir; + std::string TempDir; + std::unique_ptr<DirectoryWatcher> DirWatcher; + + std::condition_variable Condition; + std::mutex Mutex; + std::deque<EventOccurrence> EvtOccurs; + +public: + void init() { + SmallString<128> pathBuf; + std::error_code EC = createUniqueDirectory("dirwatcher", pathBuf); + ASSERT_FALSE(EC); + TempDir = pathBuf.str(); + path::append(pathBuf, "watch"); + WatchedDir = pathBuf.str(); + EC = create_directory(WatchedDir); + ASSERT_FALSE(EC); + } + + ~DirectoryWatcherTest() { + stopWatching(); + remove_directories(TempDir); + } + +public: + StringRef getWatchedDir() const { + return WatchedDir; + } + + void addFile(StringRef filename, file_status &stat) { + SmallString<128> pathBuf; + pathBuf = TempDir; + path::append(pathBuf, filename); + Expected<file_t> ft = openNativeFileForWrite(pathBuf, CD_CreateNew, OF_None); + ASSERT_TRUE((bool)ft); + closeFile(*ft); + + SmallString<128> newPath; + newPath = WatchedDir; + path::append(newPath, filename); + std::error_code EC = rename(pathBuf, newPath); + ASSERT_FALSE(EC); + + EC = status(newPath, stat); + ASSERT_FALSE(EC); + } + + void addFiles(ArrayRef<StringRef> filenames, std::vector<file_status> &stats) { + for (auto fname : filenames) { + file_status stat; + addFile(fname, stat); + stats.push_back(stat); + } + } + + void addFiles(ArrayRef<StringRef> filenames) { + std::vector<file_status> stats; + addFiles(filenames, stats); + } + + void removeFile(StringRef filename) { + SmallString<128> pathBuf; + pathBuf = WatchedDir; + path::append(pathBuf, filename); + std::error_code EC = remove(pathBuf, /*IgnoreNonExisting=*/false); + ASSERT_FALSE(EC); + } + + void removeFiles(ArrayRef<StringRef> filenames) { + for (auto fname : filenames) { + removeFile(fname); + } + } + + /// \returns true for error. + bool startWatching(bool waitInitialSync) { + std::weak_ptr<DirectoryWatcherTest> weakThis = shared_from_this(); + auto receiver = [weakThis](ArrayRef<DirectoryWatcher::Event> events, bool isInitial) { + if (auto this_ = weakThis.lock()) + this_->onEvents(events, isInitial); + }; + std::string error; + DirWatcher = DirectoryWatcher::create(getWatchedDir(), receiver, + waitInitialSync, error); + return DirWatcher == nullptr; + } + + void stopWatching() { + DirWatcher.reset(); + } + + /// \returns None if the timeout is reached before getting an event. + Optional<EventOccurrence> getNextEvent(unsigned timeout_seconds = 5) { + std::unique_lock<std::mutex> lck(Mutex); + auto pred = [&]()->bool { return !EvtOccurs.empty(); }; + bool gotEvent = Condition.wait_for(lck, std::chrono::seconds(timeout_seconds), pred); + if (!gotEvent) + return None; + + EventOccurrence occur = EvtOccurs.front(); + EvtOccurs.pop_front(); + return occur; + } + + EventOccurrence getNextEventImmediately() { + std::lock_guard<std::mutex> LG(Mutex); + assert(!EvtOccurs.empty()); + EventOccurrence occur = EvtOccurs.front(); + EvtOccurs.pop_front(); + return occur; + } + +private: + void onEvents(ArrayRef<DirectoryWatcher::Event> events, bool isInitial) { + std::lock_guard<std::mutex> LG(Mutex); + EvtOccurs.push_back({events, isInitial}); + Condition.notify_all(); + } +}; + +} + +TEST(DirectoryWatcherTest, initialScan) { + auto t = std::make_shared<DirectoryWatcherTest>(); + t->init(); + + std::vector<StringRef> fnames = {"a", "b", "c"}; + std::vector<file_status> stats; + t->addFiles(fnames, stats); + + bool err = t->startWatching(/*waitInitialSync=*/true); + ASSERT_FALSE(err); + + auto evt = t->getNextEventImmediately(); + EXPECT_TRUE(evt.IsInitial); + EventCollection coll1{evt.Events}; + EXPECT_TRUE(coll1.hasAdded(fnames, stats)); + + StringRef additionalFname = "d"; + file_status additionalStat; + t->addFile(additionalFname, additionalStat); + auto evtOpt = t->getNextEvent(); + ASSERT_TRUE(evtOpt.hasValue()); + EXPECT_FALSE(evtOpt->IsInitial); + EventCollection coll2{evtOpt->Events}; + EXPECT_TRUE(coll2.hasAdded({additionalFname}, {additionalStat})); +} + +TEST(DirectoryWatcherTest, fileEvents) { + auto t = std::make_shared<DirectoryWatcherTest>(); + t->init(); + + bool err = t->startWatching(/*waitInitialSync=*/false); + ASSERT_FALSE(err); + + auto evt = t->getNextEvent(); + ASSERT_TRUE(evt.hasValue()); + EXPECT_TRUE(evt->IsInitial); + EXPECT_TRUE(evt->Events.empty()); + return; + + { + std::vector<StringRef> fnames = {"a", "b"}; + std::vector<file_status> stats; + t->addFiles(fnames, stats); + + EventCollection coll{}; + while (coll.size() < 2) { + evt = t->getNextEvent(); + ASSERT_TRUE(evt.hasValue()); + coll.append(evt->Events); + } + EXPECT_TRUE(coll.hasAdded(fnames, stats)); + } + { + std::vector<StringRef> fnames = {"b", "c"}; + std::vector<file_status> stats; + t->addFiles(fnames, stats); + + EventCollection coll{}; + while (coll.size() < 2) { + evt = t->getNextEvent(); + ASSERT_TRUE(evt.hasValue()); + coll.append(evt->Events); + } + EXPECT_TRUE(coll.hasAdded(fnames, stats)); + } + { + std::vector<StringRef> fnames = {"a", "c"}; + std::vector<file_status> stats; + t->addFiles(fnames, stats); + t->removeFile("b"); + + EventCollection coll{}; + while (coll.size() < 3) { + evt = t->getNextEvent(); + ASSERT_TRUE(evt.hasValue()); + coll.append(evt->Events); + } + + EXPECT_TRUE(coll.hasEvents( + std::vector<StringRef>{"a", "b", "c"}, + std::vector<DirectoryWatcher::EventKind>{ + DirectoryWatcher::EventKind::Added, + DirectoryWatcher::EventKind::Removed, + DirectoryWatcher::EventKind::Added, + }, + std::vector<file_status>{ + stats[0], + file_status{}, + stats[1], + })); + } + { + std::vector<StringRef> fnames = {"a", "c"}; + t->removeFiles(fnames); + + EventCollection coll{}; + while (coll.size() < 2) { + evt = t->getNextEvent(); + ASSERT_TRUE(evt.hasValue()); + coll.append(evt->Events); + } + EXPECT_TRUE(coll.hasRemoved(fnames)); + } +} From 1bd7215ddd58bd5798313019bfbb94b62a7f0f21 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Tue, 30 Oct 2018 11:56:22 -0700 Subject: [PATCH 326/582] [DirectoryWatcher] Move FSEvents implementation to a mac-platform specific header include apple-llvm-split-commit: 3d9e2e3a543b18e9b49b22b9d87f5932e4dc5f28 apple-llvm-split-dir: clang/ --- .../DirectoryWatcher-mac.inc.h | 214 +++++++++++++++++ .../lib/DirectoryWatcher/DirectoryWatcher.cpp | 226 ++---------------- 2 files changed, 230 insertions(+), 210 deletions(-) create mode 100644 clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h b/clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h new file mode 100644 index 0000000000000..826e9bea4fdae --- /dev/null +++ b/clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h @@ -0,0 +1,214 @@ +//===- DirectoryWatcher-mac.inc.h - Mac-platform directory listening ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <CoreServices/CoreServices.h> + +struct DirectoryWatcher::Implementation { + bool initialize(StringRef Path, EventReceiver Receiver, + bool waitInitialSync, std::string &Error); + ~Implementation() { + stopFSEventStream(); + }; +private: + FSEventStreamRef EventStream = nullptr; + + bool setupFSEventStream(StringRef path, EventReceiver receiver, + dispatch_queue_t queue, + std::shared_ptr<DirectoryScan> initialScanPtr); + void stopFSEventStream(); +}; + +namespace { +struct EventStreamContextData { + std::string WatchedPath; + DirectoryWatcher::EventReceiver Receiver; + std::shared_ptr<DirectoryScan> InitialScan; + + EventStreamContextData(std::string watchedPath, DirectoryWatcher::EventReceiver receiver, + std::shared_ptr<DirectoryScan> initialScanPtr) + : WatchedPath(std::move(watchedPath)), + Receiver(std::move(receiver)), + InitialScan(std::move(initialScanPtr)) { + } + + static void dispose(const void *ctx) { + delete static_cast<const EventStreamContextData*>(ctx); + } +}; +} + +static void eventStreamCallback( + ConstFSEventStreamRef stream, + void *clientCallBackInfo, + size_t numEvents, + void *eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) { + auto *ctx = static_cast<EventStreamContextData*>(clientCallBackInfo); + + std::vector<DirectoryWatcher::Event> Events; + for (size_t i = 0; i < numEvents; ++i) { + StringRef path = ((const char **)eventPaths)[i]; + const FSEventStreamEventFlags flags = eventFlags[i]; + if (!(flags & kFSEventStreamEventFlagItemIsFile)) { + if ((flags & kFSEventStreamEventFlagItemRemoved) && path == ctx->WatchedPath) { + DirectoryWatcher::Event Evt{DirectoryWatcher::EventKind::DirectoryDeleted, path, llvm::sys::TimePoint<>{} }; + Events.push_back(Evt); + break; + } + continue; + } + DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Modified; + bool hasAddedFlag = flags & (kFSEventStreamEventFlagItemCreated | + kFSEventStreamEventFlagItemRenamed); + bool hasRemovedFlag = flags & kFSEventStreamEventFlagItemRemoved; + Optional<sys::fs::file_status> statusOpt; + // NOTE: With low latency sometimes for a file that is moved inside the + // directory, or for a file that is removed from the directory, the flags + // have both 'renamed' and 'removed'. We use getting the file status as a + // way to distinguish between the two. + if (hasAddedFlag) { + statusOpt = getFileStatus(path); + if (statusOpt.hasValue()) { + K = DirectoryWatcher::EventKind::Added; + } else { + K = DirectoryWatcher::EventKind::Removed; + } + } else if (hasRemovedFlag) { + K = DirectoryWatcher::EventKind::Removed; + } else { + statusOpt = getFileStatus(path); + if (!statusOpt.hasValue()) { + K = DirectoryWatcher::EventKind::Removed; + } + } + + if (ctx->InitialScan && K == DirectoryWatcher::EventKind::Added) { + // For the first time we get the events, check that we haven't already + // sent the 'added' event at the initial scan. + if (ctx->InitialScan->FileIDSet.count(statusOpt->getUniqueID())) { + // Already reported this event at the initial directory scan. + continue; + } + } + + llvm::sys::TimePoint<> modTime{}; + if (statusOpt.hasValue()) + modTime = statusOpt->getLastModificationTime(); + DirectoryWatcher::Event Evt{K, path, modTime}; + Events.push_back(Evt); + } + + // We won't need to check again later on. + ctx->InitialScan.reset(); + + if (!Events.empty()) { + ctx->Receiver(Events, /*isInitial=*/false); + } +} + +bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path, + EventReceiver receiver, + dispatch_queue_t queue, + std::shared_ptr<DirectoryScan> initialScanPtr) { + if (path.empty()) + return true; + + CFMutableArrayRef pathsToWatch = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); + CFStringRef cfPathStr = CFStringCreateWithBytes(nullptr, (const UInt8 *)path.data(), path.size(), kCFStringEncodingUTF8, false); + CFArrayAppendValue(pathsToWatch, cfPathStr); + CFRelease(cfPathStr); + CFAbsoluteTime latency = 0.0; // Latency in seconds. + + std::string realPath; + { + SmallString<128> Storage; + StringRef P = llvm::Twine(path).toNullTerminatedStringRef(Storage); + char Buffer[PATH_MAX]; + // Use ::realpath to get the real path name + if (::realpath(P.begin(), Buffer) != nullptr) + realPath = Buffer; + else + realPath = path; + } + + EventStreamContextData *ctxData = + new EventStreamContextData(std::move(realPath), std::move(receiver), + std::move(initialScanPtr)); + FSEventStreamContext context; + context.version = 0; + context.info = ctxData; + context.retain = nullptr; + context.release = EventStreamContextData::dispose; + context.copyDescription = nullptr; + + EventStream = FSEventStreamCreate(nullptr, + eventStreamCallback, + &context, + pathsToWatch, + kFSEventStreamEventIdSinceNow, + latency, + kFSEventStreamCreateFlagFileEvents | + kFSEventStreamCreateFlagNoDefer); + CFRelease(pathsToWatch); + if (!EventStream) { + return true; + } + FSEventStreamSetDispatchQueue(EventStream, queue); + FSEventStreamStart(EventStream); + return false; +} + +void DirectoryWatcher::Implementation::stopFSEventStream() { + if (!EventStream) + return; + FSEventStreamStop(EventStream); + FSEventStreamInvalidate(EventStream); + FSEventStreamRelease(EventStream); + EventStream = nullptr; +} + +bool DirectoryWatcher::Implementation::initialize(StringRef Path, + EventReceiver Receiver, bool waitInitialSync, std::string &Error) { + auto initialScan = std::make_shared<DirectoryScan>(); + + dispatch_queue_t queue = dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL); + dispatch_semaphore_t initScanSema = dispatch_semaphore_create(0); + dispatch_semaphore_t setupFSEventsSema = dispatch_semaphore_create(0); + + std::string copiedPath = Path; + dispatch_retain(initScanSema); + dispatch_retain(setupFSEventsSema); + dispatch_async(queue, ^{ + // Wait for the event stream to be setup before doing the initial scan, + // to make sure we won't miss any events. + dispatch_semaphore_wait(setupFSEventsSema, DISPATCH_TIME_FOREVER); + initialScan->scanDirectory(copiedPath); + Receiver(initialScan->getAsFileEvents(), /*isInitial=*/true); + dispatch_semaphore_signal(initScanSema); + dispatch_release(setupFSEventsSema); + dispatch_release(initScanSema); + }); + bool fsErr = setupFSEventStream(Path, Receiver, queue, initialScan); + dispatch_semaphore_signal(setupFSEventsSema); + + if (waitInitialSync) { + dispatch_semaphore_wait(initScanSema, DISPATCH_TIME_FOREVER); + } + dispatch_release(setupFSEventsSema); + dispatch_release(initScanSema); + dispatch_release(queue); + + if (fsErr) { + raw_string_ostream(Error) << "failed to setup FSEvents stream for path: " << Path; + return true; + } + + return false; +} diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp index e5900de9663fb..c64b5dbcf9fa4 100644 --- a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp +++ b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp @@ -17,18 +17,6 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" -#define HAVE_CORESERVICES 0 - -#if defined(__has_include) -#if __has_include(<CoreServices/CoreServices.h>) - -#include <CoreServices/CoreServices.h> -#undef HAVE_CORESERVICES -#define HAVE_CORESERVICES 1 - -#endif -#endif - using namespace clang; using namespace llvm; @@ -67,8 +55,8 @@ template <> struct DenseMapInfo<sys::fs::UniqueID> { namespace { /// Used for initial directory scan. /// -/// Note that it is only accessed while inside the serial queue so it is thread -/// safe to access it without additional protection. +/// Note that the caller must ensure serial access to it. It is not thread safe +/// to access it without additional protection. struct DirectoryScan { DenseSet<sys::fs::UniqueID> FileIDSet; std::vector<std::tuple<std::string, sys::TimePoint<>>> Files; @@ -98,173 +86,27 @@ struct DirectoryScan { }; } -struct DirectoryWatcher::Implementation { -#if HAVE_CORESERVICES - FSEventStreamRef EventStream = nullptr; - - bool setupFSEventStream(StringRef path, EventReceiver receiver, - dispatch_queue_t queue, - std::shared_ptr<DirectoryScan> initialScanPtr); - void stopFSEventStream(); +// Add platform-specific functionality. - ~Implementation() { - stopFSEventStream(); - }; +#if !defined(__has_include) +# define __has_include(x) 0 #endif -}; - -#if HAVE_CORESERVICES -namespace { -struct EventStreamContextData { - std::string WatchedPath; - DirectoryWatcher::EventReceiver Receiver; - std::shared_ptr<DirectoryScan> InitialScan; - - EventStreamContextData(std::string watchedPath, DirectoryWatcher::EventReceiver receiver, - std::shared_ptr<DirectoryScan> initialScanPtr) - : WatchedPath(std::move(watchedPath)), - Receiver(std::move(receiver)), - InitialScan(std::move(initialScanPtr)) { - } - - static void dispose(const void *ctx) { - delete static_cast<const EventStreamContextData*>(ctx); - } -}; -} - -static void eventStreamCallback( - ConstFSEventStreamRef stream, - void *clientCallBackInfo, - size_t numEvents, - void *eventPaths, - const FSEventStreamEventFlags eventFlags[], - const FSEventStreamEventId eventIds[]) { - auto *ctx = static_cast<EventStreamContextData*>(clientCallBackInfo); - - std::vector<DirectoryWatcher::Event> Events; - for (size_t i = 0; i < numEvents; ++i) { - StringRef path = ((const char **)eventPaths)[i]; - const FSEventStreamEventFlags flags = eventFlags[i]; - if (!(flags & kFSEventStreamEventFlagItemIsFile)) { - if ((flags & kFSEventStreamEventFlagItemRemoved) && path == ctx->WatchedPath) { - DirectoryWatcher::Event Evt{DirectoryWatcher::EventKind::DirectoryDeleted, path, llvm::sys::TimePoint<>{} }; - Events.push_back(Evt); - break; - } - continue; - } - DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Modified; - bool hasAddedFlag = flags & (kFSEventStreamEventFlagItemCreated | - kFSEventStreamEventFlagItemRenamed); - bool hasRemovedFlag = flags & kFSEventStreamEventFlagItemRemoved; - Optional<sys::fs::file_status> statusOpt; - // NOTE: With low latency sometimes for a file that is moved inside the - // directory, or for a file that is removed from the directory, the flags - // have both 'renamed' and 'removed'. We use getting the file status as a - // way to distinguish between the two. - if (hasAddedFlag) { - statusOpt = getFileStatus(path); - if (statusOpt.hasValue()) { - K = DirectoryWatcher::EventKind::Added; - } else { - K = DirectoryWatcher::EventKind::Removed; - } - } else if (hasRemovedFlag) { - K = DirectoryWatcher::EventKind::Removed; - } else { - statusOpt = getFileStatus(path); - if (!statusOpt.hasValue()) { - K = DirectoryWatcher::EventKind::Removed; - } - } - - if (ctx->InitialScan && K == DirectoryWatcher::EventKind::Added) { - // For the first time we get the events, check that we haven't already - // sent the 'added' event at the initial scan. - if (ctx->InitialScan->FileIDSet.count(statusOpt->getUniqueID())) { - // Already reported this event at the initial directory scan. - continue; - } - } - - llvm::sys::TimePoint<> modTime{}; - if (statusOpt.hasValue()) - modTime = statusOpt->getLastModificationTime(); - DirectoryWatcher::Event Evt{K, path, modTime}; - Events.push_back(Evt); - } - // We won't need to check again later on. - ctx->InitialScan.reset(); - - if (!Events.empty()) { - ctx->Receiver(Events, /*isInitial=*/false); - } -} - -bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path, - EventReceiver receiver, - dispatch_queue_t queue, - std::shared_ptr<DirectoryScan> initialScanPtr) { - if (path.empty()) - return true; - - CFMutableArrayRef pathsToWatch = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); - CFStringRef cfPathStr = CFStringCreateWithBytes(nullptr, (const UInt8 *)path.data(), path.size(), kCFStringEncodingUTF8, false); - CFArrayAppendValue(pathsToWatch, cfPathStr); - CFRelease(cfPathStr); - CFAbsoluteTime latency = 0.0; // Latency in seconds. - - std::string realPath; - { - SmallString<128> Storage; - StringRef P = llvm::Twine(path).toNullTerminatedStringRef(Storage); - char Buffer[PATH_MAX]; - // Use ::realpath to get the real path name - if (::realpath(P.begin(), Buffer) != nullptr) - realPath = Buffer; - else - realPath = path; - } - - EventStreamContextData *ctxData = - new EventStreamContextData(std::move(realPath), std::move(receiver), - std::move(initialScanPtr)); - FSEventStreamContext context; - context.version = 0; - context.info = ctxData; - context.retain = nullptr; - context.release = EventStreamContextData::dispose; - context.copyDescription = nullptr; +#if __has_include(<CoreServices/CoreServices.h>) +# include "DirectoryWatcher-mac.inc.h" +#else - EventStream = FSEventStreamCreate(nullptr, - eventStreamCallback, - &context, - pathsToWatch, - kFSEventStreamEventIdSinceNow, - latency, - kFSEventStreamCreateFlagFileEvents | - kFSEventStreamCreateFlagNoDefer); - CFRelease(pathsToWatch); - if (!EventStream) { +struct DirectoryWatcher::Implementation { + bool initialize(StringRef Path, EventReceiver Receiver, + bool waitInitialSync, std::string &Error) { + Error = "directory listening not supported for this platform"; return true; } - FSEventStreamSetDispatchQueue(EventStream, queue); - FSEventStreamStart(EventStream); - return false; -} +}; -void DirectoryWatcher::Implementation::stopFSEventStream() { - if (!EventStream) - return; - FSEventStreamStop(EventStream); - FSEventStreamInvalidate(EventStream); - FSEventStreamRelease(EventStream); - EventStream = nullptr; -} #endif + DirectoryWatcher::DirectoryWatcher() : Impl(*new Implementation()) {} @@ -274,8 +116,6 @@ DirectoryWatcher::~DirectoryWatcher() { std::unique_ptr<DirectoryWatcher> DirectoryWatcher::create(StringRef Path, EventReceiver Receiver, bool waitInitialSync, std::string &Error) { -#if HAVE_CORESERVICES - using namespace llvm::sys; if (!fs::exists(Path)) { @@ -301,43 +141,9 @@ std::unique_ptr<DirectoryWatcher> DirectoryWatcher::create(StringRef Path, std::unique_ptr<DirectoryWatcher> DirWatch; DirWatch.reset(new DirectoryWatcher()); auto &Impl = DirWatch->Impl; - - auto initialScan = std::make_shared<DirectoryScan>(); - - dispatch_queue_t queue = dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL); - dispatch_semaphore_t initScanSema = dispatch_semaphore_create(0); - dispatch_semaphore_t setupFSEventsSema = dispatch_semaphore_create(0); - - std::string copiedPath = Path; - dispatch_retain(initScanSema); - dispatch_retain(setupFSEventsSema); - dispatch_async(queue, ^{ - // Wait for the event stream to be setup before doing the initial scan, - // to make sure we won't miss any events. - dispatch_semaphore_wait(setupFSEventsSema, DISPATCH_TIME_FOREVER); - initialScan->scanDirectory(copiedPath); - Receiver(initialScan->getAsFileEvents(), /*isInitial=*/true); - dispatch_semaphore_signal(initScanSema); - dispatch_release(setupFSEventsSema); - dispatch_release(initScanSema); - }); - bool fsErr = Impl.setupFSEventStream(Path, Receiver, queue, initialScan); - dispatch_semaphore_signal(setupFSEventsSema); - - if (waitInitialSync) { - dispatch_semaphore_wait(initScanSema, DISPATCH_TIME_FOREVER); - } - dispatch_release(setupFSEventsSema); - dispatch_release(initScanSema); - dispatch_release(queue); - - if (fsErr) { - raw_string_ostream(Error) << "failed to setup FSEvents stream for path: " << Path; + bool hasError = Impl.initialize(Path, std::move(Receiver), waitInitialSync, Error); + if (hasError) return nullptr; - } return DirWatch; -#else - return nullptr; -#endif } From 11ac341050827ee001b3fef3a7d639d9a85fad27 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Wed, 31 Oct 2018 14:10:22 -0700 Subject: [PATCH 327/582] [DirectoryWatcher] Add a linux-specific implementation using inotify apple-llvm-split-commit: 5db0bd1ba2728a31993836774befbbb2a1a08bde apple-llvm-split-dir: clang/ --- .../DirectoryWatcher-linux.inc.h | 196 ++++++++++++++++++ .../lib/DirectoryWatcher/DirectoryWatcher.cpp | 2 + clang/tools/c-index-test/core_main.cpp | 22 +- 3 files changed, 203 insertions(+), 17 deletions(-) create mode 100644 clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h b/clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h new file mode 100644 index 0000000000000..ddb492ab2ff36 --- /dev/null +++ b/clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h @@ -0,0 +1,196 @@ +//===- DirectoryWatcher-linux.inc.h - Linux-platform directory listening --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Errno.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Mutex.h" +#include <thread> +#include <unistd.h> +#include <sys/inotify.h> + +namespace { + +struct INotifyEvent { + DirectoryWatcher::EventKind K; + std::string Filename; + Optional<sys::fs::file_status> Status; +}; + +class EventQueue { + DirectoryWatcher::EventReceiver Receiver; + sys::Mutex Mtx; + bool gotInitialScan = false; + std::vector<INotifyEvent> PendingEvents; + + DirectoryWatcher::Event toDirEvent(const INotifyEvent &evt) { + llvm::sys::TimePoint<> modTime{}; + if (evt.Status.hasValue()) modTime = evt.Status->getLastModificationTime(); + return DirectoryWatcher::Event{evt.K, evt.Filename, modTime}; + } + +public: + explicit EventQueue(DirectoryWatcher::EventReceiver receiver) + : Receiver(receiver) {} + + void onDirectoryEvents(ArrayRef<INotifyEvent> evts) { + sys::ScopedLock L(Mtx); + + if (!gotInitialScan) { + PendingEvents.insert(PendingEvents.end(), evts.begin(), evts.end()); + return; + } + + SmallVector<DirectoryWatcher::Event, 8> dirEvents; + for (const auto &evt : evts) { + dirEvents.push_back(toDirEvent(evt)); + } + Receiver(dirEvents, /*isInitial=*/false); + } + + void onInitialScan(std::shared_ptr<DirectoryScan> dirScan) { + sys::ScopedLock L(Mtx); + + std::vector<DirectoryWatcher::Event> events = dirScan->getAsFileEvents(); + Receiver(events, /*isInitial=*/true); + + events.clear(); + for (const auto &evt : PendingEvents) { + if (evt.K == DirectoryWatcher::EventKind::Added && + dirScan->FileIDSet.count(evt.Status->getUniqueID())) { + // Already reported this event at the initial directory scan. + continue; + } + events.push_back(toDirEvent(evt)); + } + if (!events.empty()) { + Receiver(events, /*isInitial=*/false); + } + + gotInitialScan = true; + PendingEvents.clear(); + } +}; +} // namespace + +struct DirectoryWatcher::Implementation { + bool initialize(StringRef Path, EventReceiver Receiver, + bool waitInitialSync, std::string &Error); + ~Implementation() { + stopListening(); + }; + +private: + int inotifyFD = -1; + + void stopListening(); +}; + +static void runWatcher(std::string pathToWatch, int inotifyFD, + std::shared_ptr<EventQueue> evtQueue) { + #define EVT_BUF_LEN (30 * (sizeof(struct inotify_event) + NAME_MAX + 1)) + char buf[EVT_BUF_LEN] __attribute__ ((aligned(8))); + + while (1) { + ssize_t numRead = read(inotifyFD, buf, EVT_BUF_LEN); + if (numRead == -1) { + return; // watcher is stopped. + } + + SmallVector<INotifyEvent, 8> iEvents; + for (char *p = buf; p < buf + numRead;) { + struct inotify_event *ievt = (struct inotify_event *)p; + p += sizeof(struct inotify_event) + ievt->len; + + if (ievt->mask & IN_DELETE_SELF) { + INotifyEvent iEvt{DirectoryWatcher::EventKind::DirectoryDeleted, + pathToWatch, None}; + iEvents.push_back(iEvt); + break; + } + + DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Added; + if (ievt->mask & IN_MODIFY) { + K = DirectoryWatcher::EventKind::Modified; + } + if (ievt->mask & IN_MOVED_TO) { + K = DirectoryWatcher::EventKind::Added; + } + if (ievt->mask & IN_DELETE) { + K = DirectoryWatcher::EventKind::Removed; + } + + assert(ievt->len > 0 && "expected a filename from inotify"); + SmallString<256> fullPath{pathToWatch}; + sys::path::append(fullPath, ievt->name); + + Optional<sys::fs::file_status> statusOpt; + if (K != DirectoryWatcher::EventKind::Removed) { + statusOpt = getFileStatus(fullPath); + if (!statusOpt.hasValue()) + K = DirectoryWatcher::EventKind::Removed; + } + INotifyEvent iEvt{K, fullPath.str(), statusOpt}; + iEvents.push_back(iEvt); + } + + if (!iEvents.empty()) + evtQueue->onDirectoryEvents(iEvents); + } +} + +bool DirectoryWatcher::Implementation::initialize(StringRef Path, + EventReceiver Receiver, + bool waitInitialSync, + std::string &errorMsg) { + auto error = [&](StringRef msg) -> bool { + errorMsg = msg; + errorMsg += ": "; + errorMsg += llvm::sys::StrError(); + return true; + }; + + auto evtQueue = std::make_shared<EventQueue>(std::move(Receiver)); + + inotifyFD = inotify_init(); + if (inotifyFD == -1) + return error("inotify_init failed"); + + std::string pathToWatch = Path; + int wd = inotify_add_watch( + inotifyFD, pathToWatch.c_str(), + IN_MOVED_TO | IN_DELETE | IN_MODIFY | IN_DELETE_SELF | IN_ONLYDIR); + if (wd == -1) + return error("inotify_add_watch failed"); + + std::thread watchThread( + std::bind(runWatcher, pathToWatch, inotifyFD, evtQueue)); + watchThread.detach(); + + auto initialScan = std::make_shared<DirectoryScan>(); + auto runScan = [pathToWatch, initialScan, evtQueue]() { + initialScan->scanDirectory(pathToWatch); + evtQueue->onInitialScan(std::move(initialScan)); + }; + + if (waitInitialSync) { + runScan(); + } else { + std::thread scanThread(runScan); + scanThread.detach(); + } + + return false; +} + +void DirectoryWatcher::Implementation::stopListening() { + if (inotifyFD == -1) + return; + close(inotifyFD); + inotifyFD = -1; +} diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp index c64b5dbcf9fa4..0feaacefe7acb 100644 --- a/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp +++ b/clang/lib/DirectoryWatcher/DirectoryWatcher.cpp @@ -94,6 +94,8 @@ struct DirectoryScan { #if __has_include(<CoreServices/CoreServices.h>) # include "DirectoryWatcher-mac.inc.h" +#elif __has_include(<sys/inotify.h>) +# include "DirectoryWatcher-linux.inc.h" #else struct DirectoryWatcher::Implementation { diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index 2231aa7f6254f..ada79ec2673ad 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -31,18 +31,7 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/PrettyStackTrace.h" - -#define HAVE_CORESERVICES 0 - -#if defined(__has_include) -#if __has_include(<CoreServices/CoreServices.h>) - -#include <CoreServices/CoreServices.h> -#undef HAVE_CORESERVICES -#define HAVE_CORESERVICES 1 - -#endif -#endif +#include <thread> using namespace clang; using namespace clang::index; @@ -797,11 +786,10 @@ static int watchDirectory(StringRef dirPath) { errs() << "failed creating directory watcher: " << Error << '\n'; return 1; } -#if HAVE_CORESERVICES - dispatch_main(); -#else - return 1; -#endif + + while(1) { + std::this_thread::yield(); + } } //===----------------------------------------------------------------------===// From 16ff20722a9152994162c8de0cd22cffbccf90d1 Mon Sep 17 00:00:00 2001 From: George Karpenkov <ekarpenkov@apple.com> Date: Thu, 1 Nov 2018 17:44:25 -0700 Subject: [PATCH 328/582] Fix up the previous DirectoryWatcher test commit Two-stage build is failed otherwise. apple-llvm-split-commit: ba54a9a3ff1fe1d4dae4ea871ac84b9764efeac2 apple-llvm-split-dir: clang/ --- clang/unittests/DirectoryWatcher/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/unittests/DirectoryWatcher/CMakeLists.txt b/clang/unittests/DirectoryWatcher/CMakeLists.txt index 45cf70261ad05..c96424e298663 100644 --- a/clang/unittests/DirectoryWatcher/CMakeLists.txt +++ b/clang/unittests/DirectoryWatcher/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_unittest(DirectoryWatcherTests target_link_libraries(DirectoryWatcherTests PRIVATE clangDirectoryWatcher + clangBasic ) if(APPLE) From 3abd88dbf0c6d24270e99d87fc8cceb55fb4cb88 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Thu, 1 Nov 2018 16:42:29 -0700 Subject: [PATCH 329/582] Revert support for serializing PPD_SKIPPED_RANGES. Revert r322513: "Fixed memory leak in unit test introduced in my previous commit r322503" Revert r322503: "[PCH] Serialize skipped preprocessor ranges" This is causing PCM files to have huge sizes (CoreImage gets around 880MB) because of PPD_SKIPPED_RANGES. There are two things that need proper fix/handling here: 1. The bitcode streamer problem with huge blobs. 2. The size of skipped ranges needs big improvements to be better optimized for size. The testcase was not reduced yet and is not a fit to be part of this revert. rdar://problem/43533418 Conflicts: include/clang/Lex/PreprocessingRecord.h include/clang/Serialization/ASTBitCodes.h include/clang/Serialization/ASTReader.h include/clang/Serialization/Module.h lib/Serialization/ASTReader.cpp unittests/libclang/LibclangTest.cpp (cherry picked from commit 2d6f167106fe2361737e57dd20c03b2e06bbed0e) apple-llvm-split-commit: c692cc7ca84eb4de3ac3fa85790683fbb6f636ae apple-llvm-split-dir: clang/ --- clang/include/clang/Lex/PreprocessingRecord.h | 16 ---- .../include/clang/Serialization/ASTBitCodes.h | 22 ----- clang/include/clang/Serialization/ASTReader.h | 10 --- clang/include/clang/Serialization/Module.h | 6 -- clang/lib/Lex/PPDirectives.cpp | 4 +- clang/lib/Lex/PreprocessingRecord.cpp | 21 +---- clang/lib/Serialization/ASTReader.cpp | 32 -------- clang/lib/Serialization/ASTWriter.cpp | 21 ----- clang/tools/libclang/CIndex.cpp | 3 - clang/unittests/libclang/LibclangTest.cpp | 81 ------------------- 10 files changed, 2 insertions(+), 214 deletions(-) diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index 44d79d9b62290..597189756e273 100644 --- a/clang/include/clang/Lex/PreprocessingRecord.h +++ b/clang/include/clang/Lex/PreprocessingRecord.h @@ -297,9 +297,6 @@ class Token; FileID FID) { return None; } - - /// Read a preallocated skipped range from the external source. - virtual SourceRange ReadSkippedRange(unsigned Index) = 0; }; /// A record of the steps taken while preprocessing a source file, @@ -325,8 +322,6 @@ class Token; /// The set of ranges that were skipped by the preprocessor, std::vector<SourceRange> SkippedRanges; - bool SkippedRangesAllLoaded = true; - /// Global (loaded or local) ID for a preprocessed entity. /// Negative values are used to indicate preprocessed entities /// loaded from the external source while non-negative values are used to @@ -382,16 +377,6 @@ class Token; /// corresponds to the first newly-allocated entity. unsigned allocateLoadedEntities(unsigned NumEntities); - /// Allocate space for a new set of loaded preprocessed skipped - /// ranges. - /// - /// \returns The index into the set of loaded preprocessed ranges, which - /// corresponds to the first newly-allocated range. - unsigned allocateSkippedRanges(unsigned NumRanges); - - /// Ensures that all external skipped ranges have been loaded. - void ensureSkippedRangesLoaded(); - /// Register a new macro definition. void RegisterMacroDefinition(MacroInfo *Macro, MacroDefinitionRecord *Def); @@ -515,7 +500,6 @@ class Token; /// Retrieve all ranges that got skipped while preprocessing. const std::vector<SourceRange> &getSkippedRanges() { - ensureSkippedRangesLoaded(); return SkippedRanges; } diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index a49295b9623a1..535714ff3da97 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -198,25 +198,6 @@ namespace serialization { } }; - /// Source range of a skipped preprocessor region - struct PPSkippedRange { - /// Raw source location of beginning of range. - unsigned Begin; - /// Raw source location of end of range. - unsigned End; - - PPSkippedRange(SourceRange R) - : Begin(R.getBegin().getRawEncoding()), - End(R.getEnd().getRawEncoding()) { } - - SourceLocation getBegin() const { - return SourceLocation::getFromRawEncoding(Begin); - } - SourceLocation getEnd() const { - return SourceLocation::getFromRawEncoding(End); - } - }; - /// Source range/offset of a preprocessed entity. struct DeclOffset { /// Raw source location. @@ -646,9 +627,6 @@ namespace serialization { /// The stack of open #ifs/#ifdefs recorded in a preamble. PP_CONDITIONAL_STACK = 62, - - /// A table of skipped ranges within the preprocessing record. - PPD_SKIPPED_RANGES = 63 }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index f97f545852f48..b3695f2caf45d 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -755,13 +755,6 @@ class ASTReader /// added to the global preprocessing entity ID to produce a local ID. GlobalPreprocessedEntityMapType GlobalPreprocessedEntityMap; - using GlobalSkippedRangeMapType = - ContinuousRangeMap<unsigned, ModuleFile *, 4>; - - /// Mapping from global skipped range base IDs to the module in which - /// the skipped ranges reside. - GlobalSkippedRangeMapType GlobalSkippedRangeMap; - /// \name CodeGen-relevant special data /// Fields containing data that is relevant to CodeGen. //@{ @@ -1716,9 +1709,6 @@ class ASTReader Optional<bool> isPreprocessedEntityInFileID(unsigned Index, FileID FID) override; - /// Read a preallocated skipped range from the external source. - SourceRange ReadSkippedRange(unsigned Index) override; - /// Read the header file information for the given file entry. HeaderFileInfo GetHeaderFileInfo(const FileEntry *FE) override; diff --git a/clang/include/clang/Serialization/Module.h b/clang/include/clang/Serialization/Module.h index d6e78e693109e..641483e917f0e 100644 --- a/clang/include/clang/Serialization/Module.h +++ b/clang/include/clang/Serialization/Module.h @@ -336,12 +336,6 @@ class ModuleFile { const PPEntityOffset *PreprocessedEntityOffsets = nullptr; unsigned NumPreprocessedEntities = 0; - /// Base ID for preprocessed skipped ranges local to this module. - unsigned BasePreprocessedSkippedRangeID = 0; - - const PPSkippedRange *PreprocessedSkippedRangeOffsets = nullptr; - unsigned NumPreprocessedSkippedRanges = 0; - // === Header search information === /// The number of local HeaderFileInfo structures. diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index e47735e627c77..2b27128301966 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -577,9 +577,7 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc, // the #if block. CurPPLexer->LexingRawMode = false; - // The last skipped range isn't actually skipped yet if it's truncated - // by the end of the preamble; we'll resume parsing after the preamble. - if (Callbacks && (Tok.isNot(tok::eof) || !isRecordingPreamble())) + if (Callbacks) Callbacks->SourceRangeSkipped( SourceRange(HashTokenLoc, CurPPLexer->getSourceLocation()), Tok.getLocation()); diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp index b37a8cf1ced49..05cf478292542 100644 --- a/clang/lib/Lex/PreprocessingRecord.cpp +++ b/clang/lib/Lex/PreprocessingRecord.cpp @@ -329,23 +329,6 @@ unsigned PreprocessingRecord::allocateLoadedEntities(unsigned NumEntities) { return Result; } -unsigned PreprocessingRecord::allocateSkippedRanges(unsigned NumRanges) { - unsigned Result = SkippedRanges.size(); - SkippedRanges.resize(SkippedRanges.size() + NumRanges); - SkippedRangesAllLoaded = false; - return Result; -} - -void PreprocessingRecord::ensureSkippedRangesLoaded() { - if (SkippedRangesAllLoaded || !ExternalSource) - return; - for (unsigned Index = 0; Index != SkippedRanges.size(); ++Index) { - if (SkippedRanges[Index].isInvalid()) - SkippedRanges[Index] = ExternalSource->ReadSkippedRange(Index); - } - SkippedRangesAllLoaded = true; -} - void PreprocessingRecord::RegisterMacroDefinition(MacroInfo *Macro, MacroDefinitionRecord *Def) { MacroDefinitions[Macro] = Def; @@ -435,7 +418,6 @@ void PreprocessingRecord::Defined(const Token &MacroNameTok, void PreprocessingRecord::SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) { - assert(Range.isValid()); SkippedRanges.emplace_back(Range.getBegin(), EndifLoc); } @@ -516,6 +498,5 @@ size_t PreprocessingRecord::getTotalMemory() const { return BumpAlloc.getTotalMemory() + llvm::capacity_in_bytes(MacroDefinitions) + llvm::capacity_in_bytes(PreprocessedEntities) - + llvm::capacity_in_bytes(LoadedPreprocessedEntities) - + llvm::capacity_in_bytes(SkippedRanges); + + llvm::capacity_in_bytes(LoadedPreprocessedEntities); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index ffa37c678373f..c2cef582edfa0 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3234,24 +3234,6 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { break; } - case PPD_SKIPPED_RANGES: { - F.PreprocessedSkippedRangeOffsets = (const PPSkippedRange*)Blob.data(); - assert(Blob.size() % sizeof(PPSkippedRange) == 0); - F.NumPreprocessedSkippedRanges = Blob.size() / sizeof(PPSkippedRange); - - if (!PP.getPreprocessingRecord()) - PP.createPreprocessingRecord(); - if (!PP.getPreprocessingRecord()->getExternalSource()) - PP.getPreprocessingRecord()->SetExternalSource(*this); - F.BasePreprocessedSkippedRangeID = PP.getPreprocessingRecord() - ->allocateSkippedRanges(F.NumPreprocessedSkippedRanges); - - if (F.NumPreprocessedSkippedRanges > 0) - GlobalSkippedRangeMap.insert( - std::make_pair(F.BasePreprocessedSkippedRangeID, &F)); - break; - } - case DECL_UPDATE_OFFSETS: if (Record.size() % 2 != 0) { Error("invalid DECL_UPDATE_OFFSETS block in AST file"); @@ -5438,20 +5420,6 @@ ASTReader::getModuleFileLevelDecls(ModuleFile &Mod) { Mod.FileSortedDecls + Mod.NumFileSortedDecls)); } -SourceRange ASTReader::ReadSkippedRange(unsigned GlobalIndex) { - auto I = GlobalSkippedRangeMap.find(GlobalIndex); - assert(I != GlobalSkippedRangeMap.end() && - "Corrupted global skipped range map"); - ModuleFile *M = I->second; - unsigned LocalIndex = GlobalIndex - M->BasePreprocessedSkippedRangeID; - assert(LocalIndex < M->NumPreprocessedSkippedRanges); - PPSkippedRange RawRange = M->PreprocessedSkippedRangeOffsets[LocalIndex]; - SourceRange Range(TranslateSourceLocation(*M, RawRange.getBegin()), - TranslateSourceLocation(*M, RawRange.getEnd())); - assert(Range.isValid()); - return Range; -} - PreprocessedEntity *ASTReader::ReadPreprocessedEntity(unsigned Index) { PreprocessedEntityID PPID = Index+1; std::pair<ModuleFile *, unsigned> PPInfo = getModulePreprocessedEntity(Index); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 42652693c4b0b..7d87e9a96c828 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1109,7 +1109,6 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(UNUSED_FILESCOPED_DECLS); RECORD(PPD_ENTITIES_OFFSETS); RECORD(VTABLE_USES); - RECORD(PPD_SKIPPED_RANGES); RECORD(REFERENCED_SELECTOR_POOL); RECORD(TU_UPDATE_LEXICAL); RECORD(SEMA_DECL_REFS); @@ -2740,26 +2739,6 @@ void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec) { Stream.EmitRecordWithBlob(PPEOffsetAbbrev, Record, bytes(PreprocessedEntityOffsets)); } - - // Write the skipped region table for the preprocessing record. - ArrayRef<SourceRange> SkippedRanges = PPRec.getSkippedRanges(); - if (SkippedRanges.size() > 0) { - std::vector<PPSkippedRange> SerializedSkippedRanges; - SerializedSkippedRanges.reserve(SkippedRanges.size()); - for (auto const& Range : SkippedRanges) - SerializedSkippedRanges.emplace_back(Range); - - using namespace llvm; - auto Abbrev = std::make_shared<BitCodeAbbrev>(); - Abbrev->Add(BitCodeAbbrevOp(PPD_SKIPPED_RANGES)); - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); - unsigned PPESkippedRangeAbbrev = Stream.EmitAbbrev(std::move(Abbrev)); - - Record.clear(); - Record.push_back(PPD_SKIPPED_RANGES); - Stream.EmitRecordWithBlob(PPESkippedRangeAbbrev, Record, - bytes(SerializedSkippedRanges)); - } } unsigned ASTWriter::getLocalOrImportedSubmoduleID(Module *Mod) { diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 1fbe3c514ca98..36040a591c5d0 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -8613,7 +8613,6 @@ CXSourceRangeList *clang_getSkippedRanges(CXTranslationUnit TU, CXFile file) { SourceManager &sm = Ctx.getSourceManager(); FileEntry *fileEntry = static_cast<FileEntry *>(file); FileID wantedFileID = sm.translateFile(fileEntry); - bool isMainFile = wantedFileID == sm.getMainFileID(); const std::vector<SourceRange> &SkippedRanges = ppRec->getSkippedRanges(); std::vector<SourceRange> wantedRanges; @@ -8621,8 +8620,6 @@ CXSourceRangeList *clang_getSkippedRanges(CXTranslationUnit TU, CXFile file) { i != ei; ++i) { if (sm.getFileID(i->getBegin()) == wantedFileID || sm.getFileID(i->getEnd()) == wantedFileID) wantedRanges.push_back(*i); - else if (isMainFile && (astUnit->isInPreambleFileID(i->getBegin()) || astUnit->isInPreambleFileID(i->getEnd()))) - wantedRanges.push_back(*i); } skipped->count = wantedRanges.size(); diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp index 4245df28d2a7d..33114881180c7 100644 --- a/clang/unittests/libclang/LibclangTest.cpp +++ b/clang/unittests/libclang/LibclangTest.cpp @@ -690,87 +690,6 @@ TEST_F(LibclangPrintingPolicyTest, SetAndGetProperties) { } } -TEST_F(LibclangReparseTest, PreprocessorSkippedRanges) { - std::string Header = "header.h", Main = "main.cpp"; - WriteFile(Header, - "#ifdef MANGOS\n" - "printf(\"mmm\");\n" - "#endif"); - WriteFile(Main, - "#include \"header.h\"\n" - "#ifdef GUAVA\n" - "#endif\n" - "#ifdef KIWIS\n" - "printf(\"mmm!!\");\n" - "#endif"); - - for (int i = 0; i != 3; ++i) { - unsigned flags = TUFlags | CXTranslationUnit_PrecompiledPreamble; - if (i == 2) - flags |= CXTranslationUnit_CreatePreambleOnFirstParse; - - if (i != 0) - clang_disposeTranslationUnit(ClangTU); // dispose from previous iter - - // parse once - ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, - nullptr, 0, flags); - if (i != 0) { - // reparse - ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */)); - } - - // Check all ranges are there - CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU); - EXPECT_EQ(3U, Ranges->count); - - CXSourceLocation cxl; - unsigned line; - cxl = clang_getRangeStart(Ranges->ranges[0]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(1U, line); - cxl = clang_getRangeEnd(Ranges->ranges[0]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(3U, line); - - cxl = clang_getRangeStart(Ranges->ranges[1]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(2U, line); - cxl = clang_getRangeEnd(Ranges->ranges[1]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(3U, line); - - cxl = clang_getRangeStart(Ranges->ranges[2]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(4U, line); - cxl = clang_getRangeEnd(Ranges->ranges[2]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(6U, line); - - clang_disposeSourceRangeList(Ranges); - - // Check obtaining ranges by each file works - CXFile cxf = clang_getFile(ClangTU, Header.c_str()); - Ranges = clang_getSkippedRanges(ClangTU, cxf); - EXPECT_EQ(1U, Ranges->count); - cxl = clang_getRangeStart(Ranges->ranges[0]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(1U, line); - clang_disposeSourceRangeList(Ranges); - - cxf = clang_getFile(ClangTU, Main.c_str()); - Ranges = clang_getSkippedRanges(ClangTU, cxf); - EXPECT_EQ(2U, Ranges->count); - cxl = clang_getRangeStart(Ranges->ranges[0]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(2U, line); - cxl = clang_getRangeStart(Ranges->ranges[1]); - clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); - EXPECT_EQ(4U, line); - clang_disposeSourceRangeList(Ranges); - } -} - class LibclangSerializationTest : public LibclangParseTest { public: bool SaveAndLoadTU(const std::string &Filename) { From a7c300d2ef28e412036336ffa49368c0152b9679 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Fri, 2 Nov 2018 13:08:59 -0700 Subject: [PATCH 330/582] [indexstore] Fix crashing issue with indexstore_store_get_unit_name_from_output_path It crashes if the passed 'buf_size' parameter is 0. Fixes rdar://45739315 apple-llvm-split-commit: 9ccad017572f93d5717f8d73dcda92c2af6112b0 apple-llvm-split-dir: clang/ --- clang/tools/IndexStore/IndexStore.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index cc517ec481553..f950fa307b877 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -589,8 +589,10 @@ indexstore_store_get_unit_name_from_output_path(indexstore_t store, SmallString<256> unitName; IndexUnitWriter::getUnitNameForAbsoluteOutputFile(output_path, unitName); size_t nameLen = unitName.size(); - strncpy(name_buf, unitName.c_str(), buf_size-1); - name_buf[buf_size-1] = '\0'; + if (buf_size != 0) { + strncpy(name_buf, unitName.c_str(), buf_size-1); + name_buf[buf_size-1] = '\0'; + } return nameLen; } From 9371ac6c2d0feb84f04f50de09577bf090e0e87d Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Fri, 2 Nov 2018 15:07:09 -0700 Subject: [PATCH 331/582] [indexstore] Enhancement for IndexStoreCXX.h's getUnitNameFromOutputPath Pass a buffer with non-zero size in the first call to indexstore_store_get_unit_name_from_output_path, to ensure it will get the name the first time. Previously it was depending on the passed in nameBuf to be non-zero, which should not be a requirement. apple-llvm-split-commit: 3bf864ec6b1efd5adc85a1e5236ac01d8eaccba1 apple-llvm-split-dir: clang/ --- clang/include/indexstore/IndexStoreCXX.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h index 3a3ba388e82aa..5b9fd896e95e9 100644 --- a/clang/include/indexstore/IndexStoreCXX.h +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -248,11 +248,14 @@ class IndexStore { void getUnitNameFromOutputPath(StringRef outputPath, llvm::SmallVectorImpl<char> &nameBuf) { llvm::SmallString<256> buf = outputPath; - size_t nameLen = indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), nameBuf.data(), nameBuf.size()); - if (nameLen+1 > nameBuf.size()) { - nameBuf.resize(nameLen+1); - indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), nameBuf.data(), nameBuf.size()); + llvm::SmallString<64> unitName; + unitName.resize(64); + size_t nameLen = indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), unitName.data(), unitName.size()); + if (nameLen+1 > unitName.size()) { + unitName.resize(nameLen+1); + indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), unitName.data(), unitName.size()); } + nameBuf.append(unitName.begin(), unitName.begin()+nameLen); } llvm::Optional<timespec> From b2fe1b3718a1449e69013f3eeba4357c5284c64f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sat, 3 Nov 2018 10:42:16 -0700 Subject: [PATCH 332/582] [index/store] Put the USR validation checking logging behind an environment variable rdar://30643167 apple-llvm-split-commit: 2ccb071095e9a8c333106adb37ad457ece7fb633 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexRecordWriter.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/clang/lib/Index/IndexRecordWriter.cpp b/clang/lib/Index/IndexRecordWriter.cpp index d22f4144aba28..e09cad632e012 100644 --- a/clang/lib/Index/IndexRecordWriter.cpp +++ b/clang/lib/Index/IndexRecordWriter.cpp @@ -138,6 +138,7 @@ static void writeDecls(BitstreamWriter &Stream, ArrayRef<DeclInfo> Decls, #ifndef NDEBUG StringSet<> USRSet; + bool enableValidation = getenv("CLANG_INDEX_VALIDATION_CHECKS") != nullptr; #endif RecordData Record; @@ -157,10 +158,12 @@ static void writeDecls(BitstreamWriter &Stream, ArrayRef<DeclInfo> Decls, Blob += SymInfo.CodeGenName; #ifndef NDEBUG - bool IsNew = USRSet.insert(SymInfo.USR).second; - if (!IsNew) { - llvm::errs() << "Index: Duplicate USR! " << SymInfo.USR << "\n"; - // FIXME: print more information so it's easier to find the declaration. + if (enableValidation) { + bool IsNew = USRSet.insert(SymInfo.USR).second; + if (!IsNew) { + llvm::errs() << "Index: Duplicate USR! " << SymInfo.USR << "\n"; + // FIXME: print more information so it's easier to find the declaration. + } } #endif From 4bfcfc50d7722445cc22643f36e2f29f03ee278c Mon Sep 17 00:00:00 2001 From: George Karpenkov <ekarpenkov@apple.com> Date: Mon, 12 Nov 2018 12:23:47 -0800 Subject: [PATCH 333/582] [indexer] Fix a fallthrough bug in IndexRecordHasher apple-llvm-split-commit: c718581990cb608b0bee8b3b2ee9da1db7eb29eb apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexRecordHasher.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp index 1edf4c151ca12..47e697c102c31 100644 --- a/clang/lib/Index/IndexRecordHasher.cpp +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -431,6 +431,7 @@ hash_code IndexRecordHasher::hashImpl(DeclarationName Name) { break; case DeclarationName::CXXLiteralOperatorName: COMBINE_HASH(computeHash(Name.getCXXLiteralIdentifier())); + break; case DeclarationName::CXXUsingDirective: break; case DeclarationName::CXXDeductionGuideName: From 2d0c835a56e97f0bb3b1d9ac85f7fc4d6fee1910 Mon Sep 17 00:00:00 2001 From: George Karpenkov <ekarpenkov@apple.com> Date: Mon, 12 Nov 2018 12:25:16 -0800 Subject: [PATCH 334/582] Add LLVM_FALLTHROUGH annotation. apple-llvm-split-commit: 4c832e1a28c72c3d26b1db8ec6a2892dfa257874 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexRecordHasher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp index 47e697c102c31..4394e7c4afc4c 100644 --- a/clang/lib/Index/IndexRecordHasher.cpp +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -340,7 +340,7 @@ static hash_code computeHash(const TemplateArgument &Arg, case TemplateArgument::TemplateExpansion: COMBINE_HASH('P'); // pack expansion of... - // Fall through + LLVM_FALLTHROUGH; case TemplateArgument::Template: COMBINE_HASH(computeHash(Arg.getAsTemplateOrTemplatePattern(), Hasher)); break; From 6dd1d9f2ca31f71c3e40928265c5d69c2523a141 Mon Sep 17 00:00:00 2001 From: George Karpenkov <ekarpenkov@apple.com> Date: Mon, 12 Nov 2018 14:16:25 -0800 Subject: [PATCH 335/582] Added another LLVM_FALLTHROUGH annotation. apple-llvm-split-commit: 856e04df865e60534c25f5fba3fb8fc404a2eb8f apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/RenameIndexedFile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index bef6aca1717dc..2c8d0bc430150 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -219,7 +219,7 @@ SelectorParser::ParseState SelectorParser::stateForToken(const Token &RawTok) { case ExpectingRParenOrColon: if (RawTok.is(tok::colon)) return ExpectingRParen; - // Fallthrough + LLVM_FALLTHROUGH; case ExpectingRParen: if (RawTok.is(tok::r_paren)) { // We found the selector that we were looking for. From 6476c5481f95625c2aa13fdde17ded141d6371ae Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Thu, 16 Feb 2017 21:04:14 -0800 Subject: [PATCH 336/582] [Modules] Prevent nonrecoverable module-out-of-date errors in projects that mix PCH and clang modules. This patch introduces a NeededByPCHOrCompilationUsesPCH language option which causes clang modules imported by PCH which goes into the module hash, thus isolating them from rebuilds triggered by other clang modules. This patch further disables module validation for modules imported by PCH, since there is no mechanism in clang to rebuild out-of-date modules when they were imported by a PCH. Initial patch by Adrian Prantl rdar://problem/35056912 rdar://problem/30384801 rdar://problem/45714111 (cherry picked from commit c26b5b27978a50a391feae178149c11db0fd3f5f) apple-llvm-split-commit: a9aeef69978b728baf9ff510a17dbb64c051e17b apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/LangOptions.def | 1 + clang/lib/Frontend/ASTUnit.cpp | 3 +++ clang/lib/Frontend/CompilerInvocation.cpp | 4 ++++ clang/lib/Frontend/FrontendAction.cpp | 4 ++++ clang/lib/Serialization/ASTReader.cpp | 5 +++++ clang/test/Modules/explicit-build.cpp | 2 +- 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 6b40a42af2dc0..816ec35241b3e 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -286,6 +286,7 @@ LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling") LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST") LANGOPT(APINotes, 1, 0, "use external API notes") LANGOPT(APINotesModules, 1, 0, "use external API notes") +LANGOPT(NeededByPCHOrCompilationUsesPCH, 1, 0, "compilation involves pch") LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " "field padding (0: none, 1:least " diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 05e47afcd9671..b4cc4a8e45ed8 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -1746,6 +1746,9 @@ ASTUnit *ASTUnit::LoadFromCommandLine( if (ModuleFormat) CI->getHeaderSearchOpts().ModuleFormat = ModuleFormat.getValue(); + if (ForSerialization) + CI->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true; + // Create the AST unit. std::unique_ptr<ASTUnit> AST; AST.reset(new ASTUnit(false)); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index dc5f5d9fc2728..1948eba853230 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3178,6 +3178,10 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), Args, Res.getFrontendOpts().ProgramAction); + if (!Res.getPreprocessorOpts().ImplicitPCHInclude.empty() || + Res.getFrontendOpts().ProgramAction == frontend::GeneratePCH) + LangOpts.NeededByPCHOrCompilationUsesPCH = true; + // Turn on -Wspir-compat for SPIR target. llvm::Triple T(Res.getTargetOpts().Triple); auto Arch = T.getArch(); diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index fc6c2e757c8de..7eac20f5e558e 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -690,6 +690,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, FileManager &FileMgr = CI.getFileManager(); PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); StringRef PCHInclude = PPOpts.ImplicitPCHInclude; + CI.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; std::string SpecificModuleCachePath = CI.getSpecificModuleCachePath(); if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) { std::error_code EC; @@ -718,6 +719,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, } } + if (CI.getFrontendOpts().ProgramAction == frontend::GeneratePCH) + CI.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; + // Set up the preprocessor if needed. When parsing model files the // preprocessor of the original source is reused. if (!isModelParsingAction()) diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 6dbb586452cae..1c8b8a83aa3e8 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4267,6 +4267,11 @@ ASTReader::readUnhashedControlBlock(ModuleFile &F, bool WasImportedBy, return Failure; } + // FIXME: Should we check the signature even if DisableValidation? + if (PP.getLangOpts().NeededByPCHOrCompilationUsesPCH || DisableValidation || + (AllowConfigurationMismatch && Result == ConfigurationMismatch)) + return Success; + if (Result == OutOfDate && F.Kind == MK_ImplicitModule) { // If this module has already been finalized in the PCMCache, we're stuck // with it; we can only load a single version of each module. diff --git a/clang/test/Modules/explicit-build.cpp b/clang/test/Modules/explicit-build.cpp index 16eb604708c9d..346983da53fd9 100644 --- a/clang/test/Modules/explicit-build.cpp +++ b/clang/test/Modules/explicit-build.cpp @@ -150,7 +150,7 @@ // RUN: -fmodule-file=%t/a.pch \ // RUN: %s 2>&1 | FileCheck --check-prefix=CHECK-A-AS-PCH %s // -// CHECK-A-AS-PCH: fatal error: AST file '{{.*}}a.pch' was not built as a module +// CHECK-A-AS-PCH: error: module file {{.*}}a.pch cannot be loaded due to a configuration mismatch with the current compilation // ------------------------------- // Try to import a non-AST file with -fmodule-file= From 774d24cbdadfb29ca99dad6013f868c00c143de5 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Fri, 16 Nov 2018 17:53:00 -0800 Subject: [PATCH 337/582] Revert "Re-apply r346985: [ADT] Drop llvm::Optional clang-specific optimization for trivially copyable types" This reverts commit 83343ed6a1b102f1175506d36ba7d3bc88dbbbed. master-next doesn't build otherwise (rdar://problem/46128545) apple-llvm-split-commit: 9580538e8487621d9baa51c31eee140be4f37608 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/Optional.h | 18 ++++++++++++++++++ llvm/unittests/ADT/OptionalTest.cpp | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/llvm/include/llvm/ADT/Optional.h b/llvm/include/llvm/ADT/Optional.h index 9fe9b2824ad61..353e5d0ec9df2 100644 --- a/llvm/include/llvm/ADT/Optional.h +++ b/llvm/include/llvm/ADT/Optional.h @@ -108,6 +108,24 @@ template <typename T, bool IsPodLike> struct OptionalStorage { } }; +#if !defined(__GNUC__) || defined(__clang__) // GCC up to GCC7 miscompiles this. +/// Storage for trivially copyable types only. +template <typename T> struct OptionalStorage<T, true> { + AlignedCharArrayUnion<T> storage; + bool hasVal = false; + + OptionalStorage() = default; + + OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); } + OptionalStorage &operator=(const T &y) { + *reinterpret_cast<T *>(storage.buffer) = y; + hasVal = true; + return *this; + } + + void reset() { hasVal = false; } +}; +#endif } // namespace optional_detail template <typename T> class Optional { diff --git a/llvm/unittests/ADT/OptionalTest.cpp b/llvm/unittests/ADT/OptionalTest.cpp index 20bc9da4d594f..2e09c5340fa3f 100644 --- a/llvm/unittests/ADT/OptionalTest.cpp +++ b/llvm/unittests/ADT/OptionalTest.cpp @@ -518,5 +518,13 @@ TEST_F(OptionalTest, OperatorGreaterEqual) { CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess); } +#if __has_feature(is_trivially_copyable) && defined(_LIBCPP_VERSION) +static_assert(std::is_trivially_copyable<Optional<int>>::value, + "Should be trivially copyable"); +static_assert( + !std::is_trivially_copyable<Optional<NonDefaultConstructible>>::value, + "Shouldn't be trivially copyable"); +#endif + } // end anonymous namespace From 18f6bc75ed0e2f22aa4431378f70adf15fa4d7ef Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai <vsapsai@apple.com> Date: Mon, 19 Nov 2018 12:15:53 -0800 Subject: [PATCH 338/582] Fixup for r347141 after BuryPointer was moved from Clang to LLVM. rdar://problem/46158139 apple-llvm-split-commit: d771d6339b1ba04aaeadf6b341c29cc0ce080ed7 apple-llvm-split-dir: clang/ --- clang/tools/c-index-test/JSONAggregation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/tools/c-index-test/JSONAggregation.cpp b/clang/tools/c-index-test/JSONAggregation.cpp index 6325b00e3e325..c428c1152464c 100644 --- a/clang/tools/c-index-test/JSONAggregation.cpp +++ b/clang/tools/c-index-test/JSONAggregation.cpp @@ -13,6 +13,7 @@ #include "clang/Index/IndexDataStoreSymbolUtils.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/BuryPointer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -397,6 +398,6 @@ bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { if (err) return true; aggregator->dumpJSON(OS); - BuryPointer(aggregator); + llvm::BuryPointer(aggregator); return false; } From 6152f711e9cd8d94a82a3572b4b7ad27c88a474d Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Sat, 24 Nov 2018 10:48:07 -0800 Subject: [PATCH 339/582] Edit: adjust for SVN r347417 `EvaluateAsInt` now returns an `EvalResult`. apple-llvm-split-commit: 5159071da70265031a53fb491881edd9dd97b14d apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingSwitchEnumCases.cpp | 8 ++++---- clang/lib/Tooling/Refactor/IfSwitchConversion.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp index 46ef3c8915219..4bb31fe6bc8f6 100644 --- a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -86,13 +86,13 @@ void edit::fillInMissingSwitchEnumCases( } const auto *CS = cast<CaseStmt>(Case); if (const auto *LHS = CS->getLHS()) { - llvm::APSInt Value; - if (!LHS->EvaluateAsInt(Value, Context)) + Expr::EvalResult Result; + if (!LHS->EvaluateAsInt(Result, Context)) continue; // Only allow constant that fix into 64 bits. - if (Value.getMinSignedBits() > 64) + if (Result.Val.getInt().getMinSignedBits() > 64) continue; - CoveredEnumCases[Value.getSExtValue()] = + CoveredEnumCases[Result.Val.getInt().getSExtValue()] = CaseInfo{Case, NextCase, CaseIndex}; // The cases in the switch are ordered back to front, so the last // case is actually the first enum case in the switch. diff --git a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp index cea4b3c6bc4c5..b8bd7308b502e 100644 --- a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -130,12 +130,12 @@ static bool isConditionValid(const Expr *E, ASTContext &Context, return false; // RHS must be a constant and unique. - llvm::APSInt Value; - if (!RHS->EvaluateAsInt(Value, Context)) + Expr::EvalResult Result; + if (!RHS->EvaluateAsInt(Result, Context)) return false; // Only allow constant that fix into 64 bits. - if (Value.getMinSignedBits() > 64 || - !RHSValues.insert(Value.getExtValue()).second) + if (Result.Val.getInt().getMinSignedBits() > 64 || + !RHSValues.insert(Result.Val.getInt().getExtValue()).second) return false; // LHS must be identical to the other LHS expressions. From aa3c9e482db09b5e9f7966d5e357f0ce03f09e51 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Wed, 28 Nov 2018 10:06:06 -0800 Subject: [PATCH 340/582] Revert "Edit: adjust for SVN r347417" This reverts commit 5159071da70265031a53fb491881edd9dd97b14d. Clang r347417 was reverted, so this also needs to be reverted for now. apple-llvm-split-commit: 06fe91a1af1e64aa38e7260f698710025cc6aa7f apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingSwitchEnumCases.cpp | 8 ++++---- clang/lib/Tooling/Refactor/IfSwitchConversion.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp index 4bb31fe6bc8f6..46ef3c8915219 100644 --- a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -86,13 +86,13 @@ void edit::fillInMissingSwitchEnumCases( } const auto *CS = cast<CaseStmt>(Case); if (const auto *LHS = CS->getLHS()) { - Expr::EvalResult Result; - if (!LHS->EvaluateAsInt(Result, Context)) + llvm::APSInt Value; + if (!LHS->EvaluateAsInt(Value, Context)) continue; // Only allow constant that fix into 64 bits. - if (Result.Val.getInt().getMinSignedBits() > 64) + if (Value.getMinSignedBits() > 64) continue; - CoveredEnumCases[Result.Val.getInt().getSExtValue()] = + CoveredEnumCases[Value.getSExtValue()] = CaseInfo{Case, NextCase, CaseIndex}; // The cases in the switch are ordered back to front, so the last // case is actually the first enum case in the switch. diff --git a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp index b8bd7308b502e..cea4b3c6bc4c5 100644 --- a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -130,12 +130,12 @@ static bool isConditionValid(const Expr *E, ASTContext &Context, return false; // RHS must be a constant and unique. - Expr::EvalResult Result; - if (!RHS->EvaluateAsInt(Result, Context)) + llvm::APSInt Value; + if (!RHS->EvaluateAsInt(Value, Context)) return false; // Only allow constant that fix into 64 bits. - if (Result.Val.getInt().getMinSignedBits() > 64 || - !RHSValues.insert(Result.Val.getInt().getExtValue()).second) + if (Value.getMinSignedBits() > 64 || + !RHSValues.insert(Value.getExtValue()).second) return false; // LHS must be identical to the other LHS expressions. From c43f3fbc8b658c39cfed122e01674769d53f1fcf Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Wed, 28 Nov 2018 15:43:59 -0800 Subject: [PATCH 341/582] Revert "[modules] Separately track whether an identifier's preprocessor information and" This adds a testcase and reverts commit dec469b8c5c697fcf99f637c15e591f522475d2a. It fixes the error below: $ rm -rf tmp; clang -fmodules standalone.c -fmodules-cache-path=tmp -fsyntax-only -I Inputs standalone.c:3:6: error: variable has incomplete type 'void' void foo __P(()); ^ standalone.c:3:9: error: expected ';' after top level declarator void foo __P(()); ^ ; 2 errors generated. Some changes were necessary to maintain Vassil's module related fix in r264913. rdar://problem/28410377 rdar://problem/30258278 apple-llvm-split-commit: d126e68b9b7b94e3329daba0a2b92d0f6a1ec9d3 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/IdentifierTable.h | 18 +----------------- clang/lib/Basic/IdentifierTable.cpp | 1 - clang/lib/Sema/IdentifierResolver.cpp | 2 +- clang/lib/Serialization/ASTWriter.cpp | 8 ++------ clang/test/Modules/Inputs/incomplete-type/A.h | 1 + clang/test/Modules/Inputs/incomplete-type/B.h | 2 ++ clang/test/Modules/Inputs/incomplete-type/B2.h | 2 ++ clang/test/Modules/Inputs/incomplete-type/C.h | 2 ++ .../Inputs/incomplete-type/module.modulemap | 15 +++++++++++++++ clang/test/Modules/incomplete-type-void.c | 9 +++++++++ .../test/Modules/minimal-identifier-tables.cpp | 10 ---------- 11 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 clang/test/Modules/Inputs/incomplete-type/A.h create mode 100644 clang/test/Modules/Inputs/incomplete-type/B.h create mode 100644 clang/test/Modules/Inputs/incomplete-type/B2.h create mode 100644 clang/test/Modules/Inputs/incomplete-type/C.h create mode 100644 clang/test/Modules/Inputs/incomplete-type/module.modulemap create mode 100644 clang/test/Modules/incomplete-type-void.c delete mode 100644 clang/test/Modules/minimal-identifier-tables.cpp diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index 478c25e023028..24d8978a79dde 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -95,10 +95,6 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { // loaded from an AST file. unsigned ChangedAfterLoad : 1; - // True if the identifier's frontend information has changed from the - // definition loaded from an AST file. - unsigned FEChangedAfterLoad : 1; - // True if revertTokenIDToIdentifier was called. unsigned RevertedTokenID : 1; @@ -109,7 +105,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { // True if this is the 'import' contextual keyword. unsigned IsModulesImport : 1; - // 29 bits left in a 64-bit word. + // 30 bits left in a 64-bit word. // Managed by the language front-end. void *FETokenInfo = nullptr; @@ -346,18 +342,6 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { ChangedAfterLoad = true; } - /// Determine whether the frontend token information for this - /// identifier has changed since it was loaded from an AST file. - bool hasFETokenInfoChangedSinceDeserialization() const { - return FEChangedAfterLoad; - } - - /// Note that the frontend token information for this identifier has - /// changed since it was loaded from an AST file. - void setFETokenInfoChangedSinceDeserialization() { - FEChangedAfterLoad = true; - } - /// Determine whether the information for this identifier is out of /// date with respect to the external source. bool isOutOfDate() const { return OutOfDate; } diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index 18580fca6c6d5..a1d1aa82ba9dc 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -49,7 +49,6 @@ IdentifierInfo::IdentifierInfo() { NeedsHandleIdentifier = false; IsFromAST = false; ChangedAfterLoad = false; - FEChangedAfterLoad = false; RevertedTokenID = false; OutOfDate = false; IsModulesImport = false; diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp index b439f7255728d..79da487dd9de6 100644 --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -386,7 +386,7 @@ void IdentifierResolver::updatingIdentifier(IdentifierInfo &II) { PP.getExternalSource()->updateOutOfDateIdentifier(II); if (II.isFromAST()) - II.setFETokenInfoChangedSinceDeserialization(); + II.setChangedSinceDeserialization(); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index ed4b280f9d8c0..ec6fbfbd50ee3 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -3585,8 +3585,6 @@ class ASTIdentifierTableTrait { NeedDecls(!IsModule || !Writer.getLangOpts().CPlusPlus), InterestingIdentifierOffsets(InterestingIdentifierOffsets) {} - bool needDecls() const { return NeedDecls; } - static hash_value_type ComputeHash(const IdentifierInfo* II) { return llvm::djbHash(II->getName()); } @@ -3739,10 +3737,8 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP, assert(II && "NULL identifier in identifier table"); // Write out identifiers if either the ID is local or the identifier has // changed since it was loaded. - if (ID >= FirstIdentID || !Chain || !II->isFromAST() - || II->hasChangedSinceDeserialization() || - (Trait.needDecls() && - II->hasFETokenInfoChangedSinceDeserialization())) + if (ID >= FirstIdentID || !Chain || !II->isFromAST() || + II->hasChangedSinceDeserialization()) Generator.insert(II, ID, Trait); } diff --git a/clang/test/Modules/Inputs/incomplete-type/A.h b/clang/test/Modules/Inputs/incomplete-type/A.h new file mode 100644 index 0000000000000..6943aaf034500 --- /dev/null +++ b/clang/test/Modules/Inputs/incomplete-type/A.h @@ -0,0 +1 @@ +#define __P(protos) () diff --git a/clang/test/Modules/Inputs/incomplete-type/B.h b/clang/test/Modules/Inputs/incomplete-type/B.h new file mode 100644 index 0000000000000..51df71e2c08ad --- /dev/null +++ b/clang/test/Modules/Inputs/incomplete-type/B.h @@ -0,0 +1,2 @@ +#import "A.h" +#import "B2.h" diff --git a/clang/test/Modules/Inputs/incomplete-type/B2.h b/clang/test/Modules/Inputs/incomplete-type/B2.h new file mode 100644 index 0000000000000..def109fd6d669 --- /dev/null +++ b/clang/test/Modules/Inputs/incomplete-type/B2.h @@ -0,0 +1,2 @@ +void test(int __P) { +} diff --git a/clang/test/Modules/Inputs/incomplete-type/C.h b/clang/test/Modules/Inputs/incomplete-type/C.h new file mode 100644 index 0000000000000..ea6a2ee01bfc4 --- /dev/null +++ b/clang/test/Modules/Inputs/incomplete-type/C.h @@ -0,0 +1,2 @@ +#import "A.h" +#import "B.h" diff --git a/clang/test/Modules/Inputs/incomplete-type/module.modulemap b/clang/test/Modules/Inputs/incomplete-type/module.modulemap new file mode 100644 index 0000000000000..17c8d7d791d2c --- /dev/null +++ b/clang/test/Modules/Inputs/incomplete-type/module.modulemap @@ -0,0 +1,15 @@ +module X { + header "A.h" + export * +} + +// Y imports X +module Y { + header "B.h" + export * +} + +// Z imports X and Y +module Z { + header "C.h" +} diff --git a/clang/test/Modules/incomplete-type-void.c b/clang/test/Modules/incomplete-type-void.c new file mode 100644 index 0000000000000..2ce00f7eadd4e --- /dev/null +++ b/clang/test/Modules/incomplete-type-void.c @@ -0,0 +1,9 @@ +// RUN: rm -rf %t.cache +// RUN: %clang_cc1 -fmodules %s -fmodules-cache-path=%t.cache \ +// RUN: -fsyntax-only -I %S/Inputs/incomplete-type -verify + +// expected-no-diagnostics + +#import "C.h" +#import "A.h" +void foo __P(()); diff --git a/clang/test/Modules/minimal-identifier-tables.cpp b/clang/test/Modules/minimal-identifier-tables.cpp deleted file mode 100644 index 0674746e07fec..0000000000000 --- a/clang/test/Modules/minimal-identifier-tables.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo 'extern int some_long_variable_name;' > %t/x.h -// RUN: echo 'extern int some_long_variable_name;' > %t/y.h -// RUN: echo 'module X { header "x.h" } module Y { header "y.h" }' > %t/map -// RUN: %clang_cc1 -fmodules -x c++ -fmodule-name=X %t/map -emit-module -o %t/x.pcm -// RUN: %clang_cc1 -fmodules -x c++ -fmodule-name=Y %t/map -fmodule-file=%t/x.pcm -emit-module -o %t/y.pcm -// RUN: cat %t/y.pcm | FileCheck %s -// -// CHECK-NOT: some_long_variable_name From f29a8fd30357d2dafc7f133ad1d1244fabc418e7 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Sat, 24 Nov 2018 10:48:07 -0800 Subject: [PATCH 342/582] Edit: adjust for SVN r347417 `EvaluateAsInt` now returns an `EvalResult`. apple-llvm-split-commit: 9b193cd69af7cbf6c9aa8d32777ac55d0b0b591d apple-llvm-split-dir: clang/ --- clang/lib/Edit/FillInMissingSwitchEnumCases.cpp | 8 ++++---- clang/lib/Tooling/Refactor/IfSwitchConversion.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp index 46ef3c8915219..4bb31fe6bc8f6 100644 --- a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -86,13 +86,13 @@ void edit::fillInMissingSwitchEnumCases( } const auto *CS = cast<CaseStmt>(Case); if (const auto *LHS = CS->getLHS()) { - llvm::APSInt Value; - if (!LHS->EvaluateAsInt(Value, Context)) + Expr::EvalResult Result; + if (!LHS->EvaluateAsInt(Result, Context)) continue; // Only allow constant that fix into 64 bits. - if (Value.getMinSignedBits() > 64) + if (Result.Val.getInt().getMinSignedBits() > 64) continue; - CoveredEnumCases[Value.getSExtValue()] = + CoveredEnumCases[Result.Val.getInt().getSExtValue()] = CaseInfo{Case, NextCase, CaseIndex}; // The cases in the switch are ordered back to front, so the last // case is actually the first enum case in the switch. diff --git a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp index cea4b3c6bc4c5..b8bd7308b502e 100644 --- a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -130,12 +130,12 @@ static bool isConditionValid(const Expr *E, ASTContext &Context, return false; // RHS must be a constant and unique. - llvm::APSInt Value; - if (!RHS->EvaluateAsInt(Value, Context)) + Expr::EvalResult Result; + if (!RHS->EvaluateAsInt(Result, Context)) return false; // Only allow constant that fix into 64 bits. - if (Value.getMinSignedBits() > 64 || - !RHSValues.insert(Value.getExtValue()).second) + if (Result.Val.getInt().getMinSignedBits() > 64 || + !RHSValues.insert(Result.Val.getInt().getExtValue()).second) return false; // LHS must be identical to the other LHS expressions. From cb54815755159382f06e4ab2f8e5bb02c4d67570 Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Fri, 30 Nov 2018 10:41:37 +0000 Subject: [PATCH 343/582] Merge remote-tracking branch 'llvm.org/master' into HEAD # Conflicts: # test/Misc/pragma-attribute-supported-attributes-list.test apple-llvm-split-commit: 0e0b0429f89599f03ef41995c846cf7560bb6ba1 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 31 ++- clang/include/clang/Basic/AttrDocs.td | 45 +++++ clang/include/clang/Sema/Sema.h | 6 +- .../Core/PathSensitive/SMTConstraintManager.h | 2 +- .../Core/PathSensitive/SymbolManager.h | 22 +- .../Core/RetainSummaryManager.h | 2 + clang/lib/Sema/SemaDeclAttr.cpp | 190 ++++++++++++------ .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 25 ++- .../Checkers/CStringChecker.cpp | 3 - .../Checkers/DynamicTypePropagation.cpp | 5 - .../Checkers/MPI-Checker/MPIChecker.cpp | 3 - .../Checkers/MacOSKeychainAPIChecker.cpp | 44 ++++ .../StaticAnalyzer/Checkers/MallocChecker.cpp | 16 +- .../Checkers/NullabilityChecker.cpp | 3 - .../RetainCountChecker/RetainCountChecker.cpp | 14 +- .../RetainCountDiagnostics.cpp | 7 +- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 34 ++-- clang/lib/StaticAnalyzer/Core/Environment.cpp | 5 - clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 65 +++--- .../Core/RangeConstraintManager.cpp | 2 +- clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 21 +- .../Core/RetainSummaryManager.cpp | 51 +++-- .../lib/StaticAnalyzer/Core/SymbolManager.cpp | 9 - clang/test/Analysis/MisusedMovedObject.cpp | 22 ++ clang/test/Analysis/keychainAPI.m | 14 +- clang/test/Analysis/loop-block-counts.c | 26 +++ .../test/Analysis/osobject-retain-release.cpp | 38 +++- clang/test/Analysis/pr22954.c | 2 +- .../Analysis/retain-release-cpp-classes.cpp | 33 +++ clang/test/Analysis/self-assign.cpp | 7 +- clang/test/Analysis/simple-stream-checks.c | 5 + clang/test/Analysis/unions.cpp | 3 +- ...a-attribute-supported-attributes-list.test | 5 +- clang/test/Sema/attr-osobject.cpp | 42 ++++ clang/test/Sema/attr-osobject.mm | 11 + 35 files changed, 576 insertions(+), 237 deletions(-) create mode 100644 clang/test/Analysis/loop-block-counts.c create mode 100644 clang/test/Analysis/retain-release-cpp-classes.cpp create mode 100644 clang/test/Sema/attr-osobject.cpp create mode 100644 clang/test/Sema/attr-osobject.mm diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 7c4c592616cd7..0a318421608c8 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -819,19 +819,38 @@ def CFUnknownTransfer : InheritableAttr { def CFReturnsRetained : InheritableAttr { let Spellings = [Clang<"cf_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def CFReturnsNotRetained : InheritableAttr { let Spellings = [Clang<"cf_returns_not_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def CFConsumed : InheritableParamAttr { let Spellings = [Clang<"cf_consumed">]; let Subjects = SubjectList<[ParmVar]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; +} + +// OSObject-based attributes. +def OSConsumed : InheritableParamAttr { + let Spellings = [Clang<"os_consumed">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [RetainBehaviorDocs]; +} + +def OSReturnsRetained : InheritableAttr { + let Spellings = [Clang<"os_returns_retained">]; + let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty]>; + let Documentation = [RetainBehaviorDocs]; +} + +def OSReturnsNotRetained : InheritableAttr { + let Spellings = [Clang<"os_returns_not_retained">]; + let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty]>; + let Documentation = [RetainBehaviorDocs]; } def Cleanup : InheritableAttr { @@ -1625,19 +1644,19 @@ def NSErrorDomain : Attr { def NSReturnsRetained : DeclOrTypeAttr { let Spellings = [Clang<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def NSReturnsNotRetained : InheritableAttr { let Spellings = [Clang<"ns_returns_not_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def NSReturnsAutoreleased : InheritableAttr { let Spellings = [Clang<"ns_returns_autoreleased">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def NSConsumesSelf : InheritableAttr { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index ba0e8b783becc..be056d9e06a42 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -847,6 +847,51 @@ Query for this feature with ``__has_attribute(objc_method_family)``. }]; } +def RetainBehaviorDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The behavior of a function with respect to reference counting for Foundation +(Objective-C), CoreFoundation (C) and OSObject (C++) is determined by a naming +convention (e.g. functions starting with "get" are assumed to return at +``+0``). + +It can be overriden using a family of the following attributes. In +Objective-C, the annotation ``__attribute__((ns_returns_retained))`` applied to +a function communicates that the object is returned at ``+1``, and the caller +is responsible for freeing it. +Similiarly, the annotation ``__attribute__((ns_returns_not_retained))`` +specifies that the object is returned at ``+0`` and the ownership remains with +the callee. +Additionally, parameters can have an annotation +``__attribute__((ns_consumed))``, which specifies that passing an owned object +as that parameter effectively transfers the ownership, and the caller is no +longer responsible for it. +These attributes affect code generation when interacting with ARC code, and +they are used by the Clang Static Analyzer. + +In C programs using CoreFoundation, a similar set of attributes: +``__attribute__((cf_returns_not_retained))``, +``__attribute__((cf_returns_retained))`` and ``__attribute__((cf_consumed))`` +have the same respective semantics when applied to CoreFoundation objects. +These attributes affect code generation when interacting with ARC code, and +they are used by the Clang Static Analyzer. + +Finally, in C++ interacting with XNU kernel (objects inheriting from OSObject), +the same attribute family is present: +``__attribute__((os_returns_not_retained))``, +``__attribute__((os_returns_retained))`` and ``__attribute__((os_consumed))``, +with the same respective semantics. +These attributes are also used by the Clang Static Analyzer. + +The family of attributes ``X_returns_X_retained`` can be added to functions, +C++ methods, and Objective-C methods and properties. +Attributes ``X_consumed`` can be added to parameters of methods, functions, +and Objective-C methods. + }]; +} + + + def NoDebugDocs : Documentation { let Category = DocCatVariable; let Content = [{ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3750559f40488..eefbb47d64e54 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8609,9 +8609,9 @@ class Sema { void AddParameterABIAttr(SourceRange AttrRange, Decl *D, ParameterABI ABI, unsigned SpellingListIndex); - void AddNSConsumedAttr(SourceRange AttrRange, Decl *D, - unsigned SpellingListIndex, bool isNSConsumed, - bool isTemplateInstantiation); + enum class RetainOwnershipKind {NS, CF, OS}; + void AddXConsumedAttr(Decl *D, SourceRange SR, unsigned SpellingIndex, + RetainOwnershipKind K, bool IsTemplateInstantiation); bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h index 54cf3c39dd21e..8eaa9365be1d7 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h @@ -198,7 +198,7 @@ class SMTConstraintManager : public clang::ento::SimpleConstraintManager { auto &CZFactory = State->get_context<ConstraintSMT>(); for (auto I = CZ.begin(), E = CZ.end(); I != E; ++I) { - if (SymReaper.maybeDead(I->first)) + if (SymReaper.isDead(I->first)) CZ = CZFactory.remove(CZ, *I); } diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index b014c63709786..d02a8abd11481 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -558,7 +558,6 @@ class SymbolReaper { SymbolMapTy TheLiving; SymbolSetTy MetadataInUse; - SymbolSetTy TheDead; RegionSetTy RegionRoots; @@ -603,21 +602,6 @@ class SymbolReaper { /// symbol marking has occurred, i.e. in the MarkLiveSymbols callback. void markInUse(SymbolRef sym); - /// If a symbol is known to be live, marks the symbol as live. - /// - /// Otherwise, if the symbol cannot be proven live, it is marked as dead. - /// Returns true if the symbol is dead, false if live. - bool maybeDead(SymbolRef sym); - - using dead_iterator = SymbolSetTy::const_iterator; - - dead_iterator dead_begin() const { return TheDead.begin(); } - dead_iterator dead_end() const { return TheDead.end(); } - - bool hasDeadSymbols() const { - return !TheDead.empty(); - } - using region_iterator = RegionSetTy::const_iterator; region_iterator region_begin() const { return RegionRoots.begin(); } @@ -626,9 +610,9 @@ class SymbolReaper { /// Returns whether or not a symbol has been confirmed dead. /// /// This should only be called once all marking of dead symbols has completed. - /// (For checkers, this means only in the evalDeadSymbols callback.) - bool isDead(SymbolRef sym) const { - return TheDead.count(sym); + /// (For checkers, this means only in the checkDeadSymbols callback.) + bool isDead(SymbolRef sym) { + return !isLive(sym); } void markLive(const MemRegion *region); diff --git a/clang/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h b/clang/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h index 9b71011a54d2a..dfcbd0c85c342 100644 --- a/clang/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h @@ -530,6 +530,8 @@ class RetainSummaryManager { /// Decrement the reference count on OS object. const RetainSummary *getOSSummaryReleaseRule(const FunctionDecl *FD); + /// Free the OS object. + const RetainSummary *getOSSummaryFreeRule(const FunctionDecl *FD); enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable }; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 513a7d5a0c0cf..14a029469365b 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -391,10 +391,50 @@ bool Sema::checkStringLiteralArgumentAttr(const ParsedAttr &AL, unsigned ArgNum, /// Applies the given attribute to the Decl without performing any /// additional semantic checking. +template <typename AttrType> +static void handleSimpleAttribute(Sema &S, Decl *D, SourceRange SR, + unsigned SpellingIndex) { + D->addAttr(::new (S.Context) AttrType(SR, S.Context, SpellingIndex)); +} + template <typename AttrType> static void handleSimpleAttribute(Sema &S, Decl *D, const ParsedAttr &AL) { - D->addAttr(::new (S.Context) AttrType(AL.getRange(), S.Context, - AL.getAttributeSpellingListIndex())); + handleSimpleAttribute<AttrType>(S, D, AL.getRange(), + AL.getAttributeSpellingListIndex()); +} + + +template <typename... DiagnosticArgs> +static const Sema::SemaDiagnosticBuilder& +appendDiagnostics(const Sema::SemaDiagnosticBuilder &Bldr) { + return Bldr; +} + +template <typename T, typename... DiagnosticArgs> +static const Sema::SemaDiagnosticBuilder& +appendDiagnostics(const Sema::SemaDiagnosticBuilder &Bldr, T &&ExtraArg, + DiagnosticArgs &&... ExtraArgs) { + return appendDiagnostics(Bldr << std::forward<T>(ExtraArg), + std::forward<DiagnosticArgs>(ExtraArgs)...); +} + +/// Add an attribute {@code AttrType} to declaration {@code D}, +/// provided the given {@code Check} function returns {@code true} +/// on type of {@code D}. +/// If check does not pass, emit diagnostic {@code DiagID}, +/// passing in all parameters specified in {@code ExtraArgs}. +template <typename AttrType, typename... DiagnosticArgs> +static void +handleSimpleAttributeWithCheck(Sema &S, ValueDecl *D, SourceRange SR, + unsigned SpellingIndex, + llvm::function_ref<bool(QualType)> Check, + unsigned DiagID, DiagnosticArgs... ExtraArgs) { + if (!Check(D->getType())) { + Sema::SemaDiagnosticBuilder DB = S.Diag(D->getBeginLoc(), DiagID); + appendDiagnostics(DB, std::forward<DiagnosticArgs>(ExtraArgs)...); + return; + } + handleSimpleAttribute<AttrType>(S, D, SR, SpellingIndex); } template <typename AttrType> @@ -4732,58 +4772,70 @@ static void handleXRayLogArgsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { //===----------------------------------------------------------------------===// // Checker-specific attribute handlers. //===----------------------------------------------------------------------===// - static bool isValidSubjectOfNSReturnsRetainedAttribute(QualType QT) { return QT->isDependentType() || QT->isObjCRetainableType(); } -static bool isValidSubjectOfNSAttribute(Sema &S, QualType QT) { +static bool isValidSubjectOfNSAttribute(QualType QT) { return QT->isDependentType() || QT->isObjCObjectPointerType() || - S.Context.isObjCNSObjectType(QT); + QT->isObjCNSObjectType(); } -static bool isValidSubjectOfCFAttribute(Sema &S, QualType QT) { +static bool isValidSubjectOfCFAttribute(QualType QT) { return QT->isDependentType() || QT->isPointerType() || - isValidSubjectOfNSAttribute(S, QT); + isValidSubjectOfNSAttribute(QT); } -static void handleNSConsumedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - S.AddNSConsumedAttr(AL.getRange(), D, AL.getAttributeSpellingListIndex(), - AL.getKind() == ParsedAttr::AT_NSConsumed, - /*template instantiation*/ false); +static bool isValidSubjectOfOSAttribute(QualType QT) { + return QT->isDependentType() || QT->isPointerType(); } -void Sema::AddNSConsumedAttr(SourceRange AttrRange, Decl *D, - unsigned SpellingIndex, bool IsNSConsumed, - bool IsTemplateInstantiation) { - const auto *Param = cast<ParmVarDecl>(D); - bool TypeOK; - - if (IsNSConsumed) - TypeOK = isValidSubjectOfNSAttribute(*this, Param->getType()); - else - TypeOK = isValidSubjectOfCFAttribute(*this, Param->getType()); +void Sema::AddXConsumedAttr(Decl *D, SourceRange SR, unsigned SpellingIndex, + RetainOwnershipKind K, + bool IsTemplateInstantiation) { + ValueDecl *VD = cast<ValueDecl>(D); + switch (K) { + case RetainOwnershipKind::OS: + handleSimpleAttributeWithCheck<OSConsumedAttr>( + *this, VD, SR, SpellingIndex, &isValidSubjectOfOSAttribute, + diag::warn_ns_attribute_wrong_parameter_type, + /*ExtraArgs=*/SR, "os_consumed", /*pointers*/ 1); + return; + case RetainOwnershipKind::NS: + handleSimpleAttributeWithCheck<NSConsumedAttr>( + *this, VD, SR, SpellingIndex, &isValidSubjectOfNSAttribute, - if (!TypeOK) { - // These attributes are normally just advisory, but in ARC, ns_consumed - // is significant. Allow non-dependent code to contain inappropriate - // attributes even in ARC, but require template instantiations to be - // set up correctly. - Diag(D->getBeginLoc(), (IsTemplateInstantiation && IsNSConsumed && - getLangOpts().ObjCAutoRefCount - ? diag::err_ns_attribute_wrong_parameter_type - : diag::warn_ns_attribute_wrong_parameter_type)) - << AttrRange << (IsNSConsumed ? "ns_consumed" : "cf_consumed") - << (IsNSConsumed ? /*objc pointers*/ 0 : /*cf pointers*/ 1); + // These attributes are normally just advisory, but in ARC, ns_consumed + // is significant. Allow non-dependent code to contain inappropriate + // attributes even in ARC, but require template instantiations to be + // set up correctly. + ((IsTemplateInstantiation && getLangOpts().ObjCAutoRefCount) + ? diag::err_ns_attribute_wrong_parameter_type + : diag::warn_ns_attribute_wrong_parameter_type), + /*ExtraArgs=*/SR, "ns_consumed", /*objc pointers*/ 0); + return; + case RetainOwnershipKind::CF: + handleSimpleAttributeWithCheck<CFConsumedAttr>( + *this, VD, SR, SpellingIndex, + &isValidSubjectOfCFAttribute, + diag::warn_ns_attribute_wrong_parameter_type, + /*ExtraArgs=*/SR, "cf_consumed", /*pointers*/1); return; } +} - if (IsNSConsumed) - D->addAttr(::new (Context) - NSConsumedAttr(AttrRange, Context, SpellingIndex)); - else - D->addAttr(::new (Context) - CFConsumedAttr(AttrRange, Context, SpellingIndex)); +static Sema::RetainOwnershipKind +parsedAttrToRetainOwnershipKind(const ParsedAttr &AL) { + switch (AL.getKind()) { + case ParsedAttr::AT_CFConsumed: + return Sema::RetainOwnershipKind::CF; + case ParsedAttr::AT_OSConsumed: + return Sema::RetainOwnershipKind::OS; + case ParsedAttr::AT_NSConsumed: + return Sema::RetainOwnershipKind::NS; + default: + llvm_unreachable("Wrong argument supplied"); + } } bool Sema::checkNSReturnsRetainedReturnType(SourceLocation Loc, QualType QT) { @@ -4795,24 +4847,26 @@ bool Sema::checkNSReturnsRetainedReturnType(SourceLocation Loc, QualType QT) { return true; } -static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, +static void handleXReturnsXRetainedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { QualType ReturnType; - if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { ReturnType = MD->getReturnType(); - else if (S.getLangOpts().ObjCAutoRefCount && hasDeclarator(D) && - (AL.getKind() == ParsedAttr::AT_NSReturnsRetained)) + } else if (S.getLangOpts().ObjCAutoRefCount && hasDeclarator(D) && + (AL.getKind() == ParsedAttr::AT_NSReturnsRetained)) { return; // ignore: was handled as a type attribute - else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) + } else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(D)) { ReturnType = PD->getType(); - else if (const auto *FD = dyn_cast<FunctionDecl>(D)) + } else if (const auto *FD = dyn_cast<FunctionDecl>(D)) { ReturnType = FD->getReturnType(); - else if (const auto *Param = dyn_cast<ParmVarDecl>(D)) { + } else if (const auto *Param = dyn_cast<ParmVarDecl>(D)) { + // Attributes on parameters are used for out-parameters, + // passed as pointers-to-pointers. ReturnType = Param->getType()->getPointeeType(); if (ReturnType.isNull()) { S.Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type) - << AL << /*pointer-to-CF*/ 2 << AL.getRange(); + << AL << /*pointer-to-CF-pointer*/ 2 << AL.getRange(); return; } } else if (AL.isUsedAsTypeAttr()) { @@ -4824,6 +4878,8 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, case ParsedAttr::AT_NSReturnsRetained: case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: ExpectedDeclKind = ExpectedFunctionOrMethod; break; @@ -4848,13 +4904,19 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: - TypeOK = isValidSubjectOfNSAttribute(S, ReturnType); + TypeOK = isValidSubjectOfNSAttribute(ReturnType); Cf = false; break; case ParsedAttr::AT_CFReturnsRetained: case ParsedAttr::AT_CFReturnsNotRetained: - TypeOK = isValidSubjectOfCFAttribute(S, ReturnType); + TypeOK = isValidSubjectOfCFAttribute(ReturnType); + Cf = true; + break; + + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: + TypeOK = isValidSubjectOfOSAttribute(ReturnType); Cf = true; break; } @@ -4887,24 +4949,25 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, default: llvm_unreachable("invalid ownership attribute"); case ParsedAttr::AT_NSReturnsAutoreleased: - D->addAttr(::new (S.Context) NSReturnsAutoreleasedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute<NSReturnsAutoreleasedAttr>(S, D, AL); return; case ParsedAttr::AT_CFReturnsNotRetained: - D->addAttr(::new (S.Context) CFReturnsNotRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute<CFReturnsNotRetainedAttr>(S, D, AL); return; case ParsedAttr::AT_NSReturnsNotRetained: - D->addAttr(::new (S.Context) NSReturnsNotRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute<NSReturnsNotRetainedAttr>(S, D, AL); return; case ParsedAttr::AT_CFReturnsRetained: - D->addAttr(::new (S.Context) CFReturnsRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute<CFReturnsRetainedAttr>(S, D, AL); return; case ParsedAttr::AT_NSReturnsRetained: - D->addAttr(::new (S.Context) NSReturnsRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute<NSReturnsRetainedAttr>(S, D, AL); + return; + case ParsedAttr::AT_OSReturnsRetained: + handleSimpleAttribute<OSReturnsRetainedAttr>(S, D, AL); + return; + case ParsedAttr::AT_OSReturnsNotRetained: + handleSimpleAttribute<OSReturnsNotRetainedAttr>(S, D, AL); return; }; } @@ -6810,17 +6873,22 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, break; case ParsedAttr::AT_CFConsumed: case ParsedAttr::AT_NSConsumed: - handleNSConsumedAttr(S, D, AL); + case ParsedAttr::AT_OSConsumed: + S.AddXConsumedAttr(D, AL.getRange(), AL.getAttributeSpellingListIndex(), + parsedAttrToRetainOwnershipKind(AL), + /*IsTemplateInstantiation=*/false); break; case ParsedAttr::AT_NSConsumesSelf: handleSimpleAttribute<NSConsumesSelfAttr>(S, D, AL); break; case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: - case ParsedAttr::AT_CFReturnsNotRetained: case ParsedAttr::AT_NSReturnsRetained: + case ParsedAttr::AT_CFReturnsNotRetained: case ParsedAttr::AT_CFReturnsRetained: - handleNSReturnsRetainedAttr(S, D, AL); + case ParsedAttr::AT_OSReturnsNotRetained: + case ParsedAttr::AT_OSReturnsRetained: + handleXReturnsXRetainedAttr(S, D, AL); break; case ParsedAttr::AT_WorkGroupSizeHint: handleWorkGroupSize<WorkGroupSizeHintAttr>(S, D, AL); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 4f7ecdfcdfda0..f810c4eb31df5 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -365,6 +365,20 @@ void Sema::InstantiateAttrsForDecl( } } +static Sema::RetainOwnershipKind +attrToRetainOwnershipKind(const Attr *A) { + switch (A->getKind()) { + case clang::attr::CFConsumed: + return Sema::RetainOwnershipKind::CF; + case clang::attr::OSConsumed: + return Sema::RetainOwnershipKind::OS; + case clang::attr::NSConsumed: + return Sema::RetainOwnershipKind::NS; + default: + llvm_unreachable("Wrong argument supplied"); + } +} + void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl, Decl *New, LateInstantiatedAttrVec *LateAttrs, @@ -438,11 +452,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, continue; } - if (isa<NSConsumedAttr>(TmplAttr) || isa<CFConsumedAttr>(TmplAttr)) { - AddNSConsumedAttr(TmplAttr->getRange(), New, - TmplAttr->getSpellingListIndex(), - isa<NSConsumedAttr>(TmplAttr), - /*template instantiation*/ true); + if (isa<NSConsumedAttr>(TmplAttr) || isa<OSConsumedAttr>(TmplAttr) || + isa<CFConsumedAttr>(TmplAttr)) { + AddXConsumedAttr(New, TmplAttr->getRange(), + TmplAttr->getSpellingListIndex(), + attrToRetainOwnershipKind(TmplAttr), + /*template instantiation=*/true); continue; } diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 98590bc066f01..0a1a108fed706 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -2385,9 +2385,6 @@ void CStringChecker::checkLiveSymbols(ProgramStateRef state, void CStringChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { - if (!SR.hasDeadSymbols()) - return; - ProgramStateRef state = C.getState(); CStringLengthTy Entries = state->get<CStringLength>(); if (Entries.isEmpty()) diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index c5acb61a44d43..57655f3533dd0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -123,11 +123,6 @@ void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, } } - if (!SR.hasDeadSymbols()) { - C.addTransition(State); - return; - } - MostSpecializedTypeArgsMapTy TyArgMap = State->get<MostSpecializedTypeArgsMap>(); for (MostSpecializedTypeArgsMapTy::iterator I = TyArgMap.begin(), diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp index 696cf39473d54..3f89c33cde8f4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -100,9 +100,6 @@ void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, CheckerContext &Ctx) const { - if (!SymReaper.hasDeadSymbols()) - return; - ProgramStateRef State = Ctx.getState(); const auto &Requests = State->get<RequestMap>(); if (Requests.isEmpty()) diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index cc29895e6975d..f5c7d52f4e1a5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -16,6 +16,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -29,6 +30,7 @@ namespace { class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, check::DeadSymbols, + check::PointerEscape, eval::Assume> { mutable std::unique_ptr<BugType> BT; @@ -58,6 +60,10 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, void checkPreStmt(const CallExpr *S, CheckerContext &C) const; void checkPostStmt(const CallExpr *S, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const; void printState(raw_ostream &Out, ProgramStateRef State, @@ -570,6 +576,44 @@ void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, C.addTransition(State, N); } +ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { + // FIXME: This branch doesn't make any sense at all, but it is an overfitted + // replacement for a previous overfitted code that was making even less sense. + if (!Call || Call->getDecl()) + return State; + + for (auto I : State->get<AllocatedData>()) { + SymbolRef Sym = I.first; + if (Escaped.count(Sym)) + State = State->remove<AllocatedData>(Sym); + + // This checker is special. Most checkers in fact only track symbols of + // SymbolConjured type, eg. symbols returned from functions such as + // malloc(). This checker tracks symbols returned as out-parameters. + // + // When a function is evaluated conservatively, the out-parameter's pointee + // base region gets invalidated with a SymbolConjured. If the base region is + // larger than the region we're interested in, the value we're interested in + // would be SymbolDerived based on that SymbolConjured. However, such + // SymbolDerived will never be listed in the Escaped set when the base + // region is invalidated because ExprEngine doesn't know which symbols + // were derived from a given symbol, while there can be infinitely many + // valid symbols derived from any given symbol. + // + // Hence the extra boilerplate: remove the derived symbol when its parent + // symbol escapes. + // + if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) { + SymbolRef ParentSym = SD->getParentSymbol(); + if (Escaped.count(ParentSym)) + State = State->remove<AllocatedData>(Sym); + } + } + return State; +} + std::shared_ptr<PathDiagnosticPiece> MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 07483cd471473..c2b4e130f345c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -2345,13 +2345,11 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { - if (!SymReaper.hasDeadSymbols()) - return; - ProgramStateRef state = C.getState(); - RegionStateTy RS = state->get<RegionState>(); + RegionStateTy OldRS = state->get<RegionState>(); RegionStateTy::Factory &F = state->get_context<RegionState>(); + RegionStateTy RS = OldRS; SmallVector<SymbolRef, 2> Errors; for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { if (SymReaper.isDead(I->first)) { @@ -2359,10 +2357,18 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, Errors.push_back(I->first); // Remove the dead symbol from the map. RS = F.remove(RS, I->first); - } } + if (RS == OldRS) { + // We shouldn't have touched other maps yet. + assert(state->get<ReallocPairs>() == + C.getState()->get<ReallocPairs>()); + assert(state->get<FreeReturnValue>() == + C.getState()->get<FreeReturnValue>()); + return; + } + // Cleanup the Realloc Pairs Map. ReallocPairsTy RP = state->get<ReallocPairs>(); for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 6345c6aac5d2d..eae0a974ce8f6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -446,9 +446,6 @@ void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg, /// Cleaning up the program state. void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { - if (!SR.hasDeadSymbols()) - return; - ProgramStateRef State = C.getState(); NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); for (NullabilityMapTy::iterator I = Nullabilities.begin(), diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index f2f01566c07c8..a8e75cdf5e404 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -1442,20 +1442,18 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, SmallVector<SymbolRef, 10> Leaked; // Update counts from autorelease pools - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - SymbolRef Sym = *I; - if (const RefVal *T = B.lookup(Sym)){ - // Use the symbol as the tag. - // FIXME: This might not be as unique as we would like. + for (const auto &I: state->get<RefBindings>()) { + SymbolRef Sym = I.first; + if (SymReaper.isDead(Sym)) { static CheckerProgramPointTag Tag(this, "DeadSymbolAutorelease"); - state = handleAutoreleaseCounts(state, Pred, &Tag, C, Sym, *T); + const RefVal &V = I.second; + state = handleAutoreleaseCounts(state, Pred, &Tag, C, Sym, V); if (!state) return; // Fetch the new reference count from the state, and use it to handle // this symbol. - state = handleSymbolDeath(state, *I, *getRefBinding(state, Sym), Leaked); + state = handleSymbolDeath(state, Sym, *getRefBinding(state, Sym), Leaked); } } diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 97ee9580c2b20..93a37aaf52208 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -31,7 +31,7 @@ static bool isNumericLiteralExpression(const Expr *E) { /// If type represents a pointer to CXXRecordDecl, /// and is not a typedef, return the decl name. /// Otherwise, return the serialization of type. -static StringRef getPrettyTypeName(QualType QT) { +static std::string getPrettyTypeName(QualType QT) { QualType PT = QT->getPointeeType(); if (!PT.isNull() && !QT->getAs<TypedefType>()) if (const auto *RD = PT->getAsCXXRecordDecl()) @@ -137,6 +137,8 @@ static void generateDiagnosticsForCallLike( } else { os << "function call"; } + } else if (const auto *NE = dyn_cast<CXXNewExpr>(S)){ + os << "Operator new"; } else { assert(isa<ObjCMessageExpr>(S)); CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); @@ -526,7 +528,8 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, os << "that is annotated as CF_RETURNS_NOT_RETAINED"; } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { os << "that is annotated as NS_RETURNS_NOT_RETAINED"; - // TODO: once the patch is ready, insert a case for OS_RETURNS_NOT_RETAINED + } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { + os << "that is annotated as OS_RETURNS_NOT_RETAINED"; } else { if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index d77975559e3f0..b3834110684c2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -383,26 +383,26 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { + ProgramStateRef state = C.getState(); + // TODO: Clean up the state. - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - SymbolRef Sym = *I; - ProgramStateRef state = C.getState(); - const StreamState *SS = state->get<StreamMap>(Sym); - if (!SS) + const StreamMapTy &Map = state->get<StreamMap>(); + for (const auto &I: Map) { + SymbolRef Sym = I.first; + const StreamState &SS = I.second; + if (!SymReaper.isDead(Sym) || !SS.isOpened()) continue; - if (SS->isOpened()) { - ExplodedNode *N = C.generateErrorNode(); - if (N) { - if (!BT_ResourceLeak) - BT_ResourceLeak.reset(new BuiltinBug( - this, "Resource Leak", - "Opened File never closed. Potential Resource leak.")); - C.emitReport(llvm::make_unique<BugReport>( - *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); - } - } + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + if (!BT_ResourceLeak) + BT_ResourceLeak.reset( + new BuiltinBug(this, "Resource Leak", + "Opened File never closed. Potential Resource leak.")); + C.emitReport(llvm::make_unique<BugReport>( + *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); } } diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp index 43bbcd7c6af7b..b45f93b6dde83 100644 --- a/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -193,11 +193,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, // Mark all symbols in the block expr's value live. RSScaner.scan(X); - continue; - } else { - SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index d001f4de9409f..49ddf1ae445bf 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -675,44 +675,35 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, // Process any special transfer function for dead symbols. // A tag to track convenience transitions, which can be removed at cleanup. static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node"); - if (!SymReaper.hasDeadSymbols()) { - // Generate a CleanedNode that has the environment and store cleaned - // up. Since no symbols are dead, we can optimize and not clean out - // the constraint manager. - StmtNodeBuilder Bldr(Pred, Out, *currBldrCtx); - Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, &cleanupTag, K); - - } else { - // Call checkers with the non-cleaned state so that they could query the - // values of the soon to be dead symbols. - ExplodedNodeSet CheckedSet; - getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, - DiagnosticStmt, *this, K); - - // For each node in CheckedSet, generate CleanedNodes that have the - // environment, the store, and the constraints cleaned up but have the - // user-supplied states as the predecessors. - StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); - for (const auto I : CheckedSet) { - ProgramStateRef CheckerState = I->getState(); - - // The constraint manager has not been cleaned up yet, so clean up now. - CheckerState = getConstraintManager().removeDeadBindings(CheckerState, - SymReaper); - - assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && - "Checkers are not allowed to modify the Environment as a part of " - "checkDeadSymbols processing."); - assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && - "Checkers are not allowed to modify the Store as a part of " - "checkDeadSymbols processing."); - - // Create a state based on CleanedState with CheckerState GDM and - // generate a transition to that state. - ProgramStateRef CleanedCheckerSt = + // Call checkers with the non-cleaned state so that they could query the + // values of the soon to be dead symbols. + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, + DiagnosticStmt, *this, K); + + // For each node in CheckedSet, generate CleanedNodes that have the + // environment, the store, and the constraints cleaned up but have the + // user-supplied states as the predecessors. + StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); + for (const auto I : CheckedSet) { + ProgramStateRef CheckerState = I->getState(); + + // The constraint manager has not been cleaned up yet, so clean up now. + CheckerState = + getConstraintManager().removeDeadBindings(CheckerState, SymReaper); + + assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && + "Checkers are not allowed to modify the Environment as a part of " + "checkDeadSymbols processing."); + assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && + "Checkers are not allowed to modify the Store as a part of " + "checkDeadSymbols processing."); + + // Create a state based on CleanedState with CheckerState GDM and + // generate a transition to that state. + ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); - } + Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); } } diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index e8c7bdbde385d..d9b58d0f5185b 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -399,7 +399,7 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State, for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { SymbolRef Sym = I.getKey(); - if (SymReaper.maybeDead(Sym)) { + if (SymReaper.isDead(Sym)) { Changed = true; CR = CRFactory.remove(CR, Sym); } diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 012058886c586..29b6058e36e93 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -2571,24 +2571,9 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, const MemRegion *Base = I.getKey(); // If the cluster has been visited, we know the region has been marked. - if (W.isVisited(Base)) - continue; - - // Remove the dead entry. - B = B.remove(Base); - - if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base)) - SymReaper.maybeDead(SymR->getSymbol()); - - // Mark all non-live symbols that this binding references as dead. - const ClusterBindings &Cluster = I.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - SVal X = CI.getData(); - SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); - } + // Otherwise, remove the dead entry. + if (!W.isVisited(Base)) + B = B.remove(Base); } return StoreRef(B.asStore(), *this); diff --git a/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp index cbaee85beb74e..2595274f2ddec 100644 --- a/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -124,10 +124,8 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, } const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return getDefaultSummary(); - StringRef FName = II->getName(); + StringRef FName = II ? II->getName() : ""; // Strip away preceding '_'. Doing this here will effect all the checks // down below. @@ -304,6 +302,12 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, if (FName == "retain") return getOSSummaryRetainRule(FD); + + if (FName == "free") + return getOSSummaryFreeRule(FD); + + if (MD->getOverloadedOperator() == OO_New) + return getOSSummaryCreateRule(MD); } } @@ -491,9 +495,11 @@ RetainSummaryManager::getSummary(const CallEvent &Call, case CE_CXXConstructor: Summ = getFunctionSummary(cast<CXXConstructorCall>(Call).getDecl()); break; + case CE_CXXAllocator: + Summ = getFunctionSummary(cast<CXXAllocatorCall>(Call).getDecl()); + break; case CE_Block: case CE_CXXDestructor: - case CE_CXXAllocator: // FIXME: These calls are currently unsupported. return getPersistentStopSummary(); case CE_ObjCMessage: { @@ -618,6 +624,14 @@ RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) { /*ThisEff=*/DecRef); } +const RetainSummary * +RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) { + return getPersistentSummary(RetEffect::MakeNoRet(), + /*ReceiverEff=*/DoNothing, + /*DefaultEff=*/DoNothing, + /*ThisEff=*/Dealloc); +} + const RetainSummary * RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) { return getPersistentSummary(RetEffect::MakeOwned(RetEffect::OS)); @@ -664,15 +678,21 @@ RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, return None; } - if (D->hasAttr<CFReturnsRetainedAttr>()) + if (D->hasAttr<CFReturnsRetainedAttr>()) { return RetEffect::MakeOwned(RetEffect::CF); - else if (hasRCAnnotation(D, "rc_ownership_returns_retained")) + } else if (D->hasAttr<OSReturnsRetainedAttr>()) { + return RetEffect::MakeOwned(RetEffect::OS); + } else if (hasRCAnnotation(D, "rc_ownership_returns_retained")) { return RetEffect::MakeOwned(RetEffect::Generalized); + } - if (D->hasAttr<CFReturnsNotRetainedAttr>()) + if (D->hasAttr<CFReturnsNotRetainedAttr>()) { return RetEffect::MakeNotOwned(RetEffect::CF); - else if (hasRCAnnotation(D, "rc_ownership_returns_not_retained")) + } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { + return RetEffect::MakeNotOwned(RetEffect::OS); + } else if (hasRCAnnotation(D, "rc_ownership_returns_not_retained")) { return RetEffect::MakeNotOwned(RetEffect::Generalized); + } return None; } @@ -688,15 +708,16 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, // Effects on the parameters. unsigned parm_idx = 0; - for (FunctionDecl::param_const_iterator pi = FD->param_begin(), + for (auto pi = FD->param_begin(), pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; - if (pd->hasAttr<NSConsumedAttr>()) + if (pd->hasAttr<NSConsumedAttr>()) { Template->addArg(AF, parm_idx, DecRefMsg); - else if (pd->hasAttr<CFConsumedAttr>() || - hasRCAnnotation(pd, "rc_ownership_consumed")) + } else if (pd->hasAttr<CFConsumedAttr>() || + pd->hasAttr<OSConsumedAttr>() || + hasRCAnnotation(pd, "rc_ownership_consumed")) { Template->addArg(AF, parm_idx, DecRef); - else if (pd->hasAttr<CFReturnsRetainedAttr>() || + } else if (pd->hasAttr<CFReturnsRetainedAttr>() || hasRCAnnotation(pd, "rc_ownership_returns_retained")) { QualType PointeeTy = pd->getType()->getPointeeType(); if (!PointeeTy.isNull()) @@ -734,9 +755,9 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, pi=MD->param_begin(), pe=MD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; - if (pd->hasAttr<NSConsumedAttr>()) + if (pd->hasAttr<NSConsumedAttr>()) { Template->addArg(AF, parm_idx, DecRefMsg); - else if (pd->hasAttr<CFConsumedAttr>()) { + } else if (pd->hasAttr<CFConsumedAttr>() || pd->hasAttr<OSConsumedAttr>()) { Template->addArg(AF, parm_idx, DecRef); } else if (pd->hasAttr<CFReturnsRetainedAttr>()) { QualType PointeeTy = pd->getType()->getPointeeType(); diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp index 4129191dd8269..66273f099a38e 100644 --- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -401,7 +401,6 @@ void SymbolReaper::markDependentsLive(SymbolRef sym) { void SymbolReaper::markLive(SymbolRef sym) { TheLiving[sym] = NotProcessed; - TheDead.erase(sym); markDependentsLive(sym); } @@ -426,14 +425,6 @@ void SymbolReaper::markInUse(SymbolRef sym) { MetadataInUse.insert(sym); } -bool SymbolReaper::maybeDead(SymbolRef sym) { - if (isLive(sym)) - return false; - - TheDead.insert(sym); - return true; -} - bool SymbolReaper::isLiveRegion(const MemRegion *MR) { if (RegionRoots.count(MR)) return true; diff --git a/clang/test/Analysis/MisusedMovedObject.cpp b/clang/test/Analysis/MisusedMovedObject.cpp index 42d3608deb823..f7ad266caef2a 100644 --- a/clang/test/Analysis/MisusedMovedObject.cpp +++ b/clang/test/Analysis/MisusedMovedObject.cpp @@ -688,3 +688,25 @@ void reportSuperClass() { c.foo(); // expected-warning {{Method call on a 'moved-from' object 'c'}} expected-note {{Method call on a 'moved-from' object 'c'}} C c2 = c; // no-warning } + +struct Empty {}; + +Empty inlinedCall() { + // Used to warn because region 'e' failed to be cleaned up because no symbols + // have ever died during the analysis and the checkDeadSymbols callback + // was skipped entirely. + Empty e{}; + return e; // no-warning +} + +void checkInlinedCallZombies() { + while (true) + inlinedCall(); +} + +void checkLoopZombies() { + while (true) { + Empty e{}; + Empty f = std::move(e); // no-warning + } +} diff --git a/clang/test/Analysis/keychainAPI.m b/clang/test/Analysis/keychainAPI.m index 1725ce15f0e66..15a3b66b1a384 100644 --- a/clang/test/Analysis/keychainAPI.m +++ b/clang/test/Analysis/keychainAPI.m @@ -212,7 +212,7 @@ int foo(CFTypeRef keychainOrArray, SecProtocolType protocol, if (st == noErr) SecKeychainItemFreeContent(ptr, outData[3]); } - if (length) { // TODO: We do not report a warning here since the symbol is no longer live, but it's not marked as dead. + if (length) { // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}} length++; } return 0; @@ -454,3 +454,15 @@ int radar_19196494() { } return 0; } +int radar_19196494_v2() { + @autoreleasepool { + AuthorizationValue login_password = {}; + OSStatus err = SecKeychainFindGenericPassword(0, 0, "", 0, "", (UInt32 *)&login_password.length, (void**)&login_password.data, 0); + if (!login_password.data) return 0; + cb.SetContextVal(&login_password); + if (err == noErr) { + SecKeychainItemFreeContent(0, login_password.data); + } + } + return 0; +} diff --git a/clang/test/Analysis/loop-block-counts.c b/clang/test/Analysis/loop-block-counts.c new file mode 100644 index 0000000000000..04a3f747c2ee9 --- /dev/null +++ b/clang/test/Analysis/loop-block-counts.c @@ -0,0 +1,26 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_eval(int); + +void callee(void **p) { + int x; + *p = &x; +} + +void loop() { + void *arr[2]; + for (int i = 0; i < 2; ++i) + callee(&arr[i]); + // FIXME: Should be UNKNOWN. + clang_analyzer_eval(arr[0] == arr[1]); // expected-warning{{TRUE}} +} + +void loopWithCall() { + void *arr[2]; + for (int i = 0; i < 2; ++i) { + int x; + arr[i] = &x; + } + // FIXME: Should be UNKNOWN. + clang_analyzer_eval(arr[0] == arr[1]); // expected-warning{{TRUE}} +} diff --git a/clang/test/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp index 46fc169965073..a917bd895050e 100644 --- a/clang/test/Analysis/osobject-retain-release.cpp +++ b/clang/test/Analysis/osobject-retain-release.cpp @@ -2,9 +2,9 @@ struct OSMetaClass; -#define OS_CONSUME __attribute__((annotate("rc_ownership_consumed"))) -#define OS_RETURNS_RETAINED __attribute__((annotate("rc_ownership_returns_retained"))) -#define OS_RETURNS_NOT_RETAINED __attribute__((annotate("rc_ownership_returns_not_retained"))) +#define OS_CONSUME __attribute__((os_consumed)) +#define OS_RETURNS_RETAINED __attribute__((os_returns_retained)) +#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained)) #define OSTypeID(type) (type::metaClass) @@ -14,6 +14,7 @@ struct OSMetaClass; struct OSObject { virtual void retain(); virtual void release() {}; + virtual void free(); virtual ~OSObject(){} unsigned int foo() { return 42; } @@ -23,6 +24,9 @@ struct OSObject { static OSObject *getObject(); static OSObject *GetObject(); + + static void * operator new(unsigned long size); + static const OSMetaClass * const metaClass; }; @@ -62,6 +66,34 @@ struct OSMetaClassBase { static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); }; +void check_free_no_error() { + OSArray *arr = OSArray::withCapacity(10); + arr->retain(); + arr->retain(); + arr->retain(); + arr->free(); +} + +void check_free_use_after_free() { + OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}} + arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}} + arr->free(); // expected-note{{Object released}} + arr->retain(); // expected-warning{{Reference-counted object is used after it is released}} + // expected-note@-1{{Reference-counted object is used after it is released}} +} + +unsigned int check_leak_explicit_new() { + OSArray *arr = new OSArray; // expected-note{{Operator new returns an OSObject of type OSArray with a +1 retain count}} + return arr->getCount(); // expected-note{{Object leaked: allocated object of type OSArray is not referenced later in this execution path and has a retain count of +1}} + // expected-warning@-1{{Potential leak of an object of type OSArray}} +} + +unsigned int check_leak_factory() { + OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}} + return arr->getCount(); // expected-note{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}} + // expected-warning@-1{{Potential leak of an object stored into 'arr'}} +} + void check_get_object() { OSObject::getObject(); } diff --git a/clang/test/Analysis/pr22954.c b/clang/test/Analysis/pr22954.c index c58a8aa714b6b..6d5b04417a1ee 100644 --- a/clang/test/Analysis/pr22954.c +++ b/clang/test/Analysis/pr22954.c @@ -585,7 +585,7 @@ int f28(int i, int j, int k, int l) { m28[j].s3[k] = 1; struct ll * l28 = (struct ll*)(&m28[1]); l28->s1[l] = 2; - char input[] = {'a', 'b', 'c', 'd'}; + char input[] = {'a', 'b', 'c', 'd'}; // expected-warning{{Potential leak of memory pointed to by field 's4'}} memcpy(l28->s1, input, 4); clang_analyzer_eval(m28[0].s3[0] == 1); // expected-warning{{UNKNOWN}} clang_analyzer_eval(m28[0].s3[1] == 1); // expected-warning{{UNKNOWN}} diff --git a/clang/test/Analysis/retain-release-cpp-classes.cpp b/clang/test/Analysis/retain-release-cpp-classes.cpp new file mode 100644 index 0000000000000..9ed1c0b3b5672 --- /dev/null +++ b/clang/test/Analysis/retain-release-cpp-classes.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-output=text -verify %s + +// expected-no-diagnostics + +typedef void *CFTypeRef; +typedef struct _CFURLCacheRef *CFURLCacheRef; + +CFTypeRef CustomCFRetain(CFTypeRef); +void invalidate(void *); +struct S1 { + CFTypeRef s; + CFTypeRef returnFieldAtPlus0() { + return s; + } +}; +struct S2 { + S1 *s1; +}; +void foo(S1 *s1) { + invalidate(s1); + S2 s2; + s2.s1 = s1; + CustomCFRetain(s1->returnFieldAtPlus0()); + + // Definitely no leak end-of-path note here. The retained pointer + // is still accessible through s1 and s2. + ((void) 0); // no-warning + + // FIXME: Ideally we need to warn after this invalidate(). The per-function + // retain-release contract is violated: the programmer should release + // the symbol after it was retained, within the same function. + invalidate(&s2); +} diff --git a/clang/test/Analysis/self-assign.cpp b/clang/test/Analysis/self-assign.cpp index 8597e9dfe7c9e..ca28c534f1e25 100644 --- a/clang/test/Analysis/self-assign.cpp +++ b/clang/test/Analysis/self-assign.cpp @@ -32,13 +32,14 @@ StringUsed& StringUsed::operator=(const StringUsed &rhs) { // expected-note{{Ass clang_analyzer_eval(*this == rhs); // expected-warning{{TRUE}} expected-warning{{UNKNOWN}} expected-note{{TRUE}} expected-note{{UNKNOWN}} free(str); // expected-note{{Memory is released}} str = strdup(rhs.str); // expected-warning{{Use of memory after it is freed}} expected-note{{Use of memory after it is freed}} +// expected-note@-1{{Memory is allocated}} return *this; } StringUsed& StringUsed::operator=(StringUsed &&rhs) { // expected-note{{Assuming rhs == *this}} expected-note{{Assuming rhs != *this}} clang_analyzer_eval(*this == rhs); // expected-warning{{TRUE}} expected-warning{{UNKNOWN}} expected-note{{TRUE}} expected-note{{UNKNOWN}} str = rhs.str; - rhs.str = nullptr; // FIXME: An improved leak checker should warn here + rhs.str = nullptr; // expected-warning{{Potential memory leak}} expected-note{{Potential memory leak}} return *this; } @@ -83,7 +84,7 @@ StringUnused::operator const char*() const { int main() { StringUsed s1 ("test"), s2; - s2 = s1; - s2 = std::move(s1); + s2 = s1; // expected-note{{Calling copy assignment operator for 'StringUsed'}} // expected-note{{Returned allocated memory}} + s2 = std::move(s1); // expected-note{{Calling move assignment operator for 'StringUsed'}} return 0; } diff --git a/clang/test/Analysis/simple-stream-checks.c b/clang/test/Analysis/simple-stream-checks.c index ca1c781575470..f37a7039f5e3b 100644 --- a/clang/test/Analysis/simple-stream-checks.c +++ b/clang/test/Analysis/simple-stream-checks.c @@ -89,3 +89,8 @@ void testPassToSystemHeaderFunctionIndirectly() { fs.p = fopen("myfile.txt", "w"); fakeSystemHeaderCall(&fs); // invalidates fs, making fs.p unreachable } // no-warning + +void testOverwrite() { + FILE *fp = fopen("myfile.txt", "w"); + fp = 0; +} // expected-warning {{Opened file is never closed; potential resource leak}} diff --git a/clang/test/Analysis/unions.cpp b/clang/test/Analysis/unions.cpp index 0713bc0eb8335..618d4c314aa34 100644 --- a/clang/test/Analysis/unions.cpp +++ b/clang/test/Analysis/unions.cpp @@ -90,9 +90,8 @@ namespace PR17596 { char str[] = "abc"; vv.s = str; - // FIXME: This is a leak of uu.s. uu = vv; - } + } // expected-warning{{leak}} void testIndirectInvalidation() { IntOrString uu; diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 73ab43867f13f..0d7bbf8ec882d 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -3,7 +3,7 @@ // The number of supported attributes should never go down! // Swift has 5 additional attributes that are not present in upstream LLVM. -// CHECK: #pragma clang attribute supports 135 attributes: +// CHECK: #pragma clang attribute supports 138 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -86,6 +86,9 @@ // CHECK-NEXT: NoThreadSafetyAnalysis (SubjectMatchRule_function) // CHECK-NEXT: NoThrow (SubjectMatchRule_function) // CHECK-NEXT: NotTailCalled (SubjectMatchRule_function) +// CHECK-NEXT: OSConsumed (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: OSReturnsNotRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) +// CHECK-NEXT: OSReturnsRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) // CHECK-NEXT: ObjCBoxable (SubjectMatchRule_record) // CHECK-NEXT: ObjCBridge (SubjectMatchRule_record, SubjectMatchRule_type_alias) // CHECK-NEXT: ObjCBridgeMutable (SubjectMatchRule_record) diff --git a/clang/test/Sema/attr-osobject.cpp b/clang/test/Sema/attr-osobject.cpp new file mode 100644 index 0000000000000..fe9ed6b912b9c --- /dev/null +++ b/clang/test/Sema/attr-osobject.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct S { + __attribute__((os_returns_retained)) S* method_returns_retained() { + return nullptr; + } +}; +__attribute__((os_returns_retained)) S *ret_retained() { + return nullptr; +} + +__attribute__((os_returns_retained)) S ret_retained_value() { // expected-warning{{'os_returns_retained' attribute only applies to functions that return a pointer}} + return {}; +} + +__attribute__((os_returns_not_retained)) S *ret_not_retained() { + return nullptr; +} + +__attribute__((os_returns_not_retained)) S ret_not_retained_value() { // expected-warning{{'os_returns_not_retained' attribute only applies to functions that return a pointer}} + return {}; +} + +void accept_consumed_arg(__attribute__((os_consumed)) S *arg) {} + +void accept_consumed_arg_by_value(__attribute__((os_consumed)) S arg) {} // expected-warning{{os_consumed attribute only applies to pointer parameters}} + +void accept_consumed_arg_no_extra_arg(__attribute__((os_consumed(10))) S *arg) {} // expected-error{{'os_consumed' attribute takes no arguments}} + +struct __attribute__((os_consumed)) NoAttrOnStruct {}; // expected-warning{{'os_consumed' attribute only applies to parameters}} + +__attribute__((os_returns_retained(10))) S* returns_retained_no_extra_arg() { // expected-error{{'os_returns_retained' attribute takes no arguments}} + return nullptr; +} + +struct __attribute__((os_returns_retained)) NoRetainAttrOnStruct {}; // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, and Objective-C properties}} + +__attribute__((os_returns_not_retained(10))) S* os_returns_no_retained_no_extra_args( S *arg) { // expected-error{{'os_returns_not_retained' attribute takes no arguments}} + return nullptr; +} + +struct __attribute__((os_returns_not_retained)) NoNotRetainedAttrOnStruct {}; // expected-warning{{'os_returns_not_retained' attribute only applies to functions, Objective-C methods, and Objective-C properties}} diff --git a/clang/test/Sema/attr-osobject.mm b/clang/test/Sema/attr-osobject.mm new file mode 100644 index 0000000000000..dbd9122a8fa8d --- /dev/null +++ b/clang/test/Sema/attr-osobject.mm @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// expected-no-diagnostics + +struct S {}; + +@interface I + @property (readonly) S* prop __attribute__((os_returns_retained)); + - (S*) generateS __attribute__((os_returns_retained)); + - (void) takeS:(S*) __attribute__((os_consumed)) s; +@end From 241651beca5ba0816422cdc246bfc17f2485a784 Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Fri, 30 Nov 2018 17:53:34 +0000 Subject: [PATCH 344/582] [clang-apply-replacements] Fix missing dependency - clangToolingRefactoring Fixes linker error: Undefined symbols for architecture x86_64: "clang::tooling::AtomicChange::replace(clang::SourceManager const&, clang::SourceLocation, unsigned int, llvm::StringRef)" apple-llvm-split-commit: afd4084ae0b064b6cdb736ff427575ab589a41f8 apple-llvm-split-dir: clang-tools-extra/ --- clang-tools-extra/clang-apply-replacements/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt index 02da0851a72be..53cfd796e35bb 100644 --- a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt +++ b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_library(clangApplyReplacements clangRewrite clangToolingCore clangToolingRefactor + clangToolingRefactoring ) include_directories( From 67dac80a6e6916df58b0acb93e6074e8a3df8a68 Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Thu, 6 Dec 2018 09:29:50 +0000 Subject: [PATCH 345/582] Merge remote-tracking branch 'llvm.org/master' into 'github/upstream-with-swift' # Conflicts: # include/clang/Basic/Attr.td apple-llvm-split-commit: 68571b25294390ce33f70901890a7f8054458cb1 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 4 + clang/include/clang/Basic/AttrDocs.td | 57 +++++ .../clang/Basic/DiagnosticCommonKinds.td | 6 + .../clang/Basic/DiagnosticFrontendKinds.td | 45 ++++ clang/include/clang/Basic/DiagnosticGroups.td | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 49 +--- clang/include/clang/Sema/Sema.h | 16 ++ clang/lib/AST/TypePrinter.cpp | 3 + clang/lib/Basic/Targets/AArch64.cpp | 1 + clang/lib/Basic/Targets/ARM.cpp | 1 + clang/lib/CodeGen/CGAtomic.cpp | 2 +- clang/lib/CodeGen/CGStmt.cpp | 1 - clang/lib/CodeGen/CodeGenFunction.cpp | 2 +- clang/lib/CodeGen/CodeGenModule.cpp | 2 +- .../ObjectFilePCHContainerOperations.cpp | 1 - clang/lib/Driver/ToolChains/Clang.cpp | 1 - clang/lib/Sema/SemaExpr.cpp | 117 +++++++++- clang/lib/Sema/SemaExprMember.cpp | 28 ++- clang/lib/Sema/SemaInit.cpp | 13 ++ clang/lib/Sema/SemaType.cpp | 48 +++- clang/test/CodeGen/swift-call-conv.c | 9 + .../CodeGenCXX/debug-prefix-map-lambda.cpp | 2 +- clang/test/Driver/rewrite-legacy-objc.m | 2 +- clang/test/Frontend/noderef.c | 209 ++++++++++++++++++ clang/test/Frontend/noderef.cpp | 102 +++++++++ clang/test/Frontend/noderef_on_non_pointers.m | 11 + clang/test/Frontend/noderef_templates.cpp | 15 ++ clang/test/Sema/attr-osobject.mm | 10 +- clang/test/Sema/swift-call-conv.c | 7 + 29 files changed, 708 insertions(+), 58 deletions(-) create mode 100644 clang/test/CodeGen/swift-call-conv.c create mode 100644 clang/test/Frontend/noderef.c create mode 100644 clang/test/Frontend/noderef.cpp create mode 100644 clang/test/Frontend/noderef_on_non_pointers.m create mode 100644 clang/test/Frontend/noderef_templates.cpp create mode 100644 clang/test/Sema/swift-call-conv.c diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 0a318421608c8..afcf00d585352 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1934,6 +1934,10 @@ def SwiftVersionedRemoval : Attr { return static_cast<attr::Kind>(getRawKind()); } }]; + +def NoDeref : TypeAttr { + let Spellings = [Clang<"noderef">]; + let Documentation = [NoDerefDocs]; } def ReqdWorkGroupSize : InheritableAttr { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index be056d9e06a42..05d187e4ca1e0 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3659,6 +3659,63 @@ corresponding line within the inlined callee. }]; } +def NoDerefDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``noderef`` attribute causes clang to diagnose dereferences of annotated pointer types. +This is ideally used with pointers that point to special memory which cannot be read +from or written to, but allowing for the pointer to be used in pointer arithmetic. +The following are examples of valid expressions where dereferences are diagnosed: + +.. code-block:: c + + int __attribute__((noderef)) *p; + int x = *p; // warning + + int __attribute__((noderef)) **p2; + x = **p2; // warning + + int * __attribute__((noderef)) *p3; + p = *p3; // warning + + struct S { + int a; + }; + struct S __attribute__((noderef)) *s; + x = s->a; // warning + x = (*s).a; // warning + +Not all dereferences may diagnose a warning if the value directed by the pointer may not be +accessed. The following are examples of valid expressions where may not be diagnosed: + +.. code-block:: c + + int *q; + int __attribute__((noderef)) *p; + q = &*p; + q = *&p; + + struct S { + int a; + }; + struct S __attribute__((noderef)) *s; + p = &s->a; + p = &(*s).a; + +``noderef`` is currently only supported for pointers and arrays and not usable for +references or Objective-C object pointers. + +.. code-block: c++ + + int x = 2; + int __attribute__((noderef)) &y = x; // warning: 'noderef' can only be used on an array or pointer type + +.. code-block: objc + + id __attribute__((noderef)) obj = [NSObject new]; // warning: 'noderef' can only be used on an array or pointer type +}]; +} + def ReinitializesDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index 51fdd06562a66..b4da8bfffae60 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -189,6 +189,12 @@ def err_seh___finally_block : Error< def note_invalid_subexpr_in_const_expr : Note< "subexpression not valid in a constant expression">; +// Sema && Frontend +let CategoryName = "Inline Assembly Issue" in { + def err_asm_invalid_type_in_input : Error< + "invalid type %0 in asm input for constraint '%1'">; +} + // Targets def err_target_unknown_triple : Error< diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index c2da7e6b3c5fa..a605d625471ae 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -249,4 +249,49 @@ def warn_stdlibcxx_not_found : Warning< "include path for stdlibc++ headers not found; pass '-stdlib=libc++' on the " "command line to use the libc++ standard library instead">, InGroup<DiagGroup<"stdlibcxx-not-found">>; + +def err_builtin_needs_feature : Error<"%0 needs target feature %1">; +def err_function_needs_feature : Error< + "always_inline function %1 requires target feature '%2', but would " + "be inlined into function %0 that is compiled without support for '%2'">; + +def err_alias_to_undefined : Error< + "%select{alias|ifunc}0 must point to a defined " + "%select{variable or |}1function">; +def warn_alias_to_weak_alias : Warning< + "%select{alias|ifunc}2 will always resolve to %0 even if weak definition of " + "%1 is overridden">, + InGroup<IgnoredAttributes>; +def err_duplicate_mangled_name : Error< + "definition with same mangled name '%0' as another definition">; +def err_cyclic_alias : Error< + "%select{alias|ifunc}0 definition is part of a cycle">; +def err_ifunc_resolver_return : Error< + "ifunc resolver function must return a pointer">; + +def warn_atomic_op_misaligned : Warning< + "%select{large|misaligned}0 atomic operation may incur " + "significant performance penalty">, InGroup<DiagGroup<"atomic-alignment">>; + +def warn_alias_with_section : Warning< + "%select{alias|ifunc}1 will not be in section '%0' but in the same section " + "as the %select{aliasee|resolver}2">, + InGroup<IgnoredAttributes>; + +let CategoryName = "Instrumentation Issue" in { +def warn_profile_data_out_of_date : Warning< + "profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1" + " mismatched data that will be ignored">, + InGroup<ProfileInstrOutOfDate>; +def warn_profile_data_missing : Warning< + "profile data may be incomplete: of %0 function%s0, %1 %plural{1:has|:have}1" + " no data">, + InGroup<ProfileInstrMissing>, + DefaultIgnore; +def warn_profile_data_unprofiled : Warning< + "no profile data available for file \"%0\"">, + InGroup<ProfileInstrUnprofiled>; + +} // end of instrumentation issue category + } diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index c7c62322c6a4f..ffeb2784d9a8e 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1040,3 +1040,5 @@ def ExperimentalISel : DiagGroup<"experimental-isel">; // A warning group specifically for warnings related to function // multiversioning. def FunctionMultiVersioning : DiagGroup<"function-multiversion">; + +def NoDeref : DiagGroup<"noderef">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2dc769e5dc65e..60897642148a6 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -622,11 +622,6 @@ def err_invalid_cpu_supports : Error<"invalid cpu feature string for builtin">; def err_invalid_cpu_is : Error<"invalid cpu name for builtin">; def err_invalid_cpu_specific_dispatch_value : Error< "invalid option '%0' for %select{cpu_specific|cpu_dispatch}1">; -def err_builtin_needs_feature : Error<"%0 needs target feature %1">; -def err_function_needs_feature - : Error<"always_inline function %1 requires target feature '%2', but would " - "be inlined into function %0 that is compiled without support for " - "'%2'">; def warn_builtin_unknown : Warning<"use of unknown builtin %0">, InGroup<ImplicitFunctionDeclare>, DefaultError; def warn_cstruct_memaccess : Warning< @@ -2886,20 +2881,6 @@ def err_attribute_weakref_without_alias : Error< "weakref declaration of %0 must also have an alias attribute">; def err_alias_not_supported_on_darwin : Error < "aliases are not supported on darwin">; -def err_alias_to_undefined : Error< - "%select{alias|ifunc}0 must point to a defined %select{variable or |}1function">; -def warn_alias_to_weak_alias : Warning< - "%select{alias|ifunc}2 will always resolve to %0 even if weak definition of %1 is overridden">, - InGroup<IgnoredAttributes>; -def warn_alias_with_section : Warning< - "%select{alias|ifunc}1 will not be in section '%0' but in the same section as the %select{aliasee|resolver}2">, - InGroup<IgnoredAttributes>; -def err_duplicate_mangled_name : Error< - "definition with same mangled name '%0' as another definition">; -def err_cyclic_alias : Error< - "%select{alias|ifunc}0 definition is part of a cycle">; -def err_ifunc_resolver_return : Error< - "ifunc resolver function must return a pointer">; def warn_attribute_wrong_decl_type_str : Warning< "%0 attribute only applies to %1">, InGroup<IgnoredAttributes>; def err_attribute_wrong_decl_type_str : Error< @@ -7218,9 +7199,6 @@ def warn_atomic_op_has_invalid_memory_order : Warning< InGroup<DiagGroup<"atomic-memory-ordering">>; def err_atomic_op_has_invalid_synch_scope : Error< "synchronization scope argument to atomic operation is invalid">; -def warn_atomic_op_misaligned : Warning< - "%select{large|misaligned}0 atomic operation may incur " - "significant performance penalty">, InGroup<DiagGroup<"atomic-alignment">>; def warn_atomic_implicit_seq_cst : Warning< "implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary">, InGroup<DiagGroup<"atomic-implicit-seq-cst">>, DefaultIgnore; @@ -7484,8 +7462,6 @@ let CategoryName = "Inline Assembly Issue" in { "invalid input constraint '%0' in asm">; def err_asm_immediate_expected : Error<"constraint '%0' expects " "an integer constant expression">; - def err_asm_invalid_type_in_input : Error< - "invalid type %0 in asm input for constraint '%1'">; def err_asm_tying_incompatible_types : Error< "unsupported inline asm: input with type " "%diff{$ matching output with type $|}0,1">; @@ -9405,22 +9381,6 @@ def warn_not_a_doxygen_trailing_member_comment : Warning< "not a Doxygen trailing comment">, InGroup<Documentation>, DefaultIgnore; } // end of documentation issue category -let CategoryName = "Instrumentation Issue" in { -def warn_profile_data_out_of_date : Warning< - "profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1" - " mismatched data that will be ignored">, - InGroup<ProfileInstrOutOfDate>; -def warn_profile_data_missing : Warning< - "profile data may be incomplete: of %0 function%s0, %1 %plural{1:has|:have}1" - " no data">, - InGroup<ProfileInstrMissing>, - DefaultIgnore; -def warn_profile_data_unprofiled : Warning< - "no profile data available for file \"%0\"">, - InGroup<ProfileInstrUnprofiled>; - -} // end of instrumentation issue category - let CategoryName = "Nullability Issue" in { def warn_mismatched_nullability_attr : Warning< @@ -9635,4 +9595,13 @@ def err_std_compare_type_not_supported : Error< "member '%2' is missing|" "the type is not trivially copyable|" "the type does not have the expected form}1">; + +def warn_dereference_of_noderef_type : Warning< + "dereferencing %0; was declared with a 'noderef' type">, InGroup<NoDeref>; +def warn_dereference_of_noderef_type_no_decl : Warning< + "dereferencing expression marked as 'noderef'">, InGroup<NoDeref>; +def warn_noderef_on_non_pointer_or_array : Warning< + "'noderef' can only be used on an array or pointer type">, InGroup<IgnoredAttributes>; +def warn_noderef_to_dereferenceable_pointer : Warning< + "casting to dereferenceable pointer removes 'noderef' attribute">, InGroup<NoDeref>; } // end of sema component. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5a424f1a18573..022f55114470a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1006,6 +1006,8 @@ class Sema { /// expressions for which we have deferred checking the destructor. SmallVector<CXXBindTemporaryExpr *, 8> DelayedDecltypeBinds; + llvm::SmallPtrSet<const Expr *, 8> PossibleDerefs; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -1039,6 +1041,9 @@ class Sema { /// A stack of expression evaluation contexts. SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts; + /// Emit a warning for all pending noderef expressions that we recorded. + void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec); + /// Compute the mangling number context for a lambda expression or /// block literal. /// @@ -1569,6 +1574,17 @@ class Sema { IdentifierInfo *AttrName); private: + /// Methods for marking which expressions involve dereferencing a pointer + /// marked with the 'noderef' attribute. Expressions are checked bottom up as + /// they are parsed, meaning that a noderef pointer may not be accessed. For + /// example, in `&*p` where `p` is a noderef pointer, we will first parse the + /// `*p`, but need to check that `address of` is called on it. This requires + /// keeping a container of all pending expressions and checking if the address + /// of them are eventually taken. + void CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E); + void CheckAddressOfNoDeref(const Expr *E); + void CheckMemberAccessOfNoDeref(const MemberExpr *E); + bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser *Diagnoser); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index f3def14972d90..1b1fb91a7d98d 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1520,6 +1520,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::PreserveAll: OS << "preserve_all"; break; + case attr::NoDeref: + OS << "noderef"; + break; } OS << "))"; } diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index e64eb21a49f91..376cba6e45516 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -514,6 +514,7 @@ WindowsARM64TargetInfo::checkCallingConvention(CallingConv CC) const { case CC_OpenCLKernel: case CC_PreserveMost: case CC_PreserveAll: + case CC_Swift: case CC_Win64: return CCCR_OK; default: diff --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp index 5345d1f8e9de5..cb202eac98588 100644 --- a/clang/lib/Basic/Targets/ARM.cpp +++ b/clang/lib/Basic/Targets/ARM.cpp @@ -996,6 +996,7 @@ WindowsARMTargetInfo::checkCallingConvention(CallingConv CC) const { case CC_OpenCLKernel: case CC_PreserveMost: case CC_PreserveAll: + case CC_Swift: return CCCR_OK; default: return CCCR_Warning; diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp index 9379c835d3dcf..24056a449def4 100644 --- a/clang/lib/CodeGen/CGAtomic.cpp +++ b/clang/lib/CodeGen/CGAtomic.cpp @@ -18,7 +18,7 @@ #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/ADT/DenseMap.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Intrinsics.h" diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index caf68913819de..bc7a18af1e5c0 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -19,7 +19,6 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Basic/TargetInfo.h" -#include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/DataLayout.h" diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 600640f78c199..7c7ba9d0c124a 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -31,7 +31,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/Frontend/CodeGenOptions.h" -#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Intrinsics.h" diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 9ebd86a98193f..81304493a3a61 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -43,7 +43,7 @@ #include "clang/Basic/Version.h" #include "clang/CodeGen/ConstantInitBuilder.h" #include "clang/Frontend/CodeGenOptions.h" -#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Analysis/TargetLibraryInfo.h" diff --git a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index 5d97807316b32..f707e1ea9e9f4 100644 --- a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -21,7 +21,6 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" -#include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/StringRef.h" #include "llvm/Bitcode/BitstreamReader.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index e5bea63fe8260..f9146403fed07 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -2828,7 +2828,6 @@ static void RenderObjCOptions(const ToolChain &TC, const Driver &D, // When ObjectiveC legacy runtime is in effect on MacOSX, turn on the option // to do Array/Dictionary subscripting by default. if (Arch == llvm::Triple::x86 && T.isMacOSX() && - !T.isMacOSXVersionLT(10, 7) && Runtime.getKind() == ObjCRuntime::FragileMacOSX && Runtime.isNeXTFamily()) CmdArgs.push_back("-fobjc-subscripting-legacy-runtime"); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 05ec9155e0f12..123c5a12372dd 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4288,7 +4288,57 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc, return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, idx); } - return CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + + if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get())) + CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get())); + + return Res; +} + +void Sema::CheckAddressOfNoDeref(const Expr *E) { + ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back(); + const Expr *StrippedExpr = E->IgnoreParenImpCasts(); + + // For expressions like `&(*s).b`, the base is recorded and what should be + // checked. + const MemberExpr *Member = nullptr; + while ((Member = dyn_cast<MemberExpr>(StrippedExpr)) && !Member->isArrow()) + StrippedExpr = Member->getBase()->IgnoreParenImpCasts(); + + LastRecord.PossibleDerefs.erase(StrippedExpr); +} + +void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) { + QualType ResultTy = E->getType(); + ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back(); + + // Bail if the element is an array since it is not memory access. + if (isa<ArrayType>(ResultTy)) + return; + + if (ResultTy->hasAttr(attr::NoDeref)) { + LastRecord.PossibleDerefs.insert(E); + return; + } + + // Check if the base type is a pointer to a member access of a struct + // marked with noderef. + const Expr *Base = E->getBase(); + QualType BaseTy = Base->getType(); + if (!(isa<ArrayType>(BaseTy) || isa<PointerType>(BaseTy))) + // Not a pointer access + return; + + const MemberExpr *Member = nullptr; + while ((Member = dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) && + Member->isArrow()) + Base = Member->getBase(); + + if (const auto *Ptr = dyn_cast<PointerType>(Base->getType())) { + if (Ptr->getPointeeType()->hasAttr(attr::NoDeref)) + LastRecord.PossibleDerefs.insert(E); + } } ExprResult Sema::ActOnOMPArraySectionExpr(Expr *Base, SourceLocation LBLoc, @@ -8157,6 +8207,17 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, ExprResult LocalRHS = CallerRHS; ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS; + if (const auto *LHSPtrType = LHSType->getAs<PointerType>()) { + if (const auto *RHSPtrType = RHS.get()->getType()->getAs<PointerType>()) { + if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) && + !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { + Diag(RHS.get()->getExprLoc(), + diag::warn_noderef_to_dereferenceable_pointer) + << RHS.get()->getSourceRange(); + } + } + } + if (getLangOpts().CPlusPlus) { if (!LHSType->isRecordType() && !LHSType->isAtomicType()) { // C++ 5.17p3: If the left operand is not of class type, the @@ -8287,6 +8348,7 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (ConvertRHS) RHS = ImpCastExprToType(E, Ty, Kind); } + return result; } @@ -12835,6 +12897,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, break; case UO_AddrOf: resultType = CheckAddressOfOperand(Input, OpLoc); + CheckAddressOfNoDeref(InputExpr); RecordModifiableNonNullParam(*this, InputExpr); break; case UO_Deref: { @@ -12999,6 +13062,11 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, auto *UO = new (Context) UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow); + + if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && + !isa<ArrayType>(UO->getType().getDesugaredType(Context))) + ExprEvalContexts.back().PossibleDerefs.insert(UO); + // Convert the result back to a half vector. if (ConvertHalfVec) return convertVector(UO, Context.HalfTy, *this); @@ -14365,6 +14433,51 @@ Sema::PushExpressionEvaluationContext( PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); } +namespace { + +const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) { + PossibleDeref = PossibleDeref->IgnoreParenImpCasts(); + if (const auto *E = dyn_cast<UnaryOperator>(PossibleDeref)) { + if (E->getOpcode() == UO_Deref) + return CheckPossibleDeref(S, E->getSubExpr()); + } else if (const auto *E = dyn_cast<ArraySubscriptExpr>(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto *E = dyn_cast<MemberExpr>(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto E = dyn_cast<DeclRefExpr>(PossibleDeref)) { + QualType Inner; + QualType Ty = E->getType(); + if (const auto *Ptr = Ty->getAs<PointerType>()) + Inner = Ptr->getPointeeType(); + else if (const auto *Arr = S.Context.getAsArrayType(Ty)) + Inner = Arr->getElementType(); + else + return nullptr; + + if (Inner->hasAttr(attr::NoDeref)) + return E; + } + return nullptr; +} + +} // namespace + +void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) { + for (const Expr *E : Rec.PossibleDerefs) { + const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E); + if (DeclRef) { + const ValueDecl *Decl = DeclRef->getDecl(); + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) + << Decl->getName() << E->getSourceRange(); + Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName(); + } else { + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl) + << E->getSourceRange(); + } + } + Rec.PossibleDerefs.clear(); +} + void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); unsigned NumTypos = Rec.NumTypos; @@ -14404,6 +14517,8 @@ void Sema::PopExpressionEvaluationContext() { } } + WarnOnPendingNoDerefs(Rec); + // When are coming out of an unevaluated context, clear out any // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 040b2e36eb907..b2b21ba9eefa0 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1708,9 +1708,31 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, } ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl}; - return BuildMemberReferenceExpr(Base, Base->getType(), OpLoc, IsArrow, SS, - TemplateKWLoc, FirstQualifierInScope, - NameInfo, TemplateArgs, S, &ExtraArgs); + ExprResult Res = BuildMemberReferenceExpr( + Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, + FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs); + + if (!Res.isInvalid() && isa<MemberExpr>(Res.get())) + CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get())); + + return Res; +} + +void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) { + QualType ResultTy = E->getType(); + + // Do not warn on member accesses to arrays since this returns an array + // lvalue and does not actually dereference memory. + if (isa<ArrayType>(ResultTy)) + return; + + if (E->isArrow()) { + if (const auto *Ptr = dyn_cast<PointerType>( + E->getBase()->getType().getDesugaredType(Context))) { + if (Ptr->getPointeeType()->hasAttr(attr::NoDeref)) + ExprEvalContexts.back().PossibleDerefs.insert(E); + } + } } ExprResult diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 1294606008436..b5c387b169aa7 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7687,6 +7687,18 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_ConversionSequence: case SK_ConversionSequenceNoNarrowing: { + if (const auto *FromPtrType = + CurInit.get()->getType()->getAs<PointerType>()) { + if (const auto *ToPtrType = Step->Type->getAs<PointerType>()) { + if (FromPtrType->getPointeeType()->hasAttr(attr::NoDeref) && + !ToPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { + S.Diag(CurInit.get()->getExprLoc(), + diag::warn_noderef_to_dereferenceable_pointer) + << CurInit.get()->getSourceRange(); + } + } + } + Sema::CheckedConversionKind CCK = Kind.isCStyleCast()? Sema::CCK_CStyleCast : Kind.isFunctionalCast()? Sema::CCK_FunctionalCast @@ -7853,6 +7865,7 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_CAssignment: { QualType SourceType = CurInit.get()->getType(); + // Save off the initial CurInit in case we need to emit a diagnostic ExprResult InitialCurInit = CurInit; ExprResult Result = CurInit; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index b82706381ea86..d18854bc66923 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -183,11 +183,15 @@ namespace { SmallVector<TypeAttrPair, 8> AttrsForTypes; bool AttrsForTypesSorted = true; + /// Flag to indicate we parsed a noderef attribute. This is used for + /// validating that noderef was used on a pointer or array. + bool parsedNoDeref; + public: TypeProcessingState(Sema &sema, Declarator &declarator) - : sema(sema), declarator(declarator), - chunkIndex(declarator.getNumTypeObjects()), - trivial(true), hasSavedAttrs(false) {} + : sema(sema), declarator(declarator), + chunkIndex(declarator.getNumTypeObjects()), trivial(true), + hasSavedAttrs(false), parsedNoDeref(false) {} Sema &getSema() const { return sema; @@ -278,6 +282,10 @@ namespace { llvm_unreachable("no Attr* for AttributedType*"); } + void setParsedNoDeref(bool parsed) { parsedNoDeref = parsed; } + + bool didParseNoDeref() const { return parsedNoDeref; } + ~TypeProcessingState() { if (trivial) return; @@ -3877,6 +3885,11 @@ static bool hasOuterPointerLikeChunk(const Declarator &D, unsigned endIndex) { return false; } +static bool IsNoDerefableChunk(DeclaratorChunk Chunk) { + return (Chunk.Kind == DeclaratorChunk::Pointer || + Chunk.Kind == DeclaratorChunk::Array); +} + template<typename AttrT> static AttrT *createSimpleAttr(ASTContext &Ctx, ParsedAttr &Attr) { Attr.setUsedAsTypeAttr(); @@ -4270,6 +4283,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } + bool ExpectNoDerefChunk = + state.getCurrentAttributes().hasAttribute(ParsedAttr::AT_NoDeref); + // Walk the DeclTypeInfo, building the recursive type as we go. // DeclTypeInfos are ordered from the identifier out, which is // opposite of what we want :). @@ -4879,8 +4895,22 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // See if there are any attributes on this declarator chunk. processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs()); + + if (DeclType.Kind != DeclaratorChunk::Paren) { + if (ExpectNoDerefChunk) { + if (!IsNoDerefableChunk(DeclType)) + S.Diag(DeclType.Loc, diag::warn_noderef_on_non_pointer_or_array); + ExpectNoDerefChunk = false; + } + + ExpectNoDerefChunk = state.didParseNoDeref(); + } } + if (ExpectNoDerefChunk) + S.Diag(state.getDeclarator().getBeginLoc(), + diag::warn_noderef_on_non_pointer_or_array); + // GNU warning -Wstrict-prototypes // Warn if a function declaration is without a prototype. // This warning is issued for all kinds of unprototyped function @@ -7307,6 +7337,9 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, // sure we visit every element once. Copy the attributes list, and iterate // over that. ParsedAttributesView AttrsCopy{attrs}; + + state.setParsedNoDeref(false); + for (ParsedAttr &attr : AttrsCopy) { // Skip attributes that were marked to be invalid. @@ -7404,6 +7437,15 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, HandleLifetimeBoundAttr(state, type, attr); break; + case ParsedAttr::AT_NoDeref: { + ASTContext &Ctx = state.getSema().Context; + type = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, attr), + type, type); + attr.setUsedAsTypeAttr(); + state.setParsedNoDeref(true); + break; + } + MS_TYPE_ATTRS_CASELIST: if (!handleMSPointerTypeQualifierAttr(state, attr, type)) attr.setUsedAsTypeAttr(); diff --git a/clang/test/CodeGen/swift-call-conv.c b/clang/test/CodeGen/swift-call-conv.c new file mode 100644 index 0000000000000..31d70b33a4980 --- /dev/null +++ b/clang/test/CodeGen/swift-call-conv.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple aarch64-unknown-windows-msvc -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple thumbv7-unknown-windows-msvc -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -emit-llvm %s -o - | FileCheck %s + +// REQUIRES: aarch64-registered-target,arm-registered-target,x86-registered-target + +void __attribute__((__swiftcall__)) f(void) {} +// CHECK-LABEL: define dso_local swiftcc void @f() + diff --git a/clang/test/CodeGenCXX/debug-prefix-map-lambda.cpp b/clang/test/CodeGenCXX/debug-prefix-map-lambda.cpp index b7f6c53829ba9..f0fb1a312c8be 100644 --- a/clang/test/CodeGenCXX/debug-prefix-map-lambda.cpp +++ b/clang/test/CodeGenCXX/debug-prefix-map-lambda.cpp @@ -4,7 +4,7 @@ template <typename T> void b(T) {} void c() { // CHECK: !DISubprogram(name: "b<(lambda at - // CHECK-SAME: /SOURCE_ROOT/debug-prefix-map-lambda.cpp + // CHECK-SAME: SOURCE_ROOT // CHECK-SAME: [[@LINE+1]]:{{[0-9]+}})>" b([]{}); } diff --git a/clang/test/Driver/rewrite-legacy-objc.m b/clang/test/Driver/rewrite-legacy-objc.m index 37b829e5e5a2b..6461aecfe5d9b 100644 --- a/clang/test/Driver/rewrite-legacy-objc.m +++ b/clang/test/Driver/rewrite-legacy-objc.m @@ -10,4 +10,4 @@ // RUN: %clang -no-canonical-prefixes -target i386-apple-macosx10.6.0 -rewrite-legacy-objc %s -o - -### 2>&1 | \ // RUN: FileCheck -check-prefix=TEST2 %s // TEST1: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" -// TEST2: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" +// TEST2: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" diff --git a/clang/test/Frontend/noderef.c b/clang/test/Frontend/noderef.c new file mode 100644 index 0000000000000..b072b995fcf64 --- /dev/null +++ b/clang/test/Frontend/noderef.c @@ -0,0 +1,209 @@ +// RUN: %clang_cc1 -Wno-unused-value -verify %s + +#define NODEREF __attribute__((noderef)) + +struct S { + int a; + int b; +}; + +struct S2 { + int a[2]; + int NODEREF a2[2]; + int *b; + int NODEREF *b2; + struct S *s; + struct S NODEREF *s2; +}; + +int NODEREF *func(int NODEREF *arg) { // expected-note{{arg declared here}} + int y = *arg; // expected-warning{{dereferencing arg; was declared with a 'noderef' type}} + return arg; +} + +void func2(int x) {} + +int test() { + int NODEREF *p; // expected-note 34 {{p declared here}} + int *p2; + + int x = *p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + x = *((int NODEREF *)p2); // expected-warning{{dereferencing expression marked as 'noderef'}} + + int NODEREF **q; + int *NODEREF *q2; // expected-note 4 {{q2 declared here}} + + // Indirection + x = **q; // expected-warning{{dereferencing expression marked as 'noderef'}} + p2 = *q2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}} + + **q; // expected-warning{{dereferencing expression marked as 'noderef'}} + + p = *&*q; + p = **&q; + q = &**&q; + p = &*p; + p = *&p; + p = &(*p); + p = *(&p); + x = **&p; // expected-warning{{dereferencing expression marked as 'noderef'}} + + *p = 2; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + *q = p; // ok + **q = 2; // expected-warning{{dereferencing expression marked as 'noderef'}} + *q2 = p2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}} + + p = p + 1; + p = &*(p + 1); + + // Struct member access + struct S NODEREF *s; // expected-note 2 {{s declared here}} + x = s->a; // expected-warning{{dereferencing s; was declared with a 'noderef' type}} + x = (*s).b; // expected-warning{{dereferencing s; was declared with a 'noderef' type}} + p = &s->a; + p = &(*s).b; + + // Nested struct access + struct S2 NODEREF *s2_noderef; // expected-note 5 {{s2_noderef declared here}} + p = s2_noderef->a; // ok since result is an array in a struct + p = s2_noderef->a2; // ok + p = s2_noderef->b; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + p = s2_noderef->b2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + s = s2_noderef->s; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + s = s2_noderef->s2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + p = s2_noderef->a + 1; + + struct S2 *s2; + p = s2->a; + p = s2->a2; + p = s2->b; + p = s2->b2; + s = s2->s; + s = s2->s2; + &(*(*s2).s2).b; + + // Subscript access + x = p[1]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + x = q[0][0]; // expected-warning{{dereferencing expression marked as 'noderef'}} + p2 = q2[0]; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}} + p = q[*p]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + x = p[*p]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + + int NODEREF arr[10]; // expected-note 1 {{arr declared here}} + x = arr[1]; // expected-warning{{dereferencing arr; was declared with a 'noderef' type}} + + int NODEREF *(arr2[10]); + int NODEREF *elem = *arr2; + + int NODEREF(*arr3)[10]; + elem = *arr3; + + // Combinations between indirection, subscript, and member access + struct S2 NODEREF *s2_arr[10]; + struct S2 NODEREF *s2_arr2[10][10]; + + p = s2_arr[1]->a; + p = s2_arr[1]->b; // expected-warning{{dereferencing expression marked as 'noderef'}} + int **bptr = &s2_arr[1]->b; + + x = s2->s2->a; // expected-warning{{dereferencing expression marked as 'noderef'}} + x = s2_noderef->a[1]; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + p = &s2_noderef->a[1]; + + // Casting to dereferenceable pointer + p2 = p; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}} + p2 = *q; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}} + p2 = q[0]; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}} + s2 = s2_arr[1]; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}} + s2 = s2_arr2[1][1]; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}} + p2 = p, p2 = *q; // expected-warning 2 {{casting to dereferenceable pointer removes 'noderef' attribute}} + + // typedefs + typedef int NODEREF *ptr_t; + ptr_t ptr; // expected-note 2 {{ptr declared here}} + ptr_t *ptr2; + *ptr; // expected-warning{{dereferencing ptr; was declared with a 'noderef' type}} + *ptr2; + **ptr2; // expected-warning{{dereferencing expression marked as 'noderef'}} + + typedef struct S2 NODEREF *s2_ptr_t; + s2_ptr_t s2_ptr; // expected-note 4 {{s2_ptr declared here}} + s2_ptr->a; // ok since result is an array in a struct + s2_ptr->a2; // ok + s2_ptr->b; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}} + s2_ptr->b2; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}} + s2_ptr->s; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}} + s2_ptr->s2; // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}} + s2_ptr->a + 1; + + typedef int(int_t); + typedef int_t NODEREF *(noderef_int_t); + typedef noderef_int_t *noderef_int_nested_t; + noderef_int_nested_t noderef_int_nested_ptr; + *noderef_int_nested_ptr; + **noderef_int_nested_ptr; // expected-warning{{dereferencing expression marked as 'noderef'}} + + typedef int_t *(NODEREF noderef_int2_t); + typedef noderef_int2_t *noderef_int2_nested_t; + noderef_int2_nested_t noderef_int2_nested_ptr; // expected-note{{noderef_int2_nested_ptr declared here}} + *noderef_int2_nested_ptr; // expected-warning{{dereferencing noderef_int2_nested_ptr; was declared with a 'noderef' type}} + + typedef int_t *(noderef_int3_t); + typedef noderef_int3_t(NODEREF(*(noderef_int3_nested_t))); + noderef_int3_nested_t noderef_int3_nested_ptr; // expected-note{{noderef_int3_nested_ptr declared here}} + *noderef_int3_nested_ptr; // expected-warning{{dereferencing noderef_int3_nested_ptr; was declared with a 'noderef' type}} + + // Parentheses + (((*((p))))); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + (*(*(&(p)))); // expected-warning{{dereferencing expression marked as 'noderef'}} + + (p[1]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + (q[0]); // ok + (q[0][0]); // expected-warning{{dereferencing expression marked as 'noderef'}} + (q2[0]); // expected-warning{{dereferencing q2; was declared with a 'noderef' type}} + (q[(*(p))]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + (p[(*(p))]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + + (*(ptr)); // expected-warning{{dereferencing ptr; was declared with a 'noderef' type}} + (*(ptr2)); + (*(*(ptr2))); // expected-warning{{dereferencing expression marked as 'noderef'}} + + // Functions + x = *(func(p)); // expected-warning{{dereferencing expression marked as 'noderef'}} + + // Casting is ok + q = (int NODEREF **)&p; + q = (int NODEREF **)&p2; + q = &p; + q = &p2; + x = s2->s2->a; // expected-warning{{dereferencing expression marked as 'noderef'}} + + // Other expressions + func2(*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2(*p + 1); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2(!*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2((x = *p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2((char)(*p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + + // Other statements + if (*p) {} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + else if (*p) {} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + switch (*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + for (*p; *p; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-2{{dereferencing p; was declared with a 'noderef' type}} + for (*p; *p;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + for (*p;; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + for (; *p; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + for (*p;;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + for (;*p;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + for (;;*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + while (*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + do {} while (*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + return *p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} +} diff --git a/clang/test/Frontend/noderef.cpp b/clang/test/Frontend/noderef.cpp new file mode 100644 index 0000000000000..15eb4e457c201 --- /dev/null +++ b/clang/test/Frontend/noderef.cpp @@ -0,0 +1,102 @@ +// RUN: %clang_cc1 -fblocks -verify %s + +/** + * Test 'noderef' attribute with c++ constructs. + */ + +#define NODEREF __attribute__((noderef)) + +void Normal() { + int NODEREF i; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int NODEREF *i_ptr; // expected-note 2 {{i_ptr declared here}} + int NODEREF **i_ptr2; // ok + int *NODEREF i_ptr3; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int *NODEREF *i_ptr4; // ok + + auto NODEREF *auto_i_ptr = i_ptr; + auto NODEREF auto_i = i; // expected-warning{{'noderef' can only be used on an array or pointer type}} + + struct { + int x; + int y; + } NODEREF *s; + + int __attribute__((noderef(10))) * no_args; // expected-error{{'noderef' attribute takes no arguments}} + + int i2 = *i_ptr; // expected-warning{{dereferencing i_ptr; was declared with a 'noderef' type}} + int &i3 = *i_ptr; // expected-warning{{dereferencing i_ptr; was declared with a 'noderef' type}} + int *i_ptr5 = i_ptr; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}} + int *i_ptr6(i_ptr); // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}} +} + +const int NODEREF *const_i_ptr; +static int NODEREF *static_i_ptr; + +void ParenTypes() { + int NODEREF(*i_ptr); // ok (same as `int NODEREF *`) + int NODEREF *(*i_ptr2); // ok (same as `int NODEREF **`) +} + +// Function declarations +int NODEREF func(); // expected-warning{{'noderef' can only be used on an array or pointer type}} +int NODEREF *func2(); // ok (returning pointer) + +typedef int NODEREF (*func3)(int); // expected-warning{{'noderef' can only be used on an array or pointer type}} +typedef int NODEREF *(*func4)(int); + +void Arrays() { + int NODEREF i_arr[10]; // ok + int NODEREF i_arr2[10][10]; // ok + int NODEREF *i_arr3[10]; // ok + int NODEREF i_arr4[] = {1, 2}; +} + +void ParenArrays() { + int NODEREF(i_ptr[10]); + int NODEREF(i_ptr2[10])[10]; +} + +typedef int NODEREF *(*func5[10])(int); + +// Arguments +void func6(int NODEREF x); // expected-warning{{'noderef' can only be used on an array or pointer type}} +void func7(int NODEREF *x); +void func8() NODEREF; + +void References() { + int x = 2; + int NODEREF &y = x; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int *xp = &x; + int NODEREF *&a = xp; // ok (reference to a NODEREF *) + int *NODEREF &b = xp; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + +void BlockPointers() { + typedef int NODEREF (^IntBlock)(); // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + +class A { +public: + int member; + int NODEREF *member2; + int NODEREF member3; // expected-warning{{'noderef' can only be used on an array or pointer type}} +}; + +void MemberPointer() { + int NODEREF A::*var = &A::member; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + +template <class Ty> +class B { + Ty NODEREF *member; + Ty NODEREF member2; // expected-warning{{'noderef' can only be used on an array or pointer type}} +}; + +void test_lambdas() { + auto l = [](int NODEREF *x){ // expected-note{{x declared here}} + return *x; // expected-warning{{dereferencing x; was declared with a 'noderef' type}} + }; +} + +int NODEREF *glob_ptr; // expected-note{{glob_ptr declared here}} +int glob_int = *glob_ptr; // expected-warning{{dereferencing glob_ptr; was declared with a 'noderef' type}} diff --git a/clang/test/Frontend/noderef_on_non_pointers.m b/clang/test/Frontend/noderef_on_non_pointers.m new file mode 100644 index 0000000000000..ced7627bb5e8d --- /dev/null +++ b/clang/test/Frontend/noderef_on_non_pointers.m @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -verify %s + +#define NODEREF __attribute__((noderef)) + +@interface NSObject ++ (id)new; +@end + +void func() { + id NODEREF obj = [NSObject new]; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} diff --git a/clang/test/Frontend/noderef_templates.cpp b/clang/test/Frontend/noderef_templates.cpp new file mode 100644 index 0000000000000..5fde6efd87c7f --- /dev/null +++ b/clang/test/Frontend/noderef_templates.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -verify %s + +#define NODEREF __attribute__((noderef)) + +template <typename T> +int func(T NODEREF *a) { // expected-note 2 {{a declared here}} + return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}} +} + +void func() { + float NODEREF *f; + int NODEREF *i; + func(f); // expected-note{{in instantiation of function template specialization 'func<float>' requested here}} + func(i); // expected-note{{in instantiation of function template specialization 'func<int>' requested here}} +} diff --git a/clang/test/Sema/attr-osobject.mm b/clang/test/Sema/attr-osobject.mm index dbd9122a8fa8d..adabb6df0fcca 100644 --- a/clang/test/Sema/attr-osobject.mm +++ b/clang/test/Sema/attr-osobject.mm @@ -1,6 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s - -// expected-no-diagnostics +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks %s struct S {}; @@ -9,3 +7,9 @@ @interface I - (S*) generateS __attribute__((os_returns_retained)); - (void) takeS:(S*) __attribute__((os_consumed)) s; @end + +typedef __attribute__((os_returns_retained)) id (^blockType)(); // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, and Objective-C properties}} + +__auto_type b = ^ id (id filter) __attribute__((os_returns_retained)) { // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, and Objective-C properties}} + return filter; +}; diff --git a/clang/test/Sema/swift-call-conv.c b/clang/test/Sema/swift-call-conv.c new file mode 100644 index 0000000000000..755c18f5183f8 --- /dev/null +++ b/clang/test/Sema/swift-call-conv.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -triple aarch64-unknown-windows-msvc -fsyntax-only %s -verify +// RUN: %clang_cc1 -triple thumbv7-unknown-windows-msvc -fsyntax-only %s -verify +// RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -fsyntax-only %s -verify + +// expected-no-diagnostics + +void __attribute__((__swiftcall__)) f(void) {} From 1e5a690778113e615e3b0dc6a56b6357549a8876 Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Thu, 6 Dec 2018 10:23:59 +0000 Subject: [PATCH 346/582] Fix incorrect merge conflict resolution from 68571b25294 apple-llvm-split-commit: 102ed98869e8f99efba3a696679d9bab64341593 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index afcf00d585352..d7b32785bc025 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1934,6 +1934,7 @@ def SwiftVersionedRemoval : Attr { return static_cast<attr::Kind>(getRawKind()); } }]; +} def NoDeref : TypeAttr { let Spellings = [Clang<"noderef">]; From 2b5186e9329d9d0522fe393637c2276f3ee556a9 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Thu, 6 Dec 2018 11:01:09 -0800 Subject: [PATCH 347/582] Fix compiler error due to our fork diverging from llvm.org because of the revert d126e68b9b7b94e3329daba0a2b92d0f6a1ec9d3 apple-llvm-split-commit: 8360ec327fd785b71d3b8bcb6bc4763186b0db67 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/IdentifierTable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index 07f10b62571c4..5de337b0ad627 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -117,7 +117,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { HadMacro(false), IsExtension(false), IsFutureCompatKeyword(false), IsPoisoned(false), IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false), IsFromAST(false), ChangedAfterLoad(false), - FEChangedAfterLoad(false), RevertedTokenID(false), OutOfDate(false), + RevertedTokenID(false), OutOfDate(false), IsModulesImport(false) {} public: From 2bb27356b23a4fdbfb41ef4ad9e837969058f5d9 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Thu, 6 Dec 2018 11:05:17 -0800 Subject: [PATCH 348/582] [ASTDumper] Update to our 'upstream-with-swift'-only code to use new upstream API apple-llvm-split-commit: faeaf7a6af0a728301182c68f4ba1105425c1883 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTDumper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index dea9466e977d7..8cd9f86f91a4f 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -1637,7 +1637,7 @@ void ASTDumper::VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *D) { NodeDumper.dumpName(D); dumpDeclRef(D->getClassInterface()); OS << " "; - dumpLocation(D->getClassInterfaceLoc()); + NodeDumper.dumpLocation(D->getClassInterfaceLoc()); } void ASTDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { From 80b592b23342561d6ca347d9449a23d72aebc747 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 7 Dec 2018 13:27:40 -0800 Subject: [PATCH 349/582] Only warn on private API notes having the wrong case when appropriate (#232) Specifically, if the private API notes are for a top-level private module named "FooKit_Private", that's different from being for a public module named "FooKit". Don't warn in that case. (And rather than check for the presence of a "FooKit_Private", look at the submodules of "FooKit" and see if any of them come from a private module map.) rdar://problem/45705724 apple-llvm-split-commit: 9a57ba2ff3befe95414d49959f7b30b2803ebe7b apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesManager.cpp | 24 +++++++++++++------ .../FrameworkWithActualPrivateModule.h | 1 + .../Modules/module.modulemap | 5 ++++ .../Modules/module.private.modulemap | 5 ++++ ...rkWithActualPrivateModule_Private.apinotes | 1 + ...FrameworkWithActualPrivateModule_Private.h | 2 ++ .../Headers/FrameworkWithWrongCase.h | 1 + .../Modules/module.modulemap | 5 ++++ .../FrameworkWithWrongCase_Private.apinotes | 1 + .../Modules/module.private.modulemap | 1 + .../Inputs/Headers/ModuleWithWrongCase.h | 1 + .../Headers/ModuleWithWrongCasePrivate.h | 2 +- .../ModuleWithWrongCase_Private.apinotes | 1 + .../APINotes/Inputs/Headers/module.modulemap | 4 ++++ .../Inputs/Headers/module.private.modulemap | 2 ++ .../APINotes/case-for-private-apinotes-file.c | 6 +++++ 16 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Headers/FrameworkWithActualPrivateModule.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.private.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Headers/FrameworkWithWrongCase.h create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Modules/module.modulemap create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/PrivateHeaders/FrameworkWithWrongCase_Private.apinotes create mode 100644 clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.private.modulemap create mode 100644 clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase.h create mode 100644 clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase_Private.apinotes diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 65a2082a60a2c..3f2b97ab1ade0 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -245,6 +245,14 @@ static void checkPrivateAPINotesName(DiagnosticsEngine &diags, diags.Report(SourceLocation(), diagID) << module->Name << realFilename; } +/// \returns true if any of \p module's immediate submodules are defined in a +/// private module map +static bool hasPrivateSubmodules(const Module *module) { + return llvm::any_of(module->submodules(), [](const Module *submodule) { + return submodule->ModuleMapIsPrivate; + }); +} + bool APINotesManager::loadCurrentModuleAPINotes( const Module *module, bool lookInModule, @@ -281,7 +289,7 @@ bool APINotesManager::loadCurrentModuleAPINotes( // // Public modules: // - Headers/Foo.apinotes - // - PrivateHeaders/Foo_private.apinotes + // - PrivateHeaders/Foo_private.apinotes (if there are private submodules) // Private modules: // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has // the word "Private" in it in practice) @@ -298,20 +306,22 @@ bool APINotesManager::loadCurrentModuleAPINotes( path.resize(pathLen); } - llvm::sys::path::append(path, "PrivateHeaders"); - if (auto privateAPINotesDir = fileMgr.getDirectory(path)) { - tryAPINotes(privateAPINotesDir, - /*wantPublic=*/module->ModuleMapIsPrivate); + if (module->ModuleMapIsPrivate || hasPrivateSubmodules(module)) { + llvm::sys::path::append(path, "PrivateHeaders"); + if (auto privateAPINotesDir = fileMgr.getDirectory(path)) { + tryAPINotes(privateAPINotesDir, + /*wantPublic=*/module->ModuleMapIsPrivate); + } } } else { // Public modules: // - Foo.apinotes - // - Foo_private.apinotes + // - Foo_private.apinotes (if there are private submodules) // Private modules: // - Bar.apinotes (except that 'Bar' probably already has the word // "Private" in it in practice) tryAPINotes(module->Directory, /*wantPublic=*/true); - if (!module->ModuleMapIsPrivate) + if (!module->ModuleMapIsPrivate && hasPrivateSubmodules(module)) tryAPINotes(module->Directory, /*wantPublic=*/false); } diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Headers/FrameworkWithActualPrivateModule.h b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Headers/FrameworkWithActualPrivateModule.h new file mode 100644 index 0000000000000..7e816819d469a --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Headers/FrameworkWithActualPrivateModule.h @@ -0,0 +1 @@ +extern int FrameworkWithActualPrivateModule; \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..859d723716be2 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module FrameworkWithActualPrivateModule { + umbrella header "FrameworkWithActualPrivateModule.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.private.modulemap b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.private.modulemap new file mode 100644 index 0000000000000..e7fafe3bcbb17 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/Modules/module.private.modulemap @@ -0,0 +1,5 @@ +framework module FrameworkWithActualPrivateModule_Private { + umbrella header "FrameworkWithActualPrivateModule_Private.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.apinotes b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.apinotes new file mode 100644 index 0000000000000..831cf1e93d351 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.apinotes @@ -0,0 +1 @@ +Name: FrameworkWithActualPrivateModule_Private diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.h b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.h new file mode 100644 index 0000000000000..3d1d2d57947a4 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithActualPrivateModule.framework/PrivateHeaders/FrameworkWithActualPrivateModule_Private.h @@ -0,0 +1,2 @@ +#include <FrameworkWithActualPrivateModule/FrameworkWithActualPrivateModule.h> +extern int FrameworkWithActualPrivateModule_Private; \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Headers/FrameworkWithWrongCase.h b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Headers/FrameworkWithWrongCase.h new file mode 100644 index 0000000000000..8def0b1d8b2f9 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Headers/FrameworkWithWrongCase.h @@ -0,0 +1 @@ +extern int FrameworkWithWrongCase; \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..e97d361039a15 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module FrameworkWithWrongCase { + umbrella header "FrameworkWithWrongCase.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/PrivateHeaders/FrameworkWithWrongCase_Private.apinotes b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/PrivateHeaders/FrameworkWithWrongCase_Private.apinotes new file mode 100644 index 0000000000000..ae5447c61e33d --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCase.framework/PrivateHeaders/FrameworkWithWrongCase_Private.apinotes @@ -0,0 +1 @@ +Name: FrameworkWithWrongCase diff --git a/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.private.modulemap b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.private.modulemap new file mode 100644 index 0000000000000..d6ad53cdc7179 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/FrameworkWithWrongCasePrivate.framework/Modules/module.private.modulemap @@ -0,0 +1 @@ +module FrameworkWithWrongCasePrivate.Inner {} diff --git a/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase.h b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase.h new file mode 100644 index 0000000000000..867a15cae9a66 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase.h @@ -0,0 +1 @@ +extern int ModuleWithWrongCase; diff --git a/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h index 867a15cae9a66..aa014296ca7d2 100644 --- a/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h +++ b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h @@ -1 +1 @@ -extern int ModuleWithWrongCase; +extern int ModuleWithWrongCasePrivate; diff --git a/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase_Private.apinotes b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase_Private.apinotes new file mode 100644 index 0000000000000..dc6dc50bab6e6 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCase_Private.apinotes @@ -0,0 +1 @@ +Name: ModuleWithWrongCasePrivate diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index db825537ce5d5..17a785af1d9ea 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -6,6 +6,10 @@ module BrokenTypes { header "BrokenTypes.h" } +module ModuleWithWrongCase { + header "ModuleWithWrongCase.h" +} + module ModuleWithWrongCasePrivate { header "ModuleWithWrongCasePrivate.h" } diff --git a/clang/test/APINotes/Inputs/Headers/module.private.modulemap b/clang/test/APINotes/Inputs/Headers/module.private.modulemap index 82e08aaaa8c67..2ecf322ed18d9 100644 --- a/clang/test/APINotes/Inputs/Headers/module.private.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.private.modulemap @@ -1,3 +1,5 @@ module PrivateLib { header "PrivateLib.h" } + +module ModuleWithWrongCasePrivate.Inner {} diff --git a/clang/test/APINotes/case-for-private-apinotes-file.c b/clang/test/APINotes/case-for-private-apinotes-file.c index e674251935e90..6aff3db54918e 100644 --- a/clang/test/APINotes/case-for-private-apinotes-file.c +++ b/clang/test/APINotes/case-for-private-apinotes-file.c @@ -9,8 +9,14 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fsyntax-only -fmodules -fapinotes-modules -fimplicit-module-maps -fmodules-cache-path=%t -iframework %S/Inputs/Frameworks -isystem %S/Inputs/Headers %s -Wnonportable-private-system-apinotes-path 2>&1 | FileCheck %s +#include <ModuleWithWrongCase.h> #include <ModuleWithWrongCasePrivate.h> +#include <FrameworkWithWrongCase/FrameworkWithWrongCase.h> #include <FrameworkWithWrongCasePrivate/FrameworkWithWrongCasePrivate.h> +#include <FrameworkWithActualPrivateModule/FrameworkWithActualPrivateModule_Private.h> +// CHECK-NOT: warning: // CHECK: warning: private API notes file for module 'ModuleWithWrongCasePrivate' should be named 'ModuleWithWrongCasePrivate_private.apinotes', not 'ModuleWithWrongCasePrivate_Private.apinotes' +// CHECK-NOT: warning: // CHECK: warning: private API notes file for module 'FrameworkWithWrongCasePrivate' should be named 'FrameworkWithWrongCasePrivate_private.apinotes', not 'FrameworkWithWrongCasePrivate_Private.apinotes' +// CHECK-NOT: warning: From 819c30c588cc1c38e096b7e8e9519543cbcf9d23 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Fri, 7 Dec 2018 13:28:40 -0800 Subject: [PATCH 350/582] [APINotes] `RetainCountConvention: none` overrules `arc_cf_audited` (#233) rdar://problem/39619530 apple-llvm-split-commit: e2077f789c216648636feee2ff503678c010f909 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 12 ++++++++--- .../Headers/SimpleKit.apinotes | 6 ++++++ .../SimpleKit.framework/Headers/SimpleKit.h | 6 ++++++ clang/test/APINotes/retain-count-convention.m | 20 ++++++++++++++++++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index b2cc19da1b6f7..ab21fdbfe4c39 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -215,7 +215,8 @@ static void handleAPINotedRetainCountAttribute(Sema &S, Decl *D, return isa<CFReturnsRetainedAttr>(next) || isa<CFReturnsNotRetainedAttr>(next) || isa<NSReturnsRetainedAttr>(next) || - isa<NSReturnsNotRetainedAttr>(next); + isa<NSReturnsNotRetainedAttr>(next) || + isa<CFAuditedTransferAttr>(next); }); }); } @@ -227,8 +228,13 @@ static void handleAPINotedRetainCountConvention( return; switch (convention.getValue()) { case api_notes::RetainCountConventionKind::None: - handleAPINotedRetainCountAttribute(S, D, /*shouldAddAttribute*/false, - metadata); + if (isa<FunctionDecl>(D)) { + handleAPINotedRetainCountAttribute<CFUnknownTransferAttr>( + S, D, /*shouldAddAttribute*/true, metadata); + } else { + handleAPINotedRetainCountAttribute(S, D, /*shouldAddAttribute*/false, + metadata); + } break; case api_notes::RetainCountConventionKind::CFReturnsRetained: handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>( diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes index f66038b8ec1ff..c584ea0d76724 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes @@ -31,6 +31,12 @@ Functions: Parameters: - Position: 0 RetainCountConvention: CFReturnsNotRetained +- Name: getCFAuditedToUnowned_DUMP + RetainCountConvention: CFReturnsNotRetained +- Name: getCFAuditedToOwned_DUMP + RetainCountConvention: CFReturnsRetained +- Name: getCFAuditedToNone_DUMP + RetainCountConvention: none Classes: - Name: MethodTest Methods: diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h index e11adb715c6ac..669e9b9d4d061 100644 --- a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h @@ -17,6 +17,12 @@ int indirectGetCFUnownedToOwned(void **out __attribute__((cf_returns_not_retaine int indirectGetCFOwnedToNone(void **out __attribute__((cf_returns_retained))); int indirectGetCFNoneToOwned(void **out); +#pragma clang arc_cf_code_audited begin +void *getCFAuditedToUnowned_DUMP(void); +void *getCFAuditedToOwned_DUMP(void); +void *getCFAuditedToNone_DUMP(void); +#pragma clang arc_cf_code_audited end + @interface MethodTest - (id)getOwnedToUnowned __attribute__((ns_returns_retained)); - (id)getUnownedToOwned __attribute__((ns_returns_not_retained)); diff --git a/clang/test/APINotes/retain-count-convention.m b/clang/test/APINotes/retain-count-convention.m index 46fb90caa6932..0c0ad7320f29d 100644 --- a/clang/test/APINotes/retain-count-convention.m +++ b/clang/test/APINotes/retain-count-convention.m @@ -1,12 +1,13 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fdisable-module-hash -fsyntax-only -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s +// RUN: %clang_cc1 -ast-dump -ast-dump-filter 'DUMP' %t/ModulesCache/SimpleKit.pcm | FileCheck -check-prefix CHECK-DUMP %s #import <SimpleKit/SimpleKit.h> // CHECK: void *getCFOwnedToUnowned() __attribute__((cf_returns_not_retained)); // CHECK: void *getCFUnownedToOwned() __attribute__((cf_returns_retained)); -// CHECK: void *getCFOwnedToNone(); +// CHECK: void *getCFOwnedToNone() __attribute__((cf_unknown_transfer)); // CHECK: id getObjCOwnedToUnowned() __attribute__((ns_returns_not_retained)); // CHECK: id getObjCUnownedToOwned() __attribute__((ns_returns_retained)); // CHECK: int indirectGetCFOwnedToUnowned(void * _Nullable *out __attribute__((cf_returns_not_retained))); @@ -18,3 +19,20 @@ // CHECK: - (id)getOwnedToUnowned __attribute__((ns_returns_not_retained)); // CHECK: - (id)getUnownedToOwned __attribute__((ns_returns_retained)); // CHECK: @end + +// CHECK-DUMP-LABEL: Dumping getCFAuditedToUnowned_DUMP: +// CHECK-DUMP-NEXT: FunctionDecl +// CHECK-DUMP-NEXT: CFReturnsNotRetainedAttr +// CHECK-DUMP-NEXT: CFAuditedTransferAttr +// CHECK-DUMP-NOT: Attr + +// CHECK-DUMP-LABEL: Dumping getCFAuditedToOwned_DUMP: +// CHECK-DUMP-NEXT: FunctionDecl +// CHECK-DUMP-NEXT: CFReturnsRetainedAttr +// CHECK-DUMP-NEXT: CFAuditedTransferAttr +// CHECK-DUMP-NOT: Attr + +// CHECK-DUMP-LABEL: Dumping getCFAuditedToNone_DUMP: +// CHECK-DUMP-NEXT: FunctionDecl +// CHECK-DUMP-NEXT: CFUnknownTransferAttr +// CHECK-DUMP-NOT: Attr From b3abd78b9469563f9d3a1df5663af475dd644ac4 Mon Sep 17 00:00:00 2001 From: Artem Dergachev <artem.dergachev@gmail.com> Date: Mon, 10 Dec 2018 14:01:26 -0800 Subject: [PATCH 351/582] [analyzer] Fix test/Analysis/padding_cpp.cpp after merge conflict. It was broken on upstream-with-swift since a14a8c8e. apple-llvm-split-commit: 79074c703976a5e5b25b724837429f2049d85ccb apple-llvm-split-dir: clang/ --- clang/test/Analysis/padding_cpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/padding_cpp.cpp b/clang/test/Analysis/padding_cpp.cpp index ee49aea0c2bed..965a159a0b4de 100644 --- a/clang/test/Analysis/padding_cpp.cpp +++ b/clang/test/Analysis/padding_cpp.cpp @@ -197,6 +197,6 @@ struct GlobalsForLambda { // no-warning char c2; } G; -// expected-warning@+1{{Excessive padding in 'class (lambda}} +// expected-warning@+1{{Excessive padding in 'class std::function<auto (void) -> void>'}} auto lambda1 = [ c1 = G.c1, i = G.i, c2 = G.c2 ]{}; auto lambda2 = [ i = G.i, c1 = G.c1, c2 = G.c2 ]{}; // no-warning From b50764c6ace8567f1435cd87dab67fa51ace0e21 Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Tue, 11 Dec 2018 11:05:37 +0000 Subject: [PATCH 352/582] Merge remote-tracking branch 'llvm.org/master' into HEAD # Conflicts: # include/clang/Frontend/CompilerInvocation.h apple-llvm-split-commit: 42b1c65575ccb7722728d63a0dd8b4ff29c94c3d apple-llvm-split-dir: clang/ --- .../{Frontend => Basic}/CodeGenOptions.def | 0 .../{Frontend => Basic}/CodeGenOptions.h | 10 +-- .../clang/Frontend/CompilerInvocation.h | 2 +- clang/include/clang/Sema/Sema.h | 6 +- clang/include/clang/module.modulemap | 9 +-- clang/lib/Basic/CMakeLists.txt | 1 + .../{Frontend => Basic}/CodeGenOptions.cpp | 4 +- clang/lib/Basic/Targets/AMDGPU.cpp | 2 +- clang/lib/CodeGen/BackendUtil.cpp | 2 +- clang/lib/CodeGen/CGCXX.cpp | 2 +- clang/lib/CodeGen/CGCall.cpp | 2 +- clang/lib/CodeGen/CGClass.cpp | 2 +- clang/lib/CodeGen/CGDebugInfo.cpp | 2 +- clang/lib/CodeGen/CGDebugInfo.h | 2 +- clang/lib/CodeGen/CGDecl.cpp | 2 +- clang/lib/CodeGen/CGDeclCXX.cpp | 2 +- clang/lib/CodeGen/CGExpr.cpp | 2 +- clang/lib/CodeGen/CGExprCXX.cpp | 2 +- clang/lib/CodeGen/CGExprScalar.cpp | 2 +- clang/lib/CodeGen/CGObjCMac.cpp | 2 +- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 2 +- clang/lib/CodeGen/CGVTables.cpp | 2 +- clang/lib/CodeGen/CodeGenABITypes.cpp | 1 - clang/lib/CodeGen/CodeGenFunction.cpp | 2 +- clang/lib/CodeGen/CodeGenFunction.h | 2 +- clang/lib/CodeGen/CodeGenModule.cpp | 2 +- clang/lib/CodeGen/CodeGenPGO.h | 1 - clang/lib/CodeGen/CodeGenTBAA.cpp | 2 +- clang/lib/CodeGen/CoverageMappingGen.h | 1 - clang/lib/CodeGen/ModuleBuilder.cpp | 2 +- .../ObjectFilePCHContainerOperations.cpp | 2 +- clang/lib/CodeGen/TargetInfo.cpp | 2 +- clang/lib/Frontend/CMakeLists.txt | 1 - clang/lib/Frontend/CompilerInvocation.cpp | 2 +- clang/lib/Sema/SemaDeclCXX.cpp | 6 +- clang/lib/Sema/SemaTemplate.cpp | 68 ++++++++++--------- clang/test/PCH/cxx-static_assert.cpp | 4 +- clang/test/Sema/static-assert.c | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp | 9 +++ clang/test/SemaCXX/static-assert.cpp | 12 +++- 40 files changed, 96 insertions(+), 87 deletions(-) rename clang/include/clang/{Frontend => Basic}/CodeGenOptions.def (100%) rename clang/include/clang/{Frontend => Basic}/CodeGenOptions.h (98%) rename clang/lib/{Frontend => Basic}/CodeGenOptions.cpp (90%) diff --git a/clang/include/clang/Frontend/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def similarity index 100% rename from clang/include/clang/Frontend/CodeGenOptions.def rename to clang/include/clang/Basic/CodeGenOptions.def diff --git a/clang/include/clang/Frontend/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h similarity index 98% rename from clang/include/clang/Frontend/CodeGenOptions.h rename to clang/include/clang/Basic/CodeGenOptions.h index caa7626614a88..5f83915225f79 100644 --- a/clang/include/clang/Frontend/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_FRONTEND_CODEGENOPTIONS_H -#define LLVM_CLANG_FRONTEND_CODEGENOPTIONS_H +#ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H +#define LLVM_CLANG_BASIC_CODEGENOPTIONS_H #include "clang/Basic/DebugInfoOptions.h" #include "clang/Basic/Sanitizers.h" @@ -33,12 +33,12 @@ class CodeGenOptionsBase { public: #define CODEGENOPT(Name, Bits, Default) unsigned Name : Bits; #define ENUM_CODEGENOPT(Name, Type, Bits, Default) -#include "clang/Frontend/CodeGenOptions.def" +#include "clang/Basic/CodeGenOptions.def" protected: #define CODEGENOPT(Name, Bits, Default) #define ENUM_CODEGENOPT(Name, Type, Bits, Default) unsigned Name : Bits; -#include "clang/Frontend/CodeGenOptions.def" +#include "clang/Basic/CodeGenOptions.def" }; /// CodeGenOptions - Track various options which control how the code @@ -288,7 +288,7 @@ class CodeGenOptions : public CodeGenOptionsBase { #define ENUM_CODEGENOPT(Name, Type, Bits, Default) \ Type get##Name() const { return static_cast<Type>(Name); } \ void set##Name(Type Value) { Name = static_cast<unsigned>(Value); } -#include "clang/Frontend/CodeGenOptions.def" +#include "clang/Basic/CodeGenOptions.def" CodeGenOptions(); diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index d6fc35f7916db..b2c6b708a3415 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -11,11 +11,11 @@ #define LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H #include "clang/APINotes/APINotesOptions.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/LangStandard.h" diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 022f55114470a..0b4b0a7bca975 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2891,11 +2891,7 @@ class Sema { /// Find the failed Boolean condition within a given Boolean /// constant expression, and describe it with a string. - /// - /// \param AllowTopLevelCond Whether to allow the result to be the - /// complete top-level condition. - std::pair<Expr *, std::string> - findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond); + std::pair<Expr *, std::string> findFailedBooleanCondition(Expr *Cond); /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any /// non-ArgDependent DiagnoseIfAttrs. diff --git a/clang/include/clang/module.modulemap b/clang/include/clang/module.modulemap index fb929228d16fc..8858f306a5a08 100644 --- a/clang/include/clang/module.modulemap +++ b/clang/include/clang/module.modulemap @@ -47,6 +47,7 @@ module Clang_Basic { textual header "Basic/BuiltinsX86.def" textual header "Basic/BuiltinsX86_64.def" textual header "Basic/BuiltinsXCore.def" + textual header "Basic/CodeGenOptions.def" textual header "Basic/DiagnosticOptions.def" textual header "Basic/Features.def" textual header "Basic/LangOptions.def" @@ -107,14 +108,6 @@ module Clang_Frontend { exclude header "Frontend/PCHContainerOperations.h" } -// Used in clangBasic -module Clang_Frontend_CodeGenOptions { - requires cplusplus - header "Frontend/CodeGenOptions.h" - textual header "Frontend/CodeGenOptions.def" - export * -} - module Clang_FrontendTool { requires cplusplus umbrella "FrontendTool" module * { export * } } module Clang_Index { requires cplusplus umbrella "Index" module * { export * } } module Clang_Lex { requires cplusplus umbrella "Lex" module * { export * } } diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index 3bef58d1dc771..2455aa90ed5c5 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -48,6 +48,7 @@ add_clang_library(clangBasic Attributes.cpp Builtins.cpp CharInfo.cpp + CodeGenOptions.cpp Cuda.cpp Diagnostic.cpp DiagnosticIDs.cpp diff --git a/clang/lib/Frontend/CodeGenOptions.cpp b/clang/lib/Basic/CodeGenOptions.cpp similarity index 90% rename from clang/lib/Frontend/CodeGenOptions.cpp rename to clang/lib/Basic/CodeGenOptions.cpp index 84a39f2d570de..aface1cd4bf91 100644 --- a/clang/lib/Frontend/CodeGenOptions.cpp +++ b/clang/lib/Basic/CodeGenOptions.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/Frontend/CodeGenOptions.h" +#include "clang/Basic/CodeGenOptions.h" #include <string.h> namespace clang { @@ -15,7 +15,7 @@ namespace clang { CodeGenOptions::CodeGenOptions() { #define CODEGENOPT(Name, Bits, Default) Name = Default; #define ENUM_CODEGENOPT(Name, Type, Bits, Default) set##Name(Default); -#include "clang/Frontend/CodeGenOptions.def" +#include "clang/Basic/CodeGenOptions.def" RelocationModel = llvm::Reloc::PIC_; memcpy(CoverageVersion, "402*", 4); diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp index 3851cdacfdd1a..4f17b17ff401d 100644 --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -13,10 +13,10 @@ #include "AMDGPU.h" #include "clang/Basic/Builtins.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/MacroBuilder.h" #include "clang/Basic/TargetBuiltins.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 38d9a5298692d..52fc08de9b6dc 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -8,10 +8,10 @@ //===----------------------------------------------------------------------===// #include "clang/CodeGen/BackendUtil.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetOptions.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/HeaderSearchOptions.h" diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp index 4e524b047a8e3..8b0733fbec3ef 100644 --- a/clang/lib/CodeGen/CGCXX.cpp +++ b/clang/lib/CodeGen/CGCXX.cpp @@ -23,7 +23,7 @@ #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtCXX.h" -#include "clang/Frontend/CodeGenOptions.h" +#include "clang/Basic/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" using namespace clang; using namespace CodeGen; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 2fddc4bd99db0..4757cd2ffae62 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -23,11 +23,11 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/SwiftCallingConv.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Analysis/ValueTracking.h" diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index ade036866653c..28ae5559b2d03 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -21,9 +21,9 @@ #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtCXX.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Metadata.h" #include "llvm/Transforms/Utils/SanitizerStats.h" diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index c556996be09b7..9a6204b5bda0d 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -25,10 +25,10 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/ModuleMap.h" diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index adb8977c2143b..8f29e27e6057c 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -20,8 +20,8 @@ #include "clang/AST/ExternalASTSource.h" #include "clang/AST/Type.h" #include "clang/AST/TypeOrdering.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/SourceLocation.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index b36b328ad47ef..f4fef45a1242c 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -26,10 +26,10 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclOpenMP.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalVariable.h" diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 2884f1387196c..08ceaa99002a5 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -15,7 +15,7 @@ #include "CGCXXABI.h" #include "CGObjCRuntime.h" #include "CGOpenMPRuntime.h" -#include "clang/Frontend/CodeGenOptions.h" +#include "clang/Basic/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 17a75c0bb1ec1..3c5eea49d1af4 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -26,7 +26,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/NSAPI.h" -#include "clang/Frontend/CodeGenOptions.h" +#include "clang/Basic/CodeGenOptions.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/DataLayout.h" diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 842ca24bc3acb..2e0d4ca76791d 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -17,8 +17,8 @@ #include "CGDebugInfo.h" #include "CGObjCRuntime.h" #include "ConstantEmitter.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Intrinsics.h" diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 4980f53cc4de3..f53bb33e463a1 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -23,9 +23,9 @@ #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/FixedPoint.h" #include "clang/Basic/TargetInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/Optional.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index eb1b455ec1181..34d649c62275e 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -23,9 +23,9 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtObjC.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SetVector.h" diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index 58aaae692552f..c754541ac121f 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -20,7 +20,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" -#include "clang/Frontend/CodeGenOptions.h" +#include "clang/Basic/CodeGenOptions.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Type.h" diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index 83466f7f0f097..09535900b5468 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -16,9 +16,9 @@ #include "CodeGenModule.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/ConstantInitBuilder.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/Format.h" #include "llvm/Transforms/Utils/Cloning.h" diff --git a/clang/lib/CodeGen/CodeGenABITypes.cpp b/clang/lib/CodeGen/CodeGenABITypes.cpp index c152291b15b93..27f5d53ffe11a 100644 --- a/clang/lib/CodeGen/CodeGenABITypes.cpp +++ b/clang/lib/CodeGen/CodeGenABITypes.cpp @@ -20,7 +20,6 @@ #include "CGRecordLayout.h" #include "CodeGenModule.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/PreprocessorOptions.h" diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index da8f327efa7cc..905e7501e01c1 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -28,9 +28,9 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/Builtins.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 9b802991e70dc..5303f3ccea75e 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -29,9 +29,9 @@ #include "clang/AST/Type.h" #include "clang/Basic/ABI.h" #include "clang/Basic/CapturedStmt.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/TargetInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b80a43b7eb25e..ce4558fac4be3 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -36,13 +36,13 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" #include "clang/CodeGen/ConstantInitBuilder.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" diff --git a/clang/lib/CodeGen/CodeGenPGO.h b/clang/lib/CodeGen/CodeGenPGO.h index 0759e65388b8f..120ab651a4a87 100644 --- a/clang/lib/CodeGen/CodeGenPGO.h +++ b/clang/lib/CodeGen/CodeGenPGO.h @@ -17,7 +17,6 @@ #include "CGBuilder.h" #include "CodeGenModule.h" #include "CodeGenTypes.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ProfileData/InstrProfReader.h" #include <array> #include <memory> diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp index ec48231e5247e..27d39716d22fa 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -20,7 +20,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" -#include "clang/Frontend/CodeGenOptions.h" +#include "clang/Basic/CodeGenOptions.h" #include "llvm/ADT/SmallSet.h" #include "llvm/IR/Constants.h" #include "llvm/IR/LLVMContext.h" diff --git a/clang/lib/CodeGen/CoverageMappingGen.h b/clang/lib/CodeGen/CoverageMappingGen.h index b08ad896d7a5c..c62db096952a9 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.h +++ b/clang/lib/CodeGen/CoverageMappingGen.h @@ -16,7 +16,6 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Lex/PPCallbacks.h" #include "llvm/ADT/DenseMap.h" #include "llvm/IR/GlobalValue.h" diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp index 511cf75d6aa64..1264893ec13f9 100644 --- a/clang/lib/CodeGen/ModuleBuilder.cpp +++ b/clang/lib/CodeGen/ModuleBuilder.cpp @@ -17,9 +17,9 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/LLVMContext.h" diff --git a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index f707e1ea9e9f4..6f00c836f93d2 100644 --- a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -14,10 +14,10 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/BackendUtil.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 0c3b81fabdfdd..ae080f5bbd103 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -19,9 +19,9 @@ #include "CGValue.h" #include "CodeGenFunction.h" #include "clang/AST/RecordLayout.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/SwiftCallingConv.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index 631ac5801891e..10f20fd23440e 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -18,7 +18,6 @@ add_clang_library(clangFrontend ASTUnit.cpp ChainedDiagnosticConsumer.cpp ChainedIncludesSource.cpp - CodeGenOptions.cpp CompilerInstance.cpp CompilerInvocation.cpp CreateInvocationFromCommandLine.cpp diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 6b71da55a3bc2..d19a5fa74837d 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -11,6 +11,7 @@ #include "TestModuleFileExtension.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/CommentOptions.h" #include "clang/Basic/DebugInfoOptions.h" #include "clang/Basic/Diagnostic.h" @@ -28,7 +29,6 @@ #include "clang/Config/config.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" -#include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/CommandLineSourceLoc.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 18495e801c578..59f60b9758d52 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -13916,9 +13916,9 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *InnerCond = nullptr; std::string InnerCondDescription; std::tie(InnerCond, InnerCondDescription) = - findFailedBooleanCondition(Converted.get(), - /*AllowTopLevelCond=*/false); - if (InnerCond) { + findFailedBooleanCondition(Converted.get()); + if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) + && !isa<IntegerLiteral>(InnerCond)) { Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed) << InnerCondDescription << !AssertMessage << Msg.str() << InnerCond->getSourceRange(); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index ed3258c7a9eda..31bcc0e19cb05 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3053,30 +3053,42 @@ static Expr *lookThroughRangesV3Condition(Preprocessor &PP, Expr *Cond) { return Cond; } -// Print a diagnostic for the failing static_assert expression. Defaults to -// pretty-printing the expression. -static void prettyPrintFailedBooleanCondition(llvm::raw_string_ostream &OS, - const Expr *FailedCond, - const PrintingPolicy &Policy) { - const auto *DR = dyn_cast<DeclRefExpr>(FailedCond); - if (DR && DR->getQualifier()) { - // If this is a qualified name, expand the template arguments in nested - // qualifiers. - DR->getQualifier()->print(OS, Policy, true); - // Then print the decl itself. - const ValueDecl *VD = DR->getDecl(); - OS << VD->getName(); - if (const auto *IV = dyn_cast<VarTemplateSpecializationDecl>(VD)) { - // This is a template variable, print the expanded template arguments. - printTemplateArgumentList(OS, IV->getTemplateArgs().asArray(), Policy); +namespace { + +// A PrinterHelper that prints more helpful diagnostics for some sub-expressions +// within failing boolean expression, such as substituting template parameters +// for actual types. +class FailedBooleanConditionPrinterHelper : public PrinterHelper { +public: + explicit FailedBooleanConditionPrinterHelper(const PrintingPolicy &P) + : Policy(P) {} + + bool handledStmt(Stmt *E, raw_ostream &OS) override { + const auto *DR = dyn_cast<DeclRefExpr>(E); + if (DR && DR->getQualifier()) { + // If this is a qualified name, expand the template arguments in nested + // qualifiers. + DR->getQualifier()->print(OS, Policy, true); + // Then print the decl itself. + const ValueDecl *VD = DR->getDecl(); + OS << VD->getName(); + if (const auto *IV = dyn_cast<VarTemplateSpecializationDecl>(VD)) { + // This is a template variable, print the expanded template arguments. + printTemplateArgumentList(OS, IV->getTemplateArgs().asArray(), Policy); + } + return true; } - return; + return false; } - FailedCond->printPretty(OS, nullptr, Policy); -} + +private: + const PrintingPolicy Policy; +}; + +} // end anonymous namespace std::pair<Expr *, std::string> -Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) { +Sema::findFailedBooleanCondition(Expr *Cond) { Cond = lookThroughRangesV3Condition(PP, Cond); // Separate out all of the terms in a conjunction. @@ -3105,18 +3117,14 @@ Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) { break; } } - - if (!FailedCond) { - if (!AllowTopLevelCond) - return { nullptr, "" }; - + if (!FailedCond) FailedCond = Cond->IgnoreParenImpCasts(); - } std::string Description; { llvm::raw_string_ostream Out(Description); - prettyPrintFailedBooleanCondition(Out, FailedCond, getPrintingPolicy()); + FailedBooleanConditionPrinterHelper Helper(getPrintingPolicy()); + FailedCond->printPretty(Out, &Helper, getPrintingPolicy()); } return { FailedCond, Description }; } @@ -3200,9 +3208,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, Expr *FailedCond; std::string FailedDescription; std::tie(FailedCond, FailedDescription) = - findFailedBooleanCondition( - TemplateArgs[0].getSourceExpression(), - /*AllowTopLevelCond=*/true); + findFailedBooleanCondition(TemplateArgs[0].getSourceExpression()); // Remove the old SFINAE diagnostic. PartialDiagnosticAt OldDiag = @@ -9656,7 +9662,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, Expr *FailedCond; std::string FailedDescription; std::tie(FailedCond, FailedDescription) = - findFailedBooleanCondition(Cond, /*AllowTopLevelCond=*/true); + findFailedBooleanCondition(Cond); Diag(FailedCond->getExprLoc(), diag::err_typename_nested_not_found_requirement) diff --git a/clang/test/PCH/cxx-static_assert.cpp b/clang/test/PCH/cxx-static_assert.cpp index 8049525fb5989..be6c0ab2a76ba 100644 --- a/clang/test/PCH/cxx-static_assert.cpp +++ b/clang/test/PCH/cxx-static_assert.cpp @@ -3,7 +3,7 @@ // Test with pch. // RUN: %clang_cc1 -std=c++11 -emit-pch -o %t %s -// RUN: %clang_cc1 -include-pch %t -verify -std=c++11 %s +// RUN: %clang_cc1 -include-pch %t -verify -std=c++11 %s #ifndef HEADER #define HEADER @@ -14,7 +14,7 @@ template<int N> struct T { #else -// expected-error@12 {{static_assert failed "N is not 2!"}} +// expected-error@12 {{static_assert failed due to requirement '1 == 2' "N is not 2!"}} T<1> t1; // expected-note {{in instantiation of template class 'T<1>' requested here}} T<2> t2; diff --git a/clang/test/Sema/static-assert.c b/clang/test/Sema/static-assert.c index 87fa0504b200a..e8cfb1fa58d71 100644 --- a/clang/test/Sema/static-assert.c +++ b/clang/test/Sema/static-assert.c @@ -38,5 +38,5 @@ struct A { typedef UNION(unsigned, struct A) U1; UNION(char[2], short) u2 = { .one = { 'a', 'b' } }; -typedef UNION(char, short) U3; // expected-error {{static_assert failed "type size mismatch"}} +typedef UNION(char, short) U3; // expected-error {{static_assert failed due to requirement 'sizeof(char) == sizeof(short)' "type size mismatch"}} typedef UNION(float, 0.5f) U4; // expected-error {{expected a type}} diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp index 7dcdb89719e0c..67b3541bea1e9 100644 --- a/clang/test/SemaCXX/static-assert-cxx17.cpp +++ b/clang/test/SemaCXX/static-assert-cxx17.cpp @@ -45,3 +45,12 @@ void foo4() { }; template void foo4<float>(); // expected-note@-1{{in instantiation of function template specialization 'foo4<float>' requested here}} + + +template <typename U, typename V> +void foo5() { + static_assert(!!(global_inline_var<U, V>)); + // expected-error@-1{{static_assert failed due to requirement '!!(global_inline_var<int, float>)'}} +} +template void foo5<int, float>(); +// expected-note@-1{{in instantiation of function template specialization 'foo5<int, float>' requested here}} diff --git a/clang/test/SemaCXX/static-assert.cpp b/clang/test/SemaCXX/static-assert.cpp index 38f82091ae783..b43d56a922c67 100644 --- a/clang/test/SemaCXX/static-assert.cpp +++ b/clang/test/SemaCXX/static-assert.cpp @@ -15,14 +15,14 @@ class C { }; template<int N> struct T { - static_assert(N == 2, "N is not 2!"); // expected-error {{static_assert failed "N is not 2!"}} + static_assert(N == 2, "N is not 2!"); // expected-error {{static_assert failed due to requirement '1 == 2' "N is not 2!"}} }; T<1> t1; // expected-note {{in instantiation of template class 'T<1>' requested here}} T<2> t2; template<typename T> struct S { - static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static_assert failed "Type not big enough!"}} + static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static_assert failed due to requirement 'sizeof(char) > sizeof(char)' "Type not big enough!"}} }; S<char> s1; // expected-note {{in instantiation of template class 'S<char>' requested here}} @@ -111,6 +111,14 @@ static_assert(std::is_same<ExampleTypes::T, ExampleTypes::U>::value, "message"); // expected-error@-1{{static_assert failed due to requirement 'std::is_same<int, float>::value' "message"}} static_assert(std::is_const<ExampleTypes::T>::value, "message"); // expected-error@-1{{static_assert failed due to requirement 'std::is_const<int>::value' "message"}} +static_assert(!std::is_const<const ExampleTypes::T>::value, "message"); +// expected-error@-1{{static_assert failed due to requirement '!std::is_const<const int>::value' "message"}} +static_assert(!(std::is_const<const ExampleTypes::T>::value), "message"); +// expected-error@-1{{static_assert failed due to requirement '!(std::is_const<const int>::value)' "message"}} +static_assert(std::is_const<const ExampleTypes::T>::value == false, "message"); +// expected-error@-1{{static_assert failed due to requirement 'std::is_const<const int>::value == false' "message"}} +static_assert(!(std::is_const<const ExampleTypes::T>::value == true), "message"); +// expected-error@-1{{static_assert failed due to requirement '!(std::is_const<const int>::value == true)' "message"}} struct BI_tag {}; struct RAI_tag : BI_tag {}; From 9d617012743600bcd3a1cf5707f15a0275b61644 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Wed, 12 Dec 2018 10:59:01 -0800 Subject: [PATCH 353/582] Add a comment for the code related to SR-9483. It should be clear why this code is in Swift's copy of LLVM even though it was removed from trunk. apple-llvm-split-commit: 10a674738029c572eeba06ed299c6a30305c6226 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/Optional.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/llvm/include/llvm/ADT/Optional.h b/llvm/include/llvm/ADT/Optional.h index b44fab81246fe..d109da418c49a 100644 --- a/llvm/include/llvm/ADT/Optional.h +++ b/llvm/include/llvm/ADT/Optional.h @@ -108,6 +108,10 @@ template <typename T, bool = isPodLike<T>::value> struct OptionalStorage { } }; +// Swift-only: The following partial template specialization was removed +// from LLVM because of a bug in GCC, but Swift is relying on it. +// (https://bugs.swift.org/browse/SR-9483) + #if !defined(__GNUC__) || defined(__clang__) // GCC up to GCC7 miscompiles this. /// Storage for trivially copyable types only. template <typename T> struct OptionalStorage<T, true> { From dc052754e5e50cb53ae374f37fe773e76b284ad8 Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Tue, 4 Dec 2018 16:10:53 +0000 Subject: [PATCH 354/582] [AST] Add another missing initialization from merge The last merge in PrettyPrinter.h didn't initialize PrintingPolicy::UseStdFunctionForLambda. rdar://46423386 apple-llvm-split-commit: 6804eefbea8a388d2774e8afb64e1831cce01787 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/PrettyPrinter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 3cab535dba0c4..d71b6d9a8b0f5 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -50,8 +50,8 @@ struct PrintingPolicy { PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), ConstantsAsWritten(false), - SuppressImplicitBase(false), FullyQualifiedName(false), - RemapFilePaths(false) {} + SuppressImplicitBase(false), UseStdFunctionForLambda(false), + FullyQualifiedName(false), RemapFilePaths(false) {} /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only From a7f21453a1ed20c680ce4d767c3a96a2ea2507e3 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Thu, 13 Dec 2018 10:46:52 -0800 Subject: [PATCH 355/582] Fix incorrect merge conflict in 41ae04dbab1b The code in TypePrinter::printFunctionProtoAfter did not actually include the change from Clang r349019. apple-llvm-split-commit: 6e5f72cada95901bf7776636f242fad4f53a7a9a apple-llvm-split-dir: clang/ --- clang/lib/AST/TypePrinter.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 55e97e9aaa451..528aabeeaccf2 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -802,14 +802,11 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, printFunctionAfter(Info, OS); - if (unsigned quals = T->getTypeQuals()) { - if (IgnoreFunctionProtoTypeConstQual) - quals &= ~unsigned(Qualifiers::Const); - if (quals) { - OS << " " << T->getTypeQuals().getAsString(); - AppendTypeQualList(OS, quals, Policy.Restrict); - } - } + Qualifiers quals = T->getTypeQuals(); + if (IgnoreFunctionProtoTypeConstQual) + quals.removeConst(); + if (!quals.empty()) + OS << " " << quals.getAsString(); switch (T->getRefQualifier()) { case RQ_None: From ab1fb45ae822209cf59595f9ce2952fdf722a90e Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Wed, 12 Dec 2018 13:16:30 -0800 Subject: [PATCH 356/582] [Sanitizer] Fix TSan check failure rdar://problem/39400035: Fix TSan check failure which aborts the process This is an internal fix until a more principled solution is implemented upstream: https://reviews.llvm.org/D45646 Committing on behalf of Kuba Mracek apple-llvm-split-commit: f4b23d2dd91de43f1332f53743a5f2fd36abdbe2 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc index 766a0f5a505fe..05ee2d8a690c3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc @@ -276,10 +276,14 @@ void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread) { void ThreadFinish(ThreadState *thr) { ThreadCheckIgnore(thr); StatInc(thr, StatThreadFinish); - if (thr->stk_addr && thr->stk_size) + if (thr->stk_addr && thr->stk_size) { + MemoryResetRange(thr, /*pc=*/ 1, thr->stk_addr, thr->stk_size); DontNeedShadowFor(thr->stk_addr, thr->stk_size); - if (thr->tls_addr && thr->tls_size) + } + if (thr->tls_addr && thr->tls_size) { + MemoryResetRange(thr, /*pc=*/ 1, thr->tls_addr, thr->tls_size); DontNeedShadowFor(thr->tls_addr, thr->tls_size); + } thr->is_dead = true; ctx->thread_registry->FinishThread(thr->tid); } From 85395296ef111802c41e065cacedc8b3d87f07c6 Mon Sep 17 00:00:00 2001 From: Adam Nemet <anemet@apple.com> Date: Thu, 13 Dec 2018 21:14:09 -0800 Subject: [PATCH 357/582] Fix up local changes after r349125 warn_attribute_type_not_supported needs the Sema diag header. rdar://problem/46720351 apple-llvm-split-commit: a1a4903bfda471b861d7c8cf8278bf69dc003a75 apple-llvm-split-dir: clang/ --- clang/lib/Parse/ParseDecl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 2db29e75ba262..28c7f72167abd 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -24,6 +24,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" +#include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" From 51578922f59653abd1f6e9e8baaed628bd24abfe Mon Sep 17 00:00:00 2001 From: Adam Nemet <anemet@apple.com> Date: Thu, 13 Dec 2018 22:23:31 -0800 Subject: [PATCH 358/582] Revert "[analyzer] Fix test/Analysis/padding_cpp.cpp after merge conflict." This reverts commit 79074c703976a5e5b25b724837429f2049d85ccb. rdar://problem/46721729 apple-llvm-split-commit: 5b425cf6491381161fb36e3f82897234f5ef7f4a apple-llvm-split-dir: clang/ --- clang/test/Analysis/padding_cpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/padding_cpp.cpp b/clang/test/Analysis/padding_cpp.cpp index 965a159a0b4de..ee49aea0c2bed 100644 --- a/clang/test/Analysis/padding_cpp.cpp +++ b/clang/test/Analysis/padding_cpp.cpp @@ -197,6 +197,6 @@ struct GlobalsForLambda { // no-warning char c2; } G; -// expected-warning@+1{{Excessive padding in 'class std::function<auto (void) -> void>'}} +// expected-warning@+1{{Excessive padding in 'class (lambda}} auto lambda1 = [ c1 = G.c1, i = G.i, c2 = G.c2 ]{}; auto lambda2 = [ i = G.i, c1 = G.c1, c2 = G.c2 ]{}; // no-warning From e3b514ecea4fb8187c778deaa195501838d8dc89 Mon Sep 17 00:00:00 2001 From: Dan Liew <dliew@apple.com> Date: Wed, 19 Dec 2018 14:40:30 +0000 Subject: [PATCH 359/582] Disable using `-fsanitize=leak` from the Clang Driver. LeakSanitizer (LSan) is not yet mature for Darwin so until this changes we should avoid supporting it in Swift Clang and AppleClang. rdar://problem/45841334 apple-llvm-split-commit: e3cc2fee0d3206b1d011cfde48ab8866e5441738 apple-llvm-split-dir: clang/ --- clang/lib/Driver/ToolChains/Darwin.cpp | 3 +++ clang/test/Driver/apple-clang-no-lsan.c | 7 +++++++ clang/test/Driver/fsanitize.c | 14 +++++++------- clang/test/Driver/sanitizer-ld.c | 5 ++--- 4 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 clang/test/Driver/apple-clang-no-lsan.c diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index bfe1e58108806..1c61dd868ba60 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -2358,6 +2358,9 @@ SanitizerMask Darwin::getSupportedSanitizers() const { Res |= SanitizerKind::FuzzerNoLink; Res |= SanitizerKind::Function; + // Apple-Clang: Don't support LSan. rdar://problem/45841334 + Res &= ~SanitizerKind::Leak; + // Prior to 10.9, macOS shipped a version of the C++ standard library without // C++11 support. The same is true of iOS prior to version 5. These OS'es are // incompatible with -fsanitize=vptr. diff --git a/clang/test/Driver/apple-clang-no-lsan.c b/clang/test/Driver/apple-clang-no-lsan.c new file mode 100644 index 0000000000000..54787e3ddba2c --- /dev/null +++ b/clang/test/Driver/apple-clang-no-lsan.c @@ -0,0 +1,7 @@ +// Apple-Clang: Don't support LSan +// REQUIRES: system-darwin +// RUN: not %clang -fsanitize=leak %s -o %t 2>&1 | FileCheck %s +// CHECK: unsupported option '-fsanitize=leak' +int main() { + return 0; +} diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 0a82174aaa41c..f8badfbf54954 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -551,25 +551,25 @@ // CHECK-ESAN-OPENBSD: error: unsupported option '-fsanitize=efficiency-{{.*}}' for target 'i386-pc-openbsd' // RUN: %clang -target x86_64-apple-darwin -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-DARWIN -// CHECK-LSAN-X86-64-DARWIN-NOT: unsupported option +// CHECK-LSAN-X86-64-DARWIN: unsupported option // RUN: %clang -target x86_64-apple-iossimulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-IOSSIMULATOR -// CHECK-LSAN-X86-64-IOSSIMULATOR-NOT: unsupported option +// CHECK-LSAN-X86-64-IOSSIMULATOR: unsupported option // RUN: %clang -target x86_64-apple-tvossimulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-TVOSSIMULATOR -// CHECK-LSAN-X86-64-TVOSSIMULATOR-NOT: unsupported option +// CHECK-LSAN-X86-64-TVOSSIMULATOR: unsupported option // RUN: %clang -target i386-apple-darwin -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-DARWIN -// CHECK-LSAN-I386-DARWIN-NOT: unsupported option +// CHECK-LSAN-I386-DARWIN: unsupported option // RUN: %clang -target arm-apple-ios -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-ARM-IOS -// CHECK-LSAN-ARM-IOS-NOT: unsupported option +// CHECK-LSAN-ARM-IOS: unsupported option // RUN: %clang -target i386-apple-iossimulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-IOSSIMULATOR -// CHECK-LSAN-I386-IOSSIMULATOR-NOT: unsupported option +// CHECK-LSAN-I386-IOSSIMULATOR: unsupported option // RUN: %clang -target i386-apple-tvossimulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-TVOSSIMULATOR -// CHECK-LSAN-I386-TVOSSIMULATOR-NOT: unsupported option +// CHECK-LSAN-I386-TVOSSIMULATOR: unsupported option // RUN: %clang -target i686-linux-gnu -fsanitize=efficiency-cache-frag %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ESAN-X86 // RUN: %clang -target i686-linux-gnu -fsanitize=efficiency-working-set %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ESAN-X86 diff --git a/clang/test/Driver/sanitizer-ld.c b/clang/test/Driver/sanitizer-ld.c index db699410f7e20..30adfa86c99c4 100644 --- a/clang/test/Driver/sanitizer-ld.c +++ b/clang/test/Driver/sanitizer-ld.c @@ -537,14 +537,13 @@ // CHECK-ASAN-DARWIN106-CXX: libclang_rt.asan_osx_dynamic.dylib // CHECK-ASAN-DARWIN106-CXX-NOT: -lc++abi +// Apple-Clang: Don't support LSan // RUN: %clangxx -fsanitize=leak %s -### -o %t.o 2>&1 \ // RUN: -mmacosx-version-min=10.6 \ // RUN: -target x86_64-apple-darwin13.4.0 -fuse-ld=ld -stdlib=platform \ // RUN: --sysroot=%S/Inputs/basic_linux_tree \ // RUN: | FileCheck --check-prefix=CHECK-LSAN-DARWIN106-CXX %s -// CHECK-LSAN-DARWIN106-CXX: "{{.*}}ld{{(.exe)?}}" -// CHECK-LSAN-DARWIN106-CXX: libclang_rt.lsan_osx_dynamic.dylib -// CHECK-LSAN-DARWIN106-CXX-NOT: -lc++abi +// CHECK-LSAN-DARWIN106-CXX: unsupported option '-fsanitize=leak' // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target x86_64-unknown-linux -fuse-ld=ld -fsanitize=safe-stack \ From e4e8e1341d392ad2cd112d14b8e791bd87d33bff Mon Sep 17 00:00:00 2001 From: Dan Liew <dliew@apple.com> Date: Thu, 20 Dec 2018 17:20:48 +0000 Subject: [PATCH 360/582] Disable running LSan tests on Darwin due the recent changes to the Clang driver in https://github.com/apple/swift-clang/pull/249. This is necessary because the standalone LSan tests will be executed and will fail without this patch when using the newer Clang. rdar://problem/45841334 apple-llvm-split-commit: 56b645581726e29691a47fec4e2adbb18e7b09e4 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/test/lsan/lit.common.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler-rt/test/lsan/lit.common.cfg b/compiler-rt/test/lsan/lit.common.cfg index a5df951e2e04b..a8866e9ec2976 100644 --- a/compiler-rt/test/lsan/lit.common.cfg +++ b/compiler-rt/test/lsan/lit.common.cfg @@ -78,3 +78,8 @@ if re.search('mthumb', config.target_cflags) is not None: config.unsupported = True config.suffixes = ['.c', '.cc', '.cpp', '.mm'] + +# Apple-Clang: Disable LSan +if config.host_os == 'Darwin': + lit_config.note('Disabling LSan tests on Darwin') + config.unsupported = True From 4817a937bc6bc8622d4146ed4ed802459320a517 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Thu, 20 Dec 2018 21:03:02 -0800 Subject: [PATCH 361/582] [APINotes] Test that 'ResultType' preserves the 'instancetype' flag (#250) It probably shouldn't, but we now have Apple frameworks relying on this behavior, and since 'instancetype' isn't a real type you can't specify it using 'ResultType' anyway. In practice, if we had to be locked into some behavior for instancetype-returning methods, this is probably the best one. apple-llvm-split-commit: a272d4ee2cc15c907786dc679fca458ef00ca0fe apple-llvm-split-dir: clang/ --- .../Inputs/Headers/InstancetypeModule.apinotes | 10 ++++++++++ .../test/APINotes/Inputs/Headers/InstancetypeModule.h | 10 ++++++++++ clang/test/APINotes/Inputs/Headers/module.modulemap | 4 ++++ clang/test/APINotes/instancetype.m | 9 +++++++++ 4 files changed, 33 insertions(+) create mode 100644 clang/test/APINotes/Inputs/Headers/InstancetypeModule.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/InstancetypeModule.h create mode 100644 clang/test/APINotes/instancetype.m diff --git a/clang/test/APINotes/Inputs/Headers/InstancetypeModule.apinotes b/clang/test/APINotes/Inputs/Headers/InstancetypeModule.apinotes new file mode 100644 index 0000000000000..813eb506f39a7 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/InstancetypeModule.apinotes @@ -0,0 +1,10 @@ +Name: InstancetypeModule +Classes: +- Name: SomeBaseClass + Methods: + - Selector: instancetypeFactoryMethod + MethodKind: Class + ResultType: SomeBaseClass * _Nonnull + - Selector: staticFactoryMethod + MethodKind: Class + ResultType: SomeBaseClass * _Nonnull diff --git a/clang/test/APINotes/Inputs/Headers/InstancetypeModule.h b/clang/test/APINotes/Inputs/Headers/InstancetypeModule.h new file mode 100644 index 0000000000000..767f201d9faf6 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/InstancetypeModule.h @@ -0,0 +1,10 @@ +@interface Object +@end + +@interface SomeBaseClass : Object ++ (nullable instancetype)instancetypeFactoryMethod; ++ (nullable SomeBaseClass *)staticFactoryMethod; +@end + +@interface SomeSubclass : SomeBaseClass +@end diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index 17a785af1d9ea..5b44e7b055843 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -2,6 +2,10 @@ module HeaderLib { header "HeaderLib.h" } +module InstancetypeModule { + header "InstancetypeModule.h" +} + module BrokenTypes { header "BrokenTypes.h" } diff --git a/clang/test/APINotes/instancetype.m b/clang/test/APINotes/instancetype.m new file mode 100644 index 0000000000000..80f12c9bafaaa --- /dev/null +++ b/clang/test/APINotes/instancetype.m @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -verify %s + +@import InstancetypeModule; + +void test() { + // The nullability is here to verify that the API notes were applied. + int good = [SomeSubclass instancetypeFactoryMethod]; // expected-warning {{initializing 'int' with an expression of type 'SomeSubclass * _Nonnull'}} + int bad = [SomeSubclass staticFactoryMethod]; // expected-warning {{initializing 'int' with an expression of type 'SomeBaseClass * _Nonnull'}} +} From c4705520c23a6e7ef94d7255c4f1972fadbb845b Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 16 Jan 2019 19:35:47 -0800 Subject: [PATCH 362/582] [docs] Document the API Notes feature added for Swift (#254) rdar://problem/38123494 apple-llvm-split-commit: 4798b87ae9ed59f6840bab93ca467f5cbc5cc7f6 apple-llvm-split-dir: clang/ --- clang/docs/APINotes.rst | 361 ++++++++++++++++++++++++++++++++++++++++ clang/docs/index.rst | 1 + 2 files changed, 362 insertions(+) create mode 100644 clang/docs/APINotes.rst diff --git a/clang/docs/APINotes.rst b/clang/docs/APINotes.rst new file mode 100644 index 0000000000000..83ce634623ca9 --- /dev/null +++ b/clang/docs/APINotes.rst @@ -0,0 +1,361 @@ +================================================ +API Notes: Annotations Without Modifying Headers +================================================ + +**The Problem:** You have headers you want to use, but you also want to add +extra information to some of the APIs. You don't want to put that information +in the headers themselves---perhaps because you want to keep them clean for +other clients, or perhaps because they're from some open source project and you +don't want to modify them at all. + +**Incomplete solution:** Redeclare all the interesting APIs in your own header +and add the attributes you want. Unfortunately, this: + +* doesn't work with attributes that must be present on a definition +* doesn't allow changing the definition in other ways +* requires your header to be included in any client code to take effect + +**Better solution:** Provide a "sidecar" file with the information you want to +add, and have that automatically get picked up by the module-building logic in +the compiler. + +That's API notes. + +API notes use a YAML-based file format. YAML is a format best explained by +example, so here is a `small example`__ from the compiler test suite of API +notes for a hypothetical "SomeKit" framework. + +__ https://github.com/apple/swift-clang/blob/upstream-with-swift/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes + + +Usage +===== + +API notes files are found relative to the module map that defines a module, +under the name "SomeKit.apinotes" for a module named "SomeKit". Additionally, a +file named "SomeKit_private.apinotes" will also be picked up to go with a +private module map. For bare modules these two files will be in the same +directory as the corresponding module map; for framework modules, they should +be placed in the Headers and PrivateHeaders directories, respectively. The +module map for a private top-level framework module should be placed in the +PrivateHeaders directory as well, though it does not need an additional +"_private" suffix on its name. + +Clang will search for API notes files next to module maps only when passed the +``-fapinotes-modules`` option. + + +Limitations +=========== + +- Since they're identified by module name, API notes cannot be used to modify + arbitrary textual headers. + + +"Versioned" API Notes +===================== + +Many API notes affect how a C API is imported into Swift. In order to change +that behavior while still remaining backwards-compatible, API notes can be +selectively applied based on the Swift compatibility version provided to the +compiler (e.g. ``-fapinotes-swift-version=5``). The rule is that an +explicitly-versioned API note applies to that version *and all earlier +versions,* and any applicable explicitly-versioned API note takes precedence +over an unversioned API note. + + +Reference +========= + +An API notes file contains a YAML dictionary with the following top-level +entries: + +:Name: + + The name of the module (the framework name, for frameworks). Note that this + is always the name of a top-level module, even within a private API notes + file. + + :: + + Name: MyFramework + +:Classes, Protocols, Tags, Typedefs, Globals, Enumerators, Functions: + + Arrays of top-level declarations. Each entry in the array must have a + 'Name' key with its Objective-C name. "Tags" refers to structs, enums, and + unions; "Enumerators" refers to enum cases. + + :: + + Classes: + - Name: MyController + … + - Name: MyView + … + +:SwiftVersions: + + Contains explicit information for backwards compatibility. Each entry in + the array contains a 'Version' key, which should be set to '4' for + annotations that only apply to Swift 4 mode and earlier. The other entries + in this dictionary are the same declaration entries as at the top level: + Classes, Protocols, Tags, Typedefs, Globals, Enumerators, and Functions. + + :: + + SwiftVersions: + - Version: 4 + Classes: … + Protocols: … + +Each entry under 'Classes' and 'Protocols' can contain "Methods" and +"Properties" arrays, in addition to the attributes described below: + +:Methods: + + Identified by 'Selector' and 'MethodKind'; the MethodKind is either + "Instance" or "Class". + + :: + + Classes: + - Name: UIViewController + Methods: + - Selector: "presentViewController:animated:" + MethodKind: Instance + … + +:Properties: + + Identified by 'Name' and 'PropertyKind'; the PropertyKind is also either + "Instance" or "Class". + + :: + + Classes: + - Name: UIView + Properties: + - Name: subviews + PropertyKind: Instance + … + +Each declaration supports the following annotations (if relevant to that +declaration kind), all of which are optional: + +:SwiftName: + + Equivalent to NS_SWIFT_NAME. For a method, must include the full Swift name + with all arguments. Use "_" to omit an argument label. + + :: + + - Selector: "presentViewController:animated:" + MethodKind: Instance + SwiftName: "present(_:animated:)" + + - Class: NSBundle + SwiftName: Bundle + +:Availability, AvailabilityMsg: + + A value of "nonswift" is equivalent to NS_SWIFT_UNAVAILABLE. A value of + "available" can be used in the "SwiftVersions" section to undo the effect of + "nonswift". + + :: + + - Selector: "dealloc" + MethodKind: Instance + Availability: nonswift + AvailabilityMsg: "prefer 'deinit'" + +:SwiftPrivate: + + Equivalent to NS_REFINED_FOR_SWIFT. + + :: + + - Name: CGColorEqualToColor + SwiftPrivate: true + +:Nullability: + + Used for properties and globals. There are four options, identified by their + initials: + + - "N"onnull (``_Nonnull``) + - "O"ptional (``_Nullable``) + - "U"nspecified (``_Null_unspecified``) + - "S"calar (deprecated) + + Note that 'Nullability' is overridden by 'Type', even in a "SwiftVersions" + section. + + .. note:: + + 'Nullability' can also be used to describe the argument types of methods + and functions, but this usage is deprecated in favor of 'Parameters' (see + below). + + :: + + - Name: dataSource + Nullability: O + +:NullabilityOfRet: + + Used for methods and functions. Describes the nullability of the return type. + + Note that 'NullabilityOfRet' is overridden by 'ResultType', even in a + "SwiftVersions" section. + + .. warning:: + + Due to a compiler bug, 'NullabilityOfRet' may change nullability of the + parameters as well (rdar://30544062). Avoid using it and instead use + 'ResultType' and specify the return type along with a nullability + annotation (see documentation for 'ResultType'). + + :: + + - Selector: superclass + MethodKind: Class + NullabilityOfRet: O + +:Type: + + Used for properties and globals. This completely overrides the type of the + declaration; it should ideally only be used for Swift backwards + compatibility, when existing type information has been made more precise in a + header. Prefer 'Nullability' and other annotations when possible. + + Note that the type is *not* parsed in the context where it will be used, + which means that macros are not available and nullability must be applied + explicitly (even in an ``NS_ASSUME_NONNULL_BEGIN`` section). + + :: + + - Name: delegate + PropertyKind: Instance + Type: "id" + +:ResultType: + + Used for methods and functions. This completely overrides the return type; it + should ideally only be used for Swift backwards compatibility, when existing + type information has been made more precise in a header. + + Note that the type is *not* parsed in the context where it will be used, + which means that macros are not available and nullability must be applied + explicitly (even in an ``NS_ASSUME_NONNULL_BEGIN`` section). + + :: + + - Selector: "subviews" + MethodKind: Instance + ResultType: "NSArray * _Nonnull" + +:SwiftImportAsAccessors: + + Used for properties. If true, the property will be exposed in Swift as its + accessor methods, rather than as a computed property using ``var``. + + :: + + - Name: currentContext + PropertyKind: Class + SwiftImportAsAccessors: true + +:NSErrorDomain: + + Used for NSError code enums. The value is the name of the associated domain + NSString constant; an empty string ("") means the enum is a normal enum + rather than an error code. + + :: + + - Name: MKErrorCode + NSErrorDomain: MKErrorDomain + +:SwiftWrapper: + + Controls NS_STRING_ENUM and NS_EXTENSIBLE_STRING_ENUM. There are three + options: + + - "struct" (extensible) + - "enum" + - "none" + + Note that even an "enum" wrapper is still presented as a struct in Swift; + it's just a "more enum-like" struct. + + :: + + - Name: AVMediaType + SwiftWrapper: none + +:EnumKind: + + Has the same effect as NS_ENUM and NS_OPTIONS. There are four options: + + - "NSEnum" / "CFEnum" + - "NSClosedEnum" / "CFClosedEnum" + - "NSOptions" / "CFOptions" + - "none" + + :: + + - Name: GKPhotoSize + EnumKind: none + +:Parameters: + + Used for methods and functions. Parameters are identified by a 0-based + 'Position' and support the 'Nullability', 'NoEscape', and 'Type' keys. + + .. note:: + + Using 'Parameters' within a parameter entry to describe the parameters of a + block is not implemented. Use 'Type' on the entire parameter instead. + + :: + + - Selector: "isEqual:" + MethodKind: Instance + Parameters: + - Position: 0 + Nullability: O + +:NoEscape: + + Used only for block parameters. Equivalent to NS_NOESCAPE. + + :: + + - Name: dispatch_sync + Parameters: + - Position: 0 + NoEscape: true + +:SwiftBridge: + + Used for Objective-C class types bridged to Swift value types. An empty + string ("") means a type is not bridged. Not supported outside of Apple + frameworks (the Swift side of it requires conforming to implementation-detail + protocols that are subject to change). + + :: + + - Name: NSIndexSet + SwiftBridge: IndexSet + +:DesignatedInit: + + Used for init methods. Equivalent to NS_DESIGNATED_INITIALIZER. + + :: + + - Selector: "initWithFrame:" + MethodKind: Instance + DesignatedInit: true diff --git a/clang/docs/index.rst b/clang/docs/index.rst index 3eb1d160c756a..184b3114ea287 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -42,6 +42,7 @@ Using Clang as a Compiler MSVCCompatibility OpenMPSupport ThinLTO + APINotes CommandGuide/index FAQ From 30fc70f349277a42e02550779b464b92a2f06c1c Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 16 Jan 2019 20:05:20 -0800 Subject: [PATCH 363/582] Remove -apinotes entry point First step in removing the notion of "compiled" API notes altogether. apple-llvm-split-commit: c59c1a43b592fd63ebe960a5cd698be3bebfa76b apple-llvm-split-dir: clang/ --- .../APINotes/Inputs/os-availability.apinotes | 53 ----- clang/test/APINotes/Inputs/roundtrip.apinotes | 197 ------------------ .../yaml-reader-errors/UIKit.apinotes} | 21 -- .../Inputs/yaml-reader-errors/UIKit.h | 1 + .../yaml-reader-errors/module.modulemap | 3 + clang/test/APINotes/module-cache.m | 35 ---- clang/test/APINotes/yaml-os-availability.c | 31 --- clang/test/APINotes/yaml-reader-errors.m | 5 + clang/test/APINotes/yaml-reader-test.c | 102 --------- clang/test/APINotes/yaml-roundtrip.c | 4 - clang/tools/driver/CMakeLists.txt | 1 - clang/tools/driver/apinotes_main.cpp | 154 -------------- clang/tools/driver/driver.cpp | 4 - 13 files changed, 9 insertions(+), 602 deletions(-) delete mode 100644 clang/test/APINotes/Inputs/os-availability.apinotes delete mode 100644 clang/test/APINotes/Inputs/roundtrip.apinotes rename clang/test/APINotes/{yaml-reader-errors.c => Inputs/yaml-reader-errors/UIKit.apinotes} (76%) create mode 100644 clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.h create mode 100644 clang/test/APINotes/Inputs/yaml-reader-errors/module.modulemap delete mode 100644 clang/test/APINotes/yaml-os-availability.c create mode 100644 clang/test/APINotes/yaml-reader-errors.m delete mode 100644 clang/test/APINotes/yaml-reader-test.c delete mode 100644 clang/test/APINotes/yaml-roundtrip.c delete mode 100644 clang/tools/driver/apinotes_main.cpp diff --git a/clang/test/APINotes/Inputs/os-availability.apinotes b/clang/test/APINotes/Inputs/os-availability.apinotes deleted file mode 100644 index d59e79ce9fc0d..0000000000000 --- a/clang/test/APINotes/Inputs/os-availability.apinotes +++ /dev/null @@ -1,53 +0,0 @@ -Name: Foundation -Classes: - - Name: NSCountedSet - Availability: iOS - Methods: - - Selector: 'initWithCapacity:' - MethodKind: Instance - DesignatedInit: true - - Name: NSArray - Methods: - - Selector: 'init' - MethodKind: Instance - DesignatedInit: true - - Selector: 'initWithObjects:' - MethodKind: Instance - DesignatedInit: true - Availability: iOS - - Selector: 'initWithObjects:count:' - MethodKind: Instance - DesignatedInit: true - Availability: iOS - Properties: - - Name: 'familyNameios' - Nullability: N - Availability: iOS - - Name: 'fontName' - Nullability: N -Protocols: - - Name: UIApplicationDelegate - AuditedForNullability: true - Methods: - - Selector: 'application:willFinishLaunchingWithOptions:' - MethodKind: Instance - Nullability: [ N, U ] - - Name: UIApplicationDelegateIOS - Availability: iOS - AuditedForNullability: true - Methods: - - Selector: 'application:willFinishLaunchingWithOptions:' - MethodKind: Instance - Nullability: [ N, U ] -Functions: - - Name: NSAvailableWindowDepthsiOS - NullabilityOfRet: N - Availability: iOS - - Name: NSAvailableWindowDepths - NullabilityOfRet: N -Globals: - - Name: NSCalibratedWhiteColorSpace - Nullability: N - Availability: OSX - AvailabilityMsg: '' - diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes deleted file mode 100644 index d6e6cfbad4e5d..0000000000000 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ /dev/null @@ -1,197 +0,0 @@ ---- -Name: AppKit -Availability: available -AvailabilityMsg: '' -SwiftInferImportAsMember: true -Classes: - - Name: NSCell - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - SwiftImportAsNonGeneric: true - SwiftObjCMembers: false - Methods: - - Selector: 'cellWithImage:' - MethodKind: Class - RetainCountConvention: NSReturnsNotRetained - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - ResultType: id - - Selector: init - MethodKind: Instance - NullabilityOfRet: U - Availability: available - AvailabilityMsg: '' - SwiftPrivate: true - SwiftName: '' - DesignatedInit: true - - Selector: 'initImageCell:' - MethodKind: Instance - Nullability: [ N ] - NullabilityOfRet: U - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - DesignatedInit: true - - Selector: 'initTextCell:' - MethodKind: Instance - Nullability: [ N ] - NullabilityOfRet: U - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - DesignatedInit: true - - Selector: 'initWithCoder:' - MethodKind: Instance - Nullability: [ N ] - NullabilityOfRet: U - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - DesignatedInit: true - Required: true - - Name: NSView - AuditedForNullability: true - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - SwiftBridge: View - SwiftObjCMembers: true - Methods: - - Selector: 'addSubview:' - MethodKind: Instance - Nullability: [ N ] - NullabilityOfRet: N - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - - Selector: 'addSubview:positioned:relativeTo:' - MethodKind: Instance - Parameters: - - Position: 0 - NoEscape: false - - Position: 1 - - Position: 2 - NoEscape: true - Type: id - Nullability: [ N, N, O ] - NullabilityOfRet: N - Availability: available - AvailabilityMsg: '' - SwiftName: '' - - Selector: 'beginDraggingSessionWithItems:event:source:' - MethodKind: Instance - Nullability: [ U, U, N ] - NullabilityOfRet: N - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: 'beginDragginSession(_:event:source:)' - Properties: - - Name: enclosingScrollView - PropertyKind: Instance - Nullability: O - Availability: available - AvailabilityMsg: '' - SwiftName: enclosing - Type: id - - Name: makeBackingLayer - PropertyKind: Class - Nullability: N - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: '' - SwiftImportAsAccessors: false -Functions: - - Name: CFURLCopyResourcePropertiesForKeys - Parameters: - - Position: 0 - - Position: 1 - - Position: 2 - RetainCountConvention: CFReturnsRetained - RetainCountConvention: CFReturnsRetained - Availability: available - AvailabilityMsg: '' - SwiftName: '' - - Name: NSAvailableWindowDepths - NullabilityOfRet: N - Availability: available - AvailabilityMsg: '' - SwiftName: 'availableWindowDepths()' - ResultType: NSInteger -Globals: - - Name: NSCalibratedWhiteColorSpace - Nullability: N - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: calibratedWhite - Type: id -Enumerators: - - Name: NSColorRed - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: Red -Tags: - - Name: NSSomeEnum - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: SomeEnum - NSErrorDomain: some_error_domain - EnumExtensibility: closed - FlagEnum: false - - Name: NSSomeStruct - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: SomeStruct - NSErrorDomain: '' -Typedefs: - - Name: NSTypedef - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: Typedef - SwiftBridge: '' - SwiftWrapper: struct -SwiftVersions: - - Version: 3.0 - Classes: - - Name: NSCell - Availability: available - AvailabilityMsg: '' - SwiftPrivate: false - SwiftName: NSBox - SwiftBridge: '' - Methods: - - Selector: init - MethodKind: Instance - NullabilityOfRet: N - Availability: available - AvailabilityMsg: '' - SwiftPrivate: true - SwiftName: '' - DesignatedInit: true - - Name: NSView - Availability: available - AvailabilityMsg: '' - SwiftName: '' - Properties: - - Name: makeBackingLayer - PropertyKind: Class - Availability: available - AvailabilityMsg: '' - SwiftName: '' - SwiftImportAsAccessors: true -... diff --git a/clang/test/APINotes/yaml-reader-errors.c b/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.apinotes similarity index 76% rename from clang/test/APINotes/yaml-reader-errors.c rename to clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.apinotes index c02260bcf2510..77db844008990 100644 --- a/clang/test/APINotes/yaml-reader-errors.c +++ b/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.apinotes @@ -1,44 +1,27 @@ -# RUN: not %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t.apinotesc %s > %t.err 2>&1 -# RUN: FileCheck %s < %t.err - --- Name: UIKit -Availability: iOS -AvailabilityMsg: iOSOnly Classes: - Name: UIFont - Availability: iOS - AvailabilityMsg: iOSOnly Methods: - Selector: 'fontWithName:size:' MethodKind: Instance Nullability: [ N ] NullabilityOfRet: O - Availability: iOS - AvailabilityMsg: iOSOnly DesignatedInit: true # CHECK: duplicate definition of method '-[UIFont fontWithName:size:]' - Selector: 'fontWithName:size:' MethodKind: Instance Nullability: [ N ] NullabilityOfRet: O - Availability: iOS - AvailabilityMsg: iOSOnly DesignatedInit: true Properties: - Name: familyName Nullability: N - Availability: iOS - AvailabilityMsg: iOSOnly - Name: fontName Nullability: N - Availability: iOS - AvailabilityMsg: iOSOnly # CHECK: duplicate definition of instance property 'UIFont.familyName' - Name: familyName Nullability: N - Availability: iOS - AvailabilityMsg: iOSOnly # CHECK: multiple definitions of class 'UIFont' - Name: UIFont Protocols: @@ -51,16 +34,12 @@ AvailabilityMsg: iOSOnly - Name: 'globalFoo' Nullability: [ N, N, O, S ] NullabilityOfRet: O - Availability: iOS - AvailabilityMsg: iOSOnly - Name: 'globalFoo2' Nullability: [ N, N, O, S ] NullabilityOfRet: O Globals: - Name: globalVar Nullability: O - Availability: iOS - AvailabilityMsg: iOSOnly - Name: globalVar2 Nullability: O Tags: diff --git a/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.h b/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.h new file mode 100644 index 0000000000000..55313ae260ae1 --- /dev/null +++ b/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.h @@ -0,0 +1 @@ +extern int yesOfCourseThisIsWhatUIKitLooksLike; diff --git a/clang/test/APINotes/Inputs/yaml-reader-errors/module.modulemap b/clang/test/APINotes/Inputs/yaml-reader-errors/module.modulemap new file mode 100644 index 0000000000000..3d683d705cacf --- /dev/null +++ b/clang/test/APINotes/Inputs/yaml-reader-errors/module.modulemap @@ -0,0 +1,3 @@ +module UIKit { + header "UIKit.h" +} diff --git a/clang/test/APINotes/module-cache.m b/clang/test/APINotes/module-cache.m index 19d6a27bec18b..5dcaf1181f9dc 100644 --- a/clang/test/APINotes/module-cache.m +++ b/clang/test/APINotes/module-cache.m @@ -46,41 +46,6 @@ // RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/after.log // RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log -// Set up a directory with pre-compiled API notes. -// RUN: mkdir -p %t/CompiledAPINotes -// RUN: rm -rf %t/ModulesCache -// RUN: rm -rf %t/APINotesCache -// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %S/Inputs/APINotes/SomeOtherKit.apinotes - -// First build: check that 'methodB' is unavailable but 'methodA' is available. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-before.log 2>&1 -// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log -// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-before.log -// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log - -// Do it again; now we're using caches. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-before.log 2>&1 -// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log -// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-before.log -// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log - -// Compile a new API notes file to replace the old one. -// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %t/APINotes/SomeOtherKit.apinotes - -// Build again: check that both methods are now unavailable and that the module rebuilt. -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-after.log 2>&1 -// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log -// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log -// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-after.log -// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log - -// Run the build again: check that both methods are now unavailable -// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-after.log 2>&1 -// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log -// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log -// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-after.log -// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log - @import SomeOtherKit; void test(A *a) { diff --git a/clang/test/APINotes/yaml-os-availability.c b/clang/test/APINotes/yaml-os-availability.c deleted file mode 100644 index 62301af3a1ea4..0000000000000 --- a/clang/test/APINotes/yaml-os-availability.c +++ /dev/null @@ -1,31 +0,0 @@ -# RUN: %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t-ios.apinotesc %S/Inputs/os-availability.apinotes -# RUN: %clang -cc1apinotes -binary-to-yaml %t-ios.apinotesc -o %t.os-availability-ios.apinotes -# RUN: FileCheck %s -check-prefix=IOS < %t.os-availability-ios.apinotes - -# RUN: %clang -cc1apinotes -yaml-to-binary -target x86_64-apple-macosx10.9 -o %t-osx.apinotesc %S/Inputs/os-availability.apinotes -# RUN: %clang -cc1apinotes -binary-to-yaml %t-osx.apinotesc -o %t.os-availability-osx.apinotes -# RUN: FileCheck %s -check-prefix=OSX < %t.os-availability-osx.apinotes - -# IOS: Foundation -# IOS: NSArray -# IOS: initWithObjects -# IOS: familyNameios -# IOS: fontName -# IOS: NSCountedSet -# IOS: UIApplicationDelegate -# IOS: UIApplicationDelegateIOS -# IOS: NSAvailableWindowDepths -# IOS: NSAvailableWindowDepthsiOS -# IOS-NOT: NSCalibratedWhiteColorSpace - -# OSX: Foundation -# qqOSX: NSArray -# OSX-NOT: initWithObjects -# OSX-NOT: familyNameios -# OSX: fontName -# OSX-NOT: NSCountedSet -# OSX: UIApplicationDelegate -# OSX-NOT: UIApplicationDelegateIOS -# OSX: NSAvailableWindowDepths -# OSX-NOT: NSAvailableWindowDepthsiOS -# OSX: NSCalibratedWhiteColorSpace diff --git a/clang/test/APINotes/yaml-reader-errors.m b/clang/test/APINotes/yaml-reader-errors.m new file mode 100644 index 0000000000000..9e5ee34c3e415 --- /dev/null +++ b/clang/test/APINotes/yaml-reader-errors.m @@ -0,0 +1,5 @@ +// RUN: rm -rf %t +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fapinotes -fapinotes-modules -fmodules-cache-path=%t -I %S/Inputs/yaml-reader-errors/ -fsyntax-only %s > %t.err 2>&1 +// RUN: FileCheck %S/Inputs/yaml-reader-errors/UIKit.apinotes < %t.err + +@import UIKit; diff --git a/clang/test/APINotes/yaml-reader-test.c b/clang/test/APINotes/yaml-reader-test.c deleted file mode 100644 index 01ad90a5a2292..0000000000000 --- a/clang/test/APINotes/yaml-reader-test.c +++ /dev/null @@ -1,102 +0,0 @@ -# RUN: %clang -cc1apinotes -dump %s | FileCheck %s ---- -Name: UIKit -Availability: iOS -AvailabilityMsg: iOSOnly -Classes: - - Name: UIFont - Availability: iOS - AvailabilityMsg: iOSOnly - Methods: - - Selector: 'fontWithName:size:' - MethodKind: Instance - Nullability: [ N ] - NullabilityOfRet: O - Availability: iOS - AvailabilityMsg: iOSOnly - DesignatedInit: true - Properties: - - Name: familyName - Nullability: N - Availability: iOS - AvailabilityMsg: iOSOnly - - Name: fontName - Nullability: N - Availability: iOS - AvailabilityMsg: iOSOnly -Protocols: - - Name: MyProto - AuditedForNullability: true - - Name: MyProto2 - AuditedForNullability: true -Functions: - - Name: 'globalFoo' - Nullability: [ N, N, O, S ] - NullabilityOfRet: O - Availability: iOS - AvailabilityMsg: iOSOnly - - Name: 'globalFoo2' - Nullability: [ N, N, O, S ] - NullabilityOfRet: O -Globals: - - Name: globalVar - Nullability: O - Availability: iOS - AvailabilityMsg: iOSOnly - - Name: globalVar2 - Nullability: O - - -# CHECK: Name: UIKit -# CHECK: Availability: iOS -# CHECK: AvailabilityMsg: iOSOnly -# CHECK: Classes: -# CHECK: - Name: UIFont -# CHECK: Availability: iOS -# CHECK: AvailabilityMsg: iOSOnly -# CHECK: Methods: -# CHECK: - Selector: 'fontWithName:size:' -# CHECK: MethodKind: Instance -# CHECK: Nullability: [ N ] -# CHECK: NullabilityOfRet: O -# CHECK: Availability: iOS -# CHECK: AvailabilityMsg: iOSOnly -# CHECK: DesignatedInit: true -# CHECK: Properties: -# CHECK: - Name: familyName -# CHECK: Nullability: N -# CHECK: Availability: iOS -# CHECK: AvailabilityMsg: iOSOnly -# CHECK: - Name: fontName -# CHECK: Nullability: N -# CHECK: Availability: iOS -# CHECK: AvailabilityMsg: iOSOnly -# CHECK:Protocols: -# CHECK: - Name: MyProto -# CHECK: AuditedForNullability: true -# CHECK: Availability: available -# CHECK: AvailabilityMsg: '' -# CHECK: - Name: MyProto2 -# CHECK: AuditedForNullability: true -# CHECK: Availability: available -# CHECK: AvailabilityMsg: '' -# CHECK:Functions: -# CHECK: - Name: globalFoo -# CHECK: Nullability: [ N, N, O, U ] -# CHECK: NullabilityOfRet: O -# CHECK: Availability: iOS -# CHECK: AvailabilityMsg: iOSOnly -# CHECK: - Name: globalFoo2 -# CHECK: Nullability: [ N, N, O, U ] -# CHECK: NullabilityOfRet: O -# CHECK: Availability: available -# CHECK: AvailabilityMsg: '' -# CHECK:Globals: -# CHECK: - Name: globalVar -# CHECK: Nullability: O -# CHECK: Availability: iOS -# CHECK: AvailabilityMsg: iOSOnly -# CHECK: - Name: globalVar2 -# CHECK: Nullability: O -# CHECK: Availability: available -# CHECK: AvailabilityMsg: diff --git a/clang/test/APINotes/yaml-roundtrip.c b/clang/test/APINotes/yaml-roundtrip.c deleted file mode 100644 index f14d18f3e85fa..0000000000000 --- a/clang/test/APINotes/yaml-roundtrip.c +++ /dev/null @@ -1,4 +0,0 @@ -# RUN: %clang -cc1apinotes -yaml-to-binary -o %t.apinotesc %S/Inputs/roundtrip.apinotes -# RUN: %clang -cc1apinotes -binary-to-yaml -o %t.apinotes %t.apinotesc -# RUN: diff %S/Inputs/roundtrip.apinotes %t.apinotes - diff --git a/clang/tools/driver/CMakeLists.txt b/clang/tools/driver/CMakeLists.txt index e990a57c2fc5e..373fe0f8f34a9 100644 --- a/clang/tools/driver/CMakeLists.txt +++ b/clang/tools/driver/CMakeLists.txt @@ -33,7 +33,6 @@ add_clang_tool(clang driver.cpp cc1_main.cpp cc1as_main.cpp - apinotes_main.cpp cc1gen_reproducer_main.cpp DEPENDS diff --git a/clang/tools/driver/apinotes_main.cpp b/clang/tools/driver/apinotes_main.cpp deleted file mode 100644 index 930dc39c3b726..0000000000000 --- a/clang/tools/driver/apinotes_main.cpp +++ /dev/null @@ -1,154 +0,0 @@ -//===-- api_notes.cpp - API Notes Driver ----------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// This file provides conversion between the YAML (source) and binary forms -/// of API notes. -/// -//===----------------------------------------------------------------------===// -#include "clang/APINotes/APINotesYAMLCompiler.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/Triple.h" - -using namespace llvm; -namespace api_notes = clang::api_notes; - -int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0, - void *MainAddr) { - - // Mark all our options with this category, everything else (except for - // -version and -help) will be hidden. - static cl::OptionCategory APINotesCategory("API Notes options"); - - static cl::opt<api_notes::ActionType> - Action(cl::desc("Mode:"), cl::init(api_notes::ActionType::None), - cl::values( - clEnumValN(api_notes::ActionType::YAMLToBinary, - "yaml-to-binary", - "Convert YAML to binary format"), - clEnumValN(api_notes::ActionType::BinaryToYAML, - "binary-to-yaml", - "Convert binary format to YAML"), - clEnumValN(api_notes::ActionType::Dump, - "dump", - "Parse and dump the output")), - cl::cat(APINotesCategory)); - - static cl::opt<std::string> - InputFilename(cl::Positional, cl::desc("<input file>"), - cl::Required, cl::cat(APINotesCategory)); - - static cl::opt<std::string> - Target("target", cl::desc("Generate binary format for the given target"), - cl::cat(APINotesCategory)); - - static cl::opt<std::string> - OutputFilename("o", cl::desc("Output file name"), cl::cat(APINotesCategory)); - - cl::HideUnrelatedOptions(APINotesCategory); - - SmallVector<const char *, 4> Args; - Args.push_back(Argv0); - Args.append(Argv.begin(), Argv.end()); - cl::ParseCommandLineOptions(Args.size(), - Args.data(), - "Clang API Notes Tool\n"); - - if (Action == clang::api_notes::ActionType::None) { - errs() << "action required\n"; - cl::PrintHelpMessage(); - return 1; - } - - auto fileBufOrErr = MemoryBuffer::getFile(InputFilename); - if (std::error_code EC = fileBufOrErr.getError()) { - llvm::errs() << "\n Could not open input file: " + EC.message() << '\n'; - return true; - } - StringRef input = fileBufOrErr.get()->getBuffer(); - - switch (Action) { - case api_notes::ActionType::None: - llvm_unreachable("handled above"); - - case api_notes::ActionType::YAMLToBinary: { - if (OutputFilename.empty()) { - errs() << "output file is required\n"; - cl::PrintHelpMessage(); - return 1; - } - - api_notes::OSType targetOS = api_notes::OSType::Absent; - // TODO: Check that we've specified the target. - if (!Target.empty()) { - llvm::Triple target(llvm::Triple::normalize(Target)); - switch (target.getOS()) { - case llvm::Triple::Darwin: - case llvm::Triple::MacOSX: - targetOS = api_notes::OSType::OSX; - break; - case llvm::Triple::IOS: - targetOS = api_notes::OSType::IOS; - break; - case llvm::Triple::WatchOS: - targetOS = api_notes::OSType::WatchOS; - break; - case llvm::Triple::TvOS: - targetOS = api_notes::OSType::TvOS; - break; - default: - errs() << "target is not supported\n"; - return 1; - } - } - std::error_code EC; - llvm::raw_fd_ostream os(OutputFilename, EC, - llvm::sys::fs::OpenFlags::F_None); - - if (api_notes::compileAPINotes(input, /*sourceFile=*/nullptr, os, targetOS)) - return 1; - - os.flush(); - - return os.has_error(); - } - - case api_notes::ActionType::BinaryToYAML: { - if (OutputFilename.empty()) { - errs() << "output file required\n"; - cl::PrintHelpMessage(); - return 1; - } - - std::error_code EC; - llvm::raw_fd_ostream os(OutputFilename, EC, - llvm::sys::fs::OpenFlags::F_None); - - if (api_notes::decompileAPINotes(std::move(fileBufOrErr.get()), os)) - return 1; - - os.flush(); - - return os.has_error(); - } - - case api_notes::ActionType::Dump: - return api_notes::parseAndDumpAPINotes(input); - } - - return 1; -} - diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index b6e31989a685b..51143e3d8cb75 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -203,8 +203,6 @@ extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr); extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr); -extern int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0, - void *MainAddr); extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr); @@ -312,8 +310,6 @@ static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) { return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); if (Tool == "as") return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); - if (Tool == "apinotes") - return cc1apinotes_main(argv.slice(2), argv[0], GetExecutablePathVP); if (Tool == "gen-reproducer") return cc1gen_reproducer_main(argv.slice(2), argv[0], GetExecutablePathVP); From 198d8221d7170442acd78acdd1ef673d685f5068 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 16 Jan 2019 21:42:18 -0800 Subject: [PATCH 364/582] Stop looking for apinotesc files. apple-llvm-split-commit: 2a78c4fe667289152c8fc72b983495cec0d3d4da apple-llvm-split-dir: clang/ --- .../include/clang/APINotes/APINotesOptions.h | 5 ++-- clang/include/clang/APINotes/Types.h | 3 --- clang/lib/APINotes/APINotesManager.cpp | 26 ------------------- 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesOptions.h b/clang/include/clang/APINotes/APINotesOptions.h index d1cb16dacfc53..4c15e7fc56e15 100644 --- a/clang/include/clang/APINotes/APINotesOptions.h +++ b/clang/include/clang/APINotes/APINotesOptions.h @@ -30,9 +30,8 @@ class APINotesOptions { /// The set of search paths where we API notes can be found for /// particular modules. /// - /// The API notes in this directory are stored as - /// <ModuleName>.apinotes or <ModuleName>.apinotesc, and are only - /// applied when building the module <ModuleName>. + /// The API notes in this directory are stored as <ModuleName>.apinotes, + /// and are only applied when building the module <ModuleName>. std::vector<std::string> ModuleSearchPaths; }; diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 04591621a792e..b2ca595b0c3b0 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -27,9 +27,6 @@ namespace api_notes { /// The file extension used for the source representation of API notes. static const char SOURCE_APINOTES_EXTENSION[] = "apinotes"; -/// The file extension used for the binary representation of API notes. -static const char BINARY_APINOTES_EXTENSION[] = "apinotesc"; - /// Opaque context ID used to refer to an Objective-C class or protocol. class ContextID { public: diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 3f2b97ab1ade0..c3478d11a5861 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -83,21 +83,6 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { PrettyStackTraceDoubleString trace("Loading API notes from ", apiNotesFile->getName()); - // If the API notes file is already in the binary form, load it directly. - StringRef apiNotesFileName = apiNotesFile->getName(); - StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName); - if (!apiNotesFileExt.empty() && - apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { - auto compiledFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User); - - // Load the file. - auto buffer = SourceMgr.getBuffer(compiledFileID, SourceLocation()); - if (!buffer) return nullptr; - - // Load the binary form. - return APINotesReader::getUnmanaged(buffer, SwiftVersion); - } - // Open the source file. auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User); auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation()); @@ -156,20 +141,9 @@ const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directo llvm::SmallString<128> path; path += directory->getName(); - unsigned pathLen = path.size(); - StringRef basenameSuffix = ""; if (!wantPublic) basenameSuffix = "_private"; - // Look for a binary API notes file. - llvm::sys::path::append(path, - llvm::Twine(basename) + basenameSuffix + "." + BINARY_APINOTES_EXTENSION); - if (const FileEntry *binaryFile = fileMgr.getFile(path, /*Open*/true)) - return binaryFile; - - // Go back to the original path. - path.resize(pathLen); - // Look for the source API notes file. llvm::sys::path::append(path, llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION); From f6184b0be8c9a3ff24ef0eec17afe8719cd8ebb8 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 16 Jan 2019 21:43:05 -0800 Subject: [PATCH 365/582] Remove API notes dumping and API notes visitor API apple-llvm-split-commit: a1c6926715a6d279d9535456eac093dbf58845c4 apple-llvm-split-dir: clang/ --- clang/include/clang/APINotes/APINotesReader.h | 55 --- .../clang/APINotes/APINotesYAMLCompiler.h | 4 - clang/lib/APINotes/APINotesReader.cpp | 180 -------- clang/lib/APINotes/APINotesYAMLCompiler.cpp | 400 ------------------ 4 files changed, 639 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 06572102a95dc..f3ad7533b6b3f 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -206,61 +206,6 @@ class APINotesReader { /// /// \returns information about the typedef, if known. VersionedInfo<TypedefInfo> lookupTypedef(StringRef name); - - /// Visitor used when walking the contents of the API notes file. - class Visitor { - public: - virtual ~Visitor(); - - /// Visit an Objective-C class. - virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit an Objective-C protocol. - virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit an Objective-C method. - virtual void visitObjCMethod(ContextID contextID, StringRef selector, - bool isInstanceMethod, - const ObjCMethodInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit an Objective-C property. - virtual void visitObjCProperty(ContextID contextID, StringRef name, - bool isInstance, - const ObjCPropertyInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit a global variable. - virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit a global function. - virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit an enumerator. - virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit a tag. - virtual void visitTag(StringRef name, const TagInfo &info, - llvm::VersionTuple swiftVersion); - - /// Visit a typedef. - virtual void visitTypedef(StringRef name, const TypedefInfo &info, - llvm::VersionTuple swiftVersion); - }; - - /// Visit the contents of the API notes file, passing each entity to the - /// given visitor. - void visit(Visitor &visitor); }; } // end namespace api_notes diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h index 508da65993e7d..c85ae29cd157f 100644 --- a/clang/include/clang/APINotes/APINotesYAMLCompiler.h +++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h @@ -52,10 +52,6 @@ namespace api_notes { void *diagHandlerCtxt = nullptr); bool parseAndDumpAPINotes(llvm::StringRef yamlInput); - - /// Converts API notes from the compiled binary format to the YAML format. - bool decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, - llvm::raw_ostream &os); } // end namespace api_notes } // end namespace clang diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 7db3c4eb7b3b4..ff03e1e939706 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -1711,183 +1711,3 @@ auto APINotesReader::lookupTypedef(StringRef name) return { Impl.SwiftVersion, *known }; } - -APINotesReader::Visitor::~Visitor() { } - -void APINotesReader::Visitor::visitObjCClass( - ContextID contextID, - StringRef name, - const ObjCContextInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitObjCProtocol( - ContextID contextID, - StringRef name, - const ObjCContextInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitObjCMethod( - ContextID contextID, - StringRef selector, - bool isInstanceMethod, - const ObjCMethodInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitObjCProperty( - ContextID contextID, - StringRef name, - bool isInstance, - const ObjCPropertyInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitGlobalVariable( - StringRef name, - const GlobalVariableInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitGlobalFunction( - StringRef name, - const GlobalFunctionInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitEnumConstant( - StringRef name, - const EnumConstantInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitTag( - StringRef name, - const TagInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::Visitor::visitTypedef( - StringRef name, - const TypedefInfo &info, - VersionTuple swiftVersion) { } - -void APINotesReader::visit(Visitor &visitor) { - // FIXME: All of these iterations would be significantly more efficient if we - // could get the keys and data together, but OnDiskIterableHashTable doesn't - // support that. - - // Build an identifier ID -> string mapping, which we'll need when visiting - // any of the tables. - llvm::DenseMap<unsigned, StringRef> identifiers; - if (Impl.IdentifierTable) { - for (auto key : Impl.IdentifierTable->keys()) { - unsigned ID = *Impl.IdentifierTable->find(key); - assert(identifiers.count(ID) == 0); - identifiers[ID] = key; - } - } - - // Visit classes and protocols. - if (Impl.ObjCContextIDTable && Impl.ObjCContextInfoTable) { - for (auto key : Impl.ObjCContextIDTable->keys()) { - auto name = identifiers[key.first]; - auto contextID = *Impl.ObjCContextIDTable->find(key); - - auto knownInfo = Impl.ObjCContextInfoTable->find(contextID); - if (knownInfo == Impl.ObjCContextInfoTable->end()) continue; - - for (const auto &versioned : *knownInfo) { - if (key.second) - visitor.visitObjCProtocol(ContextID(contextID), name, - versioned.second, versioned.first); - else - visitor.visitObjCClass(ContextID(contextID), name, versioned.second, - versioned.first); - } - } - } - - // Build a selector ID -> stored Objective-C selector mapping, which we need - // when visiting the method tables. - llvm::DenseMap<unsigned, std::string> selectors; - if (Impl.ObjCSelectorTable) { - for (auto key : Impl.ObjCSelectorTable->keys()) { - std::string selector; - if (key.NumPieces == 0) - selector = identifiers[key.Identifiers[0]]; - else { - for (auto identID : key.Identifiers) { - selector += identifiers[identID]; - selector += ':'; - } - } - - unsigned selectorID = *Impl.ObjCSelectorTable->find(key); - selectors[selectorID] = selector; - } - } - - // Visit methods. - if (Impl.ObjCMethodTable) { - for (auto key : Impl.ObjCMethodTable->keys()) { - ContextID contextID(std::get<0>(key)); - const auto &selector = selectors[std::get<1>(key)]; - for (const auto &versioned : *Impl.ObjCMethodTable->find(key)) - visitor.visitObjCMethod(contextID, selector, std::get<2>(key), - versioned.second, versioned.first); - } - } - - // Visit properties. - if (Impl.ObjCPropertyTable) { - for (auto key : Impl.ObjCPropertyTable->keys()) { - ContextID contextID(std::get<0>(key)); - auto name = identifiers[std::get<1>(key)]; - char isInstance = std::get<2>(key); - for (const auto &versioned : *Impl.ObjCPropertyTable->find(key)) { - visitor.visitObjCProperty(contextID, name, isInstance, versioned.second, - versioned.first); - } - } - } - - // Visit global functions. - if (Impl.GlobalFunctionTable) { - for (auto key : Impl.GlobalFunctionTable->keys()) { - auto name = identifiers[key]; - for (const auto &versioned : *Impl.GlobalFunctionTable->find(key)) - visitor.visitGlobalFunction(name, versioned.second, versioned.first); - } - } - - // Visit global variables. - if (Impl.GlobalVariableTable) { - for (auto key : Impl.GlobalVariableTable->keys()) { - auto name = identifiers[key]; - for (const auto &versioned : *Impl.GlobalVariableTable->find(key)) - visitor.visitGlobalVariable(name, versioned.second, versioned.first); - } - } - - // Visit global variables. - if (Impl.EnumConstantTable) { - for (auto key : Impl.EnumConstantTable->keys()) { - auto name = identifiers[key]; - for (const auto &versioned : *Impl.EnumConstantTable->find(key)) - visitor.visitEnumConstant(name, versioned.second, versioned.first); - } - } - - // Visit tags. - if (Impl.TagTable) { - for (auto key : Impl.TagTable->keys()) { - auto name = identifiers[key]; - for (const auto &versioned : *Impl.TagTable->find(key)) - visitor.visitTag(name, versioned.second, versioned.first); - } - } - - // Visit typedefs. - if (Impl.TypedefTable) { - for (auto key : Impl.TypedefTable->keys()) { - auto name = identifiers[key]; - for (const auto &versioned : *Impl.TypedefTable->find(key)) - visitor.visitTypedef(name, versioned.second, versioned.first); - } - } -} - diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 1c992bf85ee68..768ff4c52f491 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -1129,403 +1129,3 @@ bool api_notes::compileAPINotes(StringRef yamlInput, return compile(module, sourceFile, os, targetOS, diagHandler, diagHandlerCtxt); } - -namespace { - // Deserialize the API notes file into a module. - class DecompileVisitor : public APINotesReader::Visitor { - /// Allocator used to clone those strings that need it. - llvm::BumpPtrAllocator Allocator; - - /// The module we're building. - Module TheModule; - - /// A known context, which tracks what we know about a context ID. - struct KnownContext { - /// Whether this is a protocol (vs. a class). - bool isProtocol; - - /// The indices into the top-level items for this context at each - /// Swift version. - SmallVector<std::pair<VersionTuple, unsigned>, 1> indices; - - Class &getContext(const VersionTuple &swiftVersion, - TopLevelItems &items) { - ClassesSeq &seq = isProtocol ? items.Protocols : items.Classes; - - for (auto &index : indices) { - if (index.first == swiftVersion) - return seq[index.second]; - } - - indices.push_back({swiftVersion, seq.size()}); - seq.push_back(Class()); - return seq.back(); - } - }; - - /// A mapping from context ID to a pair (index, is-protocol) that indicates - /// the index of that class or protocol in the global "classes" or - /// "protocols" list. - llvm::DenseMap<unsigned, KnownContext> knownContexts; - - /// Copy a string into allocated memory so it does disappear on us. - StringRef copyString(StringRef string) { - if (string.empty()) return StringRef(); - - void *ptr = Allocator.Allocate(string.size(), 1); - memcpy(ptr, string.data(), string.size()); - return StringRef(reinterpret_cast<const char *>(ptr), string.size()); - } - - /// Copy an optional string into allocated memory so it does disappear on us. - Optional<StringRef> maybeCopyString(Optional<StringRef> string) { - if (!string) return None; - - return copyString(*string); - } - - /// Copy an optional string into allocated memory so it does disappear on us. - Optional<StringRef> maybeCopyString(Optional<std::string> string) { - if (!string) return None; - - return copyString(*string); - } - - template<typename T> - void handleCommon(T &record, const CommonEntityInfo &info) { - handleAvailability(record.Availability, info); - record.SwiftPrivate = info.isSwiftPrivate(); - record.SwiftName = copyString(info.SwiftName); - } - - template<typename T> - void handleCommonType(T &record, const CommonTypeInfo &info) { - handleCommon(record, info); - record.SwiftBridge = maybeCopyString(info.getSwiftBridge()); - record.NSErrorDomain = maybeCopyString(info.getNSErrorDomain()); - } - - /// Map Objective-C context info. - void handleObjCContext(Class &record, StringRef name, - const ObjCContextInfo &info) { - record.Name = name; - - handleCommonType(record, info); - record.SwiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric(); - record.SwiftObjCMembers = info.getSwiftObjCMembers(); - - if (info.getDefaultNullability()) { - record.AuditedForNullability = true; - } - } - - /// Map availability information, if present. - void handleAvailability(AvailabilityItem &availability, - const CommonEntityInfo &info) { - if (info.Unavailable) { - availability.Mode = APIAvailability::None; - availability.Msg = copyString(info.UnavailableMsg); - } - - if (info.UnavailableInSwift) { - availability.Mode = APIAvailability::NonSwift; - availability.Msg = copyString(info.UnavailableMsg); - } - } - - /// Map parameter information for a function. - void handleParameters(ParamsSeq ¶ms, - const FunctionInfo &info) { - unsigned position = 0; - for (const auto &pi: info.Params) { - Param p; - p.Position = position++; - p.Nullability = pi.getNullability(); - p.NoEscape = pi.isNoEscape(); - p.Type = copyString(pi.getType()); - p.RetainCountConvention = pi.getRetainCountConvention(); - params.push_back(p); - } - } - - /// Map nullability information for a function. - void handleNullability(NullabilitySeq &nullability, - llvm::Optional<NullabilityKind> &nullabilityOfRet, - const FunctionInfo &info, - unsigned numParams) { - if (info.NullabilityAudited) { - nullabilityOfRet = info.getReturnTypeInfo(); - - // Figure out the number of parameters from the selector. - for (unsigned i = 0; i != numParams; ++i) - nullability.push_back(info.getParamTypeInfo(i)); - } - } - - TopLevelItems &getTopLevelItems(VersionTuple swiftVersion) { - if (!swiftVersion) return TheModule.TopLevel; - - for (auto &versioned : TheModule.SwiftVersions) { - if (versioned.Version == swiftVersion) - return versioned.Items; - } - - TheModule.SwiftVersions.push_back(Versioned()); - TheModule.SwiftVersions.back().Version = swiftVersion; - return TheModule.SwiftVersions.back().Items; - } - - public: - virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info, - VersionTuple swiftVersion) { - // Record this known context. - auto &items = getTopLevelItems(swiftVersion); - auto &known = knownContexts[contextID.Value]; - known.isProtocol = false; - - handleObjCContext(known.getContext(swiftVersion, items), name, info); - } - - virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info, - VersionTuple swiftVersion) { - // Record this known context. - auto &items = getTopLevelItems(swiftVersion); - auto &known = knownContexts[contextID.Value]; - known.isProtocol = true; - - handleObjCContext(known.getContext(swiftVersion, items), name, info); - } - - virtual void visitObjCMethod(ContextID contextID, StringRef selector, - bool isInstanceMethod, - const ObjCMethodInfo &info, - VersionTuple swiftVersion) { - Method method; - method.Selector = copyString(selector); - method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; - - handleCommon(method, info); - handleParameters(method.Params, info); - handleNullability(method.Nullability, method.NullabilityOfRet, info, - selector.count(':')); - method.DesignatedInit = info.DesignatedInit; - method.Required = info.Required; - method.ResultType = copyString(info.ResultType); - method.RetainCountConvention = info.getRetainCountConvention(); - auto &items = getTopLevelItems(swiftVersion); - knownContexts[contextID.Value].getContext(swiftVersion, items) - .Methods.push_back(method); - } - - virtual void visitObjCProperty(ContextID contextID, StringRef name, - bool isInstance, - const ObjCPropertyInfo &info, - VersionTuple swiftVersion) { - Property property; - property.Name = name; - property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class; - handleCommon(property, info); - - // FIXME: No way to represent "not audited for nullability". - if (auto nullability = info.getNullability()) { - property.Nullability = *nullability; - } - - property.SwiftImportAsAccessors = info.getSwiftImportAsAccessors(); - - property.Type = copyString(info.getType()); - - auto &items = getTopLevelItems(swiftVersion); - knownContexts[contextID.Value].getContext(swiftVersion, items) - .Properties.push_back(property); - } - - virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info, - VersionTuple swiftVersion) { - Function function; - function.Name = name; - handleCommon(function, info); - handleParameters(function.Params, info); - if (info.NumAdjustedNullable > 0) - handleNullability(function.Nullability, function.NullabilityOfRet, - info, info.NumAdjustedNullable-1); - function.ResultType = copyString(info.ResultType); - function.RetainCountConvention = info.getRetainCountConvention(); - auto &items = getTopLevelItems(swiftVersion); - items.Functions.push_back(function); - } - - virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info, - VersionTuple swiftVersion) { - GlobalVariable global; - global.Name = name; - handleCommon(global, info); - - // FIXME: No way to represent "not audited for nullability". - if (auto nullability = info.getNullability()) { - global.Nullability = *nullability; - } - global.Type = copyString(info.getType()); - - auto &items = getTopLevelItems(swiftVersion); - items.Globals.push_back(global); - } - - virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info, - VersionTuple swiftVersion) { - EnumConstant enumConstant; - enumConstant.Name = name; - handleCommon(enumConstant, info); - - auto &items = getTopLevelItems(swiftVersion); - items.EnumConstants.push_back(enumConstant); - } - - virtual void visitTag(StringRef name, const TagInfo &info, - VersionTuple swiftVersion) { - Tag tag; - tag.Name = name; - handleCommonType(tag, info); - tag.EnumExtensibility = info.EnumExtensibility; - tag.FlagEnum = info.isFlagEnum(); - auto &items = getTopLevelItems(swiftVersion); - items.Tags.push_back(tag); - } - - virtual void visitTypedef(StringRef name, const TypedefInfo &info, - VersionTuple swiftVersion) { - Typedef td; - td.Name = name; - handleCommonType(td, info); - td.SwiftWrapper = info.SwiftWrapper; - auto &items = getTopLevelItems(swiftVersion); - items.Typedefs.push_back(td); - } - - /// Retrieve the module. - Module &getModule() { return TheModule; } - }; -} - -/// Produce a flattened, numeric value for optional method/property kinds. -static unsigned flattenPropertyKind(llvm::Optional<MethodKind> kind) { - return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0; -} - -/// Sort the items in the given block of "top-level" items. -static void sortTopLevelItems(TopLevelItems &items) { - // Sort classes. - std::sort(items.Classes.begin(), items.Classes.end(), - [](const Class &lhs, const Class &rhs) -> bool { - return lhs.Name < rhs.Name; - }); - - // Sort protocols. - std::sort(items.Protocols.begin(), items.Protocols.end(), - [](const Class &lhs, const Class &rhs) -> bool { - return lhs.Name < rhs.Name; - }); - - // Sort methods and properties within each class and protocol. - auto sortMembers = [](Class &record) { - // Sort properties. - std::sort(record.Properties.begin(), record.Properties.end(), - [](const Property &lhs, const Property &rhs) -> bool { - return lhs.Name < rhs.Name || - (lhs.Name == rhs.Name && - flattenPropertyKind(lhs.Kind) < - flattenPropertyKind(rhs.Kind)); - }); - - // Sort methods. - std::sort(record.Methods.begin(), record.Methods.end(), - [](const Method &lhs, const Method &rhs) -> bool { - return lhs.Selector < rhs.Selector || - (lhs.Selector == rhs.Selector && - static_cast<unsigned>(lhs.Kind) - < static_cast<unsigned>(rhs.Kind)); - }); - }; - std::for_each(items.Classes.begin(), items.Classes.end(), sortMembers); - std::for_each(items.Protocols.begin(), items.Protocols.end(), sortMembers); - - // Sort functions. - std::sort(items.Functions.begin(), items.Functions.end(), - [](const Function &lhs, const Function &rhs) -> bool { - return lhs.Name < rhs.Name; - }); - - // Sort global variables. - std::sort(items.Globals.begin(), items.Globals.end(), - [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool { - return lhs.Name < rhs.Name; - }); - - // Sort enum constants. - std::sort(items.EnumConstants.begin(), items.EnumConstants.end(), - [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { - return lhs.Name < rhs.Name; - }); - - // Sort tags. - std::sort(items.Tags.begin(), items.Tags.end(), - [](const Tag &lhs, const Tag &rhs) -> bool { - return lhs.Name < rhs.Name; - }); - - // Sort typedefs. - std::sort(items.Typedefs.begin(), items.Typedefs.end(), - [](const Typedef &lhs, const Typedef &rhs) -> bool { - return lhs.Name < rhs.Name; - }); -} - -bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input, - llvm::raw_ostream &os) { - // Try to read the file. - auto reader = APINotesReader::get(std::move(input), VersionTuple()); - if (!reader) { - llvm::errs() << "not a well-formed API notes binary file\n"; - return true; - } - - DecompileVisitor decompileVisitor; - reader->visit(decompileVisitor); - - // Sort the data in the module, because the API notes reader doesn't preserve - // order. - auto &module = decompileVisitor.getModule(); - - // Set module name. - module.Name = reader->getModuleName(); - - // Set module options - auto opts = reader->getModuleOptions(); - if (opts.SwiftInferImportAsMember) - module.SwiftInferImportAsMember = true; - - // Sort the top-level items. - sortTopLevelItems(module.TopLevel); - - // Sort the Swift versions. - std::sort(module.SwiftVersions.begin(), module.SwiftVersions.end(), - [](const Versioned &lhs, const Versioned &rhs) -> bool { - return lhs.Version < rhs.Version; - }); - - // Sort the top-level items within each Swift version. - for (auto &versioned : module.SwiftVersions) - sortTopLevelItems(versioned.Items); - - // Output the YAML representation. - Output yout(os); - yout << module; - - return false; -} - From 8f1288e79839e5593256a46933bc64f19fe320c2 Mon Sep 17 00:00:00 2001 From: Jordan Rose <jordan_rose@apple.com> Date: Wed, 16 Jan 2019 21:48:24 -0800 Subject: [PATCH 366/582] API notes: remove unused "Availability: <OS>" feature This went away when we stopped compiling API notes. apple-llvm-split-commit: 91e9960eab7f484e1d892b35e7c0388b1347c658 apple-llvm-split-dir: clang/ --- .../clang/APINotes/APINotesYAMLCompiler.h | 9 ----- clang/lib/APINotes/APINotesManager.cpp | 1 - clang/lib/APINotes/APINotesYAMLCompiler.cpp | 33 ++----------------- 3 files changed, 3 insertions(+), 40 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h index c85ae29cd157f..fa991d3a3d0fc 100644 --- a/clang/include/clang/APINotes/APINotesYAMLCompiler.h +++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h @@ -35,19 +35,10 @@ namespace api_notes { Dump, }; - enum class OSType { - OSX, - IOS, - TvOS, - WatchOS, - Absent - }; - /// Converts API notes from YAML format to binary format. bool compileAPINotes(llvm::StringRef yamlInput, const FileEntry *sourceFile, llvm::raw_ostream &os, - OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler = nullptr, void *diagHandlerCtxt = nullptr); diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index c3478d11a5861..0cc3d4c8410f2 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -105,7 +105,6 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { if (api_notes::compileAPINotes(sourceBuffer->getBuffer(), SourceMgr.getFileEntryForID(sourceFileID), OS, - api_notes::OSType::Absent, srcMgrAdapter.getDiagHandler(), srcMgrAdapter.getDiagContext())) return nullptr; diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 768ff4c52f491..baa77a578749f 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -643,7 +643,6 @@ namespace { const Module &TheModule; const FileEntry *SourceFile; APINotesWriter *Writer; - OSType TargetOS; llvm::raw_ostream &OS; llvm::SourceMgr::DiagHandlerTy DiagHandler; void *DiagHandlerCtxt; @@ -661,23 +660,13 @@ namespace { public: YAMLConverter(const Module &module, const FileEntry *sourceFile, - OSType targetOS, llvm::raw_ostream &os, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt) : - TheModule(module), SourceFile(sourceFile), Writer(0), TargetOS(targetOS), OS(os), + TheModule(module), SourceFile(sourceFile), Writer(0), OS(os), DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt), ErrorOccured(false) {} - bool isAvailable(const AvailabilityItem &in) { - // Check if the API is available on the OS for which we are building. - if (in.Mode == APIAvailability::OSX && TargetOS != OSType::OSX) - return false; - if (in.Mode == APIAvailability::IOS && TargetOS != OSType::IOS) - return false; - return true; - } - bool convertAvailability(const AvailabilityItem &in, CommonEntityInfo &outInfo, llvm::StringRef apiName) { @@ -742,9 +731,6 @@ namespace { template<typename T> bool convertCommon(const T& common, CommonEntityInfo &info, StringRef apiName) { - if (!isAvailable(common.Availability)) - return true; - convertAvailability(common.Availability, info, apiName); info.setSwiftPrivate(common.SwiftPrivate); info.SwiftName = common.SwiftName; @@ -869,8 +855,6 @@ namespace { // Translate from Property into ObjCPropertyInfo. ObjCPropertyInfo pInfo; - if (!isAvailable(prop.Availability)) - continue; convertAvailability(prop.Availability, pInfo, prop.Name); pInfo.setSwiftPrivate(prop.SwiftPrivate); pInfo.SwiftName = prop.SwiftName; @@ -928,8 +912,6 @@ namespace { } GlobalVariableInfo info; - if (!isAvailable(global.Availability)) - continue; convertAvailability(global.Availability, info, global.Name); info.setSwiftPrivate(global.SwiftPrivate); info.SwiftName = global.SwiftName; @@ -950,8 +932,6 @@ namespace { } GlobalFunctionInfo info; - if (!isAvailable(function.Availability)) - continue; convertAvailability(function.Availability, info, function.Name); info.setSwiftPrivate(function.SwiftPrivate); info.SwiftName = function.SwiftName; @@ -975,8 +955,6 @@ namespace { } EnumConstantInfo info; - if (!isAvailable(enumConstant.Availability)) - continue; convertAvailability(enumConstant.Availability, info, enumConstant.Name); info.setSwiftPrivate(enumConstant.SwiftPrivate); info.SwiftName = enumConstant.SwiftName; @@ -1053,9 +1031,6 @@ namespace { } bool convertModule() { - if (!isAvailable(TheModule.Availability)) - return false; - // Set up the writer. // FIXME: This is kindof ugly. APINotesWriter writer(TheModule.Name, SourceFile); @@ -1086,12 +1061,11 @@ namespace { static bool compile(const Module &module, const FileEntry *sourceFile, llvm::raw_ostream &os, - api_notes::OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt){ using namespace api_notes; - YAMLConverter c(module, sourceFile, targetOS, os, diagHandler, diagHandlerCtxt); + YAMLConverter c(module, sourceFile, os, diagHandler, diagHandlerCtxt); return c.convertModule(); } @@ -1115,7 +1089,6 @@ static void printDiagnostic(const llvm::SMDiagnostic &diag, void *context) { bool api_notes::compileAPINotes(StringRef yamlInput, const FileEntry *sourceFile, llvm::raw_ostream &os, - OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt) { Module module; @@ -1127,5 +1100,5 @@ bool api_notes::compileAPINotes(StringRef yamlInput, if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt)) return true; - return compile(module, sourceFile, os, targetOS, diagHandler, diagHandlerCtxt); + return compile(module, sourceFile, os, diagHandler, diagHandlerCtxt); } From 9e328fe90994c83b0984f6762770805b109c854e Mon Sep 17 00:00:00 2001 From: Dan Liew <dliew@apple.com> Date: Tue, 22 Jan 2019 15:31:46 +0000 Subject: [PATCH 367/582] Disable LSan sanitizer_common tests due to AppleClang not supporting LSan (rdar://problem/45841334). rdar://problem/47381908 apple-llvm-split-commit: 9353cee1fed01cfac83294b746dd99f11454e3e2 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/test/sanitizer_common/lit.common.cfg | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg b/compiler-rt/test/sanitizer_common/lit.common.cfg index 75cc7b77e70e7..a3335ae87ab37 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg @@ -70,3 +70,9 @@ config.suffixes = ['.c', '.cc', '.cpp'] if config.host_os not in ['Linux', 'Darwin', 'NetBSD', 'FreeBSD']: config.unsupported = True + +# Apple-internal: Disable LSan sanitizer_common tests +# because AppleClang doesn't support LSan (rdar://problem/47381908). +if config.tool_name == 'lsan' and config.host_os == 'Darwin': + lit_config.note('LSan sanitizer_common tests disabled') + config.unsupported = True From aa55d0ea0b69e9b01bcb1ca50fdbbdd4db333ab6 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 24 Jan 2019 12:19:11 -0800 Subject: [PATCH 368/582] Adapt SemaAPINotes.cpp to r352079 apple-llvm-split-commit: 9f121e2f36c2187783b27927ab965c730780be9d apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaAPINotes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index c0b7e47211b86..eb5ed9b670d86 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -280,6 +280,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, info.UnavailableMsg), /*Strict=*/false, /*Replacement=*/StringRef(), + /*Priority=*/Sema::AP_Explicit, /*SpellingIndex*/0); }, [](const Decl *decl) { From 8c5c6adafe3f654631078dda4312e370eb5cedb8 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Mon, 28 Jan 2019 09:26:53 -0800 Subject: [PATCH 369/582] [PM/CC1] Add -f[no-]split-cold-code CC1 options to toggle splitting (llvm) In order for the hot/cold splitting pass to graduate out of experimental status, users need some way to safely enable it. The current method of passing -mllvm -hot-cold-split=true to clang is not safe, because directly setting a cl::opt cannot interact well with other CC1 options (e.g. -O0, or -disable-llvm-passes). This patch adds -f[no-]split-cold-code CC1 options to clang so that the splitting pass can be toggled/combined with other options without issue. This makes it possible to deploy the new pass on a large scale. I've held off on adding a driver option because the ultimate goal is to remove these options, and to simply have the pass enabled by default. apple-llvm-split-commit: 378460c28e9c72b02f816e1bddadd43a5171ad8f apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Passes/PassBuilder.h | 6 ++++++ llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h | 1 + llvm/lib/Passes/PassBuilder.cpp | 6 +++++- llvm/lib/Transforms/IPO/PassManagerBuilder.cpp | 3 ++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/Passes/PassBuilder.h b/llvm/include/llvm/Passes/PassBuilder.h index 0ea3ae916aef7..6e2201bbf1e83 100644 --- a/llvm/include/llvm/Passes/PassBuilder.h +++ b/llvm/include/llvm/Passes/PassBuilder.h @@ -577,6 +577,10 @@ class PassBuilder { TopLevelPipelineParsingCallbacks.push_back(C); } + /// Enable or disable the hot/cold splitting optimization. By default, it is + /// disabled. + void setEnableHotColdSplitting(bool Enabled); + private: static Optional<std::vector<PipelineElement>> parsePipelineText(StringRef Text); @@ -664,6 +668,8 @@ class PassBuilder { // AA callbacks SmallVector<std::function<bool(StringRef Name, AAManager &AA)>, 2> AAParsingCallbacks; + // Tunable passes + bool SplitColdCode = false; }; /// This utility template takes care of adding require<> and invalidate<> diff --git a/llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h b/llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h index f82cfdc063d54..629286939908b 100644 --- a/llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h +++ b/llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h @@ -152,6 +152,7 @@ class PassManagerBuilder { bool VerifyInput; bool VerifyOutput; bool MergeFunctions; + bool SplitColdCode; bool PrepareForLTO; bool PrepareForThinLTO; bool PerformThinLTO; diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 045c8541c0d04..c990058e3b28c 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -663,7 +663,7 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level, // not grow after inlining, and 2) inhibiting inlining of cold code improves // code size & compile time. Split after Mem2Reg to make code model estimates // more accurate, but before InstCombine to allow it to clean things up. - if (EnableHotColdSplit && Phase != ThinLTOPhase::PostLink) + if ((EnableHotColdSplit || SplitColdCode) && Phase != ThinLTOPhase::PostLink) MPM.addPass(HotColdSplittingPass()); // Create a small function pass pipeline to cleanup after all the global @@ -2079,3 +2079,7 @@ Error PassBuilder::parseAAPipeline(AAManager &AA, StringRef PipelineText) { return Error::success(); } + +void PassBuilder::setEnableHotColdSplitting(bool Enabled) { + SplitColdCode = Enabled; +} diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp index 03d7088eab4ec..872e8ac5b62f2 100644 --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -168,6 +168,7 @@ PassManagerBuilder::PassManagerBuilder() { VerifyInput = false; VerifyOutput = false; MergeFunctions = false; + SplitColdCode = false; PrepareForLTO = false; EnablePGOInstrGen = false; PGOInstrGen = ""; @@ -519,7 +520,7 @@ void PassManagerBuilder::populateModulePassManager( // Split out cold code before inlining. See comment in the new PM // (\ref buildModuleSimplificationPipeline). - if (EnableHotColdSplit && DefaultOrPreLinkPipeline) + if ((EnableHotColdSplit || SplitColdCode) && DefaultOrPreLinkPipeline) MPM.add(createHotColdSplittingPass()); addInstructionCombiningPass(MPM); // Clean up after IPCP & DAE From 63688242ece6a21fb4a6a7547e09f4298f61640b Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Mon, 28 Jan 2019 09:27:11 -0800 Subject: [PATCH 370/582] [PM/CC1] Add -f[no-]split-cold-code CC1 options to toggle splitting In order for the hot/cold splitting pass to graduate out of experimental status, users need some way to safely enable it. The current method of passing -mllvm -hot-cold-split=true to clang is not safe, because directly setting a cl::opt cannot interact well with other CC1 options (e.g. -O0, or -disable-llvm-passes). This patch adds -f[no-]split-cold-code CC1 options to clang so that the splitting pass can be toggled/combined with other options without issue. This makes it possible to deploy the new pass on a large scale. I've held off on adding a driver option because the ultimate goal is to remove these options, and to simply have the pass enabled by default. apple-llvm-split-commit: 560c685d255479006ac474b05d54ef85951d45b1 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/CodeGenOptions.def | 1 + clang/include/clang/Driver/CC1Options.td | 4 + clang/lib/CodeGen/BackendUtil.cpp | 4 + clang/lib/Frontend/CompilerInvocation.cpp | 7 ++ clang/test/CodeGen/split-cold-code.c | 81 ++++++++++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 clang/test/CodeGen/split-cold-code.c diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index a7aafcf2f4d58..5ca39e127685d 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -124,6 +124,7 @@ CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can ///< linker. CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants. CODEGENOPT(MergeFunctions , 1, 0) ///< Set when -fmerge-functions is enabled. +CODEGENOPT(SplitColdCode , 1, 0) ///< Set when -fsplit-cold-code is enabled. CODEGENOPT(MSVolatile , 1, 0) ///< Set when /volatile:ms is enabled. CODEGENOPT(NoCommon , 1, 0) ///< Set when -fno-common or C++ is enabled. CODEGENOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm is diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index cde766433a75a..1ae6d559e838b 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -227,6 +227,10 @@ def fdump_vtable_layouts : Flag<["-"], "fdump-vtable-layouts">, HelpText<"Dump the layouts of all vtables that will be emitted in a translation unit">; def fmerge_functions : Flag<["-"], "fmerge-functions">, HelpText<"Permit merging of identical functions when optimizing.">; +def fsplit_cold_code : Flag<["-"], "fsplit-cold-code">, + HelpText<"Permit splitting of cold code when optimizing (off by default).">; +def fno_split_cold_code : Flag<["-"], "fno-split-cold-code">, + HelpText<"Disable splitting of cold code when optimizing.">; def femit_coverage_notes : Flag<["-"], "femit-coverage-notes">, HelpText<"Emit a gcov coverage notes file when compiling.">; def femit_coverage_data: Flag<["-"], "femit-coverage-data">, diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 76f5f915e61e1..d4322a7176253 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -554,6 +554,7 @@ void EmitAssemblyHelper::CreatePasses(legacy::PassManager &MPM, PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops; PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions; + PMBuilder.SplitColdCode = CodeGenOpts.SplitColdCode; PMBuilder.PrepareForThinLTO = CodeGenOpts.PrepareForThinLTO; PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO; PMBuilder.RerollLoops = CodeGenOpts.RerollLoops; @@ -1012,6 +1013,9 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( // configure the pipeline. PassBuilder::OptimizationLevel Level = mapToLevel(CodeGenOpts); + // -f[no-]split-cold-code + PB.setEnableHotColdSplitting(CodeGenOpts.SplitColdCode); + // Register callbacks to schedule sanitizer passes at the appropriate part of // the pipeline. // FIXME: either handle asan/the remaining sanitizers or error out diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 47fbbf3558edd..8568165fd1285 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1321,6 +1321,13 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.DefaultFunctionAttrs = Args.getAllArgValues(OPT_default_function_attr); + // -f[no-]split-cold-code + // This may only be enabled when optimizing, and when small code size + // increases are tolerable. + Opts.SplitColdCode = + (Opts.OptimizationLevel > 0) && (Opts.OptimizeSize != 2) && + Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, false); + return Success; } diff --git a/clang/test/CodeGen/split-cold-code.c b/clang/test/CodeGen/split-cold-code.c new file mode 100644 index 0000000000000..a675f81101134 --- /dev/null +++ b/clang/test/CodeGen/split-cold-code.c @@ -0,0 +1,81 @@ +// === Old PM === +// No splitting at -O0. +// RUN: %clang_cc1 -O0 -fsplit-cold-code -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s +// +// No splitting at -Oz. +// RUN: %clang_cc1 -Oz -fsplit-cold-code -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s +// +// No splitting by default, even at -O3. +// RUN: %clang_cc1 -O3 -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s +// +// No splitting when it's explicitly disabled. +// RUN: %clang_cc1 -O3 -fno-split-cold-code -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s +// +// No splitting when LLVM passes are disabled. +// RUN: %clang_cc1 -O3 -fsplit-cold-code -disable-llvm-passes -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s +// +// Split at -O1. +// RUN: %clang_cc1 -O1 -fsplit-cold-code -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s +// +// Split at -Os. +// RUN: %clang_cc1 -Os -fsplit-cold-code -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s +// +// Split at -O2. +// RUN: %clang_cc1 -O2 -fsplit-cold-code -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s +// +// Split at -O3. +// RUN: %clang_cc1 -O3 -fsplit-cold-code -mllvm -debug-pass=Structure \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s + +// OLDPM-NO-SPLIT-NOT: Hot Cold Split + +// OLDPM-SPLIT: Hot Cold Split + +// === New PM (ditto) === +// No splitting at -O0. +// RUN: %clang_cc1 -O0 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// No splitting at -Oz. +// RUN: %clang_cc1 -Oz -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// No splitting by default, even at -O3. +// RUN: %clang_cc1 -O3 -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// No splitting when it's explicitly disabled. +// RUN: %clang_cc1 -O3 -fno-split-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// No splitting when LLVM passes are disabled. +// RUN: %clang_cc1 -O3 -fsplit-cold-code -disable-llvm-passes -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// Split at -O1. +// RUN: %clang_cc1 -O1 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// +// Split at -Os. +// RUN: %clang_cc1 -Os -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// +// Split at -O2. +// RUN: %clang_cc1 -O2 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// +// Split at -O3. +// RUN: %clang_cc1 -O3 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s + +// NEWPM-NO-SPLIT-NOT: HotColdSplit + +// NEWPM-SPLIT: HotColdSplit From faf823857ecc5109d35912eb7459a2b0791d6de3 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Mon, 28 Jan 2019 09:44:14 -0800 Subject: [PATCH 371/582] [test] Set -fno-split-cold-code in tests sensitive to splitting, NFC Set -fno-split-cold-code in a few clang tests which check the output of a full optimization pipeline. These tests are sensitive to changes in the pipeline in general, and to a change which enables splitting by default in particular. apple-llvm-split-commit: b65498798e5f5c003300b357a509f57af18a62bb apple-llvm-split-dir: clang/ --- clang/test/CodeGen/pgo-sample-thinlto-summary.c | 8 ++++---- clang/test/CodeGen/split-cold-code.c | 8 ++++---- clang/test/CodeGenCXX/nrvo.cpp | 6 +++--- clang/test/CodeGenObjC/synchronized.m | 2 +- clang/test/CodeGenObjCXX/exceptions-legacy.mm | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/clang/test/CodeGen/pgo-sample-thinlto-summary.c b/clang/test/CodeGen/pgo-sample-thinlto-summary.c index eae35a040e5f8..48cdaad848797 100644 --- a/clang/test/CodeGen/pgo-sample-thinlto-summary.c +++ b/clang/test/CodeGen/pgo-sample-thinlto-summary.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -O2 -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -o - 2>&1 | FileCheck %s -check-prefix=SAMPLEPGO -// RUN: %clang_cc1 -O2 -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -flto=thin -o - 2>&1 | FileCheck %s -check-prefix=THINLTO -// RUN: %clang_cc1 -O2 -fexperimental-new-pass-manager -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -o - 2>&1 | FileCheck %s -check-prefix=SAMPLEPGO -// RUN: %clang_cc1 -O2 -fexperimental-new-pass-manager -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -flto=thin -o - 2>&1 | FileCheck %s -check-prefix=THINLTO +// RUN: %clang_cc1 -O2 -fno-split-cold-code -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -o - 2>&1 | FileCheck %s -check-prefix=SAMPLEPGO +// RUN: %clang_cc1 -O2 -fno-split-cold-code -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -flto=thin -o - 2>&1 | FileCheck %s -check-prefix=THINLTO +// RUN: %clang_cc1 -O2 -fno-split-cold-code -fexperimental-new-pass-manager -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -o - 2>&1 | FileCheck %s -check-prefix=SAMPLEPGO +// RUN: %clang_cc1 -O2 -fno-split-cold-code -fexperimental-new-pass-manager -fprofile-sample-use=%S/Inputs/pgo-sample-thinlto-summary.prof %s -emit-llvm -flto=thin -o - 2>&1 | FileCheck %s -check-prefix=THINLTO // Checks if hot call is inlined by normal compile, but not inlined by // thinlto compile. diff --git a/clang/test/CodeGen/split-cold-code.c b/clang/test/CodeGen/split-cold-code.c index a675f81101134..30d81bf42f026 100644 --- a/clang/test/CodeGen/split-cold-code.c +++ b/clang/test/CodeGen/split-cold-code.c @@ -7,9 +7,9 @@ // RUN: %clang_cc1 -Oz -fsplit-cold-code -mllvm -debug-pass=Structure \ // RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s // -// No splitting by default, even at -O3. +// Split by default. // RUN: %clang_cc1 -O3 -mllvm -debug-pass=Structure \ -// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s // // No splitting when it's explicitly disabled. // RUN: %clang_cc1 -O3 -fno-split-cold-code -mllvm -debug-pass=Structure \ @@ -48,9 +48,9 @@ // RUN: %clang_cc1 -Oz -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ // RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s // -// No splitting by default, even at -O3. +// Split by default. // RUN: %clang_cc1 -O3 -fexperimental-new-pass-manager -fdebug-pass-manager \ -// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s // // No splitting when it's explicitly disabled. // RUN: %clang_cc1 -O3 -fno-split-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ diff --git a/clang/test/CodeGenCXX/nrvo.cpp b/clang/test/CodeGenCXX/nrvo.cpp index 0f359b9c90007..6ab86710d96c6 100644 --- a/clang/test/CodeGenCXX/nrvo.cpp +++ b/clang/test/CodeGenCXX/nrvo.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fcxx-exceptions -fexceptions -std=c++03 -o - %s | FileCheck --check-prefixes=CHECK-EH,CHECK-EH-03 %s -// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fcxx-exceptions -fexceptions -std=c++11 -o - %s | FileCheck --check-prefixes=CHECK-EH,CHECK-EH-11 %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fno-split-cold-code -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fno-split-cold-code -fcxx-exceptions -fexceptions -std=c++03 -o - %s | FileCheck --check-prefixes=CHECK-EH,CHECK-EH-03 %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fno-split-cold-code -fcxx-exceptions -fexceptions -std=c++11 -o - %s | FileCheck --check-prefixes=CHECK-EH,CHECK-EH-11 %s // Test code generation for the named return value optimization. class X { diff --git a/clang/test/CodeGenObjC/synchronized.m b/clang/test/CodeGenObjC/synchronized.m index 0ce179ccc0367..1627b10172dd8 100644 --- a/clang/test/CodeGenObjC/synchronized.m +++ b/clang/test/CodeGenObjC/synchronized.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -o - %s -O2 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -o - %s -O2 -fno-split-cold-code | FileCheck %s @interface MyClass { diff --git a/clang/test/CodeGenObjCXX/exceptions-legacy.mm b/clang/test/CodeGenObjCXX/exceptions-legacy.mm index bfc8d640b7104..19474ff12f7ff 100644 --- a/clang/test/CodeGenObjCXX/exceptions-legacy.mm +++ b/clang/test/CodeGenObjCXX/exceptions-legacy.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fexceptions -fobjc-exceptions -O2 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fexceptions -fobjc-exceptions -O2 -fno-split-cold-code -o - %s | FileCheck %s // Test we maintain at least a basic amount of interoperation between // ObjC and C++ exceptions in the legacy runtime. From 0ecf71a66d5bb67d0a7398116aab5d3dbff07a2f Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Mon, 28 Jan 2019 09:48:00 -0800 Subject: [PATCH 372/582] swift-clang: Enable hot/cold splitting by default rdar://45142482 apple-llvm-split-commit: 4602da2e0bbc82cfb1665097fe63418fb956b91e apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInvocation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 8568165fd1285..a6a5f863bcf55 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1324,9 +1324,11 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, // -f[no-]split-cold-code // This may only be enabled when optimizing, and when small code size // increases are tolerable. + // + // swift-clang: Enable hot/cold splitting by default. Opts.SplitColdCode = (Opts.OptimizationLevel > 0) && (Opts.OptimizeSize != 2) && - Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, false); + Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, true); return Success; } From d564605c3d806594e70912f417065eb4967f8bde Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Wed, 30 Jan 2019 14:22:02 -0800 Subject: [PATCH 373/582] Revert "Merge remote-tracking branch 'github/upstream-with-swift' into HEAD" This reverts commit 362dfa550bde748b63e5640e6e93888d28b0486f, reversing changes made to daa6759576548a2f3825faddaa6811cabbfb45eb. apple-llvm-split-commit: 48588f96676d4242b7fb280e8b074f86db815c3b apple-llvm-split-dir: compiler-rt/ --- compiler-rt/README.txt | 1 + compiler-rt/lib/profile/InstrProfilingFile.c | 64 ++----------------- compiler-rt/lib/profile/InstrProfilingUtil.c | 19 ------ compiler-rt/lib/profile/InstrProfilingUtil.h | 2 - compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc | 8 +-- compiler-rt/test/lit.common.cfg | 3 - compiler-rt/test/lsan/lit.common.cfg | 5 -- .../test/profile/instrprof-exit-on-signal.c | 26 -------- .../test/sanitizer_common/lit.common.cfg | 6 -- 9 files changed, 7 insertions(+), 127 deletions(-) delete mode 100644 compiler-rt/test/profile/instrprof-exit-on-signal.c diff --git a/compiler-rt/README.txt b/compiler-rt/README.txt index 2d64f00e3ebe3..fc8843246e255 100644 --- a/compiler-rt/README.txt +++ b/compiler-rt/README.txt @@ -8,3 +8,4 @@ Compiler-RT is open source software. You may freely distribute it under the terms of the license agreement found in LICENSE.txt. ================================ + diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index 6a9fee4f37fe2..99b138b2dd46e 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -12,7 +12,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <signal.h> #ifdef _MSC_VER /* For _alloca. */ #include <malloc.h> @@ -63,7 +62,6 @@ static const char *getPNSStr(ProfileNameSpecifier PNS) { } #define MAX_PID_SIZE 16 -#define MAX_SIGNAL_HANDLERS 16 /* Data structure holding the result of parsed filename pattern. */ typedef struct lprofFilename { /* File name string possibly with %p or %h specifiers. */ @@ -85,14 +83,11 @@ typedef struct lprofFilename { * 2 profile data files. %1m is equivalent to %m. Also %m specifier * can only appear once at the end of the name pattern. */ unsigned MergePoolSize; - char ExitOnSignals[MAX_SIGNAL_HANDLERS]; - unsigned NumExitSignals; ProfileNameSpecifier PNS; } lprofFilename; COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0}, - {0}, 0, 0, 0, {0}, 0, - PNS_unknown}; + {0}, 0, 0, 0, PNS_unknown}; static int getCurFilenameLength(); static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf); @@ -291,19 +286,6 @@ static void truncateCurrentFile(void) { fclose(File); } -static void exitSignalHandler(int sig) { - (void)sig; - exit(0); -} - -static void installExitSignalHandlers(void) { - unsigned I; - for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { - lprofInstallSignalHandler(lprofCurFilename.ExitOnSignals[I], - exitSignalHandler); - } -} - static const char *DefaultProfileName = "default.profraw"; static void resetFilenameToDefault(void) { if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { @@ -314,25 +296,14 @@ static void resetFilenameToDefault(void) { lprofCurFilename.PNS = PNS_default; } -static int isDigit(char C) { return C >= '0' && C <= '9'; } - -static int isNonZeroDigit(char C) { return C >= '1' && C <= '9'; } - static int containsMergeSpecifier(const char *FilenamePat, int I) { return (FilenamePat[I] == 'm' || - (isNonZeroDigit(FilenamePat[I]) && + (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' && /* If FilenamePat[I] is not '\0', the next byte is guaranteed * to be in-bound as the string is null terminated. */ FilenamePat[I + 1] == 'm')); } -static int containsExitOnSignalSpecifier(const char *FilenamePat, int I) { - if (!isNonZeroDigit(FilenamePat[I])) - return 0; - return (FilenamePat[I + 1] == 'x') || - (isDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x'); -} - /* Parses the pattern string \p FilenamePat and stores the result to * lprofcurFilename structure. */ static int parseFilenamePattern(const char *FilenamePat, @@ -341,7 +312,6 @@ static int parseFilenamePattern(const char *FilenamePat, char *PidChars = &lprofCurFilename.PidChars[0]; char *Hostname = &lprofCurFilename.Hostname[0]; int MergingEnabled = 0; - char SignalNo; /* Clean up cached prefix and filename. */ if (lprofCurFilename.ProfilePathPrefix) @@ -394,22 +364,6 @@ static int parseFilenamePattern(const char *FilenamePat, lprofCurFilename.MergePoolSize = FilenamePat[I] - '0'; I++; /* advance to 'm' */ } - } else if (containsExitOnSignalSpecifier(FilenamePat, I)) { - if (lprofCurFilename.NumExitSignals == MAX_SIGNAL_HANDLERS) { - PROF_WARN("%%x specifier has been specified too many times in %s.\n", - FilenamePat); - return -1; - } - /* Grab the signal number. */ - SignalNo = FilenamePat[I] - '0'; - I++; /* advance to either another digit, or 'x' */ - if (FilenamePat[I] != 'x') { - SignalNo = (SignalNo * 10) + (FilenamePat[I] - '0'); - I++; /* advance to 'x' */ - } - lprofCurFilename.ExitOnSignals[lprofCurFilename.NumExitSignals] = - SignalNo; - ++lprofCurFilename.NumExitSignals; } } @@ -453,7 +407,6 @@ static void parseAndSetFilename(const char *FilenamePat, } truncateCurrentFile(); - installExitSignalHandlers(); } /* Return buffer length that is required to store the current profile @@ -462,12 +415,11 @@ static void parseAndSetFilename(const char *FilenamePat, #define SIGLEN 24 static int getCurFilenameLength() { int Len; - unsigned I; if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) + lprofCurFilename.MergePoolSize)) return strlen(lprofCurFilename.FilenamePat); Len = strlen(lprofCurFilename.FilenamePat) + @@ -475,11 +427,6 @@ static int getCurFilenameLength() { lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); if (lprofCurFilename.MergePoolSize) Len += SIGLEN; - for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { - Len -= 3; /* Drop the '%', signal number, and the 'x'. */ - if (lprofCurFilename.ExitOnSignals[I] >= 10) - --Len; /* Drop the second digit of the signal number. */ - } return Len; } @@ -496,7 +443,7 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) { + lprofCurFilename.MergePoolSize)) { if (!ForceUseBuf) return lprofCurFilename.FilenamePat; @@ -529,9 +476,6 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { J += S; if (FilenamePat[I] != 'm') I++; - } else if (containsExitOnSignalSpecifier(FilenamePat, I)) { - while (FilenamePat[I] != 'x') - ++I; } /* Drop any unknown substitutions. */ } else diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.c b/compiler-rt/lib/profile/InstrProfilingUtil.c index a2ecbed7bca62..2a68c35638667 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.c +++ b/compiler-rt/lib/profile/InstrProfilingUtil.c @@ -23,7 +23,6 @@ #include <sys/utsname.h> #endif -#include <signal.h> #include <stdlib.h> #include <string.h> @@ -271,24 +270,6 @@ COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) { return Sep; } -COMPILER_RT_VISIBILITY void lprofInstallSignalHandler(int sig, - void (*handler)(int)) { -#ifdef _WIN32 - void (*err)(int) = signal(sig, handler); - if (err == SIG_ERR) - PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n", - sig, errno); -#else - struct sigaction sigact; - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = handler; - int err = sigaction(sig, &sigact, NULL); - if (err) - PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n", - sig, err); -#endif -} - COMPILER_RT_VISIBILITY int lprofSuspendSigKill() { #if defined(__linux__) int PDeachSig = 0; diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.h b/compiler-rt/lib/profile/InstrProfilingUtil.h index e5910e0ad0f01..9cd0860fda237 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.h +++ b/compiler-rt/lib/profile/InstrProfilingUtil.h @@ -59,8 +59,6 @@ int lprofGetHostName(char *Name, int Len); unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV); void *lprofPtrFetchAdd(void **Mem, long ByteIncr); -void lprofInstallSignalHandler(int sig, void(*handler)(int)); - /* Temporarily suspend SIGKILL. Return value of 1 means a restore is needed. * Other return values mean no restore is needed. */ diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc index 20ae68455902b..53cc81cf207fa 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc @@ -275,14 +275,10 @@ void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread) { void ThreadFinish(ThreadState *thr) { ThreadCheckIgnore(thr); StatInc(thr, StatThreadFinish); - if (thr->stk_addr && thr->stk_size) { - MemoryResetRange(thr, /*pc=*/ 1, thr->stk_addr, thr->stk_size); + if (thr->stk_addr && thr->stk_size) DontNeedShadowFor(thr->stk_addr, thr->stk_size); - } - if (thr->tls_addr && thr->tls_size) { - MemoryResetRange(thr, /*pc=*/ 1, thr->tls_addr, thr->tls_size); + if (thr->tls_addr && thr->tls_size) DontNeedShadowFor(thr->tls_addr, thr->tls_size); - } thr->is_dead = true; ctx->thread_registry->FinishThread(thr->tid); } diff --git a/compiler-rt/test/lit.common.cfg b/compiler-rt/test/lit.common.cfg index 1a400463f43cf..6688c717a0240 100644 --- a/compiler-rt/test/lit.common.cfg +++ b/compiler-rt/test/lit.common.cfg @@ -101,9 +101,6 @@ if re.match(r'^x86_64.*-linux', config.target_triple): if config.have_zlib == "1": config.available_features.add("zlib") -if lit.util.isMacOSTriple(config.target_triple): - config.available_features.add('darwin') - # Use ugly construction to explicitly prohibit "clang", "clang++" etc. # in RUN lines. config.substitutions.append( diff --git a/compiler-rt/test/lsan/lit.common.cfg b/compiler-rt/test/lsan/lit.common.cfg index a8866e9ec2976..a5df951e2e04b 100644 --- a/compiler-rt/test/lsan/lit.common.cfg +++ b/compiler-rt/test/lsan/lit.common.cfg @@ -78,8 +78,3 @@ if re.search('mthumb', config.target_cflags) is not None: config.unsupported = True config.suffixes = ['.c', '.cc', '.cpp', '.mm'] - -# Apple-Clang: Disable LSan -if config.host_os == 'Darwin': - lit_config.note('Disabling LSan tests on Darwin') - config.unsupported = True diff --git a/compiler-rt/test/profile/instrprof-exit-on-signal.c b/compiler-rt/test/profile/instrprof-exit-on-signal.c deleted file mode 100644 index efe1013f4ba28..0000000000000 --- a/compiler-rt/test/profile/instrprof-exit-on-signal.c +++ /dev/null @@ -1,26 +0,0 @@ -// RUN: %clang_profgen -o %t %s -// -// Verify SIGTERM handling. -// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t 15 -// RUN: llvm-profdata show %t.profraw | FileCheck %s -// -// Verify SIGUSR1 handling. -// RUN: %run LLVM_PROFILE_FILE="%30x%t.profraw" %t 30 -// RUN: llvm-profdata show %t.profraw | FileCheck %s - -#include <stdlib.h> -#include <signal.h> -#include <unistd.h> - -// CHECK: Total functions: 1 -int main(int argc, char **argv) { - (void)argc; - - int sig = atoi(argv[1]); - kill(getpid(), sig); - - while (1) { - /* loop forever */ - } - return 1; -} diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg b/compiler-rt/test/sanitizer_common/lit.common.cfg index a3335ae87ab37..75cc7b77e70e7 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg @@ -70,9 +70,3 @@ config.suffixes = ['.c', '.cc', '.cpp'] if config.host_os not in ['Linux', 'Darwin', 'NetBSD', 'FreeBSD']: config.unsupported = True - -# Apple-internal: Disable LSan sanitizer_common tests -# because AppleClang doesn't support LSan (rdar://problem/47381908). -if config.tool_name == 'lsan' and config.host_os == 'Darwin': - lit_config.note('LSan sanitizer_common tests disabled') - config.unsupported = True From 2e4b15fc6bde596786946962d6447dc25d1bcff0 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Wed, 30 Jan 2019 15:18:19 -0800 Subject: [PATCH 374/582] Remove duplicate function definitions and fix merge error in fc63aafee84419321f92595b8e91e9aa04cb02fa. apple-llvm-split-commit: 25255322fb11f2b6bfc8a02e6d54172c88a528b9 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTDumper.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index 5b6ee10386d34..0948199a6eb6f 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -840,14 +840,6 @@ void ASTDumper::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { NodeDumper.dumpDeclRef(D->getPropertyIvarDecl()); } -void ASTDumper::Visit(const BlockDecl::Capture &C) { - dumpChild([=] { - NodeDumper.Visit(C); - if (C.hasCopyExpr()) - Visit(C.getCopyExpr()); - }); -} - void ASTDumper::VisitBlockDecl(const BlockDecl *D) { for (const auto &I : D->parameters()) Visit(I); From 11b8bbe3024abb81009a84d523ac2e3b4ac302a0 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Wed, 30 Jan 2019 15:56:00 -0800 Subject: [PATCH 375/582] Fix more merge errors that were introduced in fc63aafee84419321f92595b8e91e9aa04cb02fa. apple-llvm-split-commit: 066589c703d5714aac1fd4e0b036127296ead139 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTDumper.cpp | 4 ++++ clang/utils/TableGen/ClangAttrEmitter.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index 439c08f2dc7e7..ca00ac6cbe1ea 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -500,6 +500,10 @@ namespace { Visit(I); } + void VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *D); + void VisitObjCPropertyDecl(const ObjCPropertyDecl *D); + void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D); + void VisitBlockDecl(const BlockDecl *D) { for (const auto &I : D->parameters()) Visit(I); diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 7920a6f19bd82..408c4a8438122 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1256,7 +1256,7 @@ namespace { void writeDump(raw_ostream &OS) const override {} void writeDumpChildren(raw_ostream &OS) const override { - OS << " dumpAttr(SA->get" << getUpperName() << "());\n"; + OS << " Visit(SA->get" << getUpperName() << "());\n"; } void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } From 610643a54e8c6a82fee26de502ecfcc1d5271209 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Wed, 30 Jan 2019 17:19:58 -0800 Subject: [PATCH 376/582] Fix more merge errors that were introduced in fc63aafee84419321f92595b8e91e9aa04cb02fa. Revert all changes I made to ASTDumper.cpp and add the apple-only code that was committed in faeaf7a6af0a728301182c68f4ba1105425c1883 to TextNodeDumper. apple-llvm-split-commit: 886a4aa3c69f248bf9f95a32fab6b01256177090 apple-llvm-split-dir: clang/ --- clang/lib/AST/ASTDumper.cpp | 61 -------------------------------- clang/lib/AST/TextNodeDumper.cpp | 2 ++ 2 files changed, 2 insertions(+), 61 deletions(-) diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index ca00ac6cbe1ea..7cb61c8affe32 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -500,10 +500,6 @@ namespace { Visit(I); } - void VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *D); - void VisitObjCPropertyDecl(const ObjCPropertyDecl *D); - void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D); - void VisitBlockDecl(const BlockDecl *D) { for (const auto &I : D->parameters()) Visit(I); @@ -721,63 +717,6 @@ void ASTDumper::VisitVarTemplateDecl(const VarTemplateDecl *D) { dumpTemplateDecl(D, false); } -void ASTDumper::VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *D) { - NodeDumper.dumpName(D); - NodeDumper.dumpDeclRef(D->getClassInterface()); - OS << " "; - NodeDumper.dumpLocation(D->getClassInterfaceLoc()); -} - -void ASTDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { - NodeDumper.dumpName(D); - NodeDumper.dumpType(D->getType()); - - if (D->getPropertyImplementation() == ObjCPropertyDecl::Required) - OS << " required"; - else if (D->getPropertyImplementation() == ObjCPropertyDecl::Optional) - OS << " optional"; - - ObjCPropertyDecl::PropertyAttributeKind Attrs = D->getPropertyAttributes(); - if (Attrs != ObjCPropertyDecl::OBJC_PR_noattr) { - if (Attrs & ObjCPropertyDecl::OBJC_PR_readonly) - OS << " readonly"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_assign) - OS << " assign"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_readwrite) - OS << " readwrite"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_retain) - OS << " retain"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_copy) - OS << " copy"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_nonatomic) - OS << " nonatomic"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_atomic) - OS << " atomic"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_weak) - OS << " weak"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_strong) - OS << " strong"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_unsafe_unretained) - OS << " unsafe_unretained"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_class) - OS << " class"; - if (Attrs & ObjCPropertyDecl::OBJC_PR_getter) - NodeDumper.dumpDeclRef(D->getGetterMethodDecl(), "getter"); - if (Attrs & ObjCPropertyDecl::OBJC_PR_setter) - NodeDumper.dumpDeclRef(D->getSetterMethodDecl(), "setter"); - } -} - -void ASTDumper::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { - NodeDumper.dumpName(D->getPropertyDecl()); - if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) - OS << " synthesize"; - else - OS << " dynamic"; - NodeDumper.dumpDeclRef(D->getPropertyDecl()); - NodeDumper.dumpDeclRef(D->getPropertyIvarDecl()); -} - //===----------------------------------------------------------------------===// // Type method implementations //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 6e6700c7470f0..690002d13ab5b 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1841,6 +1841,8 @@ void TextNodeDumper::VisitObjCCompatibleAliasDecl( const ObjCCompatibleAliasDecl *D) { dumpName(D); dumpDeclRef(D->getClassInterface()); + OS << " "; + dumpLocation(D->getClassInterfaceLoc()); } void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { From b8d717e7aa915c3d8e154730779aabc9dcd2df94 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <compnerd@compnerd.org> Date: Mon, 4 Feb 2019 09:10:26 -0800 Subject: [PATCH 377/582] Transforms: repair Coroutines merge in f757d456f Fix a few minor mistakes in the merge in f757d456f7b4dfc09e804dcd8b4011e75b8e937f. apple-llvm-split-commit: 162362ba798ee0e7d259ae2d5075a56589956f63 apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 99ac507ef76f7..d6a4708532770 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -378,10 +378,12 @@ void CoroCloner::handleFinalSuspend() { BasicBlock *OldSwitchBB = Switch->getParent(); auto *NewSwitchBB = OldSwitchBB->splitBasicBlock(Switch, "Switch"); Builder.SetInsertPoint(OldSwitchBB->getTerminator()); - auto *GepIndex = Builder.CreateConstInBoundsGEP2_32(Shape.FrameTy, FramePtr, - 0, coro::Shape::SwitchFieldIndex::Resume,, "ResumeFn.addr"); + auto *GepIndex = Builder.CreateConstInBoundsGEP2_32( + Shape.FrameTy, NewFramePtr, 0, coro::Shape::SwitchFieldIndex::Resume, + "ResumeFn.addr"); auto *Load = Builder.CreateLoad( - Shape.FrameTy->getElementType(coro::Shape::SwiftFieldIndex::Resume), GepIndex); + Shape.FrameTy->getElementType(coro::Shape::SwitchFieldIndex::Resume), + GepIndex); auto *NullPtr = ConstantPointerNull::get(cast<PointerType>(Load->getType())); auto *Cond = Builder.CreateICmpEQ(Load, NullPtr); From 73800cf2dd002580f09637c40839c6b6163edf3f Mon Sep 17 00:00:00 2001 From: George Karpenkov <ekarpenkov@apple.com> Date: Wed, 6 Feb 2019 11:51:01 -0800 Subject: [PATCH 378/582] Hotfix after the merge: re-enable the option removed by the merge. apple-llvm-split-commit: 66ab4c5181db052d0b25415f4846c9aedbb3b6f1 apple-llvm-split-dir: llvm/ --- llvm/lib/Passes/PassBuilder.cpp | 2 +- llvm/lib/Transforms/IPO/PassManagerBuilder.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index df1b8e31f4e43..8c40172a52089 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -707,7 +707,7 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level, // not grow after inlining, and 2) inhibiting inlining of cold code improves // code size & compile time. Split after Mem2Reg to make code model estimates // more accurate, but before InstCombine to allow it to clean things up. - if (EnableHotColdSplit && Phase != ThinLTOPhase::PostLink) + if ((EnableHotColdSplit || SplitColdCode) && Phase != ThinLTOPhase::PostLink) MPM.addPass(HotColdSplittingPass()); // Require the GlobalsAA analysis for the module so we can query it within diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp index cb1f7e3f818d4..9f01f14641e3f 100644 --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -532,7 +532,7 @@ void PassManagerBuilder::populateModulePassManager( // Split out cold code before inlining. See comment in the new PM // (\ref buildModuleSimplificationPipeline). - if (EnableHotColdSplit && DefaultOrPreLinkPipeline) + if ((EnableHotColdSplit || SplitColdCode) && DefaultOrPreLinkPipeline) MPM.add(createHotColdSplittingPass()); // We add a module alias analysis pass here. In part due to bugs in the From 67c70038bcc0d771e2e39c875ad7d69e329c7fc4 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Wed, 6 Feb 2019 17:01:36 -0800 Subject: [PATCH 379/582] Reinstate r310605: [Modules] Prevent #import to reenter header if not building a module. When non-modular headers are imported while not building a module but in -fmodules mode, be conservative and preserve the default #import semantic: do not reenter headers. rdar://problem/33745031 rdar://problem/47712680 rdar://problem/33860135 rdar://problem/33860090 (cherry picked from commit eb31b95d5014635ba65156ac62bf9ecf3432a6c7) apple-llvm-split-commit: 03a4ff7d0ad68ec9678035e4c67109b353b116d7 apple-llvm-split-dir: clang/ --- clang/lib/Lex/HeaderSearch.cpp | 2 +- clang/test/Modules/Inputs/import-textual/x.h | 6 ++++++ clang/test/Modules/import-textual-nomodules.m | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 clang/test/Modules/Inputs/import-textual/x.h create mode 100644 clang/test/Modules/import-textual-nomodules.m diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp index 9c92f772c2b5a..5710f92581667 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -1233,7 +1233,7 @@ bool HeaderSearch::ShouldEnterIncludeFile(Preprocessor &PP, // headers find in the wild might rely only on #import and do not contain // controlling macros, be conservative and only try to enter textual headers // if such macro is present. - if (!FileInfo.isModuleHeader && + if (FileInfo.isCompilingModuleHeader && !FileInfo.isModuleHeader && FileInfo.getControllingMacro(ExternalLookup)) TryEnterHdr = true; return TryEnterHdr; diff --git a/clang/test/Modules/Inputs/import-textual/x.h b/clang/test/Modules/Inputs/import-textual/x.h new file mode 100644 index 0000000000000..9b41ccd0bdce3 --- /dev/null +++ b/clang/test/Modules/Inputs/import-textual/x.h @@ -0,0 +1,6 @@ +#ifndef RANDOM_DEP + +@interface X +@end + +#endif // RANDOM_DEP diff --git a/clang/test/Modules/import-textual-nomodules.m b/clang/test/Modules/import-textual-nomodules.m new file mode 100644 index 0000000000000..7cf8c1e186d27 --- /dev/null +++ b/clang/test/Modules/import-textual-nomodules.m @@ -0,0 +1,8 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -I%S/Inputs/import-textual -fmodules-cache-path=%t %s -verify + +// expected-no-diagnostics + +#import "x.h" +#import "x.h" + From 04f8ab0866ae0d89e32779120ce25d6efd7487dc Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Wed, 13 Feb 2019 11:54:42 -0800 Subject: [PATCH 380/582] [ADT] Remove partial template specialization that Swift was relying on See the following comment: > // Swift-only: The following partial template specialization was removed > // from LLVM because of a bug in GCC, but Swift is relying on it. > // (https://bugs.swift.org/browse/SR-9483) The specialization was added again in trunk and it's now causing build failures. rdar://48041871 apple-llvm-split-commit: 949d402161af8ea9295dd6530bbb8e0e06385cdd apple-llvm-split-dir: llvm/ --- llvm/include/llvm/ADT/Optional.h | 22 ---------------------- llvm/unittests/ADT/OptionalTest.cpp | 8 -------- 2 files changed, 30 deletions(-) diff --git a/llvm/include/llvm/ADT/Optional.h b/llvm/include/llvm/ADT/Optional.h index 6fb50c7c4ef85..d7b9d5cddc25c 100644 --- a/llvm/include/llvm/ADT/Optional.h +++ b/llvm/include/llvm/ADT/Optional.h @@ -146,28 +146,6 @@ template <typename T> struct OptionalStorage<T, true> : OptionalStorageBase<T> { ~OptionalStorage() = default; }; -// Swift-only: The following partial template specialization was removed -// from LLVM because of a bug in GCC, but Swift is relying on it. -// (https://bugs.swift.org/browse/SR-9483) - -#if !defined(__GNUC__) || defined(__clang__) // GCC up to GCC7 miscompiles this. -/// Storage for trivially copyable types only. -template <typename T> struct OptionalStorage<T, true> { - AlignedCharArrayUnion<T> storage; - bool hasVal = false; - - OptionalStorage() = default; - - OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); } - OptionalStorage &operator=(const T &y) { - *reinterpret_cast<T *>(storage.buffer) = y; - hasVal = true; - return *this; - } - - void reset() { hasVal = false; } -}; -#endif } // namespace optional_detail template <typename T> class Optional { diff --git a/llvm/unittests/ADT/OptionalTest.cpp b/llvm/unittests/ADT/OptionalTest.cpp index 6588c1a11fd15..c39b6727cd549 100644 --- a/llvm/unittests/ADT/OptionalTest.cpp +++ b/llvm/unittests/ADT/OptionalTest.cpp @@ -590,12 +590,4 @@ TEST_F(OptionalTest, UseInUnitTests) { EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, Comparable::get()), "object"); } -#if __has_feature(is_trivially_copyable) && defined(_LIBCPP_VERSION) -static_assert(std::is_trivially_copyable<Optional<int>>::value, - "Should be trivially copyable"); -static_assert( - !std::is_trivially_copyable<Optional<NonDefaultConstructible>>::value, - "Shouldn't be trivially copyable"); -#endif - } // end anonymous namespace From f148d1dc8b2992d814af082dad0007e5cd2a08ee Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Thu, 14 Feb 2019 14:12:33 -0800 Subject: [PATCH 381/582] Revert "swift-clang: Enable hot/cold splitting by default" This reverts commit 4602da2e0bbc82cfb1665097fe63418fb956b91e. rdar://47622429 apple-llvm-split-commit: 97c8401b7ac8b6322a2fa41d54b0e2684abd4a25 apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInvocation.cpp | 4 +--- clang/test/CodeGen/split-cold-code.c | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 4de3c528c7ca4..775bbbcf141a2 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1325,11 +1325,9 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, // -f[no-]split-cold-code // This may only be enabled when optimizing, and when small code size // increases are tolerable. - // - // swift-clang: Enable hot/cold splitting by default. Opts.SplitColdCode = (Opts.OptimizationLevel > 0) && (Opts.OptimizeSize != 2) && - Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, true); + Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, false); Opts.PassPlugins = Args.getAllArgValues(OPT_fpass_plugin_EQ); diff --git a/clang/test/CodeGen/split-cold-code.c b/clang/test/CodeGen/split-cold-code.c index 30d81bf42f026..41be7abf3eb9a 100644 --- a/clang/test/CodeGen/split-cold-code.c +++ b/clang/test/CodeGen/split-cold-code.c @@ -7,9 +7,9 @@ // RUN: %clang_cc1 -Oz -fsplit-cold-code -mllvm -debug-pass=Structure \ // RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s // -// Split by default. +// No splitting by default. // RUN: %clang_cc1 -O3 -mllvm -debug-pass=Structure \ -// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s // // No splitting when it's explicitly disabled. // RUN: %clang_cc1 -O3 -fno-split-cold-code -mllvm -debug-pass=Structure \ @@ -48,9 +48,9 @@ // RUN: %clang_cc1 -Oz -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ // RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s // -// Split by default. +// No splitting by default. // RUN: %clang_cc1 -O3 -fexperimental-new-pass-manager -fdebug-pass-manager \ -// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s // // No splitting when it's explicitly disabled. // RUN: %clang_cc1 -O3 -fno-split-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ From 08d603e3ff4a08a330806d4b13cff1ed0e2e4a84 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Fri, 15 Feb 2019 15:58:49 -0800 Subject: [PATCH 382/582] Re-apply "swift-clang: Enable hot/cold splitting by default" This reverts commit 97c8401b7ac8b6322a2fa41d54b0e2684abd4a25, thereby re-enabling splitting by default. apple-llvm-split-commit: 25995b15a64a601a256a0e9919b0886035be6302 apple-llvm-split-dir: clang/ --- clang/lib/Frontend/CompilerInvocation.cpp | 4 +++- clang/test/CodeGen/split-cold-code.c | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 775bbbcf141a2..4de3c528c7ca4 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1325,9 +1325,11 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, // -f[no-]split-cold-code // This may only be enabled when optimizing, and when small code size // increases are tolerable. + // + // swift-clang: Enable hot/cold splitting by default. Opts.SplitColdCode = (Opts.OptimizationLevel > 0) && (Opts.OptimizeSize != 2) && - Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, false); + Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, true); Opts.PassPlugins = Args.getAllArgValues(OPT_fpass_plugin_EQ); diff --git a/clang/test/CodeGen/split-cold-code.c b/clang/test/CodeGen/split-cold-code.c index 41be7abf3eb9a..30d81bf42f026 100644 --- a/clang/test/CodeGen/split-cold-code.c +++ b/clang/test/CodeGen/split-cold-code.c @@ -7,9 +7,9 @@ // RUN: %clang_cc1 -Oz -fsplit-cold-code -mllvm -debug-pass=Structure \ // RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s // -// No splitting by default. +// Split by default. // RUN: %clang_cc1 -O3 -mllvm -debug-pass=Structure \ -// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s // // No splitting when it's explicitly disabled. // RUN: %clang_cc1 -O3 -fno-split-cold-code -mllvm -debug-pass=Structure \ @@ -48,9 +48,9 @@ // RUN: %clang_cc1 -Oz -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ // RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s // -// No splitting by default. +// Split by default. // RUN: %clang_cc1 -O3 -fexperimental-new-pass-manager -fdebug-pass-manager \ -// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s // // No splitting when it's explicitly disabled. // RUN: %clang_cc1 -O3 -fno-split-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \ From 0176f64c13b087141c601e8a352106f4e3aec475 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai <vsapsai@apple.com> Date: Tue, 19 Feb 2019 13:31:21 -0800 Subject: [PATCH 383/582] [ObjC] Fix SubstObjCTypeArgsVisitor for TypedefType. TypedefType isn't always used for ObjCTypeParamDecl, handle the case when it's not. The bug was introduced during transforming code for ObjCTypeParamType to code for TypedefType. rdar://problem/48188677 apple-llvm-split-commit: 653ac70b7537db448a77e181c6a390925f4cdaca apple-llvm-split-dir: clang/ --- clang/lib/AST/Type.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 7bf7cd91759b9..8b5a950dbf745 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1177,8 +1177,9 @@ struct SubstObjCTypeArgsVisitor return Ctx.getObjCObjectPointerType(resultTy); } } + llvm_unreachable("Unexpected ObjCSubstitutionContext!"); } - llvm_unreachable("Unexpected ObjCSubstitutionContext!"); + return BaseType::VisitTypedefType(typedefTy); } QualType VisitFunctionType(const FunctionType *funcType) { From e348b5c8d625daf47702a2dad615367ec61b9446 Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai <vsapsai@apple.com> Date: Tue, 19 Feb 2019 16:35:52 -0800 Subject: [PATCH 384/582] [ObjC generics] Remove `__kindof` tests depending on reverted c7c63c5. rdar://problem/48188728 apple-llvm-split-commit: 86d47c7bb895fc01571ac049970c656640fa4b4f apple-llvm-split-dir: clang/ --- clang/test/SemaObjC/kindof.m | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/clang/test/SemaObjC/kindof.m b/clang/test/SemaObjC/kindof.m index 60bcf437da740..3398363399164 100644 --- a/clang/test/SemaObjC/kindof.m +++ b/clang/test/SemaObjC/kindof.m @@ -408,14 +408,6 @@ @interface NSDefaultGeneric<ObjectType : NSString *> : NSObject @property (copy) __kindof ObjectType kindof_object; @end -void testGeneric(NSGeneric<NSString*> *generic) { - NSObject *NSObject_obj; - // Assign from NSObject_obj to __kindof NSString*. - [generic test:NSObject_obj]; // expected-warning{{incompatible pointer types sending 'NSObject *' to parameter of type '__kindof NSString *'}} - NSString *NSString_str; - [generic test:NSString_str]; -} - void testGenericAssignment() { NSMutableString *NSMutableString_str; NSNumber *NSNumber_obj; @@ -424,13 +416,11 @@ void testGenericAssignment() { NSMutableString_str = generic.object; // expected-warning{{incompatible pointer types}} NSNumber_obj = generic.object; // expected-warning{{incompatible pointer types}} NSMutableString_str = generic.kindof_object; - NSNumber_obj = generic.kindof_object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof NSString *'}} NSGeneric<__kindof NSString*> *kindof_generic; NSMutableString_str = kindof_generic.object; NSNumber_obj = kindof_generic.object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof NSString *'}} NSMutableString_str = kindof_generic.kindof_object; - NSNumber_obj = kindof_generic.kindof_object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof __kindof NSString *'}} NSDefaultGeneric *default_generic; NSMutableString_str = default_generic.object; @@ -443,7 +433,6 @@ void testGenericAssignment() { NSMutableString_str = typedef_generic.object; // expected-warning{{incompatible pointer types}} NSNumber_obj = typedef_generic.object; // expected-warning{{incompatible pointer types}} NSMutableString_str = typedef_generic.kindof_object; - NSNumber_obj = typedef_generic.kindof_object; // expected-warning{{incompatible pointer types assigning to 'NSNumber *' from '__kindof Typedef_NSString'}} } void testKindofNonObjectType() { From 253b07402cd78662f5cc4d5a225b58b4b9fcb597 Mon Sep 17 00:00:00 2001 From: David Zarzycki <dave@znu.io> Date: Mon, 18 Feb 2019 09:05:26 -0500 Subject: [PATCH 385/582] [CMake] Add missing deps found via LLVM's -DBUILD_SHARED_LIBS=TRUE Top-of-tree builds just fine with: -DBUILD_SHARED_LIBS=TRUE apple-llvm-split-commit: 8ef579dfc4d3a0f7550dbd944d3c6079c0f61b6d apple-llvm-split-dir: clang/ --- clang/tools/IndexStore/CMakeLists.txt | 1 + clang/tools/libclang/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/clang/tools/IndexStore/CMakeLists.txt b/clang/tools/IndexStore/CMakeLists.txt index b648f54a5a1c1..1bcfc0734785d 100644 --- a/clang/tools/IndexStore/CMakeLists.txt +++ b/clang/tools/IndexStore/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES ) set(LIBS + clangIndex clangIndexDataStore ) diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt index dfb6f1c0890e6..4d09071c3fd88 100644 --- a/clang/tools/libclang/CMakeLists.txt +++ b/clang/tools/libclang/CMakeLists.txt @@ -36,6 +36,7 @@ set(SOURCES set(LIBS clangAST + clangASTMatchers clangAPINotes clangBasic clangDriver From b6c53ad05344fa306f1e903701b1e8ad79820e7f Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee.com@gmail.com> Date: Sun, 20 Jan 2019 14:29:54 -0800 Subject: [PATCH 386/582] Mark indexstore callbacks with noescape attribute apple-llvm-split-commit: af05b02365381cd57ec916745252fa3bdfb56c8d apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 50 ++++++++++++++++----------- clang/tools/IndexStore/IndexStore.cpp | 40 ++++++++++----------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index b5550b618ab53..e5154694eb2dc 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -68,12 +68,22 @@ # define __has_feature(x) 0 #endif +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + #if __has_feature(blocks) # define INDEXSTORE_HAS_BLOCKS 1 #else # define INDEXSTORE_HAS_BLOCKS 0 #endif +#if __has_attribute(noescape) +# define INDEXSTORE_NOESCAPE __attribute__((noescape)) +#else +# define INDEXSTORE_NOESCAPE +#endif + INDEXSTORE_BEGIN_DECLS typedef void *indexstore_error_t; @@ -103,13 +113,13 @@ indexstore_store_dispose(indexstore_t); #if INDEXSTORE_HAS_BLOCKS INDEXSTORE_PUBLIC bool indexstore_store_units_apply(indexstore_t, unsigned sorted, - bool(^applier)(indexstore_string_ref_t unit_name)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_string_ref_t unit_name)); #endif INDEXSTORE_PUBLIC bool indexstore_store_units_apply_f(indexstore_t, unsigned sorted, void *context, - bool(*applier)(void *context, indexstore_string_ref_t unit_name)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_string_ref_t unit_name)); typedef void *indexstore_unit_event_notification_t; typedef void *indexstore_unit_event_t; @@ -344,13 +354,13 @@ indexstore_occurrence_get_symbol(indexstore_occurrence_t); #if INDEXSTORE_HAS_BLOCKS INDEXSTORE_PUBLIC bool indexstore_occurrence_relations_apply(indexstore_occurrence_t, - bool(^applier)(indexstore_symbol_relation_t symbol_rel)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_relation_t symbol_rel)); #endif INDEXSTORE_PUBLIC bool indexstore_occurrence_relations_apply_f(indexstore_occurrence_t, void *context, - bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)); INDEXSTORE_PUBLIC uint64_t indexstore_occurrence_get_roles(indexstore_occurrence_t); @@ -376,8 +386,8 @@ indexstore_record_reader_dispose(indexstore_record_reader_t); /// interested in. INDEXSTORE_PUBLIC bool indexstore_record_reader_search_symbols(indexstore_record_reader_t, - bool(^filter)(indexstore_symbol_t symbol, bool *stop), - void(^receiver)(indexstore_symbol_t symbol)); + INDEXSTORE_NOESCAPE bool(^filter)(indexstore_symbol_t symbol, bool *stop), + INDEXSTORE_NOESCAPE void(^receiver)(indexstore_symbol_t symbol)); /// \param nocache if true, avoids allocating memory for the symbols. /// Useful when the caller does not intend to keep \c indexstore_record_reader_t @@ -385,17 +395,17 @@ indexstore_record_reader_search_symbols(indexstore_record_reader_t, INDEXSTORE_PUBLIC bool indexstore_record_reader_symbols_apply(indexstore_record_reader_t, bool nocache, - bool(^applier)(indexstore_symbol_t symbol)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_t symbol)); INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_apply(indexstore_record_reader_t, - bool(^applier)(indexstore_occurrence_t occur)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)); INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_in_line_range_apply(indexstore_record_reader_t, unsigned line_start, unsigned line_count, - bool(^applier)(indexstore_occurrence_t occur)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)); /// \param symbols if non-zero \c symbols_count, indicates the list of symbols /// that we want to get occurrences for. An empty array indicates that we want @@ -405,40 +415,40 @@ INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t, indexstore_symbol_t *symbols, size_t symbols_count, indexstore_symbol_t *related_symbols, size_t related_symbols_count, - bool(^applier)(indexstore_occurrence_t occur)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)); #endif INDEXSTORE_PUBLIC bool indexstore_record_reader_search_symbols_f(indexstore_record_reader_t, void *filter_ctx, - bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), + INDEXSTORE_NOESCAPE bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), void *receiver_ctx, - void(*receiver)(void *receiver_ctx, indexstore_symbol_t symbol)); + INDEXSTORE_NOESCAPE void(*receiver)(void *receiver_ctx, indexstore_symbol_t symbol)); INDEXSTORE_PUBLIC bool indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t, bool nocache, void *context, - bool(*applier)(void *context, indexstore_symbol_t symbol)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_t symbol)); INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_apply_f(indexstore_record_reader_t, void *context, - bool(*applier)(void *context, indexstore_occurrence_t occur)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)); INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_in_line_range_apply_f(indexstore_record_reader_t, unsigned line_start, unsigned line_count, void *context, - bool(*applier)(void *context, indexstore_occurrence_t occur)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)); INDEXSTORE_PUBLIC bool indexstore_record_reader_occurrences_of_symbols_apply_f(indexstore_record_reader_t, indexstore_symbol_t *symbols, size_t symbols_count, indexstore_symbol_t *related_symbols, size_t related_symbols_count, void *context, - bool(*applier)(void *context, indexstore_occurrence_t occur)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)); typedef void *indexstore_unit_reader_t; @@ -526,22 +536,22 @@ indexstore_unit_include_get_source_line(indexstore_unit_include_t); #if INDEXSTORE_HAS_BLOCKS INDEXSTORE_PUBLIC bool indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t, - bool(^applier)(indexstore_unit_dependency_t)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_unit_dependency_t)); INDEXSTORE_PUBLIC bool indexstore_unit_reader_includes_apply(indexstore_unit_reader_t, - bool(^applier)(indexstore_unit_include_t)); + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_unit_include_t)); #endif INDEXSTORE_PUBLIC bool indexstore_unit_reader_dependencies_apply_f(indexstore_unit_reader_t, void *context, - bool(*applier)(void *context, indexstore_unit_dependency_t)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_unit_dependency_t)); INDEXSTORE_PUBLIC bool indexstore_unit_reader_includes_apply_f(indexstore_unit_reader_t, void *context, - bool(*applier)(void *context, indexstore_unit_include_t)); + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_unit_include_t)); INDEXSTORE_END_DECLS diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index f950fa307b877..c761b5cbea05b 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -123,7 +123,7 @@ indexstore_store_dispose(indexstore_t store) { #if INDEXSTORE_HAS_BLOCKS bool indexstore_store_units_apply(indexstore_t c_store, unsigned sorted, - bool(^applier)(indexstore_string_ref_t unit_name)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_string_ref_t unit_name)) { IndexDataStore *store = static_cast<IndexDataStore*>(c_store); return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool { return applier(toIndexStoreString(unitName)); @@ -134,7 +134,7 @@ indexstore_store_units_apply(indexstore_t c_store, unsigned sorted, bool indexstore_store_units_apply_f(indexstore_t c_store, unsigned sorted, void *context, - bool(*applier)(void *context, indexstore_string_ref_t unit_name)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_string_ref_t unit_name)) { IndexDataStore *store = static_cast<IndexDataStore*>(c_store); return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool { return applier(context, toIndexStoreString(unitName)); @@ -372,7 +372,7 @@ indexstore_occurrence_get_symbol(indexstore_occurrence_t occur) { #if INDEXSTORE_HAS_BLOCKS bool indexstore_occurrence_relations_apply(indexstore_occurrence_t occur, - bool(^applier)(indexstore_symbol_relation_t symbol_rel)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_relation_t symbol_rel)) { auto *recOccur = static_cast<IndexRecordOccurrence*>(occur); for (auto &rel : recOccur->Relations) { if (!applier(&rel)) @@ -385,7 +385,7 @@ indexstore_occurrence_relations_apply(indexstore_occurrence_t occur, bool indexstore_occurrence_relations_apply_f(indexstore_occurrence_t occur, void *context, - bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)) { auto *recOccur = static_cast<IndexRecordOccurrence*>(occur); for (auto &rel : recOccur->Relations) { if (!applier(context, &rel)) @@ -442,8 +442,8 @@ indexstore_record_reader_dispose(indexstore_record_reader_t rdr) { /// interested in. bool indexstore_record_reader_search_symbols(indexstore_record_reader_t rdr, - bool(^filter)(indexstore_symbol_t symbol, bool *stop), - void(^receiver)(indexstore_symbol_t symbol)) { + INDEXSTORE_NOESCAPE bool(^filter)(indexstore_symbol_t symbol, bool *stop), + INDEXSTORE_NOESCAPE void(^receiver)(indexstore_symbol_t symbol)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto filterFn = [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn { @@ -461,7 +461,7 @@ indexstore_record_reader_search_symbols(indexstore_record_reader_t rdr, bool indexstore_record_reader_symbols_apply(indexstore_record_reader_t rdr, bool nocache, - bool(^applier)(indexstore_symbol_t symbol)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_t symbol)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordDecl *D) -> bool { return applier((indexstore_symbol_t)D); @@ -471,7 +471,7 @@ indexstore_record_reader_symbols_apply(indexstore_record_reader_t rdr, bool indexstore_record_reader_occurrences_apply(indexstore_record_reader_t rdr, - bool(^applier)(indexstore_occurrence_t occur)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { return applier((indexstore_occurrence_t)&RO); @@ -483,7 +483,7 @@ bool indexstore_record_reader_occurrences_in_line_range_apply(indexstore_record_reader_t rdr, unsigned line_start, unsigned line_count, - bool(^applier)(indexstore_occurrence_t occur)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { return applier((indexstore_occurrence_t)&RO); @@ -499,7 +499,7 @@ bool indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t rdr, indexstore_symbol_t *symbols, size_t symbols_count, indexstore_symbol_t *related_symbols, size_t related_symbols_count, - bool(^applier)(indexstore_occurrence_t occur)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { return applier((indexstore_occurrence_t)&RO); @@ -513,9 +513,9 @@ indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t bool indexstore_record_reader_search_symbols_f(indexstore_record_reader_t rdr, void *filter_ctx, - bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), + INDEXSTORE_NOESCAPE bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), void *receiver_ctx, - void(*receiver)(void *receiver_ctx, indexstore_symbol_t symbol)) { + INDEXSTORE_NOESCAPE void(*receiver)(void *receiver_ctx, indexstore_symbol_t symbol)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto filterFn = [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn { @@ -534,7 +534,7 @@ bool indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t rdr, bool nocache, void *context, - bool(*applier)(void *context, indexstore_symbol_t symbol)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_t symbol)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordDecl *D) -> bool { return applier(context, (indexstore_symbol_t)D); @@ -545,7 +545,7 @@ indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t rdr, bool indexstore_record_reader_occurrences_apply_f(indexstore_record_reader_t rdr, void *context, - bool(*applier)(void *context, indexstore_occurrence_t occur)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { return applier(context, (indexstore_occurrence_t)&RO); @@ -558,7 +558,7 @@ indexstore_record_reader_occurrences_in_line_range_apply_f(indexstore_record_rea unsigned line_start, unsigned line_count, void *context, - bool(*applier)(void *context, indexstore_occurrence_t occur)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { return applier(context, (indexstore_occurrence_t)&RO); @@ -571,7 +571,7 @@ indexstore_record_reader_occurrences_of_symbols_apply_f(indexstore_record_reader indexstore_symbol_t *symbols, size_t symbols_count, indexstore_symbol_t *related_symbols, size_t related_symbols_count, void *context, - bool(*applier)(void *context, indexstore_occurrence_t occur)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)) { auto *reader = static_cast<IndexRecordReader *>(rdr); auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool { return applier(context, (indexstore_occurrence_t)&RO); @@ -785,7 +785,7 @@ indexstore_unit_include_get_source_line(indexstore_unit_include_t c_inc) { #if INDEXSTORE_HAS_BLOCKS bool indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t rdr, - bool(^applier)(indexstore_unit_dependency_t)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_unit_dependency_t)) { auto reader = static_cast<IndexUnitReader*>(rdr); return reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &depInfo) -> bool { return applier((void*)&depInfo); @@ -794,7 +794,7 @@ indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t rdr, bool indexstore_unit_reader_includes_apply(indexstore_unit_reader_t rdr, - bool(^applier)(indexstore_unit_include_t)) { + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_unit_include_t)) { auto reader = static_cast<IndexUnitReader*>(rdr); return reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &incInfo) -> bool { return applier((void*)&incInfo); @@ -805,7 +805,7 @@ indexstore_unit_reader_includes_apply(indexstore_unit_reader_t rdr, bool indexstore_unit_reader_dependencies_apply_f(indexstore_unit_reader_t rdr, void *context, - bool(*applier)(void *context, indexstore_unit_dependency_t)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_unit_dependency_t)) { auto reader = static_cast<IndexUnitReader*>(rdr); return reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &depInfo) -> bool { return applier(context, (void*)&depInfo); @@ -815,7 +815,7 @@ indexstore_unit_reader_dependencies_apply_f(indexstore_unit_reader_t rdr, bool indexstore_unit_reader_includes_apply_f(indexstore_unit_reader_t rdr, void *context, - bool(*applier)(void *context, indexstore_unit_include_t)) { + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_unit_include_t)) { auto reader = static_cast<IndexUnitReader*>(rdr); return reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &incInfo) -> bool { return applier(context, (void*)&incInfo); From fd46ec82ca8289743fc47c3c85f7b4296c9a99f7 Mon Sep 17 00:00:00 2001 From: Bob Wilson <bob.wilson@apple.com> Date: Thu, 21 Mar 2019 09:40:45 -0700 Subject: [PATCH 387/582] Update reference to tok::angle_string_literal Clang r356530 renamed tok::angle_string_literal to tok::header_name. Update this code to match. apple-llvm-split-commit: e32680f098d3b2f3aa3c75b942226c047ce8c5a0 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/RenameIndexedFile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp index 2c8d0bc430150..47dc31390d9be 100644 --- a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -417,7 +417,7 @@ static void findInclusionDirectiveOccurrence( RawLex.setParsingPreprocessorDirective(true); RawLex.LexIncludeFilename(RawTok); if (RawTok.isNot(tok::string_literal) && - RawTok.isNot(tok::angle_string_literal)) + RawTok.isNot(tok::header_name)) return; StringRef Filename = llvm::sys::path::filename( StringRef(RawTok.getLiteralData(), RawTok.getLength()) From 093ca12637144e6f794b9bb70b5524f5d6ecf490 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Wed, 28 Mar 2018 18:31:37 -0700 Subject: [PATCH 388/582] [Modules] Skip adding unused module maps to the dependency file Current generation of dependency files when modules is on take into account all parsed module maps found while searching for a specific module. Add a new cc1 flag that allows changing that behavior: only add module maps that actually answer for the modules being searched for. rdar://problem/38394973 (cherry picked from commit 4c357ef0f3c0edc9b229e7f45357f4507023135d) Conflicts: include/clang/Frontend/DependencyOutputOptions.h include/clang/Lex/ModuleMap.h lib/Frontend/DependencyFile.cpp (cherry picked from commit 16cfd7f448f6f95587361c2a4da4bfdf17b59c80) apple-llvm-split-commit: 96163fbef9ca97f7583d45667f00e7ddd60c21b4 apple-llvm-split-dir: clang/ --- clang/include/clang/Driver/CC1Options.td | 2 ++ .../clang/Frontend/DependencyOutputOptions.h | 3 ++- clang/include/clang/Lex/ModuleMap.h | 8 ++++++++ clang/lib/Frontend/CompilerInvocation.cpp | 1 + clang/lib/Frontend/DependencyFile.cpp | 11 +++++++++++ clang/lib/Lex/ModuleMap.cpp | 13 +++++++++++-- .../Inputs/dependency-skip-unused/module.modulemap | 1 + .../dependency-skip-unused/x/module.modulemap | 1 + .../dependency-skip-unused/y/module.modulemap | 1 + .../Modules/dependency-skip-unused-modulemaps.m | 7 +++++++ 10 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 clang/test/Modules/Inputs/dependency-skip-unused/module.modulemap create mode 100644 clang/test/Modules/Inputs/dependency-skip-unused/x/module.modulemap create mode 100644 clang/test/Modules/Inputs/dependency-skip-unused/y/module.modulemap create mode 100644 clang/test/Modules/dependency-skip-unused-modulemaps.m diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 0267f35f40871..43c4064ce87c4 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -386,6 +386,8 @@ def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">; def sys_header_deps : Flag<["-"], "sys-header-deps">, HelpText<"Include system headers in dependency output">; +def skip_unused_modulemap_file_deps : Flag<["-"], "skip-unused-modulemap-deps">, + HelpText<"Include module map files only for imported modules in dependency output">; def module_file_deps : Flag<["-"], "module-file-deps">, HelpText<"Include module files in dependency output">; def header_include_file : Separate<["-"], "header-include-file">, diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h index 7a4f3337936fc..9583c43afd579 100644 --- a/clang/include/clang/Frontend/DependencyOutputOptions.h +++ b/clang/include/clang/Frontend/DependencyOutputOptions.h @@ -31,6 +31,7 @@ class DependencyOutputOptions { /// problems. unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list unsigned IncludeModuleFiles : 1; ///< Include module file dependencies. + unsigned SkipUnusedModuleMaps : 1; ///< Skip unused module map dependencies. /// Destination of cl.exe style /showIncludes info. ShowIncludesDestination ShowIncludesDest = ShowIncludesDestination::None; @@ -66,7 +67,7 @@ class DependencyOutputOptions { public: DependencyOutputOptions() : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0), - AddMissingHeaderDeps(0), IncludeModuleFiles(0) {} + AddMissingHeaderDeps(0), IncludeModuleFiles(0), SkipUnusedModuleMaps(0) {} }; } // end namespace clang diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 58a0e69d87684..47c50e55eb5a5 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -58,6 +58,14 @@ class ModuleMapCallbacks { virtual void moduleMapFileRead(SourceLocation FileStart, const FileEntry &File, bool IsSystem) {} + /// Called when a module map file matches a module lookup + /// + /// \param File The file itself. + /// \param M The module found that matches this module map. + /// \param IsSystem Whether this is a module map from a system include path. + virtual void moduleMapFoundForModule(const FileEntry &File, const Module *M, + bool IsSystem) {} + /// Called when a header is added during module map parsing. /// /// \param Filename The header file itself. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 16965d9549c72..24fb659451b83 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1351,6 +1351,7 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, Opts.Targets = Args.getAllArgValues(OPT_MT); Opts.IncludeSystemHeaders = Args.hasArg(OPT_sys_header_deps); Opts.IncludeModuleFiles = Args.hasArg(OPT_module_file_deps); + Opts.SkipUnusedModuleMaps = Args.hasArg(OPT_skip_unused_modulemap_file_deps); Opts.UsePhonyTargets = Args.hasArg(OPT_MP); Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); Opts.HeaderIncludeOutputFile = Args.getLastArgValue(OPT_header_include_file); diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index 363aff76f0224..b89e1ea350910 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -160,6 +160,7 @@ class DFGImpl : public PPCallbacks { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; + bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; @@ -176,6 +177,7 @@ class DFGImpl : public PPCallbacks { AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false), IncludeModuleFiles(Opts.IncludeModuleFiles), + SkipUnusedModuleMaps(Opts.SkipUnusedModuleMaps), OutputFormat(Opts.OutputFormat), InputFileIndex(0) { for (const auto &ExtraDep : Opts.ExtraDeps) { @@ -209,6 +211,7 @@ class DFGImpl : public PPCallbacks { bool AddFilename(StringRef Filename); bool includeSystemHeaders() const { return IncludeSystemHeaders; } bool includeModuleFiles() const { return IncludeModuleFiles; } + bool skipUnusedModuleMaps() const { return SkipUnusedModuleMaps; } }; class DFGMMCallback : public ModuleMapCallbacks { @@ -217,9 +220,17 @@ class DFGMMCallback : public ModuleMapCallbacks { DFGMMCallback(DFGImpl &Parent) : Parent(Parent) {} void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, bool IsSystem) override { + if (Parent.skipUnusedModuleMaps()) + return; if (!IsSystem || Parent.includeSystemHeaders()) Parent.AddFilename(Entry.getName()); } + void moduleMapFoundForModule(const FileEntry &Entry, const Module *M, + bool IsSystem) override { + if (Parent.skipUnusedModuleMaps() && + (!IsSystem || Parent.includeSystemHeaders())) + Parent.AddFilename(Entry.getName()); + } }; class DFGASTReaderListener : public ASTReaderListener { diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index 3abacd0ed3724..9fb1a10291a15 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -763,8 +763,17 @@ ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header, Module *ModuleMap::findModule(StringRef Name) const { llvm::StringMap<Module *>::const_iterator Known = Modules.find(Name); - if (Known != Modules.end()) - return Known->getValue(); + if (Known != Modules.end()) { + Module *M = Known->getValue(); + // Notify callbacks that we found a module map for the module. + if (!M->DefinitionLoc.isInvalid()) + for (const auto &Cb : Callbacks) + Cb->moduleMapFoundForModule( + *getContainingModuleMapFile(M), M, + SourceMgr.getFileCharacteristic(M->DefinitionLoc) == + SrcMgr::C_System_ModuleMap); + return M; + } return nullptr; } diff --git a/clang/test/Modules/Inputs/dependency-skip-unused/module.modulemap b/clang/test/Modules/Inputs/dependency-skip-unused/module.modulemap new file mode 100644 index 0000000000000..e50630d3a1818 --- /dev/null +++ b/clang/test/Modules/Inputs/dependency-skip-unused/module.modulemap @@ -0,0 +1 @@ +module A {} diff --git a/clang/test/Modules/Inputs/dependency-skip-unused/x/module.modulemap b/clang/test/Modules/Inputs/dependency-skip-unused/x/module.modulemap new file mode 100644 index 0000000000000..a8ecb09390a25 --- /dev/null +++ b/clang/test/Modules/Inputs/dependency-skip-unused/x/module.modulemap @@ -0,0 +1 @@ +module X {} diff --git a/clang/test/Modules/Inputs/dependency-skip-unused/y/module.modulemap b/clang/test/Modules/Inputs/dependency-skip-unused/y/module.modulemap new file mode 100644 index 0000000000000..e6830bc2b4f73 --- /dev/null +++ b/clang/test/Modules/Inputs/dependency-skip-unused/y/module.modulemap @@ -0,0 +1 @@ +module Y {} diff --git a/clang/test/Modules/dependency-skip-unused-modulemaps.m b/clang/test/Modules/dependency-skip-unused-modulemaps.m new file mode 100644 index 0000000000000..f3d84cb90d221 --- /dev/null +++ b/clang/test/Modules/dependency-skip-unused-modulemaps.m @@ -0,0 +1,7 @@ +// RUN: rm -rf %t.cache %t.d +// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.cache -dependency-file %t.d -MT dependencies -I%S/Inputs/dependency-skip-unused/x -I%S/Inputs/dependency-skip-unused/y -I%S/Inputs/dependency-skip-unused -skip-unused-modulemap-deps %s +// RUN: FileCheck %s < %t.d +// CHECK-NOT: dependency-skip-unused{{.}}x{{.}}module.modulemap +// CHECK-NOT: dependency-skip-unused{{.}}y{{.}}module.modulemap + +@import A; From 7264ab365a835fc03ca0c2521f1d090ce35caa65 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Thu, 29 Mar 2018 22:33:02 -0700 Subject: [PATCH 389/582] [Modules] Use -skip-unused-modulemap-deps by default This defaults dependency file generation to use -skip-unused-modulemap-deps It should allow build systems dependend on .d files to only take into consideration module maps that are used. rdar://problem/38394973 (cherry picked from commit 0ba15a4d47e9dab1460a5f76be10a1011ea0316b) (cherry picked from commit 9b5199328ea8241968600e28200b13c004231996) apple-llvm-split-commit: 243fb2e7b096ec68b8f8aabef8dabfed3efb32a5 apple-llvm-split-dir: clang/ --- clang/lib/Driver/ToolChains/Clang.cpp | 1 + clang/test/Modules/dependency-skip-unused-modulemaps.m | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 1600140f1dec3..860f50a7808a5 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1068,6 +1068,7 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, } CmdArgs.push_back("-dependency-file"); CmdArgs.push_back(DepFile); + CmdArgs.push_back("-skip-unused-modulemap-deps"); // Add a default target if one wasn't specified. if (!Args.hasArg(options::OPT_MT) && !Args.hasArg(options::OPT_MQ)) { diff --git a/clang/test/Modules/dependency-skip-unused-modulemaps.m b/clang/test/Modules/dependency-skip-unused-modulemaps.m index f3d84cb90d221..fb9a049f9c86d 100644 --- a/clang/test/Modules/dependency-skip-unused-modulemaps.m +++ b/clang/test/Modules/dependency-skip-unused-modulemaps.m @@ -1,7 +1,10 @@ -// RUN: rm -rf %t.cache %t.d +// RUN: rm -rf %t.cache %t.d %t.cmd // RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.cache -dependency-file %t.d -MT dependencies -I%S/Inputs/dependency-skip-unused/x -I%S/Inputs/dependency-skip-unused/y -I%S/Inputs/dependency-skip-unused -skip-unused-modulemap-deps %s // RUN: FileCheck %s < %t.d // CHECK-NOT: dependency-skip-unused{{.}}x{{.}}module.modulemap // CHECK-NOT: dependency-skip-unused{{.}}y{{.}}module.modulemap +// RUN: %clang -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.cache -MMD -MT dependencies -MF %t.d -I%S/Inputs/dependency-skip-unused/x -I%S/Inputs/dependency-skip-unused/y -I%S/Inputs/dependency-skip-unused %s -### 2>&1 | FileCheck -check-prefix=CC1_INV %s +// CC1_INV: -skip-unused-modulemap-deps + @import A; From 5eb392fa4dd2ee49e71da55c7ad326a05190d18f Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 26 Mar 2019 14:23:32 -0400 Subject: [PATCH 390/582] Don't run a full verifier pass in coro-splitting's private pipeline. Potentially addresses rdar://49022293. apple-llvm-split-commit: c1e3738a2ec79c083af7a5381250f41684c89322 apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 3d30549af6fc8..25e8e7ad5fccd 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -865,9 +865,14 @@ static void updateCoroFrame(coro::Shape &Shape, Function *ResumeFn, static void postSplitCleanup(Function &F) { removeUnreachableBlocks(F); + + // For now, we do a mandatory verification step because we don't + // entirely trust this pass. Note that we don't want to add a verifier + // pass to FPM below because it will also verify all the global data. + verifyFunction(F); + legacy::FunctionPassManager FPM(F.getParent()); - FPM.add(createVerifierPass()); FPM.add(createSCCPPass()); FPM.add(createCFGSimplificationPass()); FPM.add(createEarlyCSEPass()); From e288d90426163128de4508dc8139d8b0e810b29a Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Mon, 18 Mar 2019 23:48:15 -0400 Subject: [PATCH 391/582] Non-fragile ObjC ABI: Add support for __attribute__((objc_class_stub)) This attribute denotes a class with resiliently-sized metadata, which is initialized at runtime. Since there is no statically-emitted metadata in this case, Clang must reference a "class stub" instead. When emitting a classref load we have to call `objc_loadClassRef()` instead of loading directly. In the classref itself, the least significant bit of the class pointer is set to 1. The Objective-C runtime implements objc_loadClassRef() by checking if the least significant bit of the class pointer is 1; if not, it simply returns the class pointer. Otherwise, it calls an initialization hook stored inside the class stub, and stores the result of the hook back to the classref to fast path future loads. Note that categories continue to be emitted as before; the runtime will lazily attach categories when class stubs are realized. apple-llvm-split-commit: b4bcae3ea58d0c5572f612c1eda469e6f958e1c7 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/Attr.td | 12 ++- clang/include/clang/Basic/AttrDocs.td | 17 ++++ .../clang/Basic/DiagnosticSemaKinds.td | 6 ++ clang/include/clang/Basic/ObjCRuntime.h | 16 ++++ clang/lib/CodeGen/CGObjCMac.cpp | 88 ++++++++++++++++--- clang/lib/Sema/SemaDeclAttr.cpp | 3 + clang/lib/Sema/SemaDeclObjC.cpp | 8 ++ clang/test/CodeGenObjC/class-stubs.m | 80 +++++++++++++++++ ...a-attribute-supported-attributes-list.test | 1 + .../SemaObjC/class-stub-attr-unsupported.m | 10 +++ clang/test/SemaObjC/class-stub-attr.m | 27 ++++++ clang/utils/TableGen/ClangAttrEmitter.cpp | 23 +++-- 12 files changed, 271 insertions(+), 20 deletions(-) create mode 100644 clang/test/CodeGenObjC/class-stubs.m create mode 100644 clang/test/SemaObjC/class-stub-attr-unsupported.m create mode 100644 clang/test/SemaObjC/class-stub-attr.m diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 0769c9116c959..f4ebe6fdcd4bd 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -289,9 +289,10 @@ class SubjectList<list<AttrSubject> subjects, SubjectDiag diag = WarnDiag, string CustomDiag = customDiag; } -class LangOpt<string name, bit negated = 0> { +class LangOpt<string name, bit negated = 0, string customCode = ""> { string Name = name; bit Negated = negated; + string CustomCode = customCode; } def MicrosoftExt : LangOpt<"MicrosoftExt">; def Borland : LangOpt<"Borland">; @@ -303,6 +304,8 @@ def RenderScript : LangOpt<"RenderScript">; def ObjC : LangOpt<"ObjC">; def BlocksSupported : LangOpt<"Blocks">; def ObjCAutoRefCount : LangOpt<"ObjCAutoRefCount">; +def ObjCNonFragileRuntime : LangOpt<"ObjCNonFragileRuntime", 0, + "LangOpts.ObjCRuntime.allowsClassStubs()">; // Defines targets for target-specific attributes. Empty lists are unchecked. class TargetSpec { @@ -1816,6 +1819,13 @@ def ObjCRuntimeVisible : Attr { let Documentation = [ObjCRuntimeVisibleDocs]; } +def ObjCClassStub : Attr { + let Spellings = [Clang<"objc_class_stub">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [ObjCClassStubDocs]; + let LangOpts = [ObjCNonFragileRuntime]; +} + def ObjCBoxable : Attr { let Spellings = [Clang<"objc_boxable">]; let Subjects = SubjectList<[Record], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index ec934d72aea2e..3fd355a20066c 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1084,6 +1084,23 @@ This attribute specifies that the Objective-C class to which it applies is visib }]; } +def ObjCClassStubDocs : Documentation { + let Category = DocCatType; + let Content = [{ +This attribute specifies that the Objective-C class to which it applies is +instantiated at runtime. + +Unlike ``__attribute__((objc_runtime_visible))``, a class having this attribute +still has a "class stub" that is visible to the linker. This allows categories +to be defined. Static message sends with the class as a receiver use a special +access pattern to ensure the class is lazily instantiated from the class stub. + +Classes annotated with this attribute cannot be subclassed and cannot have +implementations defined for them. This attribute is intended for use in +Swift generated headers for classes defined in Swift. + }]; +} + def ObjCBoxableDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 992571a4e531f..ff2591199e5f4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -895,6 +895,12 @@ def err_objc_root_class_subclass : Error< def err_restricted_superclass_mismatch : Error< "cannot subclass a class that was declared with the " "'objc_subclassing_restricted' attribute">; +def err_class_stub_subclassing_mismatch : Error< + "'objc_class_stub' attribute cannot be specified on a class that does not " + "have the 'objc_subclassing_restricted' attribute">; +def err_implementation_of_class_stub : Error< + "cannot declare implementation of a class declared with the " + "'objc_class_stub' attribute">; def warn_objc_root_class_missing : Warning< "class %0 defined without specifying a base class">, InGroup<ObjCRootClass>; diff --git a/clang/include/clang/Basic/ObjCRuntime.h b/clang/include/clang/Basic/ObjCRuntime.h index fc87f20d562d4..2caebd58832aa 100644 --- a/clang/include/clang/Basic/ObjCRuntime.h +++ b/clang/include/clang/Basic/ObjCRuntime.h @@ -429,6 +429,22 @@ class ObjCRuntime { } } + /// Returns true if this Objective-C runtime supports Objective-C class + /// stubs. + bool allowsClassStubs() const { + switch (getKind()) { + case FragileMacOSX: + case GCC: + case GNUstep: + case ObjFW: + return false; + case MacOSX: + case iOS: + case WatchOS: + return true; + } + } + /// Try to parse an Objective-C runtime specification from the given /// string. /// diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 0a5c56052d01f..4ef33a7ae73c1 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -721,6 +721,28 @@ class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { "objc_begin_catch"); } + /// Class objc_loadClassref (void *) + /// + /// Loads from a classref. For Objective-C stub classes, this invokes the + /// initialization callback stored inside the stub. For all other classes + /// this simply dereferences the pointer. + llvm::Constant *getLoadClassrefFn() const { + // Add the non-lazy-bind attribute, since objc_loadClassref is likely to + // be called a lot. + // + // Also it is safe to make it readnone, since we never load or store the + // classref except by calling this function. + llvm::Type *params[] = { Int8PtrPtrTy }; + return CGM.CreateRuntimeFunction( + llvm::FunctionType::get(ClassnfABIPtrTy, params, false), + "objc_loadClassref", + llvm::AttributeList::get(CGM.getLLVMContext(), + llvm::AttributeList::FunctionIndex, + {llvm::Attribute::NonLazyBind, + llvm::Attribute::ReadNone, + llvm::Attribute::NoUnwind})); + } + llvm::StructType *EHTypeTy; llvm::Type *EHTypePtrTy; @@ -1464,6 +1486,12 @@ class CGObjCNonFragileABIMac : public CGObjCCommonMac { bool isMetaclass, ForDefinition_t isForDefinition); + llvm::Constant *GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID); + + llvm::Value *EmitLoadOfClassRef(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + llvm::GlobalVariable *Entry); + /// EmitClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given class reference. llvm::Value *EmitClassRef(CodeGenFunction &CGF, @@ -1911,7 +1939,7 @@ llvm::Constant *CGObjCNonFragileABIMac::getNSConstantStringClassRef() { std::string str = StringClass.empty() ? "OBJC_CLASS_$_NSConstantString" : "OBJC_CLASS_$_" + StringClass; - auto GV = GetClassGlobal(str, NotForDefinition); + llvm::Constant *GV = GetClassGlobal(str, NotForDefinition); // Make sure the result is of the correct type. auto V = llvm::ConstantExpr::getBitCast(GV, CGM.IntTy->getPointerTo()); @@ -7232,31 +7260,63 @@ CGObjCNonFragileABIMac::GetClassGlobal(StringRef Name, return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ClassnfABIPtrTy); } +llvm::Constant * +CGObjCNonFragileABIMac::GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID) { + llvm::Constant *ClassGV = GetClassGlobal(ID, /*metaclass*/ false, + NotForDefinition); + + if (!ID->hasAttr<ObjCClassStubAttr>()) + return ClassGV; + + ClassGV = llvm::ConstantExpr::getPointerCast(ClassGV, ObjCTypes.Int8PtrTy); + + // Stub classes are pointer-aligned. Classrefs pointing at stub classes + // must set the least significant bit set to 1. + auto *Idx = llvm::ConstantInt::get(CGM.Int32Ty, 1); + return llvm::ConstantExpr::getGetElementPtr(CGM.Int8Ty, ClassGV, Idx); +} + +llvm::Value * +CGObjCNonFragileABIMac::EmitLoadOfClassRef(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + llvm::GlobalVariable *Entry) { + if (ID && ID->hasAttr<ObjCClassStubAttr>()) { + // Classrefs pointing at Objective-C stub classes must be loaded by calling + // a special runtime function. + return CGF.EmitRuntimeCall( + ObjCTypes.getLoadClassrefFn(), Entry, "load_classref_result"); + } + + CharUnits Align = CGF.getPointerAlign(); + return CGF.Builder.CreateAlignedLoad(Entry, Align); +} + llvm::Value * CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, IdentifierInfo *II, const ObjCInterfaceDecl *ID) { - CharUnits Align = CGF.getPointerAlign(); llvm::GlobalVariable *&Entry = ClassReferences[II]; if (!Entry) { llvm::Constant *ClassGV; if (ID) { - ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition); + ClassGV = GetClassGlobalForClassRef(ID); } else { ClassGV = GetClassGlobal((getClassSymbolPrefix() + II->getName()).str(), NotForDefinition); + assert(ClassGV->getType() == ObjCTypes.ClassnfABIPtrTy); } - Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, + Entry = new llvm::GlobalVariable(CGM.getModule(), ClassGV->getType(), false, llvm::GlobalValue::PrivateLinkage, ClassGV, "OBJC_CLASSLIST_REFERENCES_$_"); - Entry->setAlignment(Align.getQuantity()); + Entry->setAlignment(CGF.getPointerAlign().getQuantity()); Entry->setSection(GetSectionName("__objc_classrefs", "regular,no_dead_strip")); CGM.addCompilerUsedGlobal(Entry); } - return CGF.Builder.CreateAlignedLoad(Entry, Align); + + return EmitLoadOfClassRef(CGF, ID, Entry); } llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF, @@ -7278,20 +7338,20 @@ llvm::Value *CGObjCNonFragileABIMac::EmitNSAutoreleasePoolClassRef( llvm::Value * CGObjCNonFragileABIMac::EmitSuperClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { - CharUnits Align = CGF.getPointerAlign(); llvm::GlobalVariable *&Entry = SuperClassReferences[ID->getIdentifier()]; if (!Entry) { - auto ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition); - Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, + llvm::Constant *ClassGV = GetClassGlobalForClassRef(ID); + Entry = new llvm::GlobalVariable(CGM.getModule(), ClassGV->getType(), false, llvm::GlobalValue::PrivateLinkage, ClassGV, "OBJC_CLASSLIST_SUP_REFS_$_"); - Entry->setAlignment(Align.getQuantity()); + Entry->setAlignment(CGF.getPointerAlign().getQuantity()); Entry->setSection(GetSectionName("__objc_superrefs", "regular,no_dead_strip")); CGM.addCompilerUsedGlobal(Entry); } - return CGF.Builder.CreateAlignedLoad(Entry, Align); + + return EmitLoadOfClassRef(CGF, ID, Entry); } /// EmitMetaClassRef - Return a Value * of the address of _class_t @@ -7303,7 +7363,8 @@ llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CodeGenFunction &CGF, CharUnits Align = CGF.getPointerAlign(); llvm::GlobalVariable * &Entry = MetaClassReferences[ID->getIdentifier()]; if (!Entry) { - auto MetaClassGV = GetClassGlobal(ID, /*metaclass*/ true, NotForDefinition); + llvm::Constant *MetaClassGV = GetClassGlobal(ID, /*metaclass*/ true, + NotForDefinition); Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, llvm::GlobalValue::PrivateLinkage, @@ -7323,7 +7384,8 @@ llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CodeGenFunction &CGF, llvm::Value *CGObjCNonFragileABIMac::GetClass(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { if (ID->isWeakImported()) { - auto ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition); + llvm::Constant *ClassGV = GetClassGlobal(ID, /*metaclass*/ false, + NotForDefinition); (void)ClassGV; assert(!isa<llvm::GlobalVariable>(ClassGV) || cast<llvm::GlobalVariable>(ClassGV)->hasExternalWeakLinkage()); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 58d6cf835a7d4..4c18a32cc0475 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7454,6 +7454,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_ObjCSubclassingRestricted: handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, AL); break; + case ParsedAttr::AT_ObjCClassStub: + handleSimpleAttribute<ObjCClassStubAttr>(S, D, AL); + break; case ParsedAttr::AT_ObjCCompleteDefinition: handleSimpleAttribute<ObjCCompleteDefinitionAttr>(S, D, AL); break; diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 0a96605f6fdbc..389d3009f53d3 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -4099,6 +4099,9 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, } } + if (IDecl->hasAttr<ObjCClassStubAttr>()) + Diag(IC->getLocation(), diag::err_implementation_of_class_stub); + if (LangOpts.ObjCRuntime.isNonFragile()) { while (IDecl->getSuperClass()) { DiagnoseDuplicateIvars(IDecl, IDecl->getSuperClass()); @@ -4127,6 +4130,11 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, Diag(Super->getLocation(), diag::note_class_declared); } } + + if (IntfDecl->hasAttr<ObjCClassStubAttr>()) { + if (!IntfDecl->hasAttr<ObjCSubclassingRestrictedAttr>()) + Diag(IntfDecl->getLocation(), diag::err_class_stub_subclassing_mismatch); + } } DiagnoseVariableSizedIvars(*this, OCD); if (isInterfaceDeclKind) { diff --git a/clang/test/CodeGenObjC/class-stubs.m b/clang/test/CodeGenObjC/class-stubs.m new file mode 100644 index 0000000000000..2890cfcf99b99 --- /dev/null +++ b/clang/test/CodeGenObjC/class-stubs.m @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | FileCheck %s + +// -- classref for the message send in main() +// +// The class is declared with objc_class_stub, so LSB of the class pointer +// must be set to 1. +// +// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8 + +// -- classref for the super message send in anotherClassMethod() +// +// Metaclasses do not use the "stub" mechanism and are referenced statically. +// +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_" = private global %struct._class_t* @"OBJC_METACLASS_$_Derived", section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 + +// -- classref for the super message send in anotherInstanceMethod() +// +// The class is declared with objc_class_stub, so LSB of the class pointer +// must be set to 1. +// +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 + +__attribute__((objc_class_stub)) +__attribute__((objc_subclassing_restricted)) +@interface Base ++ (void) classMethod; +- (void) instanceMethod; +@end + +__attribute__((objc_class_stub)) +__attribute__((objc_subclassing_restricted)) +@interface Derived : Base +@end + +int main() { + [Base classMethod]; +} +// CHECK-LABEL: define i32 @main() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CLASS:%.*]] = call %struct._class_t* @objc_loadClassref(i8** @"OBJC_CLASSLIST_REFERENCES_$_") +// CHECK-NEXT: [[SELECTOR:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ +// CHECK-NEXT: [[RECEIVER:%.*]] = bitcast %struct._class_t* [[CLASS]] to i8* +// CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* [[RECEIVER]], i8* [[SELECTOR]]) +// CHECK-NEXT: ret i32 0 + +// CHECK-LABEL: declare %struct._class_t* @objc_loadClassref(i8**) +// CHECK-SAME: [[ATTRLIST:#.*]] + +@implementation Derived (MyCategory) + ++ (void) anotherClassMethod { + [super classMethod]; +} +// CHECK-LABEL: define internal void @"\01+[Derived(MyCategory) anotherClassMethod]"(i8* %self, i8* %_cmd) #0 { +// CHECK-NEXT: entry: +// CHECK: [[SUPER:%.*]] = alloca %struct._objc_super, align 8 +// CHECK: [[METACLASS_REF:%.*]] = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_SUP_REFS_$_", align 8 +// CHECK: [[CAST_METACLASS_REF:%.*]] = bitcast %struct._class_t* [[METACLASS_REF]] to i8* +// CHECK: [[DEST:%.*]] = getelementptr inbounds %struct._objc_super, %struct._objc_super* [[SUPER]], i32 0, i32 1 +// CHECK: store i8* [[CAST_METACLASS_REF]], i8** [[DEST]], align 8 +// CHECK: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper2 to void (%struct._objc_super*, i8*)*)(%struct._objc_super* [[SUPER]], i8* {{%.*}}) +// CHECK: ret void + +- (void) anotherInstanceMethod { + [super instanceMethod]; +} +// CHECK-LABEL: define internal void @"\01-[Derived(MyCategory) anotherInstanceMethod]"(%0* %self, i8* %_cmd) #0 { +// CHECK-NEXT: entry: +// CHECK: [[SUPER:%.*]] = alloca %struct._objc_super, align 8 +// CHECK: [[CLASS_REF:%.*]] = call %struct._class_t* @objc_loadClassref(i8** @"OBJC_CLASSLIST_SUP_REFS_$_.1") +// CHECK: [[CAST_CLASS_REF:%.*]] = bitcast %struct._class_t* [[CLASS_REF]] to i8* +// CHECK: [[DEST:%.*]] = getelementptr inbounds %struct._objc_super, %struct._objc_super* [[SUPER]], i32 0, i32 1 +// CHECK: store i8* [[CAST_CLASS_REF]], i8** [[DEST]], align 8 +// CHECK: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper2 to void (%struct._objc_super*, i8*)*)(%struct._objc_super* [[SUPER]], i8* {{%.*}}) +// CHECK: ret void + +@end + +// -- calls to objc_loadClassRef() are readnone +// CHECK: attributes [[ATTRLIST]] = { nounwind nonlazybind readnone } diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 2e806054504ca..a59182a4e7895 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -97,6 +97,7 @@ // CHECK-NEXT: ObjCBridge (SubjectMatchRule_record, SubjectMatchRule_type_alias) // CHECK-NEXT: ObjCBridgeMutable (SubjectMatchRule_record) // CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record) +// CHECK-NEXT: ObjCClassStub (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCCompleteDefinition (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method) // CHECK-NEXT: ObjCException (SubjectMatchRule_objc_interface) diff --git a/clang/test/SemaObjC/class-stub-attr-unsupported.m b/clang/test/SemaObjC/class-stub-attr-unsupported.m new file mode 100644 index 0000000000000..cc5243fac6eab --- /dev/null +++ b/clang/test/SemaObjC/class-stub-attr-unsupported.m @@ -0,0 +1,10 @@ +// RUN: %clang -target i386-apple-darwin -fsyntax-only -Xclang -verify %s +// RUN: %clang -target i386-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s + +@interface NSObject +@end + +__attribute__((objc_class_stub)) // expected-warning {{'objc_class_stub' attribute ignored}} +__attribute__((objc_subclassing_restricted)) +@interface StubClass : NSObject +@end diff --git a/clang/test/SemaObjC/class-stub-attr.m b/clang/test/SemaObjC/class-stub-attr.m new file mode 100644 index 0000000000000..46c07d8b5f789 --- /dev/null +++ b/clang/test/SemaObjC/class-stub-attr.m @@ -0,0 +1,27 @@ +// RUN: %clang -target x86_64-apple-darwin -fsyntax-only -Xclang -verify %s +// RUN: %clang -target x86_64-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s + +@interface NSObject +@end + +__attribute__((objc_class_stub)) +@interface MissingSubclassingRestrictedAttribute : NSObject // expected-error {{'objc_class_stub' attribute cannot be specified on a class that does not have the 'objc_subclassing_restricted' attribute}} +@end + +__attribute__((objc_class_stub)) +__attribute__((objc_subclassing_restricted)) +@interface ValidClassStubAttribute : NSObject +@end + +@implementation ValidClassStubAttribute // expected-error {{cannot declare implementation of a class declared with the 'objc_class_stub' attribute}} +@end + +@implementation ValidClassStubAttribute (MyCategory) +@end + +__attribute__((objc_class_stub(123))) // expected-error {{'objc_class_stub' attribute takes no arguments}} +@interface InvalidClassStubAttribute : NSObject +@end + +__attribute__((objc_class_stub)) // expected-error {{'objc_class_stub' attribute only applies to Objective-C interfaces}} +int cannotHaveObjCClassStubAttribute() {} diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 408c4a8438122..c556bca6b1ea7 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1976,10 +1976,15 @@ PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, << ", /*IsSupported=*/"; if (!LangOpts.empty()) { for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - const StringRef Part = (*I)->getValueAsString("Name"); if ((*I)->getValueAsBit("Negated")) OS << "!"; - OS << "LangOpts." << Part; + const StringRef Code = (*I)->getValueAsString("CustomCode"); + if (!Code.empty()) { + OS << Code; + } else { + const StringRef Name = (*I)->getValueAsString("Name"); + OS << "LangOpts." << Name; + } if (I + 1 != E) OS << " || "; } @@ -3462,16 +3467,22 @@ static std::string GenerateLangOptRequirements(const Record &R, // codegen efficiency). std::string FnName = "check", Test; for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - const StringRef Part = (*I)->getValueAsString("Name"); if ((*I)->getValueAsBit("Negated")) { FnName += "Not"; Test += "!"; } - Test += "S.LangOpts."; - Test += Part; + const StringRef Name = (*I)->getValueAsString("Name"); + FnName += Name; + const StringRef Code = (*I)->getValueAsString("CustomCode"); + if (!Code.empty()) { + Test += "S."; + Test += Code; + } else { + Test += "S.LangOpts."; + Test += Name; + } if (I + 1 != E) Test += " || "; - FnName += Part; } FnName += "LangOpts"; From b8ab9d7403762d581e652d139022c9b0f531e7d2 Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Fri, 5 Apr 2019 21:20:58 -0400 Subject: [PATCH 392/582] Fix compile error in llvm::FunctionCallee getLoadClassrefFn() The API here diverged between swift-5.1-branch and upstream-with-swift. apple-llvm-split-commit: fe297b2db47fd650a48f3ac70cd9507a0c5abce4 apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCMac.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 4ef33a7ae73c1..3844ee5294a61 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -726,7 +726,7 @@ class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { /// Loads from a classref. For Objective-C stub classes, this invokes the /// initialization callback stored inside the stub. For all other classes /// this simply dereferences the pointer. - llvm::Constant *getLoadClassrefFn() const { + llvm::FunctionCallee getLoadClassrefFn() const { // Add the non-lazy-bind attribute, since objc_loadClassref is likely to // be called a lot. // From f841a32ec12517b1af8c5339cfd83b1ea7aefbb3 Mon Sep 17 00:00:00 2001 From: Puyan Lotfi <plotfi@fb.com> Date: Wed, 10 Apr 2019 10:17:02 -0700 Subject: [PATCH 393/582] Fixing swift-clang tests that fail due to missing PrintingPolicy init in ctor. The following tests that were failing were: Clang :: OpenMP/for_simd_ast_print.cpp Clang :: OpenMP/simd_ast_print.cpp Clang-Unit :: AST/./ASTTests/DeclPrinter.TestFunctionDecl4 Clang-Unit :: AST/./ASTTests/DeclPrinter.TestFunctionDecl5 They were failing because SupressStorageClassSpecifiers not being properly initialized in PrintingPolicy in include/clang/AST/PrettyPrinter.h and when it was used in lib/AST/DeclPrinter.cpp "static" was not being printed: if (!Policy.SuppressSpecifiers) { if (!Policy.SupressStorageClassSpecifiers) { switch (D->getStorageClass()) { case SC_None: break; case SC_Extern: Out << "extern "; break; case SC_Static: Out << "static "; break; These tests pass in upstream clang because SupressStorageClassSpecifiers was never pushed upstream. apple-llvm-split-commit: 0b83f23ebf2b0a6a9100a2d343ed23ffac33df17 apple-llvm-split-dir: clang/ --- clang/include/clang/AST/PrettyPrinter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index fb79e37f9ceec..01f9e2eef396a 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -38,6 +38,7 @@ struct PrintingPolicy { /// Create a default printing policy for the specified language. PrintingPolicy(const LangOptions &LO) : Indentation(2), SuppressSpecifiers(false), + SupressStorageClassSpecifiers(false), SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false), SuppressScope(false), SuppressUnwrittenScope(false), SuppressInitializers(false), ConstantArraySizeAsWritten(false), From c5bc1aa9100d7599c3c836155c33b442b95dcde0 Mon Sep 17 00:00:00 2001 From: Puyan Lotfi <plotfi@fb.com> Date: Wed, 10 Apr 2019 15:03:45 -0700 Subject: [PATCH 394/582] Fixing SemaObjC tests that take -x objective-c++ with -stdlib=libc++ Clang :: SemaObjC/class-stub-attr-unsupported.m Clang :: SemaObjC/class-stub-attr.m apple-llvm-split-commit: c1265449bb476cce99b0c076aacc2304ef1e84b5 apple-llvm-split-dir: clang/ --- clang/test/SemaObjC/class-stub-attr-unsupported.m | 2 +- clang/test/SemaObjC/class-stub-attr.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/SemaObjC/class-stub-attr-unsupported.m b/clang/test/SemaObjC/class-stub-attr-unsupported.m index cc5243fac6eab..acabca1080595 100644 --- a/clang/test/SemaObjC/class-stub-attr-unsupported.m +++ b/clang/test/SemaObjC/class-stub-attr-unsupported.m @@ -1,5 +1,5 @@ // RUN: %clang -target i386-apple-darwin -fsyntax-only -Xclang -verify %s -// RUN: %clang -target i386-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s +// RUN: %clang -target i386-apple-darwin -x objective-c++ -stdlib=libc++ -fsyntax-only -Xclang -verify %s @interface NSObject @end diff --git a/clang/test/SemaObjC/class-stub-attr.m b/clang/test/SemaObjC/class-stub-attr.m index 46c07d8b5f789..80767323099d9 100644 --- a/clang/test/SemaObjC/class-stub-attr.m +++ b/clang/test/SemaObjC/class-stub-attr.m @@ -1,5 +1,5 @@ // RUN: %clang -target x86_64-apple-darwin -fsyntax-only -Xclang -verify %s -// RUN: %clang -target x86_64-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s +// RUN: %clang -target x86_64-apple-darwin -x objective-c++ -stdlib=libc++ -fsyntax-only -Xclang -verify %s @interface NSObject @end From a1d8b3dcada993f255d54b23a206895f7b5fd65b Mon Sep 17 00:00:00 2001 From: Puyan Lotfi <plotfi@fb.com> Date: Thu, 11 Apr 2019 15:53:17 -0700 Subject: [PATCH 395/582] Fixing clang-refactor-test tests on Linux by adding -fobjc-runtime=ios-5.0 On Linux, without -fobjc-runtime=ios-5.0 the following: @implementation NoNeedForImplementationTUs { int _p2; } will get AST synthesized without: `-ObjCPropertyImplDecl 0x7fad8c077070 <<invalid sloc>, line:21:15> <invalid sloc> p2 synthesize |-ObjCProperty 0x7fad8c069b68 'p2' `-ObjCIvar 0x7fad8c069a68 '_p2' 'int' In this case, the refactoring wont find the decl in the @implementation. apple-llvm-split-commit: 34a24d946ac87377200ea55112bd48e652233c5d apple-llvm-split-dir: clang/ --- .../Refactor/Extract/extract-capture-self.m | 6 +-- clang/test/Refactor/Rename/ObjCClass.m | 48 +++++++++---------- .../Rename/ObjCImplementationTURequests.m | 8 ++-- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/clang/test/Refactor/Extract/extract-capture-self.m b/clang/test/Refactor/Extract/extract-capture-self.m index 4f8375a194d07..9c8fcd879ec72 100644 --- a/clang/test/Refactor/Extract/extract-capture-self.m +++ b/clang/test/Refactor/Extract/extract-capture-self.m @@ -33,7 +33,7 @@ - (int)instanceMethod { // CHECK1: (AClass *object) {\nobject->ivar2 = 0;\n object->ivar1 = 0;\n object->ivar2 = 0;\n object.prop = 0;\n int x = object->ivar1;\n int y = object.prop;\n [object instanceMethod];\n [AClass classMethod];\n}\n\n" // CHECK1: () {\nreturn [AClass classMethod];\n}\n\n" -// RUN: clang-refactor-test perform -action extract -selected=%s:18:3-18:12 -selected=%s:20:3-20:18 -selected=%s:21:3-21:16 -selected=%s:23:3-23:20 -selected=%s:24:3-24:24 -selected=%s:18:3-25:23 -selected=%s:25:3-25:23 %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:18:3-18:12 -selected=%s:20:3-20:18 -selected=%s:21:3-21:16 -selected=%s:23:3-23:20 -selected=%s:24:3-24:24 -selected=%s:18:3-25:23 -selected=%s:25:3-25:23 %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s + (int)classMethod { int x = self.classMethod; @@ -43,7 +43,7 @@ + (int)classMethod { // CHECK2: () {\nint x = AClass.classMethod;\n} // CHECK2: () {\nreturn [AClass classMethod];\n} -// RUN: clang-refactor-test perform -action extract -selected=%s:39:3-39:27 -selected=%s:40:3-40:21 %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test perform -action extract -selected=%s:39:3-39:27 -selected=%s:40:3-40:21 %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK2 %s - (void)rhsSelfCaptureAndRewrite:(AClass *)i { // CHECK3: "static void extracted(AClass *object, AClass *i) {\ni.prop= object.prop;\n}\n\n" // rhs-prop-begin: +1:3 @@ -51,6 +51,6 @@ - (void)rhsSelfCaptureAndRewrite:(AClass *)i { // CHECK3: "static void extracted // rhs-prop-end: -1:21 } -// RUN: clang-refactor-test perform -action extract -selected=rhs-prop %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test perform -action extract -selected=rhs-prop %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK3 %s @end diff --git a/clang/test/Refactor/Rename/ObjCClass.m b/clang/test/Refactor/Rename/ObjCClass.m index aacfc2c6b31bb..162f6c3167c0f 100644 --- a/clang/test/Refactor/Rename/ObjCClass.m +++ b/clang/test/Refactor/Rename/ObjCClass.m @@ -4,9 +4,9 @@ @interface I1 // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14 @end -// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s -// RUN: clang-refactor-test rename-initiate -at=%s:4:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s -// RUN: clang-refactor-test rename-initiate -at=%s:2:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:4:12 -new-name=foo %s -fobjc-runtime=ios-5.0| FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:2:8 -new-name=foo %s -fobjc-runtime=ios-5.0| FileCheck --check-prefix=CHECK2 %s @implementation I1 { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 I1 *interfaceIVar; // CHECK1: rename [[@LINE]]:3 -> [[@LINE]]:5 @@ -14,9 +14,9 @@ @implementation I1 { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 int ivar; // CHECK3: rename [[@LINE]]:7 -> [[@LINE]]:11 } -// RUN: clang-refactor-test rename-initiate -at=%s:11:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s -// RUN: clang-refactor-test rename-initiate -at=%s:12:3 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s -// RUN: clang-refactor-test rename-initiate -at=%s:21:20 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:11:17 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:12:3 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:21:20 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s -(void)foo: (const I1 *)bar { // CHECK1: rename [[@LINE]]:20 -> [[@LINE]]:22 @@ -27,14 +27,14 @@ -(void)foo: (const I1 *)bar { // CHECK1: rename [[@LINE]]:20 -> [[@LINE]]:22 // CHECK3: rename [[@LINE-1]]:18 -> [[@LINE-1]]:22 } -// RUN: clang-refactor-test rename-initiate -at=%s:14:7 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s -// RUN: clang-refactor-test rename-initiate -at=%s:23:3 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s -// RUN: clang-refactor-test rename-initiate -at=%s:24:9 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s -// RUN: clang-refactor-test rename-initiate -at=%s:25:14 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s -// RUN: clang-refactor-test rename-initiate -at=%s:26:18 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:14:7 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:23:3 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:24:9 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:25:14 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-refactor-test rename-initiate -at=%s:26:18 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK3 %s -// RUN: clang-refactor-test rename-initiate -at=%s:12:7 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s -// RUN: clang-refactor-test rename-initiate -at=%s:26:3 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:12:7 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK4 %s +// RUN: clang-refactor-test rename-initiate -at=%s:26:3 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK4 %s @end @@ -44,11 +44,11 @@ @interface I1 (Category) // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14 @implementation I1 (Category) // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19 @end // CHECK5: rename [[@LINE-1]]:21 -> [[@LINE-1]]:29 -// RUN: clang-refactor-test rename-initiate -at=%s:41:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s -// RUN: clang-refactor-test rename-initiate -at=%s:44:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:41:12 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:44:17 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s -// RUN: clang-refactor-test rename-initiate -at=%s:41:16 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s -// RUN: clang-refactor-test rename-initiate -at=%s:44:21 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:41:16 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK5 %s +// RUN: clang-refactor-test rename-initiate -at=%s:44:21 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK5 %s // Implementation only-category: @@ -58,10 +58,10 @@ @interface I3 // CHECK6: rename [[@LINE]]:12 -> [[@LINE]]:14 @implementation I3 (DummyCategory) // CHECK6: rename [[@LINE]]:17 -> [[@LINE]]:19 @end // CHECK7: rename [[@LINE-1]]:21 -> [[@LINE-1]]:34 -// RUN: clang-refactor-test rename-initiate -at=%s:55:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s -// RUN: clang-refactor-test rename-initiate -at=%s:58:17 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:55:12 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:58:17 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK6 %s -// RUN: clang-refactor-test rename-initiate -at=%s:58:21 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s +// RUN: clang-refactor-test rename-initiate -at=%s:58:21 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK7 %s // Class extension: @@ -71,8 +71,8 @@ @interface I3 () // CHECK6: rename [[@LINE]]:12 -> [[@LINE]]:14 @implementation I3 // CHECK6: rename [[@LINE]]:17 -> [[@LINE]]:19 @end -// RUN: clang-refactor-test rename-initiate -at=%s:68:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s -// RUN: clang-refactor-test rename-initiate -at=%s:71:17 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:68:12 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK6 %s +// RUN: clang-refactor-test rename-initiate -at=%s:71:17 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK6 %s // Ivar declared in the interface: @@ -91,5 +91,5 @@ - (void)foo { @end -// RUN: clang-refactor-test rename-initiate -at=%s:81:7 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s -// RUN: clang-refactor-test rename-initiate -at=%s:89:3 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-initiate -at=%s:81:7 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK8 %s +// RUN: clang-refactor-test rename-initiate -at=%s:89:3 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK8 %s diff --git a/clang/test/Refactor/Rename/ObjCImplementationTURequests.m b/clang/test/Refactor/Rename/ObjCImplementationTURequests.m index 9486a8a5824e9..3afa5313de8a4 100644 --- a/clang/test/Refactor/Rename/ObjCImplementationTURequests.m +++ b/clang/test/Refactor/Rename/ObjCImplementationTURequests.m @@ -6,10 +6,10 @@ @interface ExplicitIVarsInInterface { @end -// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/Inputs/ObjCImplementationTURequestsImplementation.m" -dump-symbols %s | FileCheck --check-prefix=CHECK1 %s +// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/Inputs/ObjCImplementationTURequestsImplementation.m" -dump-symbols %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK1 %s // CHECK1: Implementation TU USR: 'c:objc(cs)ExplicitIVarsInInterface@_requiresImplementationTU' -// RUN: not clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/MissingFile.m" -dump-symbols %s 2>&1 | FileCheck --check-prefix=CHECK-ERR1 %s +// RUN: not clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/MissingFile.m" -dump-symbols %s 2>&1 -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK-ERR1 %s // CHECK-ERR1: failed to load implementation TU @interface NoNeedForImplementationTUs { @@ -27,6 +27,6 @@ @implementation NoNeedForImplementationTUs { @end -// RUN: clang-refactor-test rename-initiate -at=%s:16:7 -new-name=foo %s | FileCheck --check-prefix=CHECK-NO %s -// RUN: clang-refactor-test rename-initiate -at=%s:25:7 -new-name=foo %s | FileCheck --check-prefix=CHECK-NO %s +// RUN: clang-refactor-test rename-initiate -at=%s:16:7 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK-NO %s +// RUN: clang-refactor-test rename-initiate -at=%s:25:7 -new-name=foo %s -fobjc-runtime=ios-5.0 | FileCheck --check-prefix=CHECK-NO %s // CHECK-NO-NOT: Implementation TU USR From e675d6f54887083fd6d903eac86c3440e3913caa Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <abdulras@fb.com> Date: Mon, 15 Apr 2019 12:25:16 -0700 Subject: [PATCH 396/582] Basic: adjust for overflow in diagnostics Recent upstream changes has caused the number of diagnostics to overflow. Increase the count. apple-llvm-split-commit: 713483ab656841d2f11694de04d55184b0673876 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 43bc13c0ce1e7..c9d68670e9d90 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -36,7 +36,7 @@ namespace clang { DIAG_SIZE_AST = 150, DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 3500, + DIAG_SIZE_SEMA = 3503, DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, }; From 01336d79be59329c9cb7bb27211cd22359abd08b Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Wed, 17 Apr 2019 20:21:40 -0700 Subject: [PATCH 397/582] [Modules][Objective-C] Use complete decl from module when diagnosing missing import Otherwise the definition (first found) for ObjCInterfaceDecl's might precede the module one, which will eventually lead to crash, since diagnoseMissingImport needs one coming from a module. This behavior changed after Richard's r342018, which started to look into the definition of ObjCInterfaceDecls. rdar://problem/49237144 (cherry picked from commit e828f415dff603a8613ee9ae4622d626aa5d7e67) apple-llvm-split-commit: 44e42de01fd8ac4982b09ac25c1a32c3bb3a94bd apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaLookup.cpp | 5 ++++- .../Foo.framework/Headers/Bar.h | 1 + .../Foo.framework/Headers/Foo.h | 2 ++ .../Foo.framework/Modules/module.modulemap | 6 ++++++ .../Foo.framework/PrivateHeaders/RandoPriv.h | 4 ++++ .../test/Modules/interface-diagnose-missing-import.m | 11 +++++++++++ 6 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Bar.h create mode 100644 clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Foo.h create mode 100644 clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap create mode 100644 clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/PrivateHeaders/RandoPriv.h create mode 100644 clang/test/Modules/interface-diagnose-missing-import.m diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index c5d8c2f50d268..4b08dd8b91b27 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -5048,8 +5048,11 @@ static NamedDecl *getDefinitionToImport(NamedDecl *D) { return FD->getDefinition(); if (TagDecl *TD = dyn_cast<TagDecl>(D)) return TD->getDefinition(); + // The first definition for this ObjCInterfaceDecl might be in the TU + // and not associated with any module. Use the one we know to be complete + // and have just seen in a module. if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) - return ID->getDefinition(); + return ID; if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) return PD->getDefinition(); if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Bar.h b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Bar.h new file mode 100644 index 0000000000000..3ce482e6f243a --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Bar.h @@ -0,0 +1 @@ +// interface-diagnose-missing-import/Foo.framework/Headers/Bar.h diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Foo.h b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Foo.h new file mode 100644 index 0000000000000..c9c40986d9ed3 --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Headers/Foo.h @@ -0,0 +1,2 @@ +#import <Foo/RandoPriv.h> +#import <Foo/Bar.h> diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..ebb4fa6e90248 --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +// interface-diagnose-missing-import/Foo.framework/Modules/module.modulemap +framework module Foo { + umbrella header "Foo.h" + export * + module * { export * } +} diff --git a/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/PrivateHeaders/RandoPriv.h b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/PrivateHeaders/RandoPriv.h new file mode 100644 index 0000000000000..3e195fd85b36e --- /dev/null +++ b/clang/test/Modules/Inputs/interface-diagnose-missing-import/Foo.framework/PrivateHeaders/RandoPriv.h @@ -0,0 +1,4 @@ +@interface NSObject +@end +@interface Buggy : NSObject +@end diff --git a/clang/test/Modules/interface-diagnose-missing-import.m b/clang/test/Modules/interface-diagnose-missing-import.m new file mode 100644 index 0000000000000..5bbac36423006 --- /dev/null +++ b/clang/test/Modules/interface-diagnose-missing-import.m @@ -0,0 +1,11 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -F%S/Inputs/interface-diagnose-missing-import -verify +@interface Buggy +@end + +@import Foo.Bar; + +@interface Buggy (MyExt) // expected-error {{definition of 'Buggy' must be imported from module 'Foo' before it is required}} +@end + +// expected-note@Foo/RandoPriv.h:3{{previous definition is here}} From 54d746e43944c7da289e0954b492dd32516f2716 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <abdulras@fb.com> Date: Fri, 19 Apr 2019 11:09:34 -0700 Subject: [PATCH 398/582] Basic: increase diagnostics again Until the local diagnostics are upstreamed, we need to track the number of diagnostics that we are adding. For now, just bump up the count. apple-llvm-split-commit: 06a09c1192aaf37a4f9827eaac70ca567d6c3941 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index c9d68670e9d90..d44c4e172c17d 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -36,7 +36,7 @@ namespace clang { DIAG_SIZE_AST = 150, DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 3503, + DIAG_SIZE_SEMA = 3511, DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, }; From 8d91bee645c3dcf26e4e025c8abdf0fb2430cb73 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <abdulras@fb.com> Date: Fri, 19 Apr 2019 10:59:08 -0700 Subject: [PATCH 399/582] Coroutines: adjust for SVN r358739 CallSite has been removed in favour of CallBase. Adjust the coroutine split to account for that. apple-llvm-split-commit: 7acd8b95a252a7a630642a90121a2200c88614ba apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 25e8e7ad5fccd..924e367dfdf7b 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -1547,10 +1547,12 @@ static void replacePrepare(CallInst *Prepare, CallGraph &CG) { // If so, we'll need to update the call graph. if (PrepareUserNode) { for (auto &Use : Cast->uses()) { - auto CS = CallSite(Use.getUser()); - if (!CS || !CS.isCallee(&Use)) continue; - PrepareUserNode->removeCallEdgeFor(CS); - PrepareUserNode->addCalledFunction(CS, FnNode); + if (auto *CB = dyn_cast<CallBase>(Use.getUser())) { + if (!CB->isCallee(&Use)) + continue; + PrepareUserNode->removeCallEdgeFor(*CB); + PrepareUserNode->addCalledFunction(CB, FnNode); + } } } From ef758dcb83f42e3c7805f74d40c42a8026e217e1 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Mon, 22 Apr 2019 10:45:08 -0700 Subject: [PATCH 400/582] rdar://49167240: [HotColdSplit] Reflect full cost of parameters in split penalty Make the penalty for splitting a region more accurately reflect the cost of materializing all of the inputs/outputs to/from the region. This almost entirely eliminates code growth within functions which undergo splitting in key internal frameworks, and reduces the size of those frameworks between 2.6% to 3%. rdar://49167240 apple-llvm-split-commit: 3b55056633a1fe19f313f1837c60be414798e81d apple-llvm-split-dir: llvm/ --- llvm/lib/Transforms/IPO/HotColdSplitting.cpp | 61 +++++++++++++++---- .../CodeExtractor/extract-assume.ll | 2 +- .../HotColdSplit/apply-penalty-for-inputs.ll | 21 ++++++- .../HotColdSplit/apply-penalty-for-outputs.ll | 8 ++- .../HotColdSplit/apply-successor-penalty.ll | 13 +++- 5 files changed, 83 insertions(+), 22 deletions(-) diff --git a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp index ab1a9a79cad67..1ba4ea92085d9 100644 --- a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp +++ b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp @@ -67,6 +67,7 @@ #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include <algorithm> +#include <limits> #include <cassert> #define DEBUG_TYPE "hotcoldsplit" @@ -84,6 +85,10 @@ static cl::opt<int> cl::desc("Base penalty for splitting cold code (as a " "multiple of TCC_Basic)")); +static cl::opt<int> MaxParametersForSplit( + "hotcoldsplit-max-params", cl::init(8), cl::Hidden, + cl::desc("Maximum number of parameters for a split function")); + namespace { /// A sequence of basic blocks. @@ -271,18 +276,6 @@ static int getOutliningPenalty(ArrayRef<BasicBlock *> Region, if (SplittingThreshold <= 0) return Penalty; - // The typical code size cost for materializing an argument for the outlined - // call. - LLVM_DEBUG(dbgs() << "Applying penalty for: " << NumInputs << " inputs\n"); - const int CostForArgMaterialization = TargetTransformInfo::TCC_Basic; - Penalty += CostForArgMaterialization * NumInputs; - - // The typical code size cost for an output alloca, its associated store, and - // its associated reload. - LLVM_DEBUG(dbgs() << "Applying penalty for: " << NumOutputs << " outputs\n"); - const int CostForRegionOutput = 3 * TargetTransformInfo::TCC_Basic; - Penalty += CostForRegionOutput * NumOutputs; - // Find the number of distinct exit blocks for the region. Use a conservative // check to determine whether control returns from the region. bool NoBlocksReturn = true; @@ -303,6 +296,48 @@ static int getOutliningPenalty(ArrayRef<BasicBlock *> Region, } } + // Count the number of phis in exit blocks with >= 2 incoming values from the + // outlining region. These phis are split (\ref severSplitPHINodesOfExits), + // and new outputs are created to supply the split phis. CodeExtractor can't + // report these new outputs until extraction begins, but it's important to + // factor the cost of the outputs into the cost calculation. + unsigned NumSplitExitPhis = 0; + for (BasicBlock *ExitBB : SuccsOutsideRegion) { + for (PHINode &PN : ExitBB->phis()) { + // Find all incoming values from the outlining region. + int NumIncomingVals = 0; + for (unsigned i = 0; i < PN.getNumIncomingValues(); ++i) + if (find(Region, PN.getIncomingBlock(i)) != Region.end()) { + ++NumIncomingVals; + if (NumIncomingVals > 1) { + ++NumSplitExitPhis; + break; + } + } + } + } + + // Apply an penalty for calling the split function. Factor in the cost of + // materializing all of the parameters. + int NumOutputsAndSplitPhis = NumOutputs + NumSplitExitPhis; + int NumParams = NumInputs + NumOutputsAndSplitPhis; + if (NumParams > MaxParametersForSplit) { + LLVM_DEBUG(dbgs() << NumInputs << " inputs and " << NumOutputsAndSplitPhis + << " outputs exceeds parameter limit (" + << MaxParametersForSplit << ")\n"); + return std::numeric_limits<int>::max(); + } + const int CostForArgMaterialization = 2 * TargetTransformInfo::TCC_Basic; + LLVM_DEBUG(dbgs() << "Applying penalty for: " << NumParams << " params\n"); + Penalty += CostForArgMaterialization * NumParams; + + // Apply the typical code size cost for an output alloca and its associated + // reload in the caller. Also penalize the associated store in the callee. + LLVM_DEBUG(dbgs() << "Applying penalty for: " << NumOutputsAndSplitPhis + << " outputs/split phis\n"); + const int CostForRegionOutput = 3 * TargetTransformInfo::TCC_Basic; + Penalty += CostForRegionOutput * NumOutputsAndSplitPhis; + // Apply a `noreturn` bonus. if (NoBlocksReturn) { LLVM_DEBUG(dbgs() << "Applying bonus for: " << Region.size() @@ -312,7 +347,7 @@ static int getOutliningPenalty(ArrayRef<BasicBlock *> Region, // Apply a penalty for having more than one successor outside of the region. // This penalty accounts for the switch needed in the caller. - if (!SuccsOutsideRegion.empty()) { + if (SuccsOutsideRegion.size() > 1) { LLVM_DEBUG(dbgs() << "Applying penalty for: " << SuccsOutsideRegion.size() << " non-region successors\n"); Penalty += (SuccsOutsideRegion.size() - 1) * TargetTransformInfo::TCC_Basic; diff --git a/llvm/test/Transforms/CodeExtractor/extract-assume.ll b/llvm/test/Transforms/CodeExtractor/extract-assume.ll index b79c6a6913753..1848c6e83869e 100644 --- a/llvm/test/Transforms/CodeExtractor/extract-assume.ll +++ b/llvm/test/Transforms/CodeExtractor/extract-assume.ll @@ -1,4 +1,4 @@ -; RUN: opt -passes="function(slp-vectorizer),module(hotcoldsplit),function(slp-vectorizer,print<assumptions>)" -disable-output %s 2>&1 | FileCheck %s +; RUN: opt -passes="function(slp-vectorizer),module(hotcoldsplit),function(slp-vectorizer,print<assumptions>)" -hotcoldsplit-threshold=-1 -disable-output %s 2>&1 | FileCheck %s ; ; Make sure this compiles. Check that function assumption cache is refreshed ; after extracting blocks with assume calls from the function. diff --git a/llvm/test/Transforms/HotColdSplit/apply-penalty-for-inputs.ll b/llvm/test/Transforms/HotColdSplit/apply-penalty-for-inputs.ll index fffd6f9f5dcf9..4906316816e97 100644 --- a/llvm/test/Transforms/HotColdSplit/apply-penalty-for-inputs.ll +++ b/llvm/test/Transforms/HotColdSplit/apply-penalty-for-inputs.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt -hotcoldsplit -debug-only=hotcoldsplit -S < %s -o /dev/null 2>&1 | FileCheck %s +; RUN: opt -hotcoldsplit -debug-only=hotcoldsplit -hotcoldsplit-threshold=2 -hotcoldsplit-max-params=2 -S < %s -o /dev/null 2>&1 | FileCheck %s declare void @sink(i32*, i32, i32) cold @@ -10,10 +10,27 @@ define void @foo(i32 %arg) { br i1 undef, label %cold, label %exit cold: - ; CHECK: Applying penalty for: 2 inputs + ; CHECK: Applying penalty for splitting: 2 + ; CHECK-NEXT: Applying penalty for: 2 params + ; CHECK-NEXT: Applying penalty for: 0 outputs/split phis + ; CHECK-NEXT: penalty = 6 call void @sink(i32* @g, i32 %arg, i32 %local) ret void exit: ret void } + +define void @bar(i32* %p1, i32 %p2, i32 %p3) { + br i1 undef, label %cold, label %exit + +cold: + ; CHECK: Applying penalty for splitting: 2 + ; CHECK-NEXT: 3 inputs and 0 outputs exceeds parameter limit (2) + ; CHECK-NEXT: penalty = 2147483647 + call void @sink(i32* %p1, i32 %p2, i32 %p3) + ret void + +exit: + ret void +} diff --git a/llvm/test/Transforms/HotColdSplit/apply-penalty-for-outputs.ll b/llvm/test/Transforms/HotColdSplit/apply-penalty-for-outputs.ll index a7d9f97ab030b..b7bf760b90c4b 100644 --- a/llvm/test/Transforms/HotColdSplit/apply-penalty-for-outputs.ll +++ b/llvm/test/Transforms/HotColdSplit/apply-penalty-for-outputs.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt -hotcoldsplit -debug-only=hotcoldsplit -S < %s -o /dev/null 2>&1 | FileCheck %s +; RUN: opt -hotcoldsplit -debug-only=hotcoldsplit -hotcoldsplit-threshold=2 -S < %s -o /dev/null 2>&1 | FileCheck %s declare void @sink() cold @@ -10,8 +10,10 @@ entry: br i1 undef, label %cold, label %exit cold: - ; CHECK: Applying penalty for: 1 output - ; CHECK: Applying penalty for: 1 non-region successors + ; CHECK: Applying penalty for splitting: 2 + ; CHECK-NEXT: Applying penalty for: 1 params + ; CHECK-NEXT: Applying penalty for: 1 outputs/split phis + ; CHECK-NEXT: penalty = 7 %local = load i32, i32* @g call void @sink() br label %exit diff --git a/llvm/test/Transforms/HotColdSplit/apply-successor-penalty.ll b/llvm/test/Transforms/HotColdSplit/apply-successor-penalty.ll index 3886d76da0169..a9e3fc62e37cc 100644 --- a/llvm/test/Transforms/HotColdSplit/apply-successor-penalty.ll +++ b/llvm/test/Transforms/HotColdSplit/apply-successor-penalty.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt -hotcoldsplit -debug-only=hotcoldsplit -S < %s -o /dev/null 2>&1 | FileCheck %s +; RUN: opt -hotcoldsplit -debug-only=hotcoldsplit -hotcoldsplit-threshold=2 -S < %s -o /dev/null 2>&1 | FileCheck %s declare void @sink() cold @@ -9,7 +9,10 @@ entry: br i1 undef, label %cold1, label %exit cold1: - ; CHECK: Applying penalty for: 1 non-region successor + ; CHECK: Applying penalty for splitting: 2 + ; CHECK-NEXT: Applying penalty for: 0 params + ; CHECK-NEXT: Applying penalty for: 0 outputs/split phis + ; CHECK-NEXT: penalty = 2 call void @sink() br i1 undef, label %cold2, label %cold3 @@ -32,7 +35,11 @@ entry: br i1 undef, label %cold1, label %exit1 cold1: - ; CHECK: Applying penalty for: 2 non-region successors + ; CHECK: Applying penalty for splitting: 2 + ; CHECK-NEXT: Applying penalty for: 0 params + ; CHECK-NEXT: Applying penalty for: 0 outputs/split phis + ; CHECK-NEXT: Applying penalty for: 2 non-region successors + ; CHECK-NEXT: penalty = 3 call void @sink() br i1 undef, label %cold2, label %cold3 From c82f57de102dd17b200a32a340f5ab96f45b66f8 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool <abdulras@fb.com> Date: Fri, 19 Apr 2019 11:09:34 -0700 Subject: [PATCH 401/582] Basic: increase diagnostics again Until the local diagnostics are upstreamed, we need to track the number of diagnostics that we are adding. For now, just bump up the count. apple-llvm-split-commit: 625ec7fed0d2722eab394a78d247b02e6b8aa816 apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/DiagnosticIDs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index d44c4e172c17d..4cffe0e200177 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -36,7 +36,7 @@ namespace clang { DIAG_SIZE_AST = 150, DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, - DIAG_SIZE_SEMA = 3511, + DIAG_SIZE_SEMA = 3530, DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, }; From 09c8da5fd5a68e52ecbcb35f612ff29aebff68ae Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 6 May 2019 12:26:17 -0700 Subject: [PATCH 402/582] Fix build after merge apple-llvm-split-commit: 24ff9417e78a9cbb9326322df93ebb95f43c9952 apple-llvm-split-dir: clang/ --- clang/lib/Tooling/Refactor/Extract.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index 11ec350e65bcb..60fdee574a6fa 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -1847,7 +1847,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( for (unsigned I = 0, NumTemplateParams = FD->getNumTemplateParameterLists(); I < NumTemplateParams; ++I) { - FD->getTemplateParameterList(I)->print(OS, PP, Context); + FD->getTemplateParameterList(I)->print(OS, Context, PP); OS << "\n"; } } From 79770abcf45997316c27b94a3a3ace229952b72b Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 6 May 2019 12:52:50 -0700 Subject: [PATCH 403/582] Fixup new test by adding add missing switch cases note apple-llvm-split-commit: f91a745a4ba7e7c91aa7b63f58c5273e582f6c2f apple-llvm-split-dir: clang/ --- clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp index 5f715a1ec21d3..43396d4610ba5 100644 --- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp @@ -14,7 +14,7 @@ void f() { int x; // expected-warning {{unused variable}} typedef int I; // expected-warning {{unused typedef 'I'}} E1 e; - switch (e) { // expected-warning {{enumeration value 'UsedEnumVal' not handled in switch}} + switch (e) { // expected-warning {{enumeration value 'UsedEnumVal' not handled in switch}} expected-note {{add missing}} } // Should not warn about these due to not being used. From 7bbc38d32acdc627c18181ccb701c5b6af95681c Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Fri, 18 Jan 2019 10:35:51 -0800 Subject: [PATCH 404/582] [Modules] Move search paths outside of the control block Clang does not currently hashes the search paths, so it doesnt make sense for them to be in the control block, and we should move those to the UNHASHED_CONTROL_BLOCK instead. One concern mentioned with moving the header search paths to the unhashed control block is that we stop rebuilding modules in the non-PCH scenario, which might lead to problems if the header search path actually changes something we rely for consistency. However, the control block already includes the INPUT_FILE list, which should also force the rebuild in non-PCH builds, so this should be fine. There are no testcases for this change, as it would require using llvm-bcanalyzer in clang tests, which we don't currently do. rdar://problem/45567811 (cherry picked from commit 9d7254d8eccdf7a40d7f1e626e12130fd0bdb72a) (cherry picked from commit 1c627cee13147e387476a1ca16e67a68bd1edae5) apple-llvm-split-commit: 9dbd2ad3dad75647473dd7079789caecef6d2f1a apple-llvm-split-dir: clang/ --- .../include/clang/Serialization/ASTBitCodes.h | 3 ++ clang/include/clang/Serialization/ASTReader.h | 2 + clang/lib/Serialization/ASTReader.cpp | 44 +++++++++++++------ clang/lib/Serialization/ASTWriter.cpp | 43 ++++++++++-------- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 776e768f00fa8..d29924ff120c2 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -346,6 +346,9 @@ namespace serialization { /// Record code for the diagnostic options table. DIAGNOSTIC_OPTIONS, + /// Record code for the headers search paths. + HEADER_SEARCH_PATHS, + /// Record code for \#pragma diagnostic mappings. DIAG_PRAGMA_MAPPINGS, }; diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 0241bd26b4aa5..207e423be346a 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1314,6 +1314,8 @@ class ASTReader ASTReaderListener &Listener); static bool ParseHeaderSearchOptions(const RecordData &Record, bool Complain, ASTReaderListener &Listener); + static bool ParseHeaderSearchPaths(const RecordData &Record, bool Complain, + ASTReaderListener &Listener); static bool ParsePreprocessorOptions(const RecordData &Record, bool Complain, ASTReaderListener &Listener, std::string &SuggestedPredefines); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index d68c7ee2b2bc4..838feb1ea5251 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4371,6 +4371,13 @@ ASTReader::ASTReadResult ASTReader::readUnhashedControlBlockImpl( Result = OutOfDate; // Don't return early. Read the signature. break; } + case HEADER_SEARCH_PATHS: { + bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; + if (!AllowCompatibleConfigurationMismatch && + ParseHeaderSearchPaths(Record, Complain, *Listener)) + Result = ConfigurationMismatch; + break; + } case DIAG_PRAGMA_MAPPINGS: if (!F) break; @@ -5347,6 +5354,27 @@ bool ASTReader::ParseHeaderSearchOptions(const RecordData &Record, HeaderSearchOptions HSOpts; unsigned Idx = 0; HSOpts.Sysroot = ReadString(Record, Idx); + HSOpts.ResourceDir = ReadString(Record, Idx); + HSOpts.ModuleCachePath = ReadString(Record, Idx); + HSOpts.ModuleUserBuildPath = ReadString(Record, Idx); + HSOpts.DisableModuleHash = Record[Idx++]; + HSOpts.ImplicitModuleMaps = Record[Idx++]; + HSOpts.ModuleMapFileHomeIsCwd = Record[Idx++]; + HSOpts.UseBuiltinIncludes = Record[Idx++]; + HSOpts.UseStandardSystemIncludes = Record[Idx++]; + HSOpts.UseStandardCXXIncludes = Record[Idx++]; + HSOpts.UseLibcxx = Record[Idx++]; + std::string SpecificModuleCachePath = ReadString(Record, Idx); + + return Listener.ReadHeaderSearchOptions(HSOpts, SpecificModuleCachePath, + Complain); +} + +bool ASTReader::ParseHeaderSearchPaths(const RecordData &Record, + bool Complain, + ASTReaderListener &Listener) { + HeaderSearchOptions HSOpts; + unsigned Idx = 0; // Include entries. for (unsigned N = Record[Idx++]; N; --N) { @@ -5366,20 +5394,8 @@ bool ASTReader::ParseHeaderSearchOptions(const RecordData &Record, HSOpts.SystemHeaderPrefixes.emplace_back(std::move(Prefix), IsSystemHeader); } - HSOpts.ResourceDir = ReadString(Record, Idx); - HSOpts.ModuleCachePath = ReadString(Record, Idx); - HSOpts.ModuleUserBuildPath = ReadString(Record, Idx); - HSOpts.DisableModuleHash = Record[Idx++]; - HSOpts.ImplicitModuleMaps = Record[Idx++]; - HSOpts.ModuleMapFileHomeIsCwd = Record[Idx++]; - HSOpts.UseBuiltinIncludes = Record[Idx++]; - HSOpts.UseStandardSystemIncludes = Record[Idx++]; - HSOpts.UseStandardCXXIncludes = Record[Idx++]; - HSOpts.UseLibcxx = Record[Idx++]; - std::string SpecificModuleCachePath = ReadString(Record, Idx); - - return Listener.ReadHeaderSearchOptions(HSOpts, SpecificModuleCachePath, - Complain); + // TODO: implement checking and warnings for path mismatches. + return false; } bool ASTReader::ParsePreprocessorOptions(const RecordData &Record, diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 0b060c04dbfeb..35e46bd4b108f 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1314,6 +1314,7 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(UNHASHED_CONTROL_BLOCK); RECORD(SIGNATURE); RECORD(DIAGNOSTIC_OPTIONS); + RECORD(HEADER_SEARCH_PATHS); RECORD(DIAG_PRAGMA_MAPPINGS); #undef RECORD @@ -1433,6 +1434,29 @@ ASTFileSignature ASTWriter::writeUnhashedControlBlock(Preprocessor &PP, // are generally transient files and will almost always be overridden. Stream.EmitRecord(DIAGNOSTIC_OPTIONS, Record); + // Header search paths. + Record.clear(); + const HeaderSearchOptions &HSOpts + = PP.getHeaderSearchInfo().getHeaderSearchOpts(); + + // Include entries. + Record.push_back(HSOpts.UserEntries.size()); + for (unsigned I = 0, N = HSOpts.UserEntries.size(); I != N; ++I) { + const HeaderSearchOptions::Entry &Entry = HSOpts.UserEntries[I]; + AddString(Entry.Path, Record); + Record.push_back(static_cast<unsigned>(Entry.Group)); + Record.push_back(Entry.IsFramework); + Record.push_back(Entry.IgnoreSysRoot); + } + + // System header prefixes. + Record.push_back(HSOpts.SystemHeaderPrefixes.size()); + for (unsigned I = 0, N = HSOpts.SystemHeaderPrefixes.size(); I != N; ++I) { + AddString(HSOpts.SystemHeaderPrefixes[I].Prefix, Record); + Record.push_back(HSOpts.SystemHeaderPrefixes[I].IsSystemHeader); + } + Stream.EmitRecord(HEADER_SEARCH_PATHS, Record); + // Write out the diagnostic/pragma mappings. WritePragmaDiagnosticMappings(Diags, /* IsModule = */ WritingModule); @@ -1636,25 +1660,8 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, Record.clear(); const HeaderSearchOptions &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts(); - AddString(HSOpts.Sysroot, Record); - - // Include entries. - Record.push_back(HSOpts.UserEntries.size()); - for (unsigned I = 0, N = HSOpts.UserEntries.size(); I != N; ++I) { - const HeaderSearchOptions::Entry &Entry = HSOpts.UserEntries[I]; - AddString(Entry.Path, Record); - Record.push_back(static_cast<unsigned>(Entry.Group)); - Record.push_back(Entry.IsFramework); - Record.push_back(Entry.IgnoreSysRoot); - } - - // System header prefixes. - Record.push_back(HSOpts.SystemHeaderPrefixes.size()); - for (unsigned I = 0, N = HSOpts.SystemHeaderPrefixes.size(); I != N; ++I) { - AddString(HSOpts.SystemHeaderPrefixes[I].Prefix, Record); - Record.push_back(HSOpts.SystemHeaderPrefixes[I].IsSystemHeader); - } + AddString(HSOpts.Sysroot, Record); AddString(HSOpts.ResourceDir, Record); AddString(HSOpts.ModuleCachePath, Record); AddString(HSOpts.ModuleUserBuildPath, Record); From 07aa5e7bf74325c45cfac8b296bf7a1a5cb9e4f9 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Mon, 22 Apr 2019 17:31:24 -0700 Subject: [PATCH 405/582] [Modules] Move FILE_SYSTEM_OPTIONS to the unhashed control block FILE_SYSTEM_OPTIONS is only currently used to track -working-directory. If we are not making this part of getModuleHash, we shouldn't leave it in the control block either, otherwise we can rebuild such modules. No testcases since llvm-bcanalyzer isn't available to be tested in clang. rdar://problem/49504127 (cherry picked from commit 289f8eed4f00eac75902d76addd1cbd551637a1c) apple-llvm-split-commit: a926f632283cc7a6c1fbf3af4d04bef346816997 apple-llvm-split-dir: clang/ --- clang/include/clang/Serialization/ASTBitCodes.h | 6 +++--- clang/lib/Serialization/ASTReader.cpp | 16 ++++++++-------- clang/lib/Serialization/ASTWriter.cpp | 16 ++++++++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index d29924ff120c2..a9eb4f3792ef6 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -328,9 +328,6 @@ namespace serialization { /// Record code for the target options table. TARGET_OPTIONS, - /// Record code for the filesystem options table. - FILE_SYSTEM_OPTIONS, - /// Record code for the headers search options table. HEADER_SEARCH_OPTIONS, @@ -349,6 +346,9 @@ namespace serialization { /// Record code for the headers search paths. HEADER_SEARCH_PATHS, + /// Record code for the filesystem options table. + FILE_SYSTEM_OPTIONS, + /// Record code for \#pragma diagnostic mappings. DIAG_PRAGMA_MAPPINGS, }; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 838feb1ea5251..13c8d5ce3f519 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -2299,14 +2299,6 @@ ASTReader::ASTReadResult ASTReader::ReadOptionsBlock( break; } - case FILE_SYSTEM_OPTIONS: { - bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; - if (!AllowCompatibleConfigurationMismatch && - ParseFileSystemOptions(Record, Complain, Listener)) - Result = ConfigurationMismatch; - break; - } - case HEADER_SEARCH_OPTIONS: { bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; if (!AllowCompatibleConfigurationMismatch && @@ -4378,6 +4370,14 @@ ASTReader::ASTReadResult ASTReader::readUnhashedControlBlockImpl( Result = ConfigurationMismatch; break; } + case FILE_SYSTEM_OPTIONS: { + bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; + if (!AllowCompatibleConfigurationMismatch && + ParseFileSystemOptions(Record, Complain, *Listener)) + Result = ConfigurationMismatch; + break; + } + case DIAG_PRAGMA_MAPPINGS: if (!F) break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 35e46bd4b108f..8976e33de5cdd 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1081,7 +1081,6 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(OPTIONS_BLOCK); RECORD(LANGUAGE_OPTIONS); RECORD(TARGET_OPTIONS); - RECORD(FILE_SYSTEM_OPTIONS); RECORD(HEADER_SEARCH_OPTIONS); RECORD(PREPROCESSOR_OPTIONS); @@ -1315,6 +1314,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(SIGNATURE); RECORD(DIAGNOSTIC_OPTIONS); RECORD(HEADER_SEARCH_PATHS); + RECORD(FILE_SYSTEM_OPTIONS); RECORD(DIAG_PRAGMA_MAPPINGS); #undef RECORD @@ -1457,6 +1457,13 @@ ASTFileSignature ASTWriter::writeUnhashedControlBlock(Preprocessor &PP, } Stream.EmitRecord(HEADER_SEARCH_PATHS, Record); + // File system options. + Record.clear(); + const FileSystemOptions &FSOpts = + Context.getSourceManager().getFileManager().getFileSystemOpts(); + AddString(FSOpts.WorkingDir, Record); + Stream.EmitRecord(FILE_SYSTEM_OPTIONS, Record); + // Write out the diagnostic/pragma mappings. WritePragmaDiagnosticMappings(Diags, /* IsModule = */ WritingModule); @@ -1649,13 +1656,6 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, } Stream.EmitRecord(TARGET_OPTIONS, Record); - // File system options. - Record.clear(); - const FileSystemOptions &FSOpts = - Context.getSourceManager().getFileManager().getFileSystemOpts(); - AddString(FSOpts.WorkingDir, Record); - Stream.EmitRecord(FILE_SYSTEM_OPTIONS, Record); - // Header search options. Record.clear(); const HeaderSearchOptions &HSOpts From 57bfd8b1385889b148da462284da699c45e824f1 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 7 May 2019 19:07:16 -0700 Subject: [PATCH 406/582] Fix the refactoring tests after upstream update apple-llvm-split-commit: 5505bf1fc0148b3f9446ddcfbe9521353d2c2ce6 apple-llvm-split-dir: clang/ --- clang/test/Refactor/Rename/NoNewName.cpp | 2 +- clang/test/Refactor/Rename/rename-indexed-file.cpp | 2 +- clang/test/Refactor/Rename/rename-initiate-usr.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/test/Refactor/Rename/NoNewName.cpp b/clang/test/Refactor/Rename/NoNewName.cpp index 0fe0069af369c..19d6976633329 100644 --- a/clang/test/Refactor/Rename/NoNewName.cpp +++ b/clang/test/Refactor/Rename/NoNewName.cpp @@ -1,4 +1,4 @@ // Check for an error while -new-name argument has not been passed to // clang-rename. // RUN: not clang-refactor-test rename-initiate -at=%s:1:11 %s 2>&1 | FileCheck %s -// CHECK: clang-refactor-test: for the -new-name option: must be specified at least once +// CHECK: for the --new-name option: must be specified at least once! diff --git a/clang/test/Refactor/Rename/rename-indexed-file.cpp b/clang/test/Refactor/Rename/rename-indexed-file.cpp index 4e64fdf511a5f..0c9a5da6ba21f 100644 --- a/clang/test/Refactor/Rename/rename-indexed-file.cpp +++ b/clang/test/Refactor/Rename/rename-indexed-file.cpp @@ -38,7 +38,7 @@ Test notIndexed; // CHECK1-NOT: rename [[@LINE]] // RUN: not clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s -// CHECK-ERROR1: for the -indexed-file option: must be specified at least once! +// CHECK-ERROR1: for the --indexed-file option: must be specified at least once! // It should be possible to have the filename as one of the compilation arguments // RUN: clang-refactor-test rename-indexed-file -no-textual-matches -ignore-filename-for-initiation-tu -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 -indexed-at=6:3 -indexed-at=7:4 -indexed-at=14:3 %s -c %s -Wall | FileCheck --check-prefix=CHECK1 %s diff --git a/clang/test/Refactor/Rename/rename-initiate-usr.cpp b/clang/test/Refactor/Rename/rename-initiate-usr.cpp index 2f1546522edba..4e75bc6688029 100644 --- a/clang/test/Refactor/Rename/rename-initiate-usr.cpp +++ b/clang/test/Refactor/Rename/rename-initiate-usr.cpp @@ -17,4 +17,4 @@ void foo() { // CHECK-ERROR1: error: could not rename symbol with the given USR // RUN: not clang-refactor-test rename-initiate-usr -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR2 %s -// CHECK-ERROR2: for the -usr option: must be specified at least once +// CHECK-ERROR2: for the --usr option: must be specified at least once From 69d67d3d8049ce1bf5632b7d11b9a7b77e791a27 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki <rishizaki@apple.com> Date: Thu, 9 May 2019 09:55:59 -0700 Subject: [PATCH 407/582] [IndexDataStore] Sort unit names using std::sort() std::string is not POD. apple-llvm-split-commit: dcd292d511866c4fe33a90c6a37b9cf640458216 apple-llvm-split-dir: clang/ --- clang/lib/IndexDataStore/IndexDataStore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/IndexDataStore/IndexDataStore.cpp b/clang/lib/IndexDataStore/IndexDataStore.cpp index ec7a346935cc4..6f207b4d95ee6 100644 --- a/clang/lib/IndexDataStore/IndexDataStore.cpp +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -85,7 +85,7 @@ bool IndexDataStoreImpl::foreachUnitName(bool sorted, } if (sorted) { - llvm::array_pod_sort(filenames.begin(), filenames.end()); + std::sort(filenames.begin(), filenames.end()); for (auto &fname : filenames) if (!receiver(fname)) return false; From 5bc13c5e5994bd459db3c9413154df1371da273d Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Thu, 18 Apr 2019 22:32:39 -0400 Subject: [PATCH 408/582] Objective-C categories for class stubs go in the __objc_catlist2 section apple-llvm-split-commit: d358baed4d9ecb6fc15990c3be00d40b9c08232f apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCMac.cpp | 11 ++++++++++- clang/test/CodeGenObjC/class-stubs.m | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 4a846f3e9c1f6..d4fe4638eebc2 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -899,6 +899,9 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { /// DefinedCategories - List of defined categories. SmallVector<llvm::GlobalValue*, 16> DefinedCategories; + /// DefinedStubCategories - List of defined categories on class stubs. + SmallVector<llvm::GlobalValue*, 16> DefinedStubCategories; + /// DefinedNonLazyCategories - List of defined "non-lazy" categories. SmallVector<llvm::GlobalValue*, 16> DefinedNonLazyCategories; @@ -6097,6 +6100,9 @@ void CGObjCNonFragileABIMac::FinishNonFragileABIModule() { AddModuleClassList(DefinedCategories, "OBJC_LABEL_CATEGORY_$", GetSectionName("__objc_catlist", "regular,no_dead_strip")); + AddModuleClassList(DefinedStubCategories, "OBJC_LABEL_STUB_CATEGORY_$", + GetSectionName("__objc_catlist2", + "regular,no_dead_strip")); AddModuleClassList(DefinedNonLazyCategories, "OBJC_LABEL_NONLAZY_CATEGORY_$", GetSectionName("__objc_nlcatlist", "regular,no_dead_strip")); @@ -6588,7 +6594,10 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { llvm::GlobalVariable *GCATV = finishAndCreateGlobal(values, ExtCatName.str(), CGM); CGM.addCompilerUsedGlobal(GCATV); - DefinedCategories.push_back(GCATV); + if (Interface->hasAttr<ObjCClassStubAttr>()) + DefinedStubCategories.push_back(GCATV); + else + DefinedCategories.push_back(GCATV); // Determine if this category is also "non-lazy". if (ImplementationIsNonLazy(OCD)) diff --git a/clang/test/CodeGenObjC/class-stubs.m b/clang/test/CodeGenObjC/class-stubs.m index 2890cfcf99b99..1044f5eb06f03 100644 --- a/clang/test/CodeGenObjC/class-stubs.m +++ b/clang/test/CodeGenObjC/class-stubs.m @@ -20,6 +20,10 @@ // // CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 +// -- category list for class stubs goes in __objc_catlist2. +// +// CHECK-LABEL: @"OBJC_LABEL_STUB_CATEGORY_$" = private global [1 x i8*] [i8* bitcast (%struct._category_t* @"\01l_OBJC_$_CATEGORY_Derived_$_MyCategory" to i8*)], section "__DATA,__objc_catlist2,regular,no_dead_strip", align 8 + __attribute__((objc_class_stub)) __attribute__((objc_subclassing_restricted)) @interface Base From d190eff32b592b83f41cc2dfb2209e6c7d332cbc Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Thu, 18 Apr 2019 22:33:03 -0400 Subject: [PATCH 409/582] Objective-C class references for class stubs no longer go in the __objc_classrefs section apple-llvm-split-commit: 90752746d467e2f8eb92e75b433499ce8b256680 apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCMac.cpp | 4 +++- clang/test/CodeGenObjC/class-stubs.m | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index d4fe4638eebc2..facb123640b5d 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -7331,7 +7331,9 @@ CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, getLinkageTypeForObjCMetadata(CGM, SectionName), ClassGV, "OBJC_CLASSLIST_REFERENCES_$_"); Entry->setAlignment(CGF.getPointerAlign().getQuantity()); - Entry->setSection(SectionName); + if (!ID || !ID->hasAttr<ObjCClassStubAttr>()) + Entry->setSection(SectionName); + CGM.addCompilerUsedGlobal(Entry); } diff --git a/clang/test/CodeGenObjC/class-stubs.m b/clang/test/CodeGenObjC/class-stubs.m index 1044f5eb06f03..962b690d382a7 100644 --- a/clang/test/CodeGenObjC/class-stubs.m +++ b/clang/test/CodeGenObjC/class-stubs.m @@ -5,7 +5,7 @@ // The class is declared with objc_class_stub, so LSB of the class pointer // must be set to 1. // -// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8 +// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), align 8 // -- classref for the super message send in anotherClassMethod() // From 9ca18185915c76e9ca2bb340b018308f7a15772e Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Mon, 6 May 2019 18:39:44 -0400 Subject: [PATCH 410/582] Sema: NFC cleanup for ObjCClassStubAttr checking apple-llvm-split-commit: c835966fb6ac488ecab75bc1b09fbedb08c50ea1 apple-llvm-split-dir: clang/ --- clang/lib/Sema/SemaDeclObjC.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 6c144bc3dd38e..27c406d0684bf 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -4140,10 +4140,9 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods, } } - if (IntfDecl->hasAttr<ObjCClassStubAttr>()) { - if (!IntfDecl->hasAttr<ObjCSubclassingRestrictedAttr>()) - Diag(IntfDecl->getLocation(), diag::err_class_stub_subclassing_mismatch); - } + if (IntfDecl->hasAttr<ObjCClassStubAttr>() && + !IntfDecl->hasAttr<ObjCSubclassingRestrictedAttr>()) + Diag(IntfDecl->getLocation(), diag::err_class_stub_subclassing_mismatch); } DiagnoseVariableSizedIvars(*this, OCD); if (isInterfaceDeclKind) { From d50848a0663b54785fc3b255836b30f650a460f9 Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Mon, 6 May 2019 18:40:17 -0400 Subject: [PATCH 411/582] ClangAttrEmitter: CustomCode attributes now bind LangOpts as a name Prefixing the expression with "S." was really silly because we could only refer to LangOpts at the beginning of the expression. Also clean up duplicate code to form the test expression. apple-llvm-split-commit: 39328191f726c09330d0469d59712ee29faffb0f apple-llvm-split-dir: clang/ --- clang/utils/TableGen/ClangAttrEmitter.cpp | 77 +++++++++++------------ 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index c556bca6b1ea7..76958410deec1 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1947,6 +1947,34 @@ bool PragmaClangAttributeSupport::isAttributedSupported( return true; } +static std::string GenerateTestExpression(ArrayRef<Record *> LangOpts) { + std::string Test; + + for (auto *E : LangOpts) { + if (!Test.empty()) + Test += " || "; + + if (E->getValueAsBit("Negated")) { + Test += "!"; + } + + const StringRef Code = E->getValueAsString("CustomCode"); + if (!Code.empty()) { + Test += "("; + Test += Code; + Test += ")"; + } else { + Test += "LangOpts."; + Test += E->getValueAsString("Name"); + } + } + + if (Test.empty()) + return "true"; + + return Test; +} + std::string PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, raw_ostream &OS) { @@ -1973,24 +2001,8 @@ PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, // rules if the specific language options are specified. std::vector<Record *> LangOpts = Rule.getLangOpts(); OS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() - << ", /*IsSupported=*/"; - if (!LangOpts.empty()) { - for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - if ((*I)->getValueAsBit("Negated")) - OS << "!"; - const StringRef Code = (*I)->getValueAsString("CustomCode"); - if (!Code.empty()) { - OS << Code; - } else { - const StringRef Name = (*I)->getValueAsString("Name"); - OS << "LangOpts." << Name; - } - if (I + 1 != E) - OS << " || "; - } - } else - OS << "true"; - OS << "));\n"; + << ", /*IsSupported=*/" << GenerateTestExpression(LangOpts) + << "));\n"; } } OS << "}\n\n"; @@ -3461,28 +3473,14 @@ static std::string GenerateLangOptRequirements(const Record &R, if (LangOpts.empty()) return "defaultDiagnoseLangOpts"; - // Generate the test condition, as well as a unique function name for the - // diagnostic test. The list of options should usually be short (one or two - // options), and the uniqueness isn't strictly necessary (it is just for - // codegen efficiency). - std::string FnName = "check", Test; + // Generate a unique function name for the diagnostic test. The list of + // options should usually be short (one or two options), and the + // uniqueness isn't strictly necessary (it is just for codegen efficiency). + std::string FnName = "check"; for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - if ((*I)->getValueAsBit("Negated")) { + if ((*I)->getValueAsBit("Negated")) FnName += "Not"; - Test += "!"; - } - const StringRef Name = (*I)->getValueAsString("Name"); - FnName += Name; - const StringRef Code = (*I)->getValueAsString("CustomCode"); - if (!Code.empty()) { - Test += "S."; - Test += Code; - } else { - Test += "S.LangOpts."; - Test += Name; - } - if (I + 1 != E) - Test += " || "; + FnName += (*I)->getValueAsString("Name"); } FnName += "LangOpts"; @@ -3494,7 +3492,8 @@ static std::string GenerateLangOptRequirements(const Record &R, return *I; OS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr) {\n"; - OS << " if (" << Test << ")\n"; + OS << " auto &LangOpts = S.LangOpts;\n"; + OS << " if (" << GenerateTestExpression(LangOpts) << ")\n"; OS << " return true;\n\n"; OS << " S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) "; OS << "<< Attr.getName();\n"; From bd22ed47d046747319deee073b5963b78d59f8ef Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Tue, 7 May 2019 16:28:02 -0400 Subject: [PATCH 412/582] Weak-link reference to objc_loadClassref() apple-llvm-split-commit: dac0edb94edb488df5ebaca6ee7d13ed9f88ce14 apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCMac.cpp | 6 +++++- clang/test/CodeGenObjC/class-stubs.m | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index facb123640b5d..dc6a3c31129b0 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -733,7 +733,7 @@ class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { // Also it is safe to make it readnone, since we never load or store the // classref except by calling this function. llvm::Type *params[] = { Int8PtrPtrTy }; - return CGM.CreateRuntimeFunction( + auto *F = CGM.CreateRuntimeFunction( llvm::FunctionType::get(ClassnfABIPtrTy, params, false), "objc_loadClassref", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -741,6 +741,10 @@ class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { {llvm::Attribute::NonLazyBind, llvm::Attribute::ReadNone, llvm::Attribute::NoUnwind})); + if (!CGM.getTriple().isOSBinFormatCOFF()) + cast<llvm::Function>(F)->setLinkage(llvm::Function::ExternalWeakLinkage); + + return F; } llvm::StructType *EHTypeTy; diff --git a/clang/test/CodeGenObjC/class-stubs.m b/clang/test/CodeGenObjC/class-stubs.m index 962b690d382a7..31bfd5230d97a 100644 --- a/clang/test/CodeGenObjC/class-stubs.m +++ b/clang/test/CodeGenObjC/class-stubs.m @@ -47,7 +47,7 @@ int main() { // CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* [[RECEIVER]], i8* [[SELECTOR]]) // CHECK-NEXT: ret i32 0 -// CHECK-LABEL: declare %struct._class_t* @objc_loadClassref(i8**) +// CHECK-LABEL: declare extern_weak %struct._class_t* @objc_loadClassref(i8**) // CHECK-SAME: [[ATTRLIST:#.*]] @implementation Derived (MyCategory) From 663644e921c37a95699d8e11fce3b877d0e28349 Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Thu, 9 May 2019 21:22:57 -0400 Subject: [PATCH 413/582] Fix compile error from upstream changes apple-llvm-split-commit: 60f240b25757507099f287e7695f31f3c5c69aed apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCMac.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index dc6a3c31129b0..aa621e3ec3063 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -733,7 +733,7 @@ class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { // Also it is safe to make it readnone, since we never load or store the // classref except by calling this function. llvm::Type *params[] = { Int8PtrPtrTy }; - auto *F = CGM.CreateRuntimeFunction( + auto F = CGM.CreateRuntimeFunction( llvm::FunctionType::get(ClassnfABIPtrTy, params, false), "objc_loadClassref", llvm::AttributeList::get(CGM.getLLVMContext(), @@ -742,7 +742,8 @@ class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { llvm::Attribute::ReadNone, llvm::Attribute::NoUnwind})); if (!CGM.getTriple().isOSBinFormatCOFF()) - cast<llvm::Function>(F)->setLinkage(llvm::Function::ExternalWeakLinkage); + cast<llvm::Function>(F.getCallee())->setLinkage( + llvm::Function::ExternalWeakLinkage); return F; } From ab229588830845bde500a8b96b585cd0c43f6f4e Mon Sep 17 00:00:00 2001 From: Shoaib Meenai <smeenai@fb.com> Date: Fri, 10 May 2019 12:54:20 -0700 Subject: [PATCH 414/582] [clang] Fix test/CodeGenObjC/class-stubs.m after LLVM r360359 LLVM r360359 changes Obj-C symbols from private to internal and removes the leading l_. Adjust the test accordingly. apple-llvm-split-commit: 94c67a8520374453049f7bca8bf8739413499658 apple-llvm-split-dir: clang/ --- clang/test/CodeGenObjC/class-stubs.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/test/CodeGenObjC/class-stubs.m b/clang/test/CodeGenObjC/class-stubs.m index 31bfd5230d97a..fadb4433b8054 100644 --- a/clang/test/CodeGenObjC/class-stubs.m +++ b/clang/test/CodeGenObjC/class-stubs.m @@ -5,24 +5,24 @@ // The class is declared with objc_class_stub, so LSB of the class pointer // must be set to 1. // -// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), align 8 +// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = internal global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), align 8 // -- classref for the super message send in anotherClassMethod() // // Metaclasses do not use the "stub" mechanism and are referenced statically. // -// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_" = private global %struct._class_t* @"OBJC_METACLASS_$_Derived", section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_" = internal global %struct._class_t* @"OBJC_METACLASS_$_Derived", section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 // -- classref for the super message send in anotherInstanceMethod() // // The class is declared with objc_class_stub, so LSB of the class pointer // must be set to 1. // -// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = internal global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 // -- category list for class stubs goes in __objc_catlist2. // -// CHECK-LABEL: @"OBJC_LABEL_STUB_CATEGORY_$" = private global [1 x i8*] [i8* bitcast (%struct._category_t* @"\01l_OBJC_$_CATEGORY_Derived_$_MyCategory" to i8*)], section "__DATA,__objc_catlist2,regular,no_dead_strip", align 8 +// CHECK-LABEL: @"OBJC_LABEL_STUB_CATEGORY_$" = internal global [1 x i8*] [i8* bitcast (%struct._category_t* @"_OBJC_$_CATEGORY_Derived_$_MyCategory" to i8*)], section "__DATA,__objc_catlist2,regular,no_dead_strip", align 8 __attribute__((objc_class_stub)) __attribute__((objc_subclassing_restricted)) From 61592420d3e2a896ff64b6bcd9728dd359d7d479 Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Wed, 8 May 2019 17:14:12 -0700 Subject: [PATCH 415/582] Support `ignore_noninstrumented_modules` on Linux Land a proposed upstream change so I can continue to make progress on the Swift side. If the upstream patch changes before it lands, this will create a merge conflict which I will fix by aligning this change with whatever we decided to do upstream. Original upstream patch description/commit message: We implemented the `ignore_noninstrumented_modules` flag for Darwin [1] to be able to use TSan on Darwin, where the "re-compile the world" approach is difficult to follow in practice. Now we are in the same situation when adding TSan support for Swift on Linux. The libdispatch library is a source of false positives and compiling it with TSan (although it is compiled from source on Linux) proves to be tricky and insufficient. The better path seems to be to make `ignore_noninstrumented_modules` work on Linux. The missing piece with this functionality on Linux is the detection whether or not a loaded library is instrumented (which is suprisingly difficult). In this patch we use the ELF segment metadata and symbol table to check whether any sanitizer symbols are present. [2,3] were useful resources in the development of this patch (especially, `getSymbolCountFromGnuHash`). Also enable flag for libdispatch tests on Linux to make them pass. The default value for this flag on Linux remains OFF. [1]: https://reviews.llvm.org/D28264 [2]: https://chromium.googlesource.com/crashpad/crashpad/+/1f1657d573c789aa36b6022440e34d9ec30d894c%5E%21/ [3]: https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ Differential Revision: https://reviews.llvm.org/D61708 apple-llvm-split-commit: eb0abd168a7ae46fa7c622303f3dcc4e11f956d0 apple-llvm-split-dir: compiler-rt/ --- .../lib/sanitizer_common/sanitizer_common.cc | 7 +- .../lib/sanitizer_common/sanitizer_common.h | 3 +- .../sanitizer_linux_libcdep.cc | 123 +++++++++++++++++- .../tsan/Darwin/ignore-noninstrumented.mm | 6 +- .../test/tsan/ignore-noninstrumented.cc | 71 ++++++++++ .../test/tsan/libdispatch/lit.local.cfg | 3 +- 6 files changed, 203 insertions(+), 10 deletions(-) create mode 100644 compiler-rt/test/tsan/ignore-noninstrumented.cc diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.cc b/compiler-rt/lib/sanitizer_common/sanitizer_common.cc index 80fb8f60fc0b4..5a69285385690 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.cc @@ -127,19 +127,20 @@ void RemoveANSIEscapeSequencesFromString(char *str) { *z = '\0'; } -void LoadedModule::set(const char *module_name, uptr base_address) { +void LoadedModule::set(const char *module_name, uptr base_address, + bool instrumented) { clear(); full_name_ = internal_strdup(module_name); base_address_ = base_address; + instrumented_ = instrumented; } void LoadedModule::set(const char *module_name, uptr base_address, ModuleArch arch, u8 uuid[kModuleUUIDSize], bool instrumented) { - set(module_name, base_address); + set(module_name, base_address, instrumented); arch_ = arch; internal_memcpy(uuid_, uuid, sizeof(uuid_)); - instrumented_ = instrumented; } void LoadedModule::clear() { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h index 1703899e32b5d..2193e0439d3ad 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -711,7 +711,8 @@ class LoadedModule { internal_memset(uuid_, 0, kModuleUUIDSize); ranges_.clear(); } - void set(const char *module_name, uptr base_address); + void set(const char *module_name, uptr base_address, + bool instrumented = false); void set(const char *module_name, uptr base_address, ModuleArch arch, u8 uuid[kModuleUUIDSize], bool instrumented); void clear(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc index 0608898a1464d..b53cb5912a6d3 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -541,6 +541,126 @@ struct DlIteratePhdrData { bool first; }; +class DynamicSegment { + ElfW(Addr) base_addr; + size_t symbol_count; + ElfW(Sym *) symbol_table; + const char *string_table; + + public: + DynamicSegment(ElfW(Addr) base_addr, ElfW(Addr) segment_addr) + : base_addr(base_addr), + symbol_count(0), + symbol_table(nullptr), + string_table(nullptr) { + initialize(segment_addr); + CHECK(symbol_count > 0 && symbol_table && string_table); + } + + size_t symbolCount() const { return symbol_count; } + + const char *getSymbolName(size_t index) const { + auto str_index = symbol_table[index].st_name; + return &string_table[str_index]; + } + + private: + // Elf_Addr -> dereferenceable pointer + template <typename Type> + Type *toPtr(ElfW(Addr) addr_or_offset) const { + bool offset = addr_or_offset < base_addr; + ElfW(Addr) ptr = offset ? (base_addr + addr_or_offset) : addr_or_offset; + return reinterpret_cast<Type *>(ptr); + } + + void initialize(ElfW(Addr) segment_addr) { + auto *entry = toPtr<ElfW(Dyn)>(segment_addr); + for (; entry->d_tag != DT_NULL; entry++) { + auto addr = entry->d_un.d_ptr; + switch (entry->d_tag) { + case DT_HASH: + symbol_count = getSymbolCountFromHash(addr); + break; + case DT_GNU_HASH: + // DT_HASH takes precedence over DT_GNU_HASH + if (symbol_count > 0) + break; + symbol_count = getSymbolCountFromGnuHash(addr); + break; + case DT_SYMTAB: + CHECK_EQ(symbol_table, nullptr); + symbol_table = toPtr<ElfW(Sym)>(addr); + break; + case DT_STRTAB: + CHECK_EQ(string_table, nullptr); + string_table = toPtr<const char>(addr); + break; + } + } + } + + size_t getSymbolCountFromHash(ElfW(Addr) hashtable_addr) const { + struct ht_header { + uint32_t bucket_count; + uint32_t chain_count; + }; + return toPtr<ht_header>(hashtable_addr)->chain_count; + } + + size_t getSymbolCountFromGnuHash(ElfW(Addr) hashtable_addr) const { + struct ht_header { + uint32_t bucket_count; + uint32_t symoffset; + uint32_t bloom_size; + uint32_t bloom_shift; + }; + auto header = toPtr<ht_header>(hashtable_addr); + auto word_size = FIRST_32_SECOND_64(sizeof(uint32_t), sizeof(uint64_t)); + auto buckets_addr = + hashtable_addr + sizeof(ht_header) + (word_size * header->bloom_size); + auto buckets = toPtr<uint32_t>(buckets_addr); + auto chains_addr = + buckets_addr + (header->bucket_count * sizeof(buckets[0])); + auto chains = toPtr<uint32_t>(chains_addr); + + // Locate the chain that handles the largest index bucket. + uint32_t last_symbol = 0; + for (uint32_t i = 0; i < header->bucket_count; i++) { + last_symbol = Max(buckets[i], last_symbol); + } + + // Walk the bucket's chain to add the chain length to the total. + uint32_t chain_entry; + do { + chain_entry = chains[last_symbol - header->symoffset]; + last_symbol++; + } while ((chain_entry & 1) == 0); + + return last_symbol; + } +}; + +static bool IsModuleInstrumented(dl_phdr_info *info) { + // Iterate all headers of the library. + for (size_t header = 0; header < info->dlpi_phnum; header++) { + // We are only interested in dynamic segments. + if (info->dlpi_phdr[header].p_type != PT_DYNAMIC) + continue; + + auto base_addr = info->dlpi_addr; + auto segment_addr = info->dlpi_phdr[header].p_vaddr; + DynamicSegment segment(base_addr, segment_addr); + + // Iterate symbol table. + for (size_t i = 0; i < segment.symbolCount(); i++) { + auto *name = segment.getSymbolName(i); + if (internal_strcmp(name, "__tsan_init") == 0) + return true; + } + } + return false; +} + static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { DlIteratePhdrData *data = (DlIteratePhdrData*)arg; InternalScopedString module_name(kMaxPathLength); @@ -554,7 +674,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { if (module_name[0] == '\0') return 0; LoadedModule cur_module; - cur_module.set(module_name.data(), info->dlpi_addr); + bool instrumented = IsModuleInstrumented(info); + cur_module.set(module_name.data(), info->dlpi_addr, instrumented); for (int i = 0; i < (int)info->dlpi_phnum; i++) { const Elf_Phdr *phdr = &info->dlpi_phdr[i]; if (phdr->p_type == PT_LOAD) { diff --git a/compiler-rt/test/tsan/Darwin/ignore-noninstrumented.mm b/compiler-rt/test/tsan/Darwin/ignore-noninstrumented.mm index 88d39268f1b80..480cacfc6bbbe 100644 --- a/compiler-rt/test/tsan/Darwin/ignore-noninstrumented.mm +++ b/compiler-rt/test/tsan/Darwin/ignore-noninstrumented.mm @@ -6,13 +6,13 @@ // RUN: %clang_tsan %s -o %t -framework Foundation // Check that without the flag, there are false positives. -// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0 %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE +// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0 %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE // With ignore_noninstrumented_modules=1, no races are reported. -// RUN: %env_tsan_opts=ignore_noninstrumented_modules=1 %run %t 2>&1 | FileCheck %s +// RUN: %env_tsan_opts=ignore_noninstrumented_modules=1 %run %t 2>&1 | FileCheck %s --implicit-check-not='ThreadSanitizer' // With ignore_noninstrumented_modules=1, races in user's code are still reported. -// RUN: %env_tsan_opts=ignore_noninstrumented_modules=1 %deflake %run %t race 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RACE +// RUN: %env_tsan_opts=ignore_noninstrumented_modules=1 %deflake %run %t race 2>&1 | FileCheck %s --check-prefix=CHECK-RACE #import <Foundation/Foundation.h> diff --git a/compiler-rt/test/tsan/ignore-noninstrumented.cc b/compiler-rt/test/tsan/ignore-noninstrumented.cc new file mode 100644 index 0000000000000..a166f87354154 --- /dev/null +++ b/compiler-rt/test/tsan/ignore-noninstrumented.cc @@ -0,0 +1,71 @@ +// Check that ignore_noninstrumented_modules=1 suppresses reports originating +// from interceptors that are called from an un-instrumented library. + +// RUN: %clangxx_tsan %s -fPIC -shared -DLIBRARY -fno-sanitize=thread -o %t.library.so +// RUN: %clangxx_tsan %s %t.library.so -o %t + +// Check that without the flag, there are false positives. +// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0 %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE + +// With ignore_noninstrumented_modules=1, no races are reported. +// RUN: %env_tsan_opts=ignore_noninstrumented_modules=1 %run %t 2>&1 | FileCheck %s --implicit-check-not='ThreadSanitizer' + +// With ignore_noninstrumented_modules=1, races in user's code are still reported. +// RUN: %env_tsan_opts=ignore_noninstrumented_modules=1 %deflake %run %t race 2>&1 | FileCheck %s --check-prefix=CHECK-RACE + +#include "test.h" + +#include <cstring> + +#ifdef LIBRARY +namespace library { +#endif +char global_buf[64]; + +void *Thread1(void *x) { + barrier_wait(&barrier); + strcpy(global_buf, "hello world"); // NOLINT + return NULL; +} + +void *Thread2(void *x) { + strcpy(global_buf, "world hello"); // NOLINT + barrier_wait(&barrier); + return NULL; +} + +void Race() { + barrier_init(&barrier, 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} +#ifdef LIBRARY +} // namespace library +#endif + +#ifndef LIBRARY +namespace library { + void Race(); +} + +int main(int argc, char *argv[]) { + fprintf(stderr, "Hello world.\n"); + + // Race in un-instrumented library + library::Race(); + + // Race in user code, if requested + if (argc > 1 && strcmp(argv[1], "race") == 0) + Race(); + + fprintf(stderr, "Done.\n"); +} + +#endif // LIBRARY + +// CHECK: Hello world. +// CHECK-RACE: SUMMARY: ThreadSanitizer: data race +// CHECK: Done. diff --git a/compiler-rt/test/tsan/libdispatch/lit.local.cfg b/compiler-rt/test/tsan/libdispatch/lit.local.cfg index 9d3cf75489ad2..c19aa621f9661 100644 --- a/compiler-rt/test/tsan/libdispatch/lit.local.cfg +++ b/compiler-rt/test/tsan/libdispatch/lit.local.cfg @@ -13,5 +13,4 @@ if 'libdispatch' in root.available_features: else: config.unsupported = True -if config.host_os == 'Darwin': - config.environment['TSAN_OPTIONS'] += ':ignore_noninstrumented_modules=1' +config.environment['TSAN_OPTIONS'] += ':ignore_noninstrumented_modules=1' From 1c5f0b0ea694a022196e2346290de78b31e1348e Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Mon, 13 May 2019 10:54:52 -0700 Subject: [PATCH 416/582] Enable ignore_noninstrumented_modules=1 on Linux Enable ignore_noninstrumented_modules flag on all platforms (including Linux). This is a permanent divergance of upstream and Swift's comiler-rt. apple-llvm-split-commit: 34987763d2df8ec458b6e262005b0224733a39fb apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/tsan/rtl/tsan_flags.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_flags.inc b/compiler-rt/lib/tsan/rtl/tsan_flags.inc index bfb74b696e674..a977b530f3e62 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_flags.inc +++ b/compiler-rt/lib/tsan/rtl/tsan_flags.inc @@ -76,7 +76,7 @@ TSAN_FLAG(int, io_sync, 1, TSAN_FLAG(bool, die_after_fork, true, "Die after multi-threaded fork if the child creates new threads.") TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") -TSAN_FLAG(bool, ignore_noninstrumented_modules, SANITIZER_MAC ? true : false, +TSAN_FLAG(bool, ignore_noninstrumented_modules, true, "Interceptors should only detect races when called from instrumented " "modules.") TSAN_FLAG(bool, shared_ptr_interceptor, true, From f2ba8cecb47e0db30c59c9d64870a12e1ecd23af Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Tue, 14 May 2019 11:11:12 -0700 Subject: [PATCH 417/582] [TSan] Fix link failure in test rdar://50764496 apple-llvm-split-commit: 3fe34387e2ba977aa9c9b6db922745085c87f38e apple-llvm-split-dir: compiler-rt/ --- .../test/tsan/ignore-noninstrumented.cc | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/compiler-rt/test/tsan/ignore-noninstrumented.cc b/compiler-rt/test/tsan/ignore-noninstrumented.cc index a166f87354154..533526055a02c 100644 --- a/compiler-rt/test/tsan/ignore-noninstrumented.cc +++ b/compiler-rt/test/tsan/ignore-noninstrumented.cc @@ -22,23 +22,24 @@ namespace library { #endif char global_buf[64]; -void *Thread1(void *x) { - barrier_wait(&barrier); +void *Thread1(void *arg) { + auto barrier_wait = (void (*)())arg; + barrier_wait(); strcpy(global_buf, "hello world"); // NOLINT return NULL; } -void *Thread2(void *x) { +void *Thread2(void *arg) { + auto barrier_wait = (void (*)())arg; strcpy(global_buf, "world hello"); // NOLINT - barrier_wait(&barrier); + barrier_wait(); return NULL; } -void Race() { - barrier_init(&barrier, 2); +void Race(void (*barrier_wait)()) { pthread_t t[2]; - pthread_create(&t[0], NULL, Thread1, NULL); - pthread_create(&t[1], NULL, Thread2, NULL); + pthread_create(&t[0], NULL, Thread1, (void *)barrier_wait); + pthread_create(&t[1], NULL, Thread2, (void *)barrier_wait); pthread_join(t[0], NULL); pthread_join(t[1], NULL); } @@ -48,18 +49,27 @@ void Race() { #ifndef LIBRARY namespace library { - void Race(); + void Race(void (*barrier_wait)()); +} + +// Pass pointer to this function to un-instrumented library, so it can access +// TSan-invisible barriers. +void my_barrier_wait() { + barrier_wait(&barrier); } int main(int argc, char *argv[]) { fprintf(stderr, "Hello world.\n"); // Race in un-instrumented library - library::Race(); + barrier_init(&barrier, 2); + library::Race(my_barrier_wait); // Race in user code, if requested - if (argc > 1 && strcmp(argv[1], "race") == 0) - Race(); + if (argc > 1 && strcmp(argv[1], "race") == 0) { + barrier_init(&barrier, 2); + Race(my_barrier_wait); + } fprintf(stderr, "Done.\n"); } From ee105db5b3e49a3d8673ccc081232a8a1c7d9df9 Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Thu, 16 May 2019 14:02:02 -0400 Subject: [PATCH 418/582] Update AttrDocs.td to note that adding or removing __attribute__((objc_class_stub)) breaks ABI apple-llvm-split-commit: 2085cea32827322cc63d0a5f748411e929dd5c1b apple-llvm-split-dir: clang/ --- clang/include/clang/Basic/AttrDocs.td | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 1ba45376de1c3..34d1aaf0c145c 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1099,6 +1099,8 @@ access pattern to ensure the class is lazily instantiated from the class stub. Classes annotated with this attribute cannot be subclassed and cannot have implementations defined for them. This attribute is intended for use in Swift generated headers for classes defined in Swift. + +Adding or removing this attribute to a class is an ABI-breaking change. }]; } From d66458c68e8e19d5c0579df4914c790a7d351227 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 17 May 2019 13:01:20 -0700 Subject: [PATCH 419/582] Fixup build after upstream commit apple-llvm-split-commit: 3044c4bc86175dab4d94aca2ed8815720c47f5fc apple-llvm-split-dir: clang/ --- clang/lib/Parse/ParseDecl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index a6025b7297d47..1a805f7524308 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -7117,7 +7117,8 @@ TypeResult Parser::parseTypeFromString(StringRef typeStr, StringRef context, tokens.push_back(Tok); // Enter the tokens into the token stream. - PP.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false); + PP.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false, + /*IsReinject=*/false); // Consume the current token so that we'll start parsing the tokens we // added to the stream. From c237dfab6bd771c8fc70559f33f58e6567c66f04 Mon Sep 17 00:00:00 2001 From: Davide Italiano <ditaliano@apple.com> Date: Tue, 21 May 2019 12:34:04 -0700 Subject: [PATCH 420/582] [DirectoryWatcher] Fix libclang build for upstream-with-swift. apple-llvm-split-commit: 6e5463ddca8bd91c286767669ee84c1a4f844ed0 apple-llvm-split-dir: clang/ --- clang/lib/DirectoryWatcher/CMakeLists.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clang/lib/DirectoryWatcher/CMakeLists.txt b/clang/lib/DirectoryWatcher/CMakeLists.txt index 5b506166b7aea..b1f35aab3ff91 100644 --- a/clang/lib/DirectoryWatcher/CMakeLists.txt +++ b/clang/lib/DirectoryWatcher/CMakeLists.txt @@ -6,13 +6,9 @@ add_clang_library(clangDirectoryWatcher DirectoryWatcher.cpp ) -if(BUILD_SHARED_LIBS) - if(APPLE) - check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) - if(HAVE_CORESERVICES_H) - set(DIRECTORY_WATCHER_FLAGS "${DIRECTORY_WATCHER_FLAGS} -framework CoreServices") - endif() - set_property(TARGET clangDirectoryWatcher APPEND_STRING PROPERTY - LINK_FLAGS ${DIRECTORY_WATCHER_FLAGS}) +if(APPLE) + check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H) + if(HAVE_CORESERVICES_H) + target_link_libraries(clangDirectoryWatcher PRIVATE "-framework CoreServices") endif() endif() From 25027f26ad6411f0aa06b0e5af4e5e73d8c459bf Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Wed, 15 May 2019 06:14:09 -0700 Subject: [PATCH 421/582] [Modules] Make ReadModuleMapFileBlock errors reliable During implicit module builds, there are cases where ReadASTCore is called with ImportedBy set to nullptr, which breaks expectations in ReadModuleMapFileBlock and lead to crashes instead of an error message. Fix this by improving ReadModuleMapFileBlock to handle ImportedBy properly. Could not reproduce the exact scenario where this happens but looking at a crash backtrace we could verify the ReadASTCore callsite sets ImportedBy to nullptr. There are no testcases, since we were not even able to reproduce the issue, but there were lots of crashes with the same backtrace. rdar://problem/48828801 (cherry picked from commit 26188f488e5103f84e72bfa709120977c9a450ba) apple-llvm-split-commit: 25ff973ba62cd8faa45c4d1dc0bb29f23df33671 apple-llvm-split-dir: clang/ --- .../clang/Basic/DiagnosticSerializationKinds.td | 4 ++-- clang/lib/Serialization/ASTReader.cpp | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 43ba19b5853e2..0461d2f429a7b 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -77,13 +77,13 @@ def remark_module_import : Remark< InGroup<ModuleImport>; def err_imported_module_not_found : Error< - "module '%0' in AST file '%1' (imported by AST file '%2') " + "module '%0' in AST file '%1' %select{(imported by AST file '%2') |}4" "is not defined in any loaded module map file; " "maybe you need to load '%3'?">, DefaultFatal; def note_imported_by_pch_module_not_found : Note< "consider adding '%0' to the header search path">; def err_imported_module_modmap_changed : Error< - "module '%0' imported by AST file '%1' found in a different module map file" + "module '%0' %select{in|imported by}4 AST file '%1' found in a different module map file" " (%2) than when the importing AST file was built (%3)">, DefaultFatal; def err_imported_module_relocated : Error< "module '%0' was built in directory '%1' but now resides in " diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index ba7b78544403a..563fa48c8f944 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3589,7 +3589,6 @@ ASTReader::ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F, const FileEntry *ModMap = M ? Map.getModuleMapFileForUniquing(M) : nullptr; // Don't emit module relocation error if we have -fno-validate-pch if (!PP.getPreprocessorOpts().DisablePCHValidation && !ModMap) { - assert(ImportedBy && "top-level import should be verified"); if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) { if (auto *ASTFE = M ? M->getASTFile() : nullptr) { // This module was defined by an imported (explicit) module. @@ -3598,12 +3597,13 @@ ASTReader::ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F, } else { // This module was built with a different module map. Diag(diag::err_imported_module_not_found) - << F.ModuleName << F.FileName << ImportedBy->FileName - << F.ModuleMapPath; + << F.ModuleName << F.FileName + << (ImportedBy ? ImportedBy->FileName : "") << F.ModuleMapPath + << !ImportedBy; // In case it was imported by a PCH, there's a chance the user is // just missing to include the search path to the directory containing // the modulemap. - if (ImportedBy->Kind == MK_PCH) + if (ImportedBy && ImportedBy->Kind == MK_PCH) Diag(diag::note_imported_by_pch_module_not_found) << llvm::sys::path::parent_path(F.ModuleMapPath); } @@ -3617,11 +3617,13 @@ ASTReader::ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F, const FileEntry *StoredModMap = FileMgr.getFile(F.ModuleMapPath); if (StoredModMap == nullptr || StoredModMap != ModMap) { assert(ModMap && "found module is missing module map file"); - assert(ImportedBy && "top-level import should be verified"); + assert((ImportedBy || F.Kind == MK_ImplicitModule) && + "top-level import should be verified"); + bool NotImported = F.Kind == MK_ImplicitModule && !ImportedBy; if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) Diag(diag::err_imported_module_modmap_changed) - << F.ModuleName << ImportedBy->FileName - << ModMap->getName() << F.ModuleMapPath; + << F.ModuleName << (NotImported ? F.FileName : ImportedBy->FileName) + << ModMap->getName() << F.ModuleMapPath << NotImported; return OutOfDate; } From 9c31fef1c431cce8fb02873bbd7abed7d2d9af82 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai <smeenai@fb.com> Date: Wed, 29 May 2019 11:41:12 -0700 Subject: [PATCH 422/582] [AST] Fix test for Swift clang 9890adfbee8f854732d0093bc8b2a32be1be8844 changed the AST for ObjCCompatibleAliasDecl to record the alias name as its column location rather than the @compatibility_alias. Update a newly added AST dumping test accordingly. apple-llvm-split-commit: da63a9207c242fe5ab05e974db7853ede0e92eb8 apple-llvm-split-dir: clang/ --- clang/test/AST/ast-dump-decl-json.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/AST/ast-dump-decl-json.m b/clang/test/AST/ast-dump-decl-json.m index 1c816f3111bad..c610751f3b62d 100644 --- a/clang/test/AST/ast-dump-decl-json.m +++ b/clang/test/AST/ast-dump-decl-json.m @@ -962,7 +962,7 @@ void f() { // CHECK: "kind": "ObjCCompatibleAliasDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "col": 22, // CHECK-NEXT: "file": "{{.*}}", // CHECK-NEXT: "line": 60 // CHECK-NEXT: }, From baf25f4fa4ac992b8ac67b35f026be6c7ec65012 Mon Sep 17 00:00:00 2001 From: Slava Pestov <spestov@apple.com> Date: Fri, 31 May 2019 15:25:19 -0400 Subject: [PATCH 423/582] Fix compile error from bad merge apple-llvm-split-commit: eba11339bb8dd5b18f262ff863eaeeec5f72c0b2 apple-llvm-split-dir: clang/ --- clang/lib/CodeGen/CGObjCMac.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 715213e83204e..b43a5751d177e 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -7313,37 +7313,6 @@ CGObjCNonFragileABIMac::EmitLoadOfClassRef(CodeGenFunction &CGF, return CGF.Builder.CreateAlignedLoad(Entry, Align); } -llvm::Constant * -CGObjCNonFragileABIMac::GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID) { - llvm::Constant *ClassGV = GetClassGlobal(ID, /*metaclass*/ false, - NotForDefinition); - - if (!ID->hasAttr<ObjCClassStubAttr>()) - return ClassGV; - - ClassGV = llvm::ConstantExpr::getPointerCast(ClassGV, ObjCTypes.Int8PtrTy); - - // Stub classes are pointer-aligned. Classrefs pointing at stub classes - // must set the least significant bit set to 1. - auto *Idx = llvm::ConstantInt::get(CGM.Int32Ty, 1); - return llvm::ConstantExpr::getGetElementPtr(CGM.Int8Ty, ClassGV, Idx); -} - -llvm::Value * -CGObjCNonFragileABIMac::EmitLoadOfClassRef(CodeGenFunction &CGF, - const ObjCInterfaceDecl *ID, - llvm::GlobalVariable *Entry) { - if (ID && ID->hasAttr<ObjCClassStubAttr>()) { - // Classrefs pointing at Objective-C stub classes must be loaded by calling - // a special runtime function. - return CGF.EmitRuntimeCall( - ObjCTypes.getLoadClassrefFn(), Entry, "load_classref_result"); - } - - CharUnits Align = CGF.getPointerAlign(); - return CGF.Builder.CreateAlignedLoad(Entry, Align); -} - llvm::Value * CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, IdentifierInfo *II, From a02454b91d2aec347b9ce03020656c445f3b2841 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 6 Jun 2019 14:08:52 -0700 Subject: [PATCH 424/582] Add support for __isPlatformVersionAtLeast (new API for iOS 13.0 / macOS Catalina) to compiler-rt (#41) apple-llvm-split-commit: 2a8b14cb81cab88548b77763bae67bc1f427fd1a apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/builtins/os_version_check.c | 44 ++++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/compiler-rt/lib/builtins/os_version_check.c b/compiler-rt/lib/builtins/os_version_check.c index 3794b979434cc..808b5bcb1248f 100644 --- a/compiler-rt/lib/builtins/os_version_check.c +++ b/compiler-rt/lib/builtins/os_version_check.c @@ -25,6 +25,19 @@ static int32_t GlobalMajor, GlobalMinor, GlobalSubminor; static dispatch_once_t DispatchOnceCounter; +// _availability_version_check darwin API support. +typedef uint32_t dyld_platform_t; + +typedef struct { + dyld_platform_t platform; + uint32_t version; +} dyld_build_version_t; + +typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count, + dyld_build_version_t versions[]); + +static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck; + // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so // just forward declare everything that we need from it. @@ -73,8 +86,15 @@ typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex, typedef void (*CFReleaseFuncTy)(CFTypeRef); // Find and parse the SystemVersion.plist file. -static void parseSystemVersionPList(void *Unused) { +static void initializeAvailabilityCheck(void *Unused) { (void)Unused; + + // Use the new API if it's is available. Still load the PLIST to ensure that the + // existing calls to __isOSVersionAtLeast still work even with new + // compiler-rt and new OSes. + AvailabilityVersionCheck = (AvailabilityVersionCheckFuncTy)dlsym( + RTLD_DEFAULT, "_availability_version_check"); + // Load CoreFoundation dynamically const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull"); if (!NullAllocator) @@ -201,9 +221,12 @@ static void parseSystemVersionPList(void *Unused) { fclose(PropertyList); } +// This old API entry point is no longer used by Clang. We still need to keep it +// around to ensure that object files that reference it are still usable when +// linked with new compiler-rt. int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { // Populate the global version variables, if they haven't already. - dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList); + dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck); if (Major < GlobalMajor) return 1; @@ -216,6 +239,23 @@ int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { return Subminor <= GlobalSubminor; } +static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor, + uint32_t Subminor) { + return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff); +} + +int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major, + uint32_t Minor, uint32_t Subminor) { + dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck); + + if (!AvailabilityVersionCheck) { + return __isOSVersionAtLeast(Major, Minor, Subminor); + } + dyld_build_version_t Versions[] = { + {Platform, ConstructVersion(Major, Minor, Subminor)}}; + return AvailabilityVersionCheck(1, Versions); +} + #else // Silence an empty translation unit warning. From 975f8f1991733fe67d0a0f30f5697979961e2dce Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Sun, 9 Jun 2019 11:17:28 -0700 Subject: [PATCH 425/582] [CMake] Enable '-index-store-path' compiler option on a Debug build, if the compiler supports it and for non-IDE generators It can also be explicitely disabled via `LLVM_DISABLE_INDEX_STORE`. apple-llvm-split-commit: e574526c29d1f39c3363ef44c11e1a5f039bc5e3 apple-llvm-split-dir: llvm/ --- llvm/cmake/modules/HandleLLVMOptions.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake index 966a2b9294e6d..2d8c948528277 100644 --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -669,6 +669,17 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND NOT LLVM_ENABLE_WARNINGS) append("-w" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() +# Enable '-index-store-path' on a Debug build, if the compiler supports it and for non-IDE generators. +set(LLVM_DISABLE_INDEX_STORE OFF CACHE BOOL "Disable '-index-store-path' flag" FORCE) +if (NOT LLVM_DISABLE_INDEX_STORE AND NOT XCODE AND NOT MSVC_IDE AND uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") + set(INDEX_DATA_STORE_PATH "${PROJECT_BINARY_DIR}/IndexStore" CACHE STRING "Index store path" FORCE) + + check_c_compiler_flag("-Werror -index-store-path \"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/IndexStore\"" "C_SUPPORTS_INDEX_STORE") + append_if("C_SUPPORTS_INDEX_STORE" "-index-store-path \"${INDEX_DATA_STORE_PATH}\"" CMAKE_C_FLAGS) + check_cxx_compiler_flag("-Werror -index-store-path \"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/IndexStore\"" "CXX_SUPPORTS_INDEX_STORE") + append_if("CXX_SUPPORTS_INDEX_STORE" "-index-store-path \"${INDEX_DATA_STORE_PATH}\"" CMAKE_CXX_FLAGS) +endif() + macro(append_common_sanitizer_flags) if (NOT MSVC) # Append -fno-omit-frame-pointer and turn on debug info to get better From 7d2bedeaf2a0ed9efb38bd939366a2dba0e2da3f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis <kyrtzidis@apple.com> Date: Tue, 11 Jun 2019 10:54:27 -0700 Subject: [PATCH 426/582] [CMake] Change `LLVM_DISABLE_INDEX_STORE` to be an option and remove 'FORCE' from `INDEX_DATA_STORE_PATH` apple-llvm-split-commit: 845582b9def7518baf0c90e9a3647e58a58c73a6 apple-llvm-split-dir: llvm/ --- llvm/cmake/modules/HandleLLVMOptions.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake index 2d8c948528277..d83ac2c810d56 100644 --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -670,9 +670,9 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND NOT LLVM_ENABLE_WARNINGS) endif() # Enable '-index-store-path' on a Debug build, if the compiler supports it and for non-IDE generators. -set(LLVM_DISABLE_INDEX_STORE OFF CACHE BOOL "Disable '-index-store-path' flag" FORCE) +option(LLVM_DISABLE_INDEX_STORE "Disable '-index-store-path' flag" Off) if (NOT LLVM_DISABLE_INDEX_STORE AND NOT XCODE AND NOT MSVC_IDE AND uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") - set(INDEX_DATA_STORE_PATH "${PROJECT_BINARY_DIR}/IndexStore" CACHE STRING "Index store path" FORCE) + set(INDEX_DATA_STORE_PATH "${PROJECT_BINARY_DIR}/IndexStore" CACHE STRING "Index store path") check_c_compiler_flag("-Werror -index-store-path \"${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/IndexStore\"" "C_SUPPORTS_INDEX_STORE") append_if("C_SUPPORTS_INDEX_STORE" "-index-store-path \"${INDEX_DATA_STORE_PATH}\"" CMAKE_C_FLAGS) From 7000e51128cab971224c6817d4af5c382d7d0e5c Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 19 Jun 2019 10:28:29 -0700 Subject: [PATCH 427/582] Temporarily rollback support for skipping unused module maps in lib/Frontend/DependencyFile.cpp for easier merge apple-llvm-split-commit: a714acbad54a446a00a66311567a8acc095dd8be apple-llvm-split-dir: clang/ --- clang/lib/Frontend/DependencyFile.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index b89e1ea350910..363aff76f0224 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -160,7 +160,6 @@ class DFGImpl : public PPCallbacks { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; - bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; @@ -177,7 +176,6 @@ class DFGImpl : public PPCallbacks { AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false), IncludeModuleFiles(Opts.IncludeModuleFiles), - SkipUnusedModuleMaps(Opts.SkipUnusedModuleMaps), OutputFormat(Opts.OutputFormat), InputFileIndex(0) { for (const auto &ExtraDep : Opts.ExtraDeps) { @@ -211,7 +209,6 @@ class DFGImpl : public PPCallbacks { bool AddFilename(StringRef Filename); bool includeSystemHeaders() const { return IncludeSystemHeaders; } bool includeModuleFiles() const { return IncludeModuleFiles; } - bool skipUnusedModuleMaps() const { return SkipUnusedModuleMaps; } }; class DFGMMCallback : public ModuleMapCallbacks { @@ -220,17 +217,9 @@ class DFGMMCallback : public ModuleMapCallbacks { DFGMMCallback(DFGImpl &Parent) : Parent(Parent) {} void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, bool IsSystem) override { - if (Parent.skipUnusedModuleMaps()) - return; if (!IsSystem || Parent.includeSystemHeaders()) Parent.AddFilename(Entry.getName()); } - void moduleMapFoundForModule(const FileEntry &Entry, const Module *M, - bool IsSystem) override { - if (Parent.skipUnusedModuleMaps() && - (!IsSystem || Parent.includeSystemHeaders())) - Parent.AddFilename(Entry.getName()); - } }; class DFGASTReaderListener : public ASTReaderListener { From bb064521f26be4c99457e6cd2469a041fcccb341 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 19 Jun 2019 10:46:52 -0700 Subject: [PATCH 428/582] Reimplement support for skipping unused module maps in lib/Frontend/DependencyFile.cpp after upstream merge apple-llvm-split-commit: 1e967a7ea4526cbd16b0e04f2e3bece7b149a312 apple-llvm-split-dir: clang/ --- clang/include/clang/Frontend/Utils.h | 1 + clang/lib/Frontend/DependencyFile.cpp | 38 ++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index 951d51bbc15f4..ce44aeab7907e 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -142,6 +142,7 @@ class DependencyFileGenerator : public DependencyCollector { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; + bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; }; diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index b52e25bb0eb1c..92f604a862861 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -112,6 +112,36 @@ struct DepCollectorMMCallbacks : public ModuleMapCallbacks { } }; +// FIXME: This should not be separate from upstream, but we haven't +// upstreamed support for SkipUnusedModuleMaps. +struct DFGMMCallback : public ModuleMapCallbacks { + DependencyCollector &DepCollector; + bool SkipUnusedModuleMaps; + DFGMMCallback(DependencyCollector &DC, bool SkipUnusedModuleMaps) + : DepCollector(DC), SkipUnusedModuleMaps(SkipUnusedModuleMaps) {} + + void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, + bool IsSystem) override { + if (SkipUnusedModuleMaps) + return; + StringRef Filename = Entry.getName(); + DepCollector.maybeAddDependency(Filename, /*FromModule*/ false, + /*IsSystem*/ IsSystem, + /*IsModuleFile*/ false, + /*IsMissing*/ false); + } + + void moduleMapFoundForModule(const FileEntry &Entry, const Module *M, + bool IsSystem) override { + if (!SkipUnusedModuleMaps) + return; + DepCollector.maybeAddDependency(Entry.getName(), /*FromModule*/ false, + /*IsSystem*/ IsSystem, + /*IsModuleFile*/ false, + /*IsMissing*/ false); + } +}; + struct DepCollectorASTListener : public ASTReaderListener { DependencyCollector &DepCollector; DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { } @@ -184,6 +214,7 @@ DependencyFileGenerator::DependencyFileGenerator( PhonyTarget(Opts.UsePhonyTargets), AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false), IncludeModuleFiles(Opts.IncludeModuleFiles), + SkipUnusedModuleMaps(Opts.SkipUnusedModuleMaps), OutputFormat(Opts.OutputFormat), InputFileIndex(0) { for (const auto &ExtraDep : Opts.ExtraDeps) { if (addDependency(ExtraDep)) @@ -201,7 +232,12 @@ void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) { if (AddMissingHeaderDeps) PP.SetSuppressIncludeNotFoundError(true); - DependencyCollector::attachToPreprocessor(PP); + // FIXME: Restore the call to DependencyCollector::attachToPreprocessor(PP); + // once the SkipUnusedModuleMaps is upstreamed. + PP.addPPCallbacks(llvm::make_unique<DepCollectorPPCallbacks>( + *this, PP.getSourceManager(), PP.getDiagnostics())); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + llvm::make_unique<DFGMMCallback>(*this, SkipUnusedModuleMaps)); } bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule, From 3b04589717fff70d24d988da17a3b8e4d2fd66ef Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Wed, 26 Jun 2019 15:59:21 -0700 Subject: [PATCH 429/582] [TSan] Turn on ignore_interceptors_accesses Bring back and turn on ignore_interceptors_accesses. This flag is usually enabled on Darwin. Turn it on unconditionally for Swift, even on Linux. Reason: Silences the remaining false positives for TSan in the swift-nio test suite. rdar://52215193 apple-llvm-split-commit: 0de5fe9997575c446d58e4af386e1f64aa2f3715 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/tsan/rtl/tsan_flags.inc | 2 ++ compiler-rt/lib/tsan/rtl/tsan_interceptors.cc | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_flags.inc b/compiler-rt/lib/tsan/rtl/tsan_flags.inc index a977b530f3e62..f733fac45987a 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_flags.inc +++ b/compiler-rt/lib/tsan/rtl/tsan_flags.inc @@ -76,6 +76,8 @@ TSAN_FLAG(int, io_sync, 1, TSAN_FLAG(bool, die_after_fork, true, "Die after multi-threaded fork if the child creates new threads.") TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") +TSAN_FLAG(bool, ignore_interceptors_accesses, false, + "Ignore reads and writes from all interceptors.") TSAN_FLAG(bool, ignore_noninstrumented_modules, true, "Interceptors should only detect races when called from instrumented " "modules.") diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc index e8908ac98ed69..2d8d881b156ba 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc @@ -253,7 +253,8 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, if (!thr_->ignore_interceptors) FuncEntry(thr, pc); DPrintf("#%d: intercept %s()\n", thr_->tid, fname); ignoring_ = - !thr_->in_ignored_lib && libignore()->IsIgnored(pc, &in_ignored_lib_); + !thr_->in_ignored_lib && (flags()->ignore_interceptors_accesses || + libignore()->IsIgnored(pc, &in_ignored_lib_)); EnableIgnores(); } From af934467296e686ac0f74f0d966542cfc3012e86 Mon Sep 17 00:00:00 2001 From: JF Bastien <jfbastien@apple.com> Date: Wed, 26 Jun 2019 17:45:29 -0700 Subject: [PATCH 430/582] Fix APINotes following bitstream reader API update Upstream change in rL364464 broke downstream APINotes. apple-llvm-split-commit: c0ab2b05f0bbbb9c667e3f2421bfdb140ae0c119 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesReader.cpp | 380 ++++++++++++++++++++++---- 1 file changed, 334 insertions(+), 46 deletions(-) diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index ff03e1e939706..513d9c5ee4d00 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -702,7 +702,14 @@ bool APINotesReader::Implementation::readControlBlock( bool sawMetadata = false; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -713,13 +720,26 @@ bool APINotesReader::Implementation::readControlBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); + switch (kind) { case control_block::METADATA: // Already saw metadata. @@ -750,7 +770,13 @@ bool APINotesReader::Implementation::readControlBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return !sawMetadata; @@ -762,7 +788,14 @@ bool APINotesReader::Implementation::readIdentifierBlock( if (cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -773,13 +806,25 @@ bool APINotesReader::Implementation::readIdentifierBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case identifier_block::IDENTIFIER_DATA: { // Already saw identifier table. @@ -803,7 +848,13 @@ bool APINotesReader::Implementation::readIdentifierBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -815,7 +866,14 @@ bool APINotesReader::Implementation::readObjCContextBlock( if (cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -826,13 +884,25 @@ bool APINotesReader::Implementation::readObjCContextBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case objc_context_block::OBJC_CONTEXT_ID_DATA: { // Already saw Objective-C context ID table. @@ -873,7 +943,13 @@ bool APINotesReader::Implementation::readObjCContextBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -885,7 +961,14 @@ bool APINotesReader::Implementation::readObjCPropertyBlock( if (cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -896,13 +979,25 @@ bool APINotesReader::Implementation::readObjCPropertyBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case objc_property_block::OBJC_PROPERTY_DATA: { // Already saw Objective-C property table. @@ -927,7 +1022,13 @@ bool APINotesReader::Implementation::readObjCPropertyBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -939,7 +1040,13 @@ bool APINotesReader::Implementation::readObjCMethodBlock( if (cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -950,13 +1057,25 @@ bool APINotesReader::Implementation::readObjCMethodBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case objc_method_block::OBJC_METHOD_DATA: { // Already saw Objective-C method table. @@ -980,7 +1099,13 @@ bool APINotesReader::Implementation::readObjCMethodBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -992,7 +1117,13 @@ bool APINotesReader::Implementation::readObjCSelectorBlock( if (cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -1003,13 +1134,25 @@ bool APINotesReader::Implementation::readObjCSelectorBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case objc_selector_block::OBJC_SELECTOR_DATA: { // Already saw Objective-C selector table. @@ -1034,7 +1177,13 @@ bool APINotesReader::Implementation::readObjCSelectorBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -1046,7 +1195,13 @@ bool APINotesReader::Implementation::readGlobalVariableBlock( if (cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -1057,13 +1212,25 @@ bool APINotesReader::Implementation::readGlobalVariableBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case global_variable_block::GLOBAL_VARIABLE_DATA: { // Already saw global variable table. @@ -1088,7 +1255,13 @@ bool APINotesReader::Implementation::readGlobalVariableBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -1100,7 +1273,13 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( if (cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -1111,13 +1290,25 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case global_function_block::GLOBAL_FUNCTION_DATA: { // Already saw global function table. @@ -1142,7 +1333,13 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -1154,7 +1351,13 @@ bool APINotesReader::Implementation::readEnumConstantBlock( if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -1165,13 +1368,25 @@ bool APINotesReader::Implementation::readEnumConstantBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case enum_constant_block::ENUM_CONSTANT_DATA: { // Already saw enumerator table. @@ -1196,7 +1411,13 @@ bool APINotesReader::Implementation::readEnumConstantBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -1208,7 +1429,13 @@ bool APINotesReader::Implementation::readTagBlock( if (cursor.EnterSubBlock(TAG_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -1219,13 +1446,25 @@ bool APINotesReader::Implementation::readTagBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case tag_block::TAG_DATA: { // Already saw tag table. @@ -1249,7 +1488,13 @@ bool APINotesReader::Implementation::readTagBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -1261,7 +1506,13 @@ bool APINotesReader::Implementation::readTypedefBlock( if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) return true; - auto next = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + llvm::BitstreamEntry next = maybeNext.get(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; @@ -1272,13 +1523,25 @@ bool APINotesReader::Implementation::readTypedefBlock( if (cursor.SkipBlock()) return true; - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); continue; } scratch.clear(); StringRef blobData; - unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + llvm::Expected<unsigned> maybeKind = cursor.readRecord(next.ID, scratch, &blobData); + if (!maybeKind) { + // FIXME this drops the error on the floor. + consumeError(maybeKind.takeError()); + return false; + } + unsigned kind = maybeKind.get(); switch (kind) { case typedef_block::TYPEDEF_DATA: { // Already saw typedef table. @@ -1302,7 +1565,13 @@ bool APINotesReader::Implementation::readTypedefBlock( break; } - next = cursor.advance(); + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); } return false; @@ -1324,7 +1593,18 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, // Validate signature. for (auto byte : API_NOTES_SIGNATURE) { - if (cursor.AtEndOfStream() || cursor.Read(8) != byte) { + if (cursor.AtEndOfStream()) { + failed = true; + return; + } + if (Expected<llvm::SimpleBitstreamCursor::word_t> maybeRead = cursor.Read(8)) { + if (maybeRead.get() != byte) { + failed = true; + return; + } + } else { + // FIXME this drops the error on the floor. + consumeError(maybeRead.takeError()); failed = true; return; } @@ -1334,7 +1614,15 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, bool hasValidControlBlock = false; SmallVector<uint64_t, 64> scratch; while (!cursor.AtEndOfStream()) { - auto topLevelEntry = cursor.advance(); + llvm::Expected<llvm::BitstreamEntry> maybeTopLevelEntry = cursor.advance(); + if (!maybeTopLevelEntry) { + // FIXME this drops the error on the floor. + consumeError(maybeTopLevelEntry.takeError()); + failed = true; + return; + } + llvm::BitstreamEntry topLevelEntry = maybeTopLevelEntry.get(); + if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; From d779dda7d0f9a5f6dfebac3529170b50b558ef19 Mon Sep 17 00:00:00 2001 From: JF Bastien <jfbastien@apple.com> Date: Wed, 26 Jun 2019 22:09:34 -0700 Subject: [PATCH 431/582] Fix index while building following bitstream reader API update Upstream change in rL364464 broke downstream index while building. apple-llvm-split-commit: 994094aa63b43bc0bc2d900ce27c0f376ac10f5c apple-llvm-split-dir: clang/ --- clang/lib/Index/BitstreamVisitor.h | 50 +++++++++++++++++++++------ clang/lib/Index/IndexRecordReader.cpp | 41 +++++++++++++++++----- clang/lib/Index/IndexUnitReader.cpp | 22 ++++++++---- 3 files changed, 87 insertions(+), 26 deletions(-) diff --git a/clang/lib/Index/BitstreamVisitor.h b/clang/lib/Index/BitstreamVisitor.h index d324f1a4fdbac..b39c49e2aa0b2 100644 --- a/clang/lib/Index/BitstreamVisitor.h +++ b/clang/lib/Index/BitstreamVisitor.h @@ -26,7 +26,9 @@ struct SavedStreamPosition { : Cursor(Cursor), Offset(Cursor.GetCurrentBitNo()) { } ~SavedStreamPosition() { - Cursor.JumpToBit(Offset); + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + llvm::report_fatal_error("SavedStreamPosition failed jumping: " + + toString(std::move(Err))); } private: @@ -62,7 +64,12 @@ class BitstreamVisitor { ASTReader::RecordData Record; while (1) { - llvm::BitstreamEntry Entry = Stream.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + Expected<llvm::BitstreamEntry> MaybeEntry = Stream.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + if (!MaybeEntry) { + Error = toString(MaybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry Entry = MaybeEntry.get(); switch (Entry.Kind) { case llvm::BitstreamEntry::Error: @@ -83,7 +90,12 @@ class BitstreamVisitor { case llvm::BitstreamEntry::SubBlock: { if (Entry.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) { - BlockInfo = Stream.ReadBlockInfoBlock(); + Expected<Optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo = Stream.ReadBlockInfoBlock(); + if (!MaybeBlockInfo) { + Error = toString(MaybeBlockInfo.takeError()); + return false; + } + BlockInfo = MaybeBlockInfo.get(); if (!BlockInfo) { Error = "malformed BlockInfoBlock"; return false; @@ -99,7 +111,10 @@ class BitstreamVisitor { Error = "malformed block record"; return false; } - readBlockAbbrevs(Stream); + if (llvm::Error Err = readBlockAbbrevs(Stream)) { + Error = toString(std::move(Err)); + return false; + } BlockStack.push_back(Entry.ID); break; @@ -121,7 +136,12 @@ class BitstreamVisitor { case llvm::BitstreamEntry::Record: { Record.clear(); StringRef Blob; - unsigned RecID = Stream.readRecord(Entry.ID, Record, &Blob); + Expected<unsigned> MaybeRecID = Stream.readRecord(Entry.ID, Record, &Blob); + if (!MaybeRecID) { + Error = toString(MaybeRecID.takeError()); + return false; + } + unsigned RecID = MaybeRecID.get(); unsigned BlockID = BlockStack.empty() ? 0 : BlockStack.back(); StreamVisit Ret = static_cast<ImplClass*>(this)->visitRecord(BlockID, RecID, Record, Blob); switch (Ret) { @@ -129,7 +149,10 @@ class BitstreamVisitor { break; case StreamVisit::Skip: - Stream.skipRecord(Entry.ID); + if (Expected<unsigned> Skipped = Stream.skipRecord(Entry.ID)) { + Error = toString(Skipped.takeError()); + return false; + } break; case StreamVisit::Abort: @@ -141,17 +164,22 @@ class BitstreamVisitor { } } - static void readBlockAbbrevs(llvm::BitstreamCursor &Cursor) { + static llvm::Error readBlockAbbrevs(llvm::BitstreamCursor &Cursor) { while (true) { uint64_t Offset = Cursor.GetCurrentBitNo(); - unsigned Code = Cursor.ReadCode(); + Expected<unsigned> MaybeCode = Cursor.ReadCode(); + if (!MaybeCode) + return MaybeCode.takeError(); + unsigned Code = MaybeCode.get(); // We expect all abbrevs to be at the start of the block. if (Code != llvm::bitc::DEFINE_ABBREV) { - Cursor.JumpToBit(Offset); - return; + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + return Err; + return llvm::Error::success(); } - Cursor.ReadAbbrevRecord(); + if (llvm::Error Err = Cursor.ReadAbbrevRecord()) + return Err; } } }; diff --git a/clang/lib/Index/IndexRecordReader.cpp b/clang/lib/Index/IndexRecordReader.cpp index 1ba3165c7fdab..86d253fe46dcb 100644 --- a/clang/lib/Index/IndexRecordReader.cpp +++ b/clang/lib/Index/IndexRecordReader.cpp @@ -91,9 +91,22 @@ struct IndexRecordReader::Implementation { void readDecl(unsigned Index, IndexRecordDecl &RecD) { RecordData Record; StringRef Blob; - DeclCursor.JumpToBit(DeclOffsets[Index]); - unsigned Code = DeclCursor.ReadCode(); - unsigned RecID = DeclCursor.readRecord(Code, Record, &Blob); + if (llvm::Error Err = DeclCursor.JumpToBit(DeclOffsets[Index])) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + } + Expected<unsigned> MaybeCode = DeclCursor.ReadCode(); + if (!MaybeCode) { + // FIXME this drops the error on the floor. + consumeError(MaybeCode.takeError()); + } + unsigned Code = MaybeCode.get(); + Expected<unsigned> MaybeRecID = DeclCursor.readRecord(Code, Record, &Blob); + if (!MaybeRecID) { + // FIXME this drops the error on the floor. + consumeError(MaybeRecID.takeError()); + } + unsigned RecID = MaybeRecID.get(); assert(RecID == REC_DECLINFO); (void)RecID; @@ -273,7 +286,10 @@ class IndexBitstreamVisitor : public BitstreamVisitor<IndexBitstreamVisitor> { *Error = "malformed block record"; return StreamVisit::Abort; } - readBlockAbbrevs(Reader.DeclCursor); + if (llvm::Error Err = readBlockAbbrevs(Reader.DeclCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } return StreamVisit::Skip; case REC_DECLOCCURRENCES_BLOCK_ID: @@ -282,7 +298,10 @@ class IndexBitstreamVisitor : public BitstreamVisitor<IndexBitstreamVisitor> { *Error = "malformed block record"; return StreamVisit::Abort; } - readBlockAbbrevs(Reader.OccurCursor); + if (llvm::Error Err = readBlockAbbrevs(Reader.OccurCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } return StreamVisit::Skip; } @@ -358,10 +377,14 @@ IndexRecordReader::createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer, } // Sniff for the signature. - if (Stream.Read(8) != 'I' || - Stream.Read(8) != 'D' || - Stream.Read(8) != 'X' || - Stream.Read(8) != 'R') { + for (unsigned char C : {'I', 'D', 'X', 'R'}) { + if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) { + if (Res.get() == C) + continue; + } else { + Error = toString(Res.takeError()); + return nullptr; + } Error = "not a serialized index record file"; return nullptr; } diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp index 5559d91bacc96..4d8f7d1a3cc52 100644 --- a/clang/lib/Index/IndexUnitReader.cpp +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -123,7 +123,10 @@ class IndexUnitBitstreamVisitor : public BitstreamVisitor<IndexUnitBitstreamVisi *Error = "malformed unit dependencies block record"; return StreamVisit::Abort; } - readBlockAbbrevs(Reader.DependCursor); + if (llvm::Error Err = readBlockAbbrevs(Reader.DependCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } return StreamVisit::Skip; case UNIT_INCLUDES_BLOCK_ID: Reader.IncludeCursor = Stream; @@ -131,7 +134,10 @@ class IndexUnitBitstreamVisitor : public BitstreamVisitor<IndexUnitBitstreamVisi *Error = "malformed unit includes block record"; return StreamVisit::Abort; } - readBlockAbbrevs(Reader.IncludeCursor); + if (llvm::Error Err = readBlockAbbrevs(Reader.IncludeCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } return StreamVisit::Skip; } @@ -276,10 +282,14 @@ bool IndexUnitReaderImpl::init(std::unique_ptr<MemoryBuffer> Buf, } // Sniff for the signature. - if (Stream.Read(8) != 'I' || - Stream.Read(8) != 'D' || - Stream.Read(8) != 'X' || - Stream.Read(8) != 'U') { + for (unsigned char C : {'I', 'D', 'X', 'U'}) { + if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) { + if (Res.get() == C) + continue; + } else { + Error = toString(Res.takeError()); + return true; + } Error = "not a serialized index unit file"; return true; } From fb31b13f352a6c276e9a2602d5b226afd01f8375 Mon Sep 17 00:00:00 2001 From: Nathan Lanza <nathan@lanza.io> Date: Wed, 26 Jun 2019 17:19:06 -0700 Subject: [PATCH 432/582] [AST] Fix test for Swift clang 9890adfbee8f854732d0093bc8b2a32be1be8844 changed the AST for ObjCCompatibleAliasDecl to record the alias name as its column location rather than the @compatibility_alias. Update a newly added AST dumping test accordingly. apple-llvm-split-commit: fec2d7fb45e4ffa35b56f4199f24a720af04ec52 apple-llvm-split-dir: clang/ --- clang/test/AST/ast-dump-decl-json.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/test/AST/ast-dump-decl-json.m b/clang/test/AST/ast-dump-decl-json.m index b11c27ee14200..93ba7fcb50a22 100644 --- a/clang/test/AST/ast-dump-decl-json.m +++ b/clang/test/AST/ast-dump-decl-json.m @@ -933,11 +933,10 @@ void f() { // CHECK: "kind": "ObjCCompatibleAliasDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "col": 22, // CHECK-NEXT: "file": "{{.*}}", // CHECK-NEXT: "line": 60, -// CHECK-NEXT: "col": 1, -// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: "col": 22, +// CHECK-NEXT: "tokLen": 27 // CHECK-NEXT: }, // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { From 282612931a82b20f9505504ffe4b7304c8832366 Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Wed, 3 Jul 2019 17:13:13 -0700 Subject: [PATCH 433/582] RecordLayout.h: llvm/Bitcode -> llvm/Bitstream This header is not in upstream llvm and needs to be updated to use the new paths for bitstream. apple-llvm-split-commit: 2b6648be8ebe2964c93e454764dfdec49c3abbd2 apple-llvm-split-dir: llvm/ --- llvm/include/llvm/Bitcode/RecordLayout.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/Bitcode/RecordLayout.h b/llvm/include/llvm/Bitcode/RecordLayout.h index 5b199cb408165..1878b0a8fad9d 100644 --- a/llvm/include/llvm/Bitcode/RecordLayout.h +++ b/llvm/include/llvm/Bitcode/RecordLayout.h @@ -33,8 +33,8 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/Bitcode/BitCodes.h" -#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Bitstream/BitCodes.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MathExtras.h" From 9e2de3861a5186a9f4ffa4a907fbdb1b99a1ae0a Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Wed, 3 Jul 2019 19:09:21 -0700 Subject: [PATCH 434/582] [Bitcode] Update includes: llvm/Bitcode -> llvm/Bitstream apple-llvm-split-commit: 152b357e96ce96dfe46ac419c51e1ced09dd7d5f apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesReader.cpp | 2 +- clang/lib/Index/BitstreamVisitor.h | 2 +- clang/lib/Index/IndexDataStoreUtils.cpp | 2 +- clang/lib/Index/IndexDataStoreUtils.h | 2 +- clang/lib/Index/IndexRecordReader.cpp | 2 +- clang/lib/Index/IndexRecordWriter.cpp | 2 +- clang/lib/Index/IndexUnitReader.cpp | 2 +- clang/lib/Index/IndexUnitWriter.cpp | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 513d9c5ee4d00..328995d42c91c 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -15,7 +15,7 @@ //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesReader.h" #include "APINotesFormat.h" -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/DJB.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" diff --git a/clang/lib/Index/BitstreamVisitor.h b/clang/lib/Index/BitstreamVisitor.h index b39c49e2aa0b2..481327eafba10 100644 --- a/clang/lib/Index/BitstreamVisitor.h +++ b/clang/lib/Index/BitstreamVisitor.h @@ -10,7 +10,7 @@ #ifndef LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H #define LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "clang/Basic/LLVM.h" #include "clang/Serialization/ASTReader.h" #include <string> diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 39dea609c83eb..d88f8ffa17ce5 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -10,7 +10,7 @@ #include "clang/Index/IndexDataStoreSymbolUtils.h" #include "IndexDataStoreUtils.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" diff --git a/clang/lib/Index/IndexDataStoreUtils.h b/clang/lib/Index/IndexDataStoreUtils.h index ad310e191605e..3618ae8137822 100644 --- a/clang/lib/Index/IndexDataStoreUtils.h +++ b/clang/lib/Index/IndexDataStoreUtils.h @@ -10,7 +10,7 @@ #ifndef LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H #define LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H -#include "llvm/Bitcode/BitCodes.h" +#include "llvm/Bitstream/BitCodes.h" #include "clang/Basic/LLVM.h" namespace llvm { diff --git a/clang/lib/Index/IndexRecordReader.cpp b/clang/lib/Index/IndexRecordReader.cpp index 86d253fe46dcb..65592eda3c9e2 100644 --- a/clang/lib/Index/IndexRecordReader.cpp +++ b/clang/lib/Index/IndexRecordReader.cpp @@ -12,7 +12,7 @@ #include "BitstreamVisitor.h" #include "clang/Index/IndexDataStoreSymbolUtils.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" diff --git a/clang/lib/Index/IndexRecordWriter.cpp b/clang/lib/Index/IndexRecordWriter.cpp index e09cad632e012..95ed25df9b9dd 100644 --- a/clang/lib/Index/IndexRecordWriter.cpp +++ b/clang/lib/Index/IndexRecordWriter.cpp @@ -14,7 +14,7 @@ #include "llvm/ADT/APInt.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringSet.h" -#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp index 4d8f7d1a3cc52..2ef21ec0bc52b 100644 --- a/clang/lib/Index/IndexUnitReader.cpp +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -14,7 +14,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" diff --git a/clang/lib/Index/IndexUnitWriter.cpp b/clang/lib/Index/IndexUnitWriter.cpp index c608982c3eeb0..0cebb5187e7d8 100644 --- a/clang/lib/Index/IndexUnitWriter.cpp +++ b/clang/lib/Index/IndexUnitWriter.cpp @@ -14,7 +14,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringMap.h" -#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" From 9eba2ac9ed4eafbb0a1dc71ad3e244b6ccc2d56e Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Mon, 8 Jul 2019 10:49:52 -0700 Subject: [PATCH 435/582] [APINotes] Explicitly include Bitstream/BitCodes.h This fixes a modules issue: error: declaration of 'bitc' must be imported from module 'LLVM_Bitcode.LLVMBitCodes' before it is required CONTROL_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, rdar://52785864 apple-llvm-split-commit: d00582d5dc4bc5b2ebd823d9a0293d4a8107d157 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesFormat.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 3e1b2d56ec8de..4cd2d9b904679 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -20,6 +20,7 @@ #include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Bitstream/BitCodes.h" namespace clang { namespace api_notes { From db0c9a513a0eb6d49430945647f5a3580506df98 Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Mon, 8 Jul 2019 15:53:08 -0700 Subject: [PATCH 436/582] [Frontend] Explicitly include Bitstream/BitCodes.h and BitstreamWriter.h This fixes a modules issue: error: declaration of 'bitc' must be imported from module 'Clang_Serialization.ASTBitCodes' before it is required Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); apple-llvm-split-commit: 9196d782eaa0b1460d124e2121e68ea18f5661d7 apple-llvm-split-dir: clang/ --- clang/lib/Frontend/SerializedDiagnosticPrinter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp b/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp index 754351488e963..c1434a95cc709 100644 --- a/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -20,6 +20,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitCodes.h" +#include "llvm/Bitstream/BitstreamReader.h" #include "llvm/Support/raw_ostream.h" #include <utility> From 3dab785d525efd49ed491d54304eb049e4cfa24d Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Wed, 5 Jun 2019 13:49:11 -0700 Subject: [PATCH 437/582] [IndexStore] Changed enum values EventType::Modified was removed and EventType::WatcherGotInvalidated was added in DirectoryWatcher. apple-llvm-split-commit: ed81bf9c9a60a71452f6b0a689e212ae2dca8583 apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexDataStore.h | 2 +- clang/include/indexstore/IndexStoreCXX.h | 4 ++-- clang/include/indexstore/indexstore.h | 8 ++++---- clang/lib/IndexDataStore/IndexDataStore.cpp | 18 +++++++++++------- clang/tools/IndexStore/IndexStore.cpp | 4 ++-- clang/tools/c-index-test/core_main.cpp | 15 +++++++-------- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h index 145a83dbcb7c8..ba60e6df2624c 100644 --- a/clang/include/clang/Index/IndexDataStore.h +++ b/clang/include/clang/Index/IndexDataStore.h @@ -36,11 +36,11 @@ class IndexDataStore { static unsigned getFormatVersion(); enum class UnitEventKind { - Added, Removed, Modified, /// The directory got deleted. No more events will follow. DirectoryDeleted, + Failure }; struct UnitEvent { UnitEventKind Kind; diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h index 5b9fd896e95e9..46a67b3730539 100644 --- a/clang/include/indexstore/IndexStoreCXX.h +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -152,19 +152,19 @@ class IndexStore { UnitEvent(indexstore_unit_event_t obj) : obj(obj) {} enum class Kind { - Added, Removed, Modified, DirectoryDeleted, + Failure }; Kind getKind() const { indexstore_unit_event_kind_t c_k = indexstore_unit_event_get_kind(obj); Kind K; switch (c_k) { - case INDEXSTORE_UNIT_EVENT_ADDED: K = Kind::Added; break; case INDEXSTORE_UNIT_EVENT_REMOVED: K = Kind::Removed; break; case INDEXSTORE_UNIT_EVENT_MODIFIED: K = Kind::Modified; break; case INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED: K = Kind::DirectoryDeleted; break; + case INDEXSTORE_UNIT_EVENT_FAILURE: K = Kind::Failure; break; } return K; } diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index e5154694eb2dc..7da7cdc91a1f8 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -134,10 +134,10 @@ INDEXSTORE_PUBLIC bool indexstore_unit_event_notification_is_initial(indexstore_unit_event_notification_t); typedef enum { - INDEXSTORE_UNIT_EVENT_ADDED = 1, - INDEXSTORE_UNIT_EVENT_REMOVED = 2, - INDEXSTORE_UNIT_EVENT_MODIFIED = 3, - INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 4, + INDEXSTORE_UNIT_EVENT_REMOVED = 1, + INDEXSTORE_UNIT_EVENT_MODIFIED = 2, + INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 3, + INDEXSTORE_UNIT_EVENT_FAILURE = 4, } indexstore_unit_event_kind_t; INDEXSTORE_PUBLIC indexstore_unit_event_kind_t diff --git a/clang/lib/IndexDataStore/IndexDataStore.cpp b/clang/lib/IndexDataStore/IndexDataStore.cpp index 6f207b4d95ee6..50edae6856457 100644 --- a/clang/lib/IndexDataStore/IndexDataStore.cpp +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -115,16 +115,18 @@ bool IndexDataStoreImpl::startEventListening(bool waitInitialSync, std::string & IndexDataStore::UnitEventKind K; StringRef UnitName = sys::path::filename(evt.Filename); switch (evt.Kind) { - case DirectoryWatcher::EventKind::Added: - K = IndexDataStore::UnitEventKind::Added; break; - case DirectoryWatcher::EventKind::Removed: + case DirectoryWatcher::Event::EventKind::Removed: K = IndexDataStore::UnitEventKind::Removed; break; - case DirectoryWatcher::EventKind::Modified: + case DirectoryWatcher::Event::EventKind::Modified: K = IndexDataStore::UnitEventKind::Modified; break; - case DirectoryWatcher::EventKind::DirectoryDeleted: + case DirectoryWatcher::Event::EventKind::WatchedDirRemoved: K = IndexDataStore::UnitEventKind::DirectoryDeleted; UnitName = StringRef(); break; + case DirectoryWatcher::Event::EventKind::WatcherGotInvalidated: + K = IndexDataStore::UnitEventKind::Failure; + UnitName = StringRef(); + break; } UnitEvents.push_back(IndexDataStore::UnitEvent{K, UnitName, evt.ModTime}); } @@ -136,9 +138,11 @@ bool IndexDataStoreImpl::startEventListening(bool waitInitialSync, std::string & }; DirWatcher = DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, - waitInitialSync, Error); - if (!DirWatcher) + waitInitialSync); + if (!DirWatcher) { + Error = "failed to create directory watcher"; return true; + } return false; } diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index c761b5cbea05b..0f98b86911fee 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -164,14 +164,14 @@ indexstore_unit_event_get_kind(indexstore_unit_event_t c_evt) { auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt); indexstore_unit_event_kind_t k; switch (evt->Kind) { - case IndexDataStore::UnitEventKind::Added: - k = INDEXSTORE_UNIT_EVENT_ADDED; break; case IndexDataStore::UnitEventKind::Removed: k = INDEXSTORE_UNIT_EVENT_REMOVED; break; case IndexDataStore::UnitEventKind::Modified: k = INDEXSTORE_UNIT_EVENT_MODIFIED; break; case IndexDataStore::UnitEventKind::DirectoryDeleted: k = INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED; break; + case IndexDataStore::UnitEventKind::Failure: + k = INDEXSTORE_UNIT_EVENT_FAILURE; break; } return k; } diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index e886ce09fbe34..62ae6f17cda6e 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -765,24 +765,23 @@ static int watchDirectory(StringRef dirPath) { OS << "-- " << Events.size() << " :\n"; for (auto evt : Events) { switch (evt.Kind) { - case DirectoryWatcher::EventKind::Added: - OS << "added: "; break; - case DirectoryWatcher::EventKind::Modified: + case DirectoryWatcher::Event::EventKind::Modified: OS << "modified: "; break; - case DirectoryWatcher::EventKind::Removed: + case DirectoryWatcher::Event::EventKind::Removed: OS << "removed: "; break; - case DirectoryWatcher::EventKind::DirectoryDeleted: + case DirectoryWatcher::Event::EventKind::WatchedDirRemoved: OS << "dir deleted: "; break; + case DirectoryWatcher::Event::EventKind::WatcherGotInvalidated: + OS << "watcher got invalidated: "; break; } OS << evt.Filename << '\n'; } }; - std::string Error; auto watcher = DirectoryWatcher::create(dirPath, receiver, - /*waitInitialSync=*/true, Error); + /*waitInitialSync=*/true); if (!watcher) { - errs() << "failed creating directory watcher: " << Error << '\n'; + errs() << "failed creating directory watcher" << '\n'; return 1; } From dcdc1f485e065821367e107cf69993d093216097 Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Tue, 9 Jul 2019 16:53:11 -0700 Subject: [PATCH 438/582] [IndexStore] Remove modification time apple-llvm-split-commit: 31aad7a32693dcda06d5a5b0d039fa34b2ebbc63 apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexDataStore.h | 1 - clang/include/indexstore/IndexStoreCXX.h | 21 --------------------- clang/include/indexstore/indexstore.h | 3 --- clang/lib/IndexDataStore/IndexDataStore.cpp | 2 +- clang/tools/IndexStore/IndexStore.cpp | 6 ------ clang/tools/IndexStore/IndexStore.exports | 1 - 6 files changed, 1 insertion(+), 33 deletions(-) diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h index ba60e6df2624c..8e438fd7dc861 100644 --- a/clang/include/clang/Index/IndexDataStore.h +++ b/clang/include/clang/Index/IndexDataStore.h @@ -45,7 +45,6 @@ class IndexDataStore { struct UnitEvent { UnitEventKind Kind; StringRef UnitName; - llvm::sys::TimePoint<> ModTime; }; struct UnitEventNotification { bool IsInitial; diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h index 46a67b3730539..6765ffa9e827f 100644 --- a/clang/include/indexstore/IndexStoreCXX.h +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -19,7 +19,6 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" -#include <ctime> namespace indexstore { using llvm::ArrayRef; @@ -172,8 +171,6 @@ class IndexStore { StringRef getUnitName() const { return stringFromIndexStoreStringRef(indexstore_unit_event_get_unit_name(obj)); } - - timespec getModificationTime() const { return indexstore_unit_event_get_modification_time(obj); } }; class UnitEventNotification { @@ -258,24 +255,6 @@ class IndexStore { nameBuf.append(unitName.begin(), unitName.begin()+nameLen); } - llvm::Optional<timespec> - getUnitModificationTime(StringRef unitName, std::string &error) { - llvm::SmallString<64> buf = unitName; - int64_t seconds, nanoseconds; - indexstore_error_t c_err = nullptr; - bool err = indexstore_store_get_unit_modification_time(obj, buf.c_str(), - &seconds, &nanoseconds, &c_err); - if (err && c_err) { - error = indexstore_error_get_description(c_err); - indexstore_error_dispose(c_err); - return llvm::None; - } - timespec ts; - ts.tv_sec = seconds; - ts.tv_nsec = nanoseconds; - return ts; - } - void purgeStaleData() { indexstore_store_purge_stale_data(obj); } diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 7da7cdc91a1f8..1d671fefefc76 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -146,9 +146,6 @@ indexstore_unit_event_get_kind(indexstore_unit_event_t); INDEXSTORE_PUBLIC indexstore_string_ref_t indexstore_unit_event_get_unit_name(indexstore_unit_event_t); -INDEXSTORE_PUBLIC struct timespec -indexstore_unit_event_get_modification_time(indexstore_unit_event_t); - #if INDEXSTORE_HAS_BLOCKS typedef void (^indexstore_unit_event_handler_t)(indexstore_unit_event_notification_t); diff --git a/clang/lib/IndexDataStore/IndexDataStore.cpp b/clang/lib/IndexDataStore/IndexDataStore.cpp index 50edae6856457..863cd1d38aca7 100644 --- a/clang/lib/IndexDataStore/IndexDataStore.cpp +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -128,7 +128,7 @@ bool IndexDataStoreImpl::startEventListening(bool waitInitialSync, std::string & UnitName = StringRef(); break; } - UnitEvents.push_back(IndexDataStore::UnitEvent{K, UnitName, evt.ModTime}); + UnitEvents.push_back(IndexDataStore::UnitEvent{K, UnitName}); } if (auto handler = localUnitEventHandlerData->getHandler()) { diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index 0f98b86911fee..8fda780489887 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -182,12 +182,6 @@ indexstore_unit_event_get_unit_name(indexstore_unit_event_t c_evt) { return toIndexStoreString(evt->UnitName); } -timespec -indexstore_unit_event_get_modification_time(indexstore_unit_event_t c_evt) { - auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt); - return toTimeSpec(evt->ModTime); -} - #if INDEXSTORE_HAS_BLOCKS void indexstore_store_set_unit_event_handler(indexstore_t c_store, diff --git a/clang/tools/IndexStore/IndexStore.exports b/clang/tools/IndexStore/IndexStore.exports index 9196ad60c38a3..5f738156a81f5 100644 --- a/clang/tools/IndexStore/IndexStore.exports +++ b/clang/tools/IndexStore/IndexStore.exports @@ -48,7 +48,6 @@ indexstore_unit_dependency_get_modulename indexstore_unit_dependency_get_name indexstore_unit_dependency_is_system indexstore_unit_event_get_kind -indexstore_unit_event_get_modification_time indexstore_unit_event_get_unit_name indexstore_unit_event_notification_get_event indexstore_unit_event_notification_get_events_count From 1d3f2b7d72bf64441d87efe3500830650795cc66 Mon Sep 17 00:00:00 2001 From: Puyan Lotfi <plotfi@fb.com> Date: Thu, 11 Jul 2019 17:49:00 -0700 Subject: [PATCH 439/582] Adding sys::fs::convertFDToNativeFile(FD), to address llvm.org/D63452 IndexUnitReader.cpp is not in upstream llvm. It was using an older API. apple-llvm-split-commit: 22e736d26defec479b7fcd0fdbbe42ad0ac4ae10 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexUnitReader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp index 2ef21ec0bc52b..30e474d6d6165 100644 --- a/clang/lib/Index/IndexUnitReader.cpp +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -425,7 +425,8 @@ IndexUnitReader::createWithFilePath(StringRef FilePath, std::string &Error) { return nullptr; } - auto ErrOrBuf = MemoryBuffer::getOpenFile(FD, FilePath, /*FileSize=*/-1, + auto ErrOrBuf = MemoryBuffer::getOpenFile(sys::fs::convertFDToNativeFile(FD), + FilePath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); if (!ErrOrBuf) { raw_string_ostream(Error) << "Failed opening '" << FilePath << "': " From b7d57e14bb944f1f7d6456c07b7bd5fa15c63551 Mon Sep 17 00:00:00 2001 From: Puyan Lotfi <plotfi@fb.com> Date: Fri, 12 Jul 2019 10:08:11 -0700 Subject: [PATCH 440/582] DirectoryWatcher for Windows stubs (to fix build break). This is just a code skeleton for DirectoryWatcher-windows.cpp so the build on Windows stops breaking. apple-llvm-split-commit: c802543b7eeb61f97635b12cc5e3d438287258b3 apple-llvm-split-dir: clang/ --- clang/lib/DirectoryWatcher/CMakeLists.txt | 2 + .../windows/DirectoryWatcher-windows.cpp | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp diff --git a/clang/lib/DirectoryWatcher/CMakeLists.txt b/clang/lib/DirectoryWatcher/CMakeLists.txt index b0bbb93693339..65521c8160891 100644 --- a/clang/lib/DirectoryWatcher/CMakeLists.txt +++ b/clang/lib/DirectoryWatcher/CMakeLists.txt @@ -18,6 +18,8 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") find_package(Threads REQUIRED) set(DIRECTORY_WATCHER_LINK_LIBS ${CMAKE_THREAD_LIBS_INIT}) endif() +elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") + list(APPEND DIRECTORY_WATCHER_SOURCES windows/DirectoryWatcher-windows.cpp) endif() add_clang_library(clangDirectoryWatcher diff --git a/clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp b/clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp new file mode 100644 index 0000000000000..6a12715592b3f --- /dev/null +++ b/clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp @@ -0,0 +1,48 @@ +//===- DirectoryWatcher-windows.cpp - Windows-platform directory watching -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// TODO: This is not yet an implementation, but it will make it so Windows +// builds don't fail. + +#include "DirectoryScanner.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/Support/AlignOf.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Path.h" +#include <atomic> +#include <condition_variable> +#include <mutex> +#include <queue> +#include <string> +#include <thread> +#include <vector> + +namespace { + +using namespace llvm; +using namespace clang; + +class DirectoryWatcherWindows : public clang::DirectoryWatcher { +public: + ~DirectoryWatcherWindows() override { } + void InitialScan() { } + void EventReceivingLoop() { } + void StopWork() { } +}; +} // namespace + +std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create( + StringRef Path, + std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver, + bool WaitForInitialSync) { + return nullptr; +} From 299fb759852ce2f2787758011f9698e9d79ba333 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 12 Jul 2019 10:20:43 -0700 Subject: [PATCH 441/582] Add push config for swift/master-next LLDB pushes apple-llvm-split-commit: 22d02f3126479e3bfb01d5af90def72e87db051a apple-llvm-split-dir: - --- apple-llvm-config/push/swift-master-next.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 apple-llvm-config/push/swift-master-next.json diff --git a/apple-llvm-config/push/swift-master-next.json b/apple-llvm-config/push/swift-master-next.json new file mode 100644 index 0000000000000..cf46ac0c4c332 --- /dev/null +++ b/apple-llvm-config/push/swift-master-next.json @@ -0,0 +1,8 @@ +{ + "branch_to_dest_branch_mapping": { + "swift/master-next:lldb": "upstream-with-swift", + }, + "repo_mapping": { + "lldb": "https://github.com/apple/swift-lldb.git" + } +} From 01c77b1deb53ed7b49840daeb39b94c29a7f2ae9 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 12 Jul 2019 10:22:19 -0700 Subject: [PATCH 442/582] Add push config for swift/master LLDB pushes apple-llvm-split-commit: b7eb3515f7c63a695f5e0eb466ac1b0d91a55a7a apple-llvm-split-dir: - --- apple-llvm-config/push/swift-master.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 apple-llvm-config/push/swift-master.json diff --git a/apple-llvm-config/push/swift-master.json b/apple-llvm-config/push/swift-master.json new file mode 100644 index 0000000000000..79a8627c03cad --- /dev/null +++ b/apple-llvm-config/push/swift-master.json @@ -0,0 +1,8 @@ +{ + "branch_to_dest_branch_mapping": { + "swift/master:lldb": "stable", + }, + "repo_mapping": { + "lldb": "https://github.com/apple/swift-lldb.git" + } +} From e130c5bc6a16fd7e600dff1c2e4cfbe800dc9cba Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Mon, 15 Jul 2019 09:11:52 -0700 Subject: [PATCH 443/582] [Bitcode] Add proper inclues to unit test Another modules issue. apple-llvm-split-commit: 55700c6015975545cb4975f5056329d82a689fe9 apple-llvm-split-dir: llvm/ --- llvm/unittests/Bitcode/RecordLayout.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/unittests/Bitcode/RecordLayout.cpp b/llvm/unittests/Bitcode/RecordLayout.cpp index 9a7a099a6a19e..9ae8bbad4892d 100644 --- a/llvm/unittests/Bitcode/RecordLayout.cpp +++ b/llvm/unittests/Bitcode/RecordLayout.cpp @@ -8,6 +8,8 @@ //===----------------------------------------------------------------------===// #include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Bitstream/BitCodes.h" +#include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/Support/Compiler.h" using namespace llvm; From fc52e4031886f650fa49e6ed7015c5590320af8e Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 17 Jul 2019 12:41:53 -0700 Subject: [PATCH 444/582] Add a disclaimer to readme apple-llvm-split-commit: e2780815a0b31c54a49377fbb50359898e87b25e apple-llvm-split-dir: - --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index de0891a7044a9..5b6bc05709548 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ +# Disclaimer + +The [llvm-monorepo-root](https://github.com/apple/llvm-monorepo-root), +the [llvm-project-v1](https://github.com/apple/llvm-project-v1), and +the [llvm-project-v1-split](https://github.com/apple/llvm-project-v1-split) are +WIP repositories that are used for development and prototyping of an llvm-project monorepo +for Swift. Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition/25689) +for more details. + +Original readme follows: + # The LLVM Compiler Infrastructure This directory and its subdirectories contain source code for LLVM, From 1d69916da777f196a155c9d002a4e1945db63da5 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 17 Jul 2019 12:43:25 -0700 Subject: [PATCH 445/582] Fixup the disclaimer in the readme apple-llvm-split-commit: 20489d4918388778e444896a79de2118eca944b7 apple-llvm-split-dir: - --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5b6bc05709548..4598d31a64788 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Disclaimer The [llvm-monorepo-root](https://github.com/apple/llvm-monorepo-root), -the [llvm-project-v1](https://github.com/apple/llvm-project-v1), and -the [llvm-project-v1-split](https://github.com/apple/llvm-project-v1-split) are +[llvm-project-v1](https://github.com/apple/llvm-project-v1), and +[llvm-project-v1-split](https://github.com/apple/llvm-project-v1-split) repositories are WIP repositories that are used for development and prototyping of an llvm-project monorepo for Swift. Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition/25689) for more details. From 88f3e7fbb0592bcbe0c6e1413635d477060d79ef Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 17 Jul 2019 12:48:15 -0700 Subject: [PATCH 446/582] Update README apple-llvm-split-commit: e228fa8e31b9494c857cb52f5537a74cd2ce5711 apple-llvm-split-dir: - --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4598d31a64788..17019c30c9af6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ The [llvm-monorepo-root](https://github.com/apple/llvm-monorepo-root), [llvm-project-v1](https://github.com/apple/llvm-project-v1), and [llvm-project-v1-split](https://github.com/apple/llvm-project-v1-split) repositories are WIP repositories that are used for development and prototyping of an llvm-project monorepo -for Swift. Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition/25689) +that will be used for Apple's compiler releases and for the open source Swift project. +Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition/25689) for more details. Original readme follows: From 852d7801cce2d44c250ab03fa023ade8db2b5791 Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Wed, 17 Jul 2019 13:49:53 -0700 Subject: [PATCH 447/582] [TSan] Turn on ignore_interceptors_accesses This was already done on [swift-5.1-branch], but I forgot to also apply the change to [upstream-with-swift]: This flag is usually enabled on Darwin. Turn it on unconditionally for Swift, even on Linux. Reason: Silences the remaining false positives for TSan in the swift-nio test suite. rdar://52215193 apple-llvm-split-commit: 0f52cc47d62a27311bf4b92d9d25584d7936be11 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/tsan/rtl/tsan_flags.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_flags.inc b/compiler-rt/lib/tsan/rtl/tsan_flags.inc index f733fac45987a..fbc84cddf868e 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_flags.inc +++ b/compiler-rt/lib/tsan/rtl/tsan_flags.inc @@ -76,7 +76,7 @@ TSAN_FLAG(int, io_sync, 1, TSAN_FLAG(bool, die_after_fork, true, "Die after multi-threaded fork if the child creates new threads.") TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") -TSAN_FLAG(bool, ignore_interceptors_accesses, false, +TSAN_FLAG(bool, ignore_interceptors_accesses, true, "Ignore reads and writes from all interceptors.") TSAN_FLAG(bool, ignore_noninstrumented_modules, true, "Interceptors should only detect races when called from instrumented " From 2d0a94da23d0e540d540e3148c558dc2131d15dd Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 17 Jul 2019 13:58:34 -0700 Subject: [PATCH 448/582] Add an llvm.org license as a top-level license file apple-llvm-split-commit: a3976f5be5ed6146f0c498f78e846cb392078568 apple-llvm-split-dir: - --- LICENSE.txt | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000000..fa6ac54000703 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,279 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + From 67bb9b1bef011490b28f05fbef91f2235baf69cd Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Wed, 17 Jul 2019 16:26:02 -0700 Subject: [PATCH 449/582] [TSan] Follow-up for my previous commit I enabled ignore_interceptors_accesses, but forgot to disable it for tests to avoid false negatives. apple-llvm-split-commit: 77a1e0d62108f1363fbe868b09eb53d42b923aae apple-llvm-split-dir: compiler-rt/ --- compiler-rt/test/tsan/lit.cfg.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler-rt/test/tsan/lit.cfg.py b/compiler-rt/test/tsan/lit.cfg.py index 813544831cab9..161aa8ed2dfd5 100644 --- a/compiler-rt/test/tsan/lit.cfg.py +++ b/compiler-rt/test/tsan/lit.cfg.py @@ -28,6 +28,7 @@ def get_required_attr(config, attr_name): # suppresses some races the tests are supposed to find. Let's run without this # setting, but turn it back on for Darwin tests (see Darwin/lit.local.cfg.py). default_tsan_opts += ':ignore_noninstrumented_modules=0' + default_tsan_opts += ':ignore_interceptors_accesses=0' # Platform-specific default TSAN_OPTIONS for lit tests. if default_tsan_opts: From 38811eed731a3e9266677b668c3dfe995a839e4f Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Wed, 17 Jul 2019 16:32:01 -0700 Subject: [PATCH 450/582] [DirectoryWatcher][NFC] Remove dead-code These two files are not longer used. Probably a fallout of some merge. apple-llvm-split-commit: 522c4b20d4f4ff2a31fa7812d14a88ae40ffccd1 apple-llvm-split-dir: clang/ --- .../DirectoryWatcher-linux.inc.h | 196 ---------------- .../DirectoryWatcher-mac.inc.h | 214 ------------------ 2 files changed, 410 deletions(-) delete mode 100644 clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h delete mode 100644 clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h b/clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h deleted file mode 100644 index ddb492ab2ff36..0000000000000 --- a/clang/lib/DirectoryWatcher/DirectoryWatcher-linux.inc.h +++ /dev/null @@ -1,196 +0,0 @@ -//===- DirectoryWatcher-linux.inc.h - Linux-platform directory listening --===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/Errno.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Mutex.h" -#include <thread> -#include <unistd.h> -#include <sys/inotify.h> - -namespace { - -struct INotifyEvent { - DirectoryWatcher::EventKind K; - std::string Filename; - Optional<sys::fs::file_status> Status; -}; - -class EventQueue { - DirectoryWatcher::EventReceiver Receiver; - sys::Mutex Mtx; - bool gotInitialScan = false; - std::vector<INotifyEvent> PendingEvents; - - DirectoryWatcher::Event toDirEvent(const INotifyEvent &evt) { - llvm::sys::TimePoint<> modTime{}; - if (evt.Status.hasValue()) modTime = evt.Status->getLastModificationTime(); - return DirectoryWatcher::Event{evt.K, evt.Filename, modTime}; - } - -public: - explicit EventQueue(DirectoryWatcher::EventReceiver receiver) - : Receiver(receiver) {} - - void onDirectoryEvents(ArrayRef<INotifyEvent> evts) { - sys::ScopedLock L(Mtx); - - if (!gotInitialScan) { - PendingEvents.insert(PendingEvents.end(), evts.begin(), evts.end()); - return; - } - - SmallVector<DirectoryWatcher::Event, 8> dirEvents; - for (const auto &evt : evts) { - dirEvents.push_back(toDirEvent(evt)); - } - Receiver(dirEvents, /*isInitial=*/false); - } - - void onInitialScan(std::shared_ptr<DirectoryScan> dirScan) { - sys::ScopedLock L(Mtx); - - std::vector<DirectoryWatcher::Event> events = dirScan->getAsFileEvents(); - Receiver(events, /*isInitial=*/true); - - events.clear(); - for (const auto &evt : PendingEvents) { - if (evt.K == DirectoryWatcher::EventKind::Added && - dirScan->FileIDSet.count(evt.Status->getUniqueID())) { - // Already reported this event at the initial directory scan. - continue; - } - events.push_back(toDirEvent(evt)); - } - if (!events.empty()) { - Receiver(events, /*isInitial=*/false); - } - - gotInitialScan = true; - PendingEvents.clear(); - } -}; -} // namespace - -struct DirectoryWatcher::Implementation { - bool initialize(StringRef Path, EventReceiver Receiver, - bool waitInitialSync, std::string &Error); - ~Implementation() { - stopListening(); - }; - -private: - int inotifyFD = -1; - - void stopListening(); -}; - -static void runWatcher(std::string pathToWatch, int inotifyFD, - std::shared_ptr<EventQueue> evtQueue) { - #define EVT_BUF_LEN (30 * (sizeof(struct inotify_event) + NAME_MAX + 1)) - char buf[EVT_BUF_LEN] __attribute__ ((aligned(8))); - - while (1) { - ssize_t numRead = read(inotifyFD, buf, EVT_BUF_LEN); - if (numRead == -1) { - return; // watcher is stopped. - } - - SmallVector<INotifyEvent, 8> iEvents; - for (char *p = buf; p < buf + numRead;) { - struct inotify_event *ievt = (struct inotify_event *)p; - p += sizeof(struct inotify_event) + ievt->len; - - if (ievt->mask & IN_DELETE_SELF) { - INotifyEvent iEvt{DirectoryWatcher::EventKind::DirectoryDeleted, - pathToWatch, None}; - iEvents.push_back(iEvt); - break; - } - - DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Added; - if (ievt->mask & IN_MODIFY) { - K = DirectoryWatcher::EventKind::Modified; - } - if (ievt->mask & IN_MOVED_TO) { - K = DirectoryWatcher::EventKind::Added; - } - if (ievt->mask & IN_DELETE) { - K = DirectoryWatcher::EventKind::Removed; - } - - assert(ievt->len > 0 && "expected a filename from inotify"); - SmallString<256> fullPath{pathToWatch}; - sys::path::append(fullPath, ievt->name); - - Optional<sys::fs::file_status> statusOpt; - if (K != DirectoryWatcher::EventKind::Removed) { - statusOpt = getFileStatus(fullPath); - if (!statusOpt.hasValue()) - K = DirectoryWatcher::EventKind::Removed; - } - INotifyEvent iEvt{K, fullPath.str(), statusOpt}; - iEvents.push_back(iEvt); - } - - if (!iEvents.empty()) - evtQueue->onDirectoryEvents(iEvents); - } -} - -bool DirectoryWatcher::Implementation::initialize(StringRef Path, - EventReceiver Receiver, - bool waitInitialSync, - std::string &errorMsg) { - auto error = [&](StringRef msg) -> bool { - errorMsg = msg; - errorMsg += ": "; - errorMsg += llvm::sys::StrError(); - return true; - }; - - auto evtQueue = std::make_shared<EventQueue>(std::move(Receiver)); - - inotifyFD = inotify_init(); - if (inotifyFD == -1) - return error("inotify_init failed"); - - std::string pathToWatch = Path; - int wd = inotify_add_watch( - inotifyFD, pathToWatch.c_str(), - IN_MOVED_TO | IN_DELETE | IN_MODIFY | IN_DELETE_SELF | IN_ONLYDIR); - if (wd == -1) - return error("inotify_add_watch failed"); - - std::thread watchThread( - std::bind(runWatcher, pathToWatch, inotifyFD, evtQueue)); - watchThread.detach(); - - auto initialScan = std::make_shared<DirectoryScan>(); - auto runScan = [pathToWatch, initialScan, evtQueue]() { - initialScan->scanDirectory(pathToWatch); - evtQueue->onInitialScan(std::move(initialScan)); - }; - - if (waitInitialSync) { - runScan(); - } else { - std::thread scanThread(runScan); - scanThread.detach(); - } - - return false; -} - -void DirectoryWatcher::Implementation::stopListening() { - if (inotifyFD == -1) - return; - close(inotifyFD); - inotifyFD = -1; -} diff --git a/clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h b/clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h deleted file mode 100644 index 826e9bea4fdae..0000000000000 --- a/clang/lib/DirectoryWatcher/DirectoryWatcher-mac.inc.h +++ /dev/null @@ -1,214 +0,0 @@ -//===- DirectoryWatcher-mac.inc.h - Mac-platform directory listening ------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include <CoreServices/CoreServices.h> - -struct DirectoryWatcher::Implementation { - bool initialize(StringRef Path, EventReceiver Receiver, - bool waitInitialSync, std::string &Error); - ~Implementation() { - stopFSEventStream(); - }; -private: - FSEventStreamRef EventStream = nullptr; - - bool setupFSEventStream(StringRef path, EventReceiver receiver, - dispatch_queue_t queue, - std::shared_ptr<DirectoryScan> initialScanPtr); - void stopFSEventStream(); -}; - -namespace { -struct EventStreamContextData { - std::string WatchedPath; - DirectoryWatcher::EventReceiver Receiver; - std::shared_ptr<DirectoryScan> InitialScan; - - EventStreamContextData(std::string watchedPath, DirectoryWatcher::EventReceiver receiver, - std::shared_ptr<DirectoryScan> initialScanPtr) - : WatchedPath(std::move(watchedPath)), - Receiver(std::move(receiver)), - InitialScan(std::move(initialScanPtr)) { - } - - static void dispose(const void *ctx) { - delete static_cast<const EventStreamContextData*>(ctx); - } -}; -} - -static void eventStreamCallback( - ConstFSEventStreamRef stream, - void *clientCallBackInfo, - size_t numEvents, - void *eventPaths, - const FSEventStreamEventFlags eventFlags[], - const FSEventStreamEventId eventIds[]) { - auto *ctx = static_cast<EventStreamContextData*>(clientCallBackInfo); - - std::vector<DirectoryWatcher::Event> Events; - for (size_t i = 0; i < numEvents; ++i) { - StringRef path = ((const char **)eventPaths)[i]; - const FSEventStreamEventFlags flags = eventFlags[i]; - if (!(flags & kFSEventStreamEventFlagItemIsFile)) { - if ((flags & kFSEventStreamEventFlagItemRemoved) && path == ctx->WatchedPath) { - DirectoryWatcher::Event Evt{DirectoryWatcher::EventKind::DirectoryDeleted, path, llvm::sys::TimePoint<>{} }; - Events.push_back(Evt); - break; - } - continue; - } - DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Modified; - bool hasAddedFlag = flags & (kFSEventStreamEventFlagItemCreated | - kFSEventStreamEventFlagItemRenamed); - bool hasRemovedFlag = flags & kFSEventStreamEventFlagItemRemoved; - Optional<sys::fs::file_status> statusOpt; - // NOTE: With low latency sometimes for a file that is moved inside the - // directory, or for a file that is removed from the directory, the flags - // have both 'renamed' and 'removed'. We use getting the file status as a - // way to distinguish between the two. - if (hasAddedFlag) { - statusOpt = getFileStatus(path); - if (statusOpt.hasValue()) { - K = DirectoryWatcher::EventKind::Added; - } else { - K = DirectoryWatcher::EventKind::Removed; - } - } else if (hasRemovedFlag) { - K = DirectoryWatcher::EventKind::Removed; - } else { - statusOpt = getFileStatus(path); - if (!statusOpt.hasValue()) { - K = DirectoryWatcher::EventKind::Removed; - } - } - - if (ctx->InitialScan && K == DirectoryWatcher::EventKind::Added) { - // For the first time we get the events, check that we haven't already - // sent the 'added' event at the initial scan. - if (ctx->InitialScan->FileIDSet.count(statusOpt->getUniqueID())) { - // Already reported this event at the initial directory scan. - continue; - } - } - - llvm::sys::TimePoint<> modTime{}; - if (statusOpt.hasValue()) - modTime = statusOpt->getLastModificationTime(); - DirectoryWatcher::Event Evt{K, path, modTime}; - Events.push_back(Evt); - } - - // We won't need to check again later on. - ctx->InitialScan.reset(); - - if (!Events.empty()) { - ctx->Receiver(Events, /*isInitial=*/false); - } -} - -bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path, - EventReceiver receiver, - dispatch_queue_t queue, - std::shared_ptr<DirectoryScan> initialScanPtr) { - if (path.empty()) - return true; - - CFMutableArrayRef pathsToWatch = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); - CFStringRef cfPathStr = CFStringCreateWithBytes(nullptr, (const UInt8 *)path.data(), path.size(), kCFStringEncodingUTF8, false); - CFArrayAppendValue(pathsToWatch, cfPathStr); - CFRelease(cfPathStr); - CFAbsoluteTime latency = 0.0; // Latency in seconds. - - std::string realPath; - { - SmallString<128> Storage; - StringRef P = llvm::Twine(path).toNullTerminatedStringRef(Storage); - char Buffer[PATH_MAX]; - // Use ::realpath to get the real path name - if (::realpath(P.begin(), Buffer) != nullptr) - realPath = Buffer; - else - realPath = path; - } - - EventStreamContextData *ctxData = - new EventStreamContextData(std::move(realPath), std::move(receiver), - std::move(initialScanPtr)); - FSEventStreamContext context; - context.version = 0; - context.info = ctxData; - context.retain = nullptr; - context.release = EventStreamContextData::dispose; - context.copyDescription = nullptr; - - EventStream = FSEventStreamCreate(nullptr, - eventStreamCallback, - &context, - pathsToWatch, - kFSEventStreamEventIdSinceNow, - latency, - kFSEventStreamCreateFlagFileEvents | - kFSEventStreamCreateFlagNoDefer); - CFRelease(pathsToWatch); - if (!EventStream) { - return true; - } - FSEventStreamSetDispatchQueue(EventStream, queue); - FSEventStreamStart(EventStream); - return false; -} - -void DirectoryWatcher::Implementation::stopFSEventStream() { - if (!EventStream) - return; - FSEventStreamStop(EventStream); - FSEventStreamInvalidate(EventStream); - FSEventStreamRelease(EventStream); - EventStream = nullptr; -} - -bool DirectoryWatcher::Implementation::initialize(StringRef Path, - EventReceiver Receiver, bool waitInitialSync, std::string &Error) { - auto initialScan = std::make_shared<DirectoryScan>(); - - dispatch_queue_t queue = dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL); - dispatch_semaphore_t initScanSema = dispatch_semaphore_create(0); - dispatch_semaphore_t setupFSEventsSema = dispatch_semaphore_create(0); - - std::string copiedPath = Path; - dispatch_retain(initScanSema); - dispatch_retain(setupFSEventsSema); - dispatch_async(queue, ^{ - // Wait for the event stream to be setup before doing the initial scan, - // to make sure we won't miss any events. - dispatch_semaphore_wait(setupFSEventsSema, DISPATCH_TIME_FOREVER); - initialScan->scanDirectory(copiedPath); - Receiver(initialScan->getAsFileEvents(), /*isInitial=*/true); - dispatch_semaphore_signal(initScanSema); - dispatch_release(setupFSEventsSema); - dispatch_release(initScanSema); - }); - bool fsErr = setupFSEventStream(Path, Receiver, queue, initialScan); - dispatch_semaphore_signal(setupFSEventsSema); - - if (waitInitialSync) { - dispatch_semaphore_wait(initScanSema, DISPATCH_TIME_FOREVER); - } - dispatch_release(setupFSEventsSema); - dispatch_release(initScanSema); - dispatch_release(queue); - - if (fsErr) { - raw_string_ostream(Error) << "failed to setup FSEvents stream for path: " << Path; - return true; - } - - return false; -} From 3f41b4b59bc12302b53b27866cbb8f32556d3562 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 17 Jul 2019 18:40:34 -0700 Subject: [PATCH 451/582] fix the JSON in apple-llvm-config/push/swift-master-next.json apple-llvm-split-commit: 55eab79a3b30d0a3aec80361ad54d4d1dc14d5a1 apple-llvm-split-dir: - --- apple-llvm-config/push/swift-master-next.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple-llvm-config/push/swift-master-next.json b/apple-llvm-config/push/swift-master-next.json index cf46ac0c4c332..020500e2d35dd 100644 --- a/apple-llvm-config/push/swift-master-next.json +++ b/apple-llvm-config/push/swift-master-next.json @@ -1,6 +1,6 @@ { "branch_to_dest_branch_mapping": { - "swift/master-next:lldb": "upstream-with-swift", + "swift/master-next:lldb": "upstream-with-swift" }, "repo_mapping": { "lldb": "https://github.com/apple/swift-lldb.git" From 59460544d67dc52ee88a51dcf12fd111dd9a69f6 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 17 Jul 2019 18:40:52 -0700 Subject: [PATCH 452/582] fix the JSON in apple-llvm-config/push/swift-master.json apple-llvm-split-commit: 010d4e6b3223b95d0ff66e6ddbde0033958f41d0 apple-llvm-split-dir: - --- apple-llvm-config/push/swift-master.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple-llvm-config/push/swift-master.json b/apple-llvm-config/push/swift-master.json index 79a8627c03cad..b025c19d07299 100644 --- a/apple-llvm-config/push/swift-master.json +++ b/apple-llvm-config/push/swift-master.json @@ -1,6 +1,6 @@ { "branch_to_dest_branch_mapping": { - "swift/master:lldb": "stable", + "swift/master:lldb": "stable" }, "repo_mapping": { "lldb": "https://github.com/apple/swift-lldb.git" From b3555cef3533bd9ebe9543336097bdf2b584514b Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Thu, 18 Jul 2019 14:06:11 -0700 Subject: [PATCH 453/582] Fixup for "[TSan] Turn on ignore_interceptors_accesses" Fixup for previous commit: 0f52cc47d62a27311bf4b92d9d25584d7936be11 apple-llvm-split-commit: 365c2d6dc9b80f0817f6cc4d46ffc9240fa5bf8c apple-llvm-split-dir: compiler-rt/ --- compiler-rt/test/sanitizer_common/lit.common.cfg.py | 1 + compiler-rt/test/tsan/Unit/lit.site.cfg.py.in | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg.py b/compiler-rt/test/sanitizer_common/lit.common.cfg.py index a9965f070f860..f22144d4a4a4c 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg.py +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg.py @@ -35,6 +35,7 @@ # On Darwin, we default to `abort_on_error=1`, which would make tests run # much slower. Let's override this and run lit tests with 'abort_on_error=0'. default_tool_options += ['abort_on_error=0'] + default_tool_options += ['ignore_interceptors_accesses=0'] elif config.android: # The same as on Darwin, we default to "abort_on_error=1" which slows down # testing. Also, all existing tests are using "not" instead of "not --crash" diff --git a/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in b/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in index b9307b52f9e58..714501a0d76de 100644 --- a/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in +++ b/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in @@ -21,3 +21,4 @@ if config.host_os == 'Darwin': config.environment['TSAN_OPTIONS'] += ':ignore_noninstrumented_modules=0' else: config.environment['TSAN_OPTIONS'] = 'ignore_noninstrumented_modules=0' + config.environment['TSAN_OPTIONS'] += ':ignore_interceptors_accesses=0' From e72e6304c11670d11915c61af446ad965cbf308b Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Thu, 18 Jul 2019 18:11:41 -0700 Subject: [PATCH 454/582] More fixup for "[TSan] Turn on ignore_interceptors_accesses" More fixup for previous commit: 0f52cc47d62a27311bf4b92d9d25584d7936be11 apple-llvm-split-commit: 11240e9836b9baf408d9290e246ecf0530503a14 apple-llvm-split-dir: compiler-rt/ --- .../test/sanitizer_common/TestCases/Darwin/abort_on_error.cc | 2 +- compiler-rt/test/sanitizer_common/lit.common.cfg.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler-rt/test/sanitizer_common/TestCases/Darwin/abort_on_error.cc b/compiler-rt/test/sanitizer_common/TestCases/Darwin/abort_on_error.cc index e25a676df3a78..e0907061bae26 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/Darwin/abort_on_error.cc +++ b/compiler-rt/test/sanitizer_common/TestCases/Darwin/abort_on_error.cc @@ -4,7 +4,7 @@ // RUN: %clangxx -DUSING_%tool_name %s -o %t // Intentionally don't inherit the default options. -// RUN: env %tool_options='' not --crash %run %t 2>&1 +// RUN: env %tool_options='' TSAN_OPTIONS=ignore_interceptors_accesses=0 not --crash %run %t 2>&1 // When we use lit's default options, we shouldn't crash. // RUN: not %run %t 2>&1 diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg.py b/compiler-rt/test/sanitizer_common/lit.common.cfg.py index f22144d4a4a4c..e10b1bbdacb5d 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg.py +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg.py @@ -35,7 +35,8 @@ # On Darwin, we default to `abort_on_error=1`, which would make tests run # much slower. Let's override this and run lit tests with 'abort_on_error=0'. default_tool_options += ['abort_on_error=0'] - default_tool_options += ['ignore_interceptors_accesses=0'] + if config.tool_name == "tsan": + default_tool_options += ['ignore_interceptors_accesses=0'] elif config.android: # The same as on Darwin, we default to "abort_on_error=1" which slows down # testing. Also, all existing tests are using "not" instead of "not --crash" From 6a7bf397f257397870d9b9c92e59eaa228ad74c0 Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Fri, 19 Jul 2019 16:25:52 -0700 Subject: [PATCH 455/582] [TSan] Suppress false positive in test macOS 10.15 has introduced some change in the Objective-C runtime that throws TSan off. We can use `ignore_interceptors_accesses=1` for this test to suppress the report. rdar://49869765 apple-llvm-split-commit: de583a7dc0d6237fdf47977b571dcc52715195ff apple-llvm-split-dir: compiler-rt/ --- compiler-rt/test/tsan/Darwin/norace-objcxx-run-time.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-rt/test/tsan/Darwin/norace-objcxx-run-time.mm b/compiler-rt/test/tsan/Darwin/norace-objcxx-run-time.mm index ee76ec155cb42..b6a38d702240b 100644 --- a/compiler-rt/test/tsan/Darwin/norace-objcxx-run-time.mm +++ b/compiler-rt/test/tsan/Darwin/norace-objcxx-run-time.mm @@ -1,5 +1,5 @@ // RUN: %clang_tsan %s -lc++ -fobjc-arc -lobjc -o %t -framework Foundation %darwin_min_target_with_full_runtime_arc_support -// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s // Check that we do not report races between: // - Object retain and initialize From 3ae687d2245485767a7312f7f0e6bb3654ef458d Mon Sep 17 00:00:00 2001 From: Amara Emerson <aemerson@apple.com> Date: Wed, 24 Jul 2019 10:30:52 -0700 Subject: [PATCH 456/582] Fix member initialization order in FrontendOptions.h apple-llvm-split-commit: b50161dddacfc02c170cbe3a38ce58962ba67a53 apple-llvm-split-dir: clang/ --- clang/include/clang/Frontend/FrontendOptions.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index ad8a8bc0c4bd7..391dcb3738682 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -468,8 +468,8 @@ class FrontendOptions { UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), ModulesEmbedAllFiles(false), - IncludeTimestamps(true), TimeTraceGranularity(500), - IndexIgnoreSystemSymbols(false), IndexRecordCodegenName(false) {} + IncludeTimestamps(true), IndexIgnoreSystemSymbols(false), + IndexRecordCodegenName(false), TimeTraceGranularity(500) {} /// getInputKindForExtension - Return the appropriate input kind for a file /// extension. For example, "c" would return InputKind::C. From c96f764005d35da1b1392dda5e3b9701e83a8631 Mon Sep 17 00:00:00 2001 From: Harlan Haskins <harlan@apple.com> Date: Thu, 1 Aug 2019 17:00:41 -0700 Subject: [PATCH 457/582] [APINotes] Adopt FileManager's error-returning APIs In https://reviews.llvm.org/D65534, FileManager's getFile and getDirectory APIs were updated to return errors, not just null-or-valid pointers. Update our internal APINotes usage of FileManager for this. Fixes rdar://53837986 apple-llvm-split-commit: 1c2ca31a8c6153000daaa40120ec021fa62f1077 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesManager.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 0cc3d4c8410f2..5205400bbdc42 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -146,7 +146,8 @@ const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directo // Look for the source API notes file. llvm::sys::path::append(path, llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION); - return fileMgr.getFile(path, /*Open*/true); + auto file = fileMgr.getFile(path, /*Open*/true); + return file ? *file : nullptr; } const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( @@ -171,7 +172,7 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( + SOURCE_APINOTES_EXTENSION)); // Try to open the APINotes file. - const FileEntry *APINotesFile = FileMgr.getFile(Path); + auto APINotesFile = FileMgr.getFile(Path); if (!APINotesFile) return nullptr; @@ -183,12 +184,12 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( llvm::sys::path::append(Path, "PrivateHeaders"); // Try to access the header directory. - const DirectoryEntry *HeaderDir = FileMgr.getDirectory(Path); + auto HeaderDir = FileMgr.getDirectory(Path); if (!HeaderDir) return nullptr; // Try to load the API notes. - if (loadAPINotes(HeaderDir, APINotesFile)) + if (loadAPINotes(*HeaderDir, *APINotesFile)) return nullptr; // Success: return the header directory. @@ -274,7 +275,7 @@ bool APINotesManager::loadCurrentModuleAPINotes( llvm::sys::path::append(path, "Headers"); if (auto apinotesDir = fileMgr.getDirectory(path)) - tryAPINotes(apinotesDir, /*wantPublic=*/true); + tryAPINotes(*apinotesDir, /*wantPublic=*/true); path.resize(pathLen); } @@ -282,7 +283,7 @@ bool APINotesManager::loadCurrentModuleAPINotes( if (module->ModuleMapIsPrivate || hasPrivateSubmodules(module)) { llvm::sys::path::append(path, "PrivateHeaders"); if (auto privateAPINotesDir = fileMgr.getDirectory(path)) { - tryAPINotes(privateAPINotesDir, + tryAPINotes(*privateAPINotesDir, /*wantPublic=*/module->ModuleMapIsPrivate); } } @@ -306,7 +307,7 @@ bool APINotesManager::loadCurrentModuleAPINotes( // notes search paths. for (const auto &searchPath : searchPaths) { if (auto searchDir = fileMgr.getDirectory(searchPath)) { - if (auto file = findAPINotesFile(searchDir, moduleName)) { + if (auto file = findAPINotesFile(*searchDir, moduleName)) { CurrentModuleReaders[0] = loadAPINotes(file).release(); return !getCurrentModuleReaders().empty(); } @@ -414,8 +415,8 @@ llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocat // If there is an API notes file here, try to load it. ++NumDirectoriesSearched; - if (const FileEntry *APINotesFile = FileMgr.getFile(APINotesPath)) { - if (!loadAPINotes(Dir, APINotesFile)) { + if (auto APINotesFile = FileMgr.getFile(APINotesPath)) { + if (!loadAPINotes(Dir, *APINotesFile)) { ++NumHeaderAPINotes; if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>()) Results.push_back(Reader); @@ -437,7 +438,8 @@ llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocat if (ParentPath.empty()) { Dir = nullptr; } else { - Dir = FileMgr.getDirectory(ParentPath); + auto DirEntry = FileMgr.getDirectory(ParentPath) + Dir = DirEntry ? *DirEntry : nullptr; } } while (Dir); From d822919c8db5382ee130667284d2396008773b24 Mon Sep 17 00:00:00 2001 From: Francis Visoiu Mistrih <francisvm@yahoo.com> Date: Fri, 2 Aug 2019 08:55:13 -0700 Subject: [PATCH 458/582] [clang] Fix build after FileManager changes apple-llvm-split-commit: 138f523697b13a0377d4ba5c274e586d7b582a80 apple-llvm-split-dir: clang/ --- clang/lib/APINotes/APINotesManager.cpp | 4 ++-- clang/lib/Index/IndexingAction.cpp | 11 ++++++----- clang/lib/Tooling/Refactor/ASTStateSerialization.cpp | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/clang/lib/APINotes/APINotesManager.cpp b/clang/lib/APINotes/APINotesManager.cpp index 5205400bbdc42..11b087931fef1 100644 --- a/clang/lib/APINotes/APINotesManager.cpp +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -197,7 +197,7 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( ++NumPublicFrameworkAPINotes; else ++NumPrivateFrameworkAPINotes; - return HeaderDir; + return *HeaderDir; } static void checkPrivateAPINotesName(DiagnosticsEngine &diags, @@ -438,7 +438,7 @@ llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocat if (ParentPath.empty()) { Dir = nullptr; } else { - auto DirEntry = FileMgr.getDirectory(ParentPath) + auto DirEntry = FileMgr.getDirectory(ParentPath); Dir = DirEntry ? *DirEntry : nullptr; } } while (Dir); diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index c3a89dbc718b8..df3f2f23c6cb7 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -501,13 +501,14 @@ class SourceFilesIndexDependencyCollector : public DependencyCollector, bool IsModuleFile, bool IsMissing) override { bool sawIt = DependencyCollector::sawDependency( Filename, FromModule, IsSystem, IsModuleFile, IsMissing); - if (auto *FE = SourceMgr->getFileManager().getFile(Filename)) { + if (llvm::ErrorOr<const clang::FileEntry *> FE = + SourceMgr->getFileManager().getFile(Filename)) { if (sawIt) - Entries.insert(FE); + Entries.insert(*FE); // Record system-ness for all files that we pass through. - if (IsSystemByUID.size() < FE->getUID() + 1) - IsSystemByUID.resize(FE->getUID() + 1); - IsSystemByUID[FE->getUID()] = IsSystem || isInSysroot(Filename); + if (IsSystemByUID.size() < (*FE)->getUID() + 1) + IsSystemByUID.resize((*FE)->getUID() + 1); + IsSystemByUID[(*FE)->getUID()] = IsSystem || isInSysroot(Filename); } return sawIt; } diff --git a/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp index 2d10fbc31ad33..50b446165cf86 100644 --- a/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp +++ b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp @@ -63,8 +63,8 @@ void PersistentToASTSpecificStateConverter::runCoalescedConversions() { FileID PersistentToASTSpecificStateConverter::convert(const PersistentFileID &Ref) { FileManager &FM = Context.getSourceManager().getFileManager(); - const FileEntry *Entry = FM.getFile(Ref.Filename); + llvm::ErrorOr<const FileEntry *> Entry = FM.getFile(Ref.Filename); if (!Entry) return FileID(); - return Context.getSourceManager().translateFile(Entry); + return Context.getSourceManager().translateFile(*Entry); } From 1856e3af8fadce5241531918055b8b80319a676a Mon Sep 17 00:00:00 2001 From: Tim Northover <t.p.northover@gmail.com> Date: Mon, 5 Aug 2019 12:24:22 +0100 Subject: [PATCH 459/582] IR: fix tests after upstream change to print arguments apple-llvm-split-commit: 5d4e1cd3e76bba5fe08a6e76a6a6d0c3a2741454 apple-llvm-split-dir: llvm/ --- llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll | 8 ++++---- llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll | 4 ++-- .../test/Transforms/Coroutines/coro-retcon-once-value2.ll | 2 +- .../Transforms/Coroutines/coro-retcon-resume-values.ll | 2 +- .../Transforms/Coroutines/coro-retcon-resume-values2.ll | 6 +++--- llvm/test/Transforms/Coroutines/coro-retcon-value.ll | 2 +- llvm/test/Transforms/Coroutines/coro-retcon.ll | 2 +- llvm/test/Transforms/Coroutines/coro-swifterror.ll | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll index b0c77f874aae9..17aec4eed1b23 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll @@ -39,7 +39,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i8*, i32 } @f.resume.0(i8* noalias nonnull, i1) +; CHECK-LABEL: define internal { i8*, i8*, i32 } @f.resume.0(i8* noalias nonnull %0, i1 %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, i8* %0, i64 8 ; CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i8** @@ -83,7 +83,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @g.resume.0(i8* noalias nonnull, i1) +; CHECK-LABEL: define internal { i8*, i32 } @g.resume.0(i8* noalias nonnull %0, i1 %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : @@ -132,7 +132,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @h.resume.0(i8* noalias nonnull, i1) +; CHECK-LABEL: define internal { i8*, i32 } @h.resume.0(i8* noalias nonnull %0, i1 %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : @@ -180,7 +180,7 @@ loop2: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @i.resume.0(i8* noalias nonnull) +; CHECK-LABEL: define internal { i8*, i32 } @i.resume.0(i8* noalias nonnull %0) ; CHECK-NEXT: : ; CHECK-NEXT: [[NSLOT:%.*]] = bitcast i8* %0 to i32* ; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[NSLOT]], align 4 diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll index cb7cfda6df0b2..ac49b22ee6bb9 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll @@ -45,7 +45,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[T1]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i1 zeroext) +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : @@ -57,7 +57,7 @@ cleanup: ; CHECK-NEXT: ret void ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull, i1 zeroext) +; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll index d1f312e33ac7c..ad49f24dc547a 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll @@ -37,7 +37,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32* } [[T0]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull, i1 zeroext) +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll index 3f613ed5f45ad..ac99dd15b9882 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll @@ -30,7 +30,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32, i1)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i32, i1 zeroext) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i32 %1, i1 zeroext %2) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* ; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll index bdc2f0f9be9d9..43f98e958aab3 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll @@ -29,7 +29,7 @@ entry: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i32) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i32 %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] @@ -45,7 +45,7 @@ entry: ; CHECK-NEXT: ret i8* [[CONT]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.1(i8* noalias nonnull, i32) +; CHECK-LABEL: define internal i8* @f.resume.1(i8* noalias nonnull %0, i32 %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] @@ -64,7 +64,7 @@ entry: ; CHECK-NEXT: ret i8* [[CONT]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.2(i8* noalias nonnull, i32) +; CHECK-LABEL: define internal i8* @f.resume.2(i8* noalias nonnull %0, i32 %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll index bf4dc8af72e15..cfda73bbe754a 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll @@ -30,7 +30,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @f.resume.0(i8* noalias nonnull, i8 zeroext) +; CHECK-LABEL: define internal { i8*, i32 } @f.resume.0(i8* noalias nonnull %0, i8 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 ; CHECK-NEXT: br i1 [[T0]], diff --git a/llvm/test/Transforms/Coroutines/coro-retcon.ll b/llvm/test/Transforms/Coroutines/coro-retcon.ll index 9d86fba729a5a..5cd4cb61d94cc 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon.ll @@ -30,7 +30,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i1 zeroext) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : diff --git a/llvm/test/Transforms/Coroutines/coro-swifterror.ll b/llvm/test/Transforms/Coroutines/coro-swifterror.ll index c330137964692..cf50bcd054723 100644 --- a/llvm/test/Transforms/Coroutines/coro-swifterror.ll +++ b/llvm/test/Transforms/Coroutines/coro-swifterror.ll @@ -40,7 +40,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1, i8**)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull, i1 zeroext, i8** swifterror) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1, i8** swifterror %2) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : @@ -102,7 +102,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @g.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @g.resume.0(i8* noalias nonnull, i1 zeroext) +; CHECK-LABEL: define internal i8* @g.resume.0(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[ERRORSLOT:%.*]] = alloca swifterror i8*, align 4 ; CHECK-NEXT: br i1 %1, From e626fb698244d73e6be0f2bed88c6eefccc6ff94 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai <smeenai@fb.com> Date: Tue, 6 Aug 2019 12:10:55 -0700 Subject: [PATCH 460/582] [IndexStore] Update for DirectoryWatcher API change DirectoryWatcher returns an llvm::Expected after r367979. Update IndexStore accordingly. apple-llvm-split-commit: 9fc3072aa5a52c912c860276879ee99f65a5d232 apple-llvm-split-dir: clang/ --- clang/lib/IndexDataStore/IndexDataStore.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clang/lib/IndexDataStore/IndexDataStore.cpp b/clang/lib/IndexDataStore/IndexDataStore.cpp index 863cd1d38aca7..96054ab48fb59 100644 --- a/clang/lib/IndexDataStore/IndexDataStore.cpp +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -137,13 +137,14 @@ bool IndexDataStoreImpl::startEventListening(bool waitInitialSync, std::string & } }; - DirWatcher = DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, - waitInitialSync); - if (!DirWatcher) { - Error = "failed to create directory watcher"; - return true; + llvm::Expected<std::unique_ptr<DirectoryWatcher>> ExpectedDirWatcher = + DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, waitInitialSync); + if (!ExpectedDirWatcher) { + Error = llvm::toString(ExpectedDirWatcher.takeError()); + return true; } + DirWatcher = std::move(ExpectedDirWatcher.get()); return false; } From 332bbcecb2869a6e6bde19dfeb773c3c62175784 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai <smeenai@fb.com> Date: Tue, 6 Aug 2019 14:59:17 -0700 Subject: [PATCH 461/582] [DirectoryWatcher] Fix Windows stub after LLVM change r367979 changed DirectoryWatcher::Create to return an llvm::Expected. Adjust the Windows stub accordingly. apple-llvm-split-commit: 4165783d0996da5e91751973a54d1a5cfb653b03 apple-llvm-split-dir: clang/ --- .../DirectoryWatcher/windows/DirectoryWatcher-windows.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp b/clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp index 6a12715592b3f..25cbcf536388a 100644 --- a/clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp +++ b/clang/lib/DirectoryWatcher/windows/DirectoryWatcher-windows.cpp @@ -40,9 +40,11 @@ class DirectoryWatcherWindows : public clang::DirectoryWatcher { }; } // namespace -std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create( +llvm::Expected<std::unique_ptr<DirectoryWatcher>> +clang::DirectoryWatcher::create( StringRef Path, std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver, bool WaitForInitialSync) { - return nullptr; + return llvm::Expected<std::unique_ptr<DirectoryWatcher>>( + llvm::errorCodeToError(std::make_error_code(std::errc::not_supported))); } From d76a30ef8569635ce7c03368ffb67275e620f161 Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Fri, 9 Aug 2019 08:58:49 -0700 Subject: [PATCH 462/582] [compiler-rt] Rename test file to ensure it gets executed In upstream there was a rename of files from `.cc` -> `.cpp` in compiler-rt. They also changed lit to no longer look for files with a `.cc` extension. Rename test to make sure it gets executed. rdar://54105330 apple-llvm-split-commit: e942dfe6c440d4253b8f76faa5d415312677031c apple-llvm-split-dir: compiler-rt/ --- .../{ignore-noninstrumented.cc => ignore-noninstrumented.ccp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler-rt/test/tsan/{ignore-noninstrumented.cc => ignore-noninstrumented.ccp} (100%) diff --git a/compiler-rt/test/tsan/ignore-noninstrumented.cc b/compiler-rt/test/tsan/ignore-noninstrumented.ccp similarity index 100% rename from compiler-rt/test/tsan/ignore-noninstrumented.cc rename to compiler-rt/test/tsan/ignore-noninstrumented.ccp From c611c46c9ed448ab395c8cd2f5d936e7adf9b9f8 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 9 Aug 2019 12:07:44 -0700 Subject: [PATCH 463/582] README: update link to forum post and mention v2 apple-llvm-split-commit: abf4c9b72140aeaea31904b28c4e581b1cd795d7 apple-llvm-split-dir: - --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 17019c30c9af6..c01872a6f62c7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # Disclaimer The [llvm-monorepo-root](https://github.com/apple/llvm-monorepo-root), +[llvm-project-v2](https://github.com/apple/llvm-project-v2), +[llvm-project-v2-split](https://github.com/apple/llvm-project-v2-split), [llvm-project-v1](https://github.com/apple/llvm-project-v1), and [llvm-project-v1-split](https://github.com/apple/llvm-project-v1-split) repositories are WIP repositories that are used for development and prototyping of an llvm-project monorepo that will be used for Apple's compiler releases and for the open source Swift project. -Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition/25689) +Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition-update/27079) for more details. Original readme follows: From 48837a3a44502d6969212d035744ae63bde941b3 Mon Sep 17 00:00:00 2001 From: Alex Langford <apl@fb.com> Date: Wed, 14 Aug 2019 17:53:30 -0700 Subject: [PATCH 464/582] Remove merge marker apple-llvm-split-commit: 1849adbc68c24a810fffeabd526e0fb32e58087f apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexingAction.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 9479b368d5087..0ebb46f0249ce 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -205,16 +205,10 @@ index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, IndexingOptions Opts, std::unique_ptr<FrontendAction> WrappedAction) { if (WrappedAction) -<<<<<<< HEAD - return llvm::make_unique<WrappingIndexAction>( - std::move(WrappedAction), std::move(DataConsumer), Opts); - return llvm::make_unique<IndexAction>(std::move(DataConsumer), Opts); -======= return std::make_unique<WrappingIndexAction>(std::move(WrappedAction), std::move(DataConsumer), Opts); return std::make_unique<IndexAction>(std::move(DataConsumer), Opts); ->>>>>>> mirror/master } static bool topLevelDeclVisitor(void *context, const Decl *D) { From 4db58db8c2c18c64a2fa11006671819d6979c5bc Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 15 Aug 2019 13:38:50 -0700 Subject: [PATCH 465/582] [llvm] Migrate llvm::make_unique to std::make_unique Update a swift-only use of the function. apple-llvm-split-commit: 04a8630862f594bf36ae0c4b608381c6de351359 apple-llvm-split-dir: llvm/ --- llvm/lib/Analysis/CallGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/CallGraph.cpp b/llvm/lib/Analysis/CallGraph.cpp index a7bb16299a5eb..6d311b1e13132 100644 --- a/llvm/lib/Analysis/CallGraph.cpp +++ b/llvm/lib/Analysis/CallGraph.cpp @@ -179,7 +179,7 @@ CallGraphNode *CallGraph::getOrInsertNodeForCalleesMD(MDNode *Callees) { auto &CGN = DummyNodeMap[Callees]; if (CGN) return CGN.get(); - CGN = llvm::make_unique<CallGraphNode>(nullptr); + CGN = std::make_unique<CallGraphNode>(nullptr); for (const MDOperand &Op : Callees->operands()) if (auto *MDConstant = mdconst::extract_or_null<Constant>(Op)) { auto *F = cast<Function>(MDConstant); From 5204c6d9b016cd4798c6566f1ccb8018ae0b4b09 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 15 Aug 2019 13:47:21 -0700 Subject: [PATCH 466/582] Migrate llvm::make_unique to std::make_unique Update a swift-only use of the function. apple-llvm-split-commit: 29cebb79001003a8c8b6e77304b44f9a248d808f apple-llvm-split-dir: clang/ --- clang/include/clang/Tooling/Refactor/IndexerQuery.h | 2 +- clang/lib/Edit/FillInMissingProtocolStubs.cpp | 2 +- clang/lib/Frontend/DependencyFile.cpp | 4 ++-- clang/lib/Index/IndexingAction.cpp | 10 +++++----- clang/lib/Tooling/Refactor/Extract.cpp | 6 +++--- .../Refactor/ExtractRepeatedExpressionIntoVariable.cpp | 4 ++-- clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp | 2 +- .../FillInMissingMethodStubsFromAbstractClasses.cpp | 2 +- .../Tooling/Refactor/FillInMissingProtocolStubs.cpp | 2 +- .../lib/Tooling/Refactor/ImplementDeclaredMethods.cpp | 2 +- clang/lib/Tooling/Refactor/IndexerQueries.cpp | 6 +++--- .../lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp | 2 +- clang/lib/Tooling/Refactor/RefactoringContinuations.h | 2 +- clang/lib/Tooling/Refactor/RefactoringOperation.cpp | 4 ++-- clang/tools/c-index-test/JSONAggregation.cpp | 4 ++-- clang/tools/libclang/CIndexDiagnostic.cpp | 2 +- clang/tools/libclang/CRefactor.cpp | 8 ++++---- clang/unittests/Tooling/RefactoringTest.cpp | 2 +- 18 files changed, 33 insertions(+), 33 deletions(-) diff --git a/clang/include/clang/Tooling/Refactor/IndexerQuery.h b/clang/include/clang/Tooling/Refactor/IndexerQuery.h index b90f6d7130255..8e379d6f38d03 100644 --- a/clang/include/clang/Tooling/Refactor/IndexerQuery.h +++ b/clang/include/clang/Tooling/Refactor/IndexerQuery.h @@ -301,7 +301,7 @@ filter(ArrayRef<const T *> Declarations, BoolDeclPredicate (*Fn)(const DeclEntity &), typename std::enable_if<std::is_base_of<Decl, T>::value>::type * = nullptr) { - return llvm::make_unique<ManyToManyDeclarationsQuery<T>>( + return std::make_unique<ManyToManyDeclarationsQuery<T>>( Declarations, detail::DeclPredicateNode::create(Fn(DeclEntity()))); } diff --git a/clang/lib/Edit/FillInMissingProtocolStubs.cpp b/clang/lib/Edit/FillInMissingProtocolStubs.cpp index 02f11bc008acd..4265b0d1d9f6d 100644 --- a/clang/lib/Edit/FillInMissingProtocolStubs.cpp +++ b/clang/lib/Edit/FillInMissingProtocolStubs.cpp @@ -363,7 +363,7 @@ operator=(FillInMissingProtocolStubs &&Other) { bool FillInMissingProtocolStubs::initiate(ASTContext &Context, const ObjCContainerDecl *Container) { - Impl = llvm::make_unique<FillInMissingProtocolStubsImpl>(); + Impl = std::make_unique<FillInMissingProtocolStubsImpl>(); if (!::initiate(*Impl, Context, Container)) return true; return false; diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index 91bbe37922990..6833eb4a354f8 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -234,10 +234,10 @@ void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) { // FIXME: Restore the call to DependencyCollector::attachToPreprocessor(PP); // once the SkipUnusedModuleMaps is upstreamed. - PP.addPPCallbacks(llvm::make_unique<DepCollectorPPCallbacks>( + PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>( *this, PP.getSourceManager(), PP.getDiagnostics())); PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( - llvm::make_unique<DFGMMCallback>(*this, SkipUnusedModuleMaps)); + std::make_unique<DFGMMCallback>(*this, SkipUnusedModuleMaps)); } bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule, diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 0ebb46f0249ce..d35f24a00a4f3 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -435,7 +435,7 @@ class SourceFilesIndexDependencyCollector : public DependencyCollector, virtual void attachToPreprocessor(Preprocessor &PP) override { DependencyCollector::attachToPreprocessor(PP); - PP.addPPCallbacks(llvm::make_unique<IncludePPCallbacks>( + PP.addPPCallbacks(std::make_unique<IncludePPCallbacks>( IndexCtx, RecordOpts, Includes, PP.getSourceManager())); } @@ -541,7 +541,7 @@ class IndexRecordActionBase { DepCollector.setSysrootPath(IndexCtx->getSysrootPath()); DepCollector.attachToPreprocessor(PP); - return llvm::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), + return std::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), IndexCtx); } @@ -587,7 +587,7 @@ class WrappingIndexRecordAction : public WrapperFrontendAction, std::vector<std::unique_ptr<ASTConsumer>> Consumers; Consumers.push_back(std::move(OtherConsumer)); Consumers.push_back(createIndexASTConsumer(CI)); - return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); + return std::make_unique<MultiplexConsumer>(std::move(Consumers)); } void EndSourceFileAction() override { @@ -887,9 +887,9 @@ createIndexDataRecordingAction(IndexingOptions IndexOpts, RecordingOptions RecordOpts, std::unique_ptr<FrontendAction> WrappedAction) { if (WrappedAction) - return llvm::make_unique<WrappingIndexRecordAction>( + return std::make_unique<WrappingIndexRecordAction>( std::move(WrappedAction), std::move(IndexOpts), std::move(RecordOpts)); - return llvm::make_unique<IndexRecordAction>(std::move(IndexOpts), + return std::make_unique<IndexRecordAction>(std::move(IndexOpts), std::move(RecordOpts)); } diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp index 60fdee574a6fa..717ed17fd1185 100644 --- a/clang/lib/Tooling/Refactor/Extract.cpp +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -282,7 +282,7 @@ initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, Stmts.containsSelectionRangeEnd); } - auto Operation = llvm::make_unique<ExtractOperation>( + auto Operation = std::make_unique<ExtractOperation>( Selected, ParentStmt, ParentDecl, std::move(Candidates), ExtractedStmtRange, FirstCandidateInfo, Kind); auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo; @@ -1485,7 +1485,7 @@ ExtractOperation::performExpressionExtraction(ASTContext &Context, QualType VarType = findExpressionLexicalType(FunctionLikeParentDecl, E, E->getType(), PP, Context); StringRef VarName = "extractedExpr"; - auto CreatedSymbol = llvm::make_unique<RefactoringResultAssociatedSymbol>( + auto CreatedSymbol = std::make_unique<RefactoringResultAssociatedSymbol>( OldSymbolName(VarName)); SourceRange ExtractedTokenRange = CandidateExtractionInfo[0].Range; @@ -1805,7 +1805,7 @@ llvm::Expected<RefactoringResult> ExtractOperation::perform( ExtractedNamePieces.push_back(Var.getName()); } std::unique_ptr<RefactoringResultAssociatedSymbol> CreatedSymbol = - llvm::make_unique<RefactoringResultAssociatedSymbol>( + std::make_unique<RefactoringResultAssociatedSymbol>( OldSymbolName(ExtractedNamePieces)); SourceLocation FunctionExtractionLoc = computeFunctionExtractionLocation( diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp index 42b67c31dc212..ef4b8744cf941 100644 --- a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -227,7 +227,7 @@ clang::tooling::initiateExtractRepeatedExpressionIntoVariableOperation( if (!CreateOperation) return Result; auto Operation = - llvm::make_unique<ExtractRepeatedExpressionIntoVariableOperation>( + std::make_unique<ExtractRepeatedExpressionIntoVariableOperation>( E, DupFinder.DuplicateExpressions, ParentDecl); Result.RefactoringOp = std::move(Operation); return Result; @@ -257,7 +257,7 @@ ExtractRepeatedExpressionIntoVariableOperation::perform( StringRef Name = nameForExtractedVariable(E); Result.AssociatedSymbols.push_back( - llvm::make_unique<RefactoringResultAssociatedSymbol>( + std::make_unique<RefactoringResultAssociatedSymbol>( OldSymbolName(Name))); RefactoringResultAssociatedSymbol *CreatedSymbol = Result.AssociatedSymbols.back().get(); diff --git a/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp b/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp index 2cab0ee182c2c..8338091b4c009 100644 --- a/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp +++ b/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp @@ -91,7 +91,7 @@ clang::tooling::initiateFillInEnumSwitchCasesOperation( Result.Initiated = true; if (!CreateOperation) return Result; - auto Operation = llvm::make_unique<FillInEnumSwitchCasesOperation>( + auto Operation = std::make_unique<FillInEnumSwitchCasesOperation>( ED, Switch, dyn_cast<DeclContext>(ParentDecl)); Result.RefactoringOp = std::move(Operation); return Result; diff --git a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp index 2ddd5f95922d7..f23b342e872ff 100644 --- a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp +++ b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp @@ -74,7 +74,7 @@ clang::tooling::initiateFillInMissingMethodStubsFromAbstractClassesOperation( if (!CreateOperation) return Result; auto Operation = - llvm::make_unique<FillInMissingMethodStubsFromAbstractClassesOperation>( + std::make_unique<FillInMissingMethodStubsFromAbstractClassesOperation>( Class); Result.RefactoringOp = std::move(Operation); return Result; diff --git a/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp b/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp index de8cfbe4ce7c7..0295bb0a4e916 100644 --- a/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp +++ b/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp @@ -74,7 +74,7 @@ clang::tooling::initiateFillInMissingProtocolStubsOperation( Result.Initiated = true; if (!CreateOperation) return Result; - auto Operation = llvm::make_unique<FillInMissingProtocolStubsOperation>( + auto Operation = std::make_unique<FillInMissingProtocolStubsOperation>( Container, std::move(Impl)); Result.RefactoringOp = std::move(Operation); return Result; diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp index a3bbbfa42a036..cf022b914248d 100644 --- a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -50,7 +50,7 @@ class ImplementDeclaredMethodsOperation : public RefactoringOperation { Result.Initiated = true; if (!CreateOperation) return Result; - auto Operation = llvm::make_unique<Derived>(Container, Methods); + auto Operation = std::make_unique<Derived>(Container, Methods); Result.RefactoringOp = std::move(Operation); return Result; } diff --git a/clang/lib/Tooling/Refactor/IndexerQueries.cpp b/clang/lib/Tooling/Refactor/IndexerQueries.cpp index 7a3a0903e1ae8..e1d415527dde8 100644 --- a/clang/lib/Tooling/Refactor/IndexerQueries.cpp +++ b/clang/lib/Tooling/Refactor/IndexerQueries.cpp @@ -29,20 +29,20 @@ const char *DeclPredicateNotPredicate::NameUIDString = "not.decl.predicate"; std::unique_ptr<DeclPredicateNode> DeclPredicateNode::create(const DeclPredicate &Predicate) { - return llvm::make_unique<DeclPredicateNodePredicate>(Predicate); + return std::make_unique<DeclPredicateNodePredicate>(Predicate); } std::unique_ptr<DeclPredicateNode> DeclPredicateNode::create(const BoolDeclPredicate &Predicate) { if (Predicate.IsInverted) - return llvm::make_unique<DeclPredicateNotPredicate>( + return std::make_unique<DeclPredicateNotPredicate>( create(Predicate.Predicate)); return create(Predicate.Predicate); } std::unique_ptr<ASTUnitForImplementationOfDeclarationQuery> clang::tooling::indexer::fileThatShouldContainImplementationOf(const Decl *D) { - return llvm::make_unique<ASTUnitForImplementationOfDeclarationQuery>(D); + return std::make_unique<ASTUnitForImplementationOfDeclarationQuery>(D); } bool ASTUnitForImplementationOfDeclarationQuery::verify(ASTContext &Context) { diff --git a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp index 3bbffefea7ad7..e0876b7d4649d 100644 --- a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp +++ b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp @@ -58,7 +58,7 @@ clang::tooling::initiateLocalizeObjCStringLiteralOperation( Result.Initiated = true; if (!CreateOperation) return Result; - auto Operation = llvm::make_unique<LocalizeObjCStringLiteralOperation>(E); + auto Operation = std::make_unique<LocalizeObjCStringLiteralOperation>(E); Result.RefactoringOp = std::move(Operation); return Result; } diff --git a/clang/lib/Tooling/Refactor/RefactoringContinuations.h b/clang/lib/Tooling/Refactor/RefactoringContinuations.h index ec2ee1bcf50ca..2b09956285092 100644 --- a/clang/lib/Tooling/Refactor/RefactoringContinuations.h +++ b/clang/lib/Tooling/Refactor/RefactoringContinuations.h @@ -387,7 +387,7 @@ continueInExternalASTUnit( typename detail::SpecificRefactoringContinuation< ASTQueryType, QueryOrState...>::ConsumerFn Consumer, QueryOrState... Inputs) { - return llvm::make_unique< + return std::make_unique< detail::SpecificRefactoringContinuation<ASTQueryType, QueryOrState...>>( Consumer, std::move(ASTQuery), std::move(Inputs)...); } diff --git a/clang/lib/Tooling/Refactor/RefactoringOperation.cpp b/clang/lib/Tooling/Refactor/RefactoringOperation.cpp index a9f431f98d0a6..9b94cffe00d52 100644 --- a/clang/lib/Tooling/Refactor/RefactoringOperation.cpp +++ b/clang/lib/Tooling/Refactor/RefactoringOperation.cpp @@ -43,7 +43,7 @@ RefactoringOperationResult clang::tooling::initiateRefactoringOperationAt( RefactoringOperationResult Result; Result.Initiated = true; if (CreateOperation) - Result.SymbolOp = llvm::make_unique<SymbolOperation>(FoundDecl, Context); + Result.SymbolOp = std::make_unique<SymbolOperation>(FoundDecl, Context); return Result; } SourceManager &SM = Context.getSourceManager(); @@ -88,6 +88,6 @@ RefactoringOperationResult clang::tooling::initiateRefactoringOperationOnDecl( return None; RefactoringOperationResult Result; Result.Initiated = true; - Result.SymbolOp = llvm::make_unique<SymbolOperation>(FoundDecl, Context); + Result.SymbolOp = std::make_unique<SymbolOperation>(FoundDecl, Context); return Result; } diff --git a/clang/tools/c-index-test/JSONAggregation.cpp b/clang/tools/c-index-test/JSONAggregation.cpp index c428c1152464c..9c00d61d8257c 100644 --- a/clang/tools/c-index-test/JSONAggregation.cpp +++ b/clang/tools/c-index-test/JSONAggregation.cpp @@ -138,7 +138,7 @@ bool Aggregator::process() { void Aggregator::processUnit(StringRef name, IndexUnitReader &UnitReader) { auto workDir = UnitReader.getWorkingDirectory(); - auto unit = llvm::make_unique<UnitInfo>(); + auto unit = std::make_unique<UnitInfo>(); unit->Name = name; unit->Triple = getTripleString(UnitReader.getTarget()); unit->OutFile = getFilePathIndex(UnitReader.getOutputFile(), workDir); @@ -218,7 +218,7 @@ std::unique_ptr<RecordInfo> Aggregator::processRecord(StringRef recordFile) { errs() << "failed reading record file: " << recordFile << '\n'; ::exit(1); } - auto record = llvm::make_unique<RecordInfo>(); + auto record = std::make_unique<RecordInfo>(); recordReader.foreachOccurrence([&](IndexRecordOccurrence idxOccur) -> bool { SymbolIndex symIdx = getSymbolIndex(idxOccur.getSymbol()); SymbolInfo &symInfo = Symbols[symIdx]; diff --git a/clang/tools/libclang/CIndexDiagnostic.cpp b/clang/tools/libclang/CIndexDiagnostic.cpp index 8c35b45249592..3b16b1fa8d6b3 100644 --- a/clang/tools/libclang/CIndexDiagnostic.cpp +++ b/clang/tools/libclang/CIndexDiagnostic.cpp @@ -156,7 +156,7 @@ class CXStoredDiagnosticSet : public CXDiagnosticSetImpl { : CXDiagnosticSetImpl(/*isManaged=*/true), Diags(Diags.begin(), Diags.end()) { for (const auto &Diag : this->Diags) - appendDiagnostic(llvm::make_unique<CXStoredDiagnostic>(Diag, LangOpts)); + appendDiagnostic(std::make_unique<CXStoredDiagnostic>(Diag, LangOpts)); } }; } diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index bf60509801990..64ce4fe57217b 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -620,7 +620,7 @@ CXErrorCode performIndexedFileRename( rename::IndexedFileRenamerLock Lock(*ClangToolConstructionMutex); auto Runner = - llvm::make_unique<ToolRunner>(Symbols, IndexedSymbols, Lock, Options); + std::make_unique<ToolRunner>(Symbols, IndexedSymbols, Lock, Options); // Run a clang tool on the input file. std::string Name = Filename.str(); @@ -725,7 +725,7 @@ CXErrorCode performIndexedSymbolSearch( }; rename::IndexedFileRenamerLock Lock(*ClangToolConstructionMutex); - auto Runner = llvm::make_unique<ToolRunner>(IndexedSymbols, Lock, Options); + auto Runner = std::make_unique<ToolRunner>(IndexedSymbols, Lock, Options); // Run a clang tool on the input file. std::string Name = Filename.str(); @@ -1422,7 +1422,7 @@ enum CXErrorCode clang_Refactoring_initiateAction( ActionType, TU); else *OutAction = new RefactoringAction( - llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(), + std::make_unique<RenamingAction>(CXXUnit->getLangOpts(), std::move(*Operation.SymbolOp)), TU); return CXError_Success; @@ -1461,7 +1461,7 @@ enum CXErrorCode clang_Refactoring_initiateActionOnDecl( ActionType, TU); else *OutAction = new RefactoringAction( - llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(), + std::make_unique<RenamingAction>(CXXUnit->getLangOpts(), std::move(*Operation.SymbolOp)), TU); return CXError_Success; diff --git a/clang/unittests/Tooling/RefactoringTest.cpp b/clang/unittests/Tooling/RefactoringTest.cpp index c4eae4960a9dc..2ddab750dca34 100644 --- a/clang/unittests/Tooling/RefactoringTest.cpp +++ b/clang/unittests/Tooling/RefactoringTest.cpp @@ -1417,7 +1417,7 @@ class RefactoringOperationTest { llvm::StringRef) override { Test->PP = &Compiler.getPreprocessor(); Test->Context = &Compiler.getASTContext(); - return llvm::make_unique<TestConsumer>(Test); + return std::make_unique<TestConsumer>(Test); } private: From 44d12bcc44719545b413d27b108ea0a2b2a13a04 Mon Sep 17 00:00:00 2001 From: Alex Langford <apl@fb.com> Date: Thu, 15 Aug 2019 14:02:41 -0700 Subject: [PATCH 467/582] Replace last uses of llvm::index_sequence{,_for} apple-llvm-split-commit: 5d4e4c0382d41f8b894d48b083361c113c16d291 apple-llvm-split-dir: clang/ --- .../Tooling/Refactor/RefactoringContinuations.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/lib/Tooling/Refactor/RefactoringContinuations.h b/clang/lib/Tooling/Refactor/RefactoringContinuations.h index 2b09956285092..9e0ad9d334631 100644 --- a/clang/lib/Tooling/Refactor/RefactoringContinuations.h +++ b/clang/lib/Tooling/Refactor/RefactoringContinuations.h @@ -223,7 +223,7 @@ struct ContinuationFunction { ASTContext &Context, const ASTQueryType &Query, const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...> &Arguments, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { auto ASTQueryResult = Converter.convert(Query.getResult()); return Fn(Context, ASTQueryResult, std::get<Is>(Arguments)...); } @@ -242,7 +242,7 @@ struct ContinuationFunction<void, ASTQueryType, QueryOrState...> { ASTContext &Context, const ASTQueryType &, const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...> &Arguments, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { return Fn(Context, std::get<Is>(Arguments)...); } }; @@ -277,7 +277,7 @@ class SpecificRefactoringContinuation final : public RefactoringContinuation { /// TU independent state. template <size_t... Is> std::tuple<typename StateTraits<QueryOrState>::PersistentType...> - convertToPersistentImpl(llvm::index_sequence<Is...>) { + convertToPersistentImpl(std::index_sequence<Is...>) { assert(Inputs && "TU-dependent state is already converted"); return std::make_tuple( detail::convertToPersistentRepresentation(std::get<Is>(*Inputs))...); @@ -301,7 +301,7 @@ class SpecificRefactoringContinuation final : public RefactoringContinuation { template <size_t... Is> std::vector<indexer::IndexerQuery *> - gatherQueries(llvm::index_sequence<Is...>) { + gatherQueries(std::index_sequence<Is...>) { assert(Inputs && "TU-dependent state is already converted"); std::vector<indexer::IndexerQuery *> Queries; std::make_tuple(gatherQueries(Queries, std::get<Is>(*Inputs))...); @@ -312,7 +312,7 @@ class SpecificRefactoringContinuation final : public RefactoringContinuation { /// whose values are converted from the TU-independent to TU-specific values. template <size_t... Is> llvm::Expected<RefactoringResult> dispatch(ASTContext &Context, - llvm::index_sequence<Is...> Seq) { + std::index_sequence<Is...> Seq) { assert(State && "TU-independent state is not yet produced"); detail::PersistentToASTSpecificStateConverter Converter(Context); (void)std::make_tuple(Converter.addConvertible(std::get<Is>(*State))...); @@ -341,7 +341,7 @@ class SpecificRefactoringContinuation final : public RefactoringContinuation { } std::vector<indexer::IndexerQuery *> getAdditionalIndexerQueries() override { - return gatherQueries(llvm::index_sequence_for<QueryOrState...>()); + return gatherQueries(std::index_sequence_for<QueryOrState...>()); } /// Query results are fetched. State is converted to a persistent @@ -349,7 +349,7 @@ class SpecificRefactoringContinuation final : public RefactoringContinuation { void persistTUSpecificState() override { ASTQuery->invalidateTUSpecificState(); State = - convertToPersistentImpl(llvm::index_sequence_for<QueryOrState...>()); + convertToPersistentImpl(std::index_sequence_for<QueryOrState...>()); Inputs = None; } @@ -357,7 +357,7 @@ class SpecificRefactoringContinuation final : public RefactoringContinuation { /// and the continuation is dispatched. llvm::Expected<RefactoringResult> runInExternalASTUnit(ASTContext &Context) override { - return dispatch(Context, llvm::index_sequence_for<QueryOrState...>()); + return dispatch(Context, std::index_sequence_for<QueryOrState...>()); } }; From 91f23fd7a203926777d5c43ec1c36a9574e3d839 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 15 Aug 2019 16:10:18 -0700 Subject: [PATCH 468/582] Migrate swift-only instances of llvm::index_sequence. Upstream removed llvm::index_sequence(for) and replaced it with its C++14 variant. This fixes the remaining Swift-only uses. apple-llvm-split-commit: c0f7273478dda930bc634a2f0989808ecc39811a apple-llvm-split-dir: clang/ --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h | 4 ++-- clang/include/clang/Sema/Sema.h | 4 ++-- .../Tooling/Refactoring/RefactoringActionRulesInternal.h | 8 ++++---- clang/lib/CodeGen/EHScopeStack.h | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index 46e47a3100b59..ae7b9e7c3ee5e 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1334,14 +1334,14 @@ template <typename... Ps> class VariadicOperatorMatcher { template <typename T> operator Matcher<T>() const { return DynTypedMatcher::constructVariadic( Op, ast_type_traits::ASTNodeKind::getFromNodeKind<T>(), - getMatchers<T>(llvm::index_sequence_for<Ps...>())) + getMatchers<T>(std::index_sequence_for<Ps...>())) .template unconditionalConvertTo<T>(); } private: // Helper method to unpack the tuple into a vector. template <typename T, std::size_t... Is> - std::vector<DynTypedMatcher> getMatchers(llvm::index_sequence<Is...>) const { + std::vector<DynTypedMatcher> getMatchers(std::index_sequence<Is...>) const { return {Matcher<T>(std::get<Is>(Params))...}; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 15f7c2d3c8c1c..9643a8cb4b8d9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1628,7 +1628,7 @@ class Sema { template <std::size_t... Is> void emit(const SemaDiagnosticBuilder &DB, - llvm::index_sequence<Is...>) const { + std::index_sequence<Is...>) const { // Apply all tuple elements to the builder in order. bool Dummy[] = {false, (DB << getPrintable(std::get<Is>(Args)))...}; (void)Dummy; @@ -1642,7 +1642,7 @@ class Sema { void diagnose(Sema &S, SourceLocation Loc, QualType T) override { const SemaDiagnosticBuilder &DB = S.Diag(Loc, DiagID); - emit(DB, llvm::index_sequence_for<Ts...>()); + emit(DB, std::index_sequence_for<Ts...>()); DB << T; } }; diff --git a/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h b/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h index aaa9e50faa9a0..fb373fcf5029a 100644 --- a/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h +++ b/clang/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h @@ -47,7 +47,7 @@ template <typename RuleType, typename... RequirementTypes, size_t... Is> void invokeRuleAfterValidatingRequirements( RefactoringResultConsumer &Consumer, RefactoringRuleContext &Context, const std::tuple<RequirementTypes...> &Requirements, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { // Check if the requirements we're interested in can be evaluated. auto Values = std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...); @@ -87,7 +87,7 @@ template <typename... RequirementTypes, size_t... Is> void visitRefactoringOptions( RefactoringOptionVisitor &Visitor, const std::tuple<RequirementTypes...> &Requirements, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { visitRefactoringOptionsImpl(Visitor, std::get<Is>(Requirements)...); } @@ -131,7 +131,7 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { RefactoringRuleContext &Context) override { internal::invokeRuleAfterValidatingRequirements<RuleType>( Consumer, Context, Requirements, - llvm::index_sequence_for<RequirementTypes...>()); + std::index_sequence_for<RequirementTypes...>()); } bool hasSelectionRequirement() override { @@ -142,7 +142,7 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) override { internal::visitRefactoringOptions( Visitor, Requirements, - llvm::index_sequence_for<RequirementTypes...>()); + std::index_sequence_for<RequirementTypes...>()); } private: std::tuple<RequirementTypes...> Requirements; diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h index 3b0db35d982ba..0ed67aabcd621 100644 --- a/clang/lib/CodeGen/EHScopeStack.h +++ b/clang/lib/CodeGen/EHScopeStack.h @@ -199,14 +199,14 @@ class EHScopeStack { SavedTuple Saved; template <std::size_t... Is> - T restore(CodeGenFunction &CGF, llvm::index_sequence<Is...>) { + T restore(CodeGenFunction &CGF, std::index_sequence<Is...>) { // It's important that the restores are emitted in order. The braced init // list guarantees that. return T{DominatingValue<As>::restore(CGF, std::get<Is>(Saved))...}; } void Emit(CodeGenFunction &CGF, Flags flags) override { - restore(CGF, llvm::index_sequence_for<As...>()).Emit(CGF, flags); + restore(CGF, std::index_sequence_for<As...>()).Emit(CGF, flags); } public: From a6f2bf7f12a7f11c6ed1bc687c5857035378d8dd Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai <vsapsai@apple.com> Date: Tue, 14 May 2019 14:49:06 -0700 Subject: [PATCH 469/582] [CrashReproducer] Upstream a test for -index-store-path support. Patch by Bruno Cardoso Lopes. apple-llvm-split-commit: 1b668bed0b82c408faf97b5240224a3dc7bbd9d4 apple-llvm-split-dir: clang/ --- clang/test/Modules/crash-index-store-path.m | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 clang/test/Modules/crash-index-store-path.m diff --git a/clang/test/Modules/crash-index-store-path.m b/clang/test/Modules/crash-index-store-path.m new file mode 100644 index 0000000000000..aa997015d8c28 --- /dev/null +++ b/clang/test/Modules/crash-index-store-path.m @@ -0,0 +1,38 @@ +// REQUIRES: crash-recovery, shell, system-darwin + +// RUN: rm -rf %t +// RUN: mkdir -p %t/m + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -index-store-path %t/index \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECKMOD %s +// RUN: FileCheck --check-prefix=CHECKMOD_SH %s -input-file %t/crash-index-*.sh + +// RUN: rm -rf %t +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -index-store-path %t/index \ +// RUN: 2>&1 | FileCheck --check-prefix=CHECK %s +// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-index-*.sh +int foo() { return 0; } + +// CHECKMOD: Preprocessed source(s) and associated run script(s) are located at: +// CHECKMOD-NEXT: note: diagnostic msg: {{.*}}.m +// CHECKMOD-NEXT: note: diagnostic msg: {{.*}}.cache + +// CHECKMOD_SH: # Crash reproducer +// CHECKMOD_SH-NEXT: # Driver args: "-fsyntax-only" +// CHECKMOD_SH-NEXT: # Original command: {{.*$}} +// CHECKMOD_SH-NEXT: "-cc1" +// CHECKMOD_SH: "crash-index-{{[^ ]*}}.m" +// CHECKMOD_SH: "-index-store-path" "crash-index-{{[^ ]*}}.cache/index-store" + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m + +// CHECKSH: # Crash reproducer +// CHECKSH-NEXT: # Driver args: "-fsyntax-only" +// CHECKSH-NEXT: # Original command: {{.*$}} +// CHECKSH-NEXT: "-cc1" +// CHECKSH: "crash-index-{{[^ ]*}}.m" +// CHECKSH: "-index-store-path" "index-store" From 96c62cebdb9fed84bc61cfc4e7d45967b1c4998d Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 22 Aug 2019 12:25:47 -0700 Subject: [PATCH 470/582] Use ssh repo link for pushes to lldb apple-llvm-split-commit: c972772bdd5a13b187b7709edef5cf2e9a1559d0 apple-llvm-split-dir: - --- apple-llvm-config/push/swift-master-next.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple-llvm-config/push/swift-master-next.json b/apple-llvm-config/push/swift-master-next.json index 020500e2d35dd..1e4a8ced439f1 100644 --- a/apple-llvm-config/push/swift-master-next.json +++ b/apple-llvm-config/push/swift-master-next.json @@ -3,6 +3,6 @@ "swift/master-next:lldb": "upstream-with-swift" }, "repo_mapping": { - "lldb": "https://github.com/apple/swift-lldb.git" + "lldb": "git@github.com:apple/swift-lldb.git" } } From 8f376ab9e4dc93b58b37bfafb86b51469ce6710d Mon Sep 17 00:00:00 2001 From: Erik Pilkington <erik.pilkington@gmail.com> Date: Thu, 29 Aug 2019 12:13:40 -0700 Subject: [PATCH 471/582] Revert a commit series that leads to a tricky merge until Jan can take a look. Revert "[Index] Added a ShouldSkipFunctionBody callback to libIndex, and refactored clients to use it instead of inventing their own solution" This reverts commit f2dd7c98997c776fe1ce7ae651cb437379e405fd. Revert "[Index] Stopped wrapping FrontendActions in libIndex and its users" This reverts commit ca7487190698eb46511a983fb2f469db2bdb8836. Revert "[Index] Moved the IndexDataConsumer::finish call into the IndexASTConsumer from IndexAction" This reverts commit be0b64577e9c909b1394d05f4beb0f24b03c149b. Revert "[Index] Create PP callbacks in the ASTConsumer" This reverts commit fd5b0011a78b2d028fc7c8f3ba4298b888355978. Revert "[Index] Marked a bunch of classes 'final'" This reverts commit ca0fc1ec65b232fbfed974a4dee9677190814a97. apple-llvm-split-commit: 96b6383be3e03fea6d7d498931163b49bf824ce3 apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexingAction.h | 20 +-- clang/lib/Index/IndexingAction.cpp | 153 ++++++++++++++------- clang/tools/c-index-test/core_main.cpp | 5 +- clang/tools/libclang/Indexing.cpp | 91 ++++++------ 4 files changed, 151 insertions(+), 118 deletions(-) diff --git a/clang/include/clang/Index/IndexingAction.h b/clang/include/clang/Index/IndexingAction.h index f0f10fbc88fde..9756f3c539e65 100644 --- a/clang/include/clang/Index/IndexingAction.h +++ b/clang/include/clang/Index/IndexingAction.h @@ -9,7 +9,6 @@ #ifndef LLVM_CLANG_INDEX_INDEXINGACTION_H #define LLVM_CLANG_INDEX_INDEXINGACTION_H -#include "clang/AST/ASTConsumer.h" #include "clang/Basic/LLVM.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" @@ -18,7 +17,6 @@ namespace clang { class ASTContext; - class ASTConsumer; class ASTReader; class ASTUnit; class Decl; @@ -51,24 +49,12 @@ struct IndexingOptions { bool IndexTemplateParameters = false; }; -/// Creates an ASTConsumer that indexes all symbols (macros and AST decls). -std::unique_ptr<ASTConsumer> createIndexingASTConsumer( - std::shared_ptr<IndexDataConsumer> DataConsumer, - const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP, - std::function<bool(const Decl *)> ShouldSkipFunctionBody); - -inline std::unique_ptr<ASTConsumer> createIndexingASTConsumer( - std::shared_ptr<IndexDataConsumer> DataConsumer, - const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP) { - return createIndexingASTConsumer( - std::move(DataConsumer), Opts, std::move(PP), - /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); -} - /// Creates a frontend action that indexes all symbols (macros and AST decls). +/// \param WrappedAction another frontend action to wrap over or null. std::unique_ptr<FrontendAction> createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - const IndexingOptions &Opts); + IndexingOptions Opts, + std::unique_ptr<FrontendAction> WrappedAction); /// Recursively indexes all decls in the AST. void indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 6d6133e89d864..41f1008a475fe 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -23,7 +23,39 @@ using namespace clang::index; namespace { -class IndexPPCallbacks final : public PPCallbacks { +class IndexASTConsumer : public ASTConsumer { + std::shared_ptr<Preprocessor> PP; + std::shared_ptr<IndexingContext> IndexCtx; + +public: + IndexASTConsumer(std::shared_ptr<Preprocessor> PP, + std::shared_ptr<IndexingContext> IndexCtx) + : PP(std::move(PP)), IndexCtx(std::move(IndexCtx)) {} + +protected: + void Initialize(ASTContext &Context) override { + IndexCtx->setASTContext(Context); + IndexCtx->getDataConsumer().initialize(Context); + IndexCtx->getDataConsumer().setPreprocessor(PP); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return IndexCtx->indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx->indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + } +}; + +class IndexPPCallbacks : public PPCallbacks { std::shared_ptr<IndexingContext> IndexCtx; public: @@ -53,88 +85,103 @@ class IndexPPCallbacks final : public PPCallbacks { } }; -class IndexASTConsumer final : public ASTConsumer { +class IndexActionBase { +protected: std::shared_ptr<IndexDataConsumer> DataConsumer; std::shared_ptr<IndexingContext> IndexCtx; - std::shared_ptr<Preprocessor> PP; - std::function<bool(const Decl *)> ShouldSkipFunctionBody; -public: - IndexASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer, - const IndexingOptions &Opts, - std::shared_ptr<Preprocessor> PP, - std::function<bool(const Decl *)> ShouldSkipFunctionBody) - : DataConsumer(std::move(DataConsumer)), - IndexCtx(new IndexingContext(Opts, *this->DataConsumer)), - PP(std::move(PP)), - ShouldSkipFunctionBody(std::move(ShouldSkipFunctionBody)) { - assert(this->DataConsumer != nullptr); - assert(this->PP != nullptr); - } + IndexActionBase(std::shared_ptr<IndexDataConsumer> dataConsumer, + IndexingOptions Opts) + : DataConsumer(std::move(dataConsumer)), + IndexCtx(new IndexingContext(Opts, *DataConsumer)) {} -protected: - void Initialize(ASTContext &Context) override { - IndexCtx->setASTContext(Context); - IndexCtx->getDataConsumer().initialize(Context); - IndexCtx->getDataConsumer().setPreprocessor(PP); - PP->addPPCallbacks(std::make_unique<IndexPPCallbacks>(IndexCtx)); + std::unique_ptr<IndexASTConsumer> + createIndexASTConsumer(CompilerInstance &CI) { + return std::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), + IndexCtx); } - bool HandleTopLevelDecl(DeclGroupRef DG) override { - return IndexCtx->indexDeclGroupRef(DG); + std::unique_ptr<PPCallbacks> createIndexPPCallbacks() { + return std::make_unique<IndexPPCallbacks>(IndexCtx); } - void HandleInterestingDecl(DeclGroupRef DG) override { - // Ignore deserialized decls. + void finish() { + DataConsumer->finish(); } +}; - void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { - IndexCtx->indexDeclGroupRef(DG); +class IndexAction : public ASTFrontendAction, IndexActionBase { +public: + IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, + IndexingOptions Opts) + : IndexActionBase(std::move(DataConsumer), Opts) {} + +protected: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return createIndexASTConsumer(CI); } - void HandleTranslationUnit(ASTContext &Ctx) override { - DataConsumer->finish(); + bool BeginSourceFileAction(clang::CompilerInstance &CI) override { + CI.getPreprocessor().addPPCallbacks(createIndexPPCallbacks()); + return true; } - bool shouldSkipFunctionBody(Decl *D) override { - return ShouldSkipFunctionBody(D); + void EndSourceFileAction() override { + FrontendAction::EndSourceFileAction(); + finish(); } }; -class IndexAction final : public ASTFrontendAction { - std::shared_ptr<IndexDataConsumer> DataConsumer; - IndexingOptions Opts; +class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { + bool IndexActionFailed = false; public: - IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - const IndexingOptions &Opts) - : DataConsumer(std::move(DataConsumer)), Opts(Opts) { - assert(this->DataConsumer != nullptr); - } + WrappingIndexAction(std::unique_ptr<FrontendAction> WrappedAction, + std::shared_ptr<IndexDataConsumer> DataConsumer, + IndexingOptions Opts) + : WrapperFrontendAction(std::move(WrappedAction)), + IndexActionBase(std::move(DataConsumer), Opts) {} protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { - return std::make_unique<IndexASTConsumer>( - DataConsumer, Opts, CI.getPreprocessorPtr(), - /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (!OtherConsumer) { + IndexActionFailed = true; + return nullptr; + } + + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + Consumers.push_back(std::move(OtherConsumer)); + Consumers.push_back(createIndexASTConsumer(CI)); + return std::make_unique<MultiplexConsumer>(std::move(Consumers)); + } + + bool BeginSourceFileAction(clang::CompilerInstance &CI) override { + WrapperFrontendAction::BeginSourceFileAction(CI); + CI.getPreprocessor().addPPCallbacks(createIndexPPCallbacks()); + return true; + } + + void EndSourceFileAction() override { + // Invoke wrapped action's method. + WrapperFrontendAction::EndSourceFileAction(); + if (!IndexActionFailed) + finish(); } }; } // anonymous namespace -std::unique_ptr<ASTConsumer> index::createIndexingASTConsumer( - std::shared_ptr<IndexDataConsumer> DataConsumer, - const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP, - std::function<bool(const Decl *)> ShouldSkipFunctionBody) { - return std::make_unique<IndexASTConsumer>(DataConsumer, Opts, PP, - ShouldSkipFunctionBody); -} - std::unique_ptr<FrontendAction> index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - const IndexingOptions &Opts) { - assert(DataConsumer != nullptr); + IndexingOptions Opts, + std::unique_ptr<FrontendAction> WrappedAction) { + if (WrappedAction) + return std::make_unique<WrappingIndexAction>(std::move(WrappedAction), + std::move(DataConsumer), + Opts); return std::make_unique<IndexAction>(std::move(DataConsumer), Opts); } diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index f1edce0092cdd..2738e125cb979 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -221,8 +221,9 @@ static bool printSourceSymbols(const char *Executable, auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS); IndexingOptions IndexOpts; IndexOpts.IndexFunctionLocals = indexLocals; - std::unique_ptr<FrontendAction> IndexAction = - createIndexingAction(DataConsumer, IndexOpts); + std::unique_ptr<FrontendAction> IndexAction; + IndexAction = createIndexingAction(DataConsumer, IndexOpts, + /*WrappedAction=*/nullptr); auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( diff --git a/clang/tools/libclang/Indexing.cpp b/clang/tools/libclang/Indexing.cpp index 5e567b3276aad..ce1e6b3828fc0 100644 --- a/clang/tools/libclang/Indexing.cpp +++ b/clang/tools/libclang/Indexing.cpp @@ -19,7 +19,6 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" -#include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/Utils.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/HeaderSearch.h" @@ -297,20 +296,54 @@ class IndexPPCallbacks : public PPCallbacks { class IndexingConsumer : public ASTConsumer { CXIndexDataConsumer &DataConsumer; + ParsedSrcLocationsTracker *ParsedLocsTracker; public: IndexingConsumer(CXIndexDataConsumer &dataConsumer, ParsedSrcLocationsTracker *parsedLocsTracker) - : DataConsumer(dataConsumer) {} + : DataConsumer(dataConsumer), ParsedLocsTracker(parsedLocsTracker) {} + + // ASTConsumer Implementation void Initialize(ASTContext &Context) override { DataConsumer.setASTContext(Context); DataConsumer.startedTranslationUnit(); } + void HandleTranslationUnit(ASTContext &Ctx) override { + if (ParsedLocsTracker) + ParsedLocsTracker->syncWithStorage(); + } + bool HandleTopLevelDecl(DeclGroupRef DG) override { return !DataConsumer.shouldAbort(); } + + bool shouldSkipFunctionBody(Decl *D) override { + if (!ParsedLocsTracker) { + // Always skip bodies. + return true; + } + + const SourceManager &SM = DataConsumer.getASTContext().getSourceManager(); + SourceLocation Loc = D->getLocation(); + if (Loc.isMacroID()) + return false; + if (SM.isInSystemHeader(Loc)) + return true; // always skip bodies from system headers. + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + // Don't skip bodies from main files; this may be revisited. + if (SM.getMainFileID() == FID) + return false; + const FileEntry *FE = SM.getFileEntryForID(FID); + if (!FE) + return false; + + return ParsedLocsTracker->hasAlredyBeenParsed(Loc, FID, FE); + } }; //===----------------------------------------------------------------------===// @@ -334,16 +367,14 @@ class CaptureDiagnosticConsumer : public DiagnosticConsumer { class IndexingFrontendAction : public ASTFrontendAction { std::shared_ptr<CXIndexDataConsumer> DataConsumer; - IndexingOptions Opts; SharedParsedRegionsStorage *SKData; std::unique_ptr<ParsedSrcLocationsTracker> ParsedLocsTracker; public: IndexingFrontendAction(std::shared_ptr<CXIndexDataConsumer> dataConsumer, - const IndexingOptions &Opts, SharedParsedRegionsStorage *skData) - : DataConsumer(std::move(dataConsumer)), Opts(Opts), SKData(skData) {} + : DataConsumer(std::move(dataConsumer)), SKData(skData) {} std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { @@ -367,39 +398,8 @@ class IndexingFrontendAction : public ASTFrontendAction { std::make_unique<ParsedSrcLocationsTracker>(*SKData, *PPRec, PP); } - std::vector<std::unique_ptr<ASTConsumer>> Consumers; - Consumers.push_back(std::make_unique<IndexingConsumer>( - *DataConsumer, ParsedLocsTracker.get())); - Consumers.push_back(createIndexingASTConsumer( - DataConsumer, Opts, CI.getPreprocessorPtr(), - [this](const Decl *D) { return this->shouldSkipFunctionBody(D); })); - return std::make_unique<MultiplexConsumer>(std::move(Consumers)); - } - - bool shouldSkipFunctionBody(const Decl *D) { - if (!ParsedLocsTracker) { - // Always skip bodies. - return true; - } - - const SourceManager &SM = D->getASTContext().getSourceManager(); - SourceLocation Loc = D->getLocation(); - if (Loc.isMacroID()) - return false; - if (SM.isInSystemHeader(Loc)) - return true; // always skip bodies from system headers. - - FileID FID; - unsigned Offset; - std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); - // Don't skip bodies from main files; this may be revisited. - if (SM.getMainFileID() == FID) - return false; - const FileEntry *FE = SM.getFileEntryForID(FID); - if (!FE) - return false; - - return ParsedLocsTracker->hasAlredyBeenParsed(Loc, FID, FE); + return std::make_unique<IndexingConsumer>(*DataConsumer, + ParsedLocsTracker.get()); } TranslationUnitKind getTranslationUnitKind() override { @@ -409,11 +409,6 @@ class IndexingFrontendAction : public ASTFrontendAction { return TU_Prefix; } bool hasCodeCompletionSupport() const override { return false; } - - void EndSourceFileAction() override { - if (ParsedLocsTracker) - ParsedLocsTracker->syncWithStorage(); - } }; //===----------------------------------------------------------------------===// @@ -574,9 +569,12 @@ static CXErrorCode clang_indexSourceFile_Impl( auto DataConsumer = std::make_shared<CXIndexDataConsumer>(client_data, CB, index_options, CXTU->getTU()); - auto IndexAction = std::make_unique<IndexingFrontendAction>( - DataConsumer, getIndexingOptionsFromCXOptions(index_options), - SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); + auto InterAction = std::make_unique<IndexingFrontendAction>(DataConsumer, + SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); + std::unique_ptr<FrontendAction> IndexAction; + IndexAction = createIndexingAction(DataConsumer, + getIndexingOptionsFromCXOptions(index_options), + std::move(InterAction)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<FrontendAction> @@ -997,3 +995,4 @@ CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]); return cxloc::translateSourceLocation(DataConsumer.getASTContext(), Loc); } + From 9fac594889c19207bee92815f09a21829001255c Mon Sep 17 00:00:00 2001 From: Erik Pilkington <erik.pilkington@gmail.com> Date: Thu, 29 Aug 2019 16:21:14 -0700 Subject: [PATCH 472/582] Fix some internal fallout from r370379 apple-llvm-split-commit: bde22c8e17b36b9610745352c9f596516f4cd972 apple-llvm-split-dir: clang/ --- clang/tools/libclang/CRefactor.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/clang/tools/libclang/CRefactor.cpp b/clang/tools/libclang/CRefactor.cpp index 64ce4fe57217b..7ee6ec9568b0f 100644 --- a/clang/tools/libclang/CRefactor.cpp +++ b/clang/tools/libclang/CRefactor.cpp @@ -594,9 +594,10 @@ CXErrorCode performIndexedFileRename( : Symbols(Symbols), IndexedSymbols(IndexedSymbols), Lock(Lock), Options(Options), Result(nullptr), Err(CXError_Success) {} - clang::FrontendAction *create() override { - return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, - Lock, Options); + std::unique_ptr<FrontendAction> create() override { + return std::unique_ptr<FrontendAction>( + new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, Lock, + Options)); } void handleOccurrence(const rename::OldSymbolOccurrence &Occurrence, @@ -706,9 +707,10 @@ CXErrorCode performIndexedSymbolSearch( : IndexedSymbols(IndexedSymbols), Lock(Lock), Options(Options), Result(nullptr) {} - clang::FrontendAction *create() override { - return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, - Lock, Options); + std::unique_ptr<clang::FrontendAction> create() override { + return std::unique_ptr<clang::FrontendAction>( + new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, Lock, + Options)); } void handleOccurrence(const rename::OldSymbolOccurrence &Occurrence, From 328cafe7e86224bf6e8aaac871b21a2c3fd71d8b Mon Sep 17 00:00:00 2001 From: Erik Pilkington <erik.pilkington@gmail.com> Date: Fri, 30 Aug 2019 15:05:00 -0400 Subject: [PATCH 473/582] Fix a compile error from a merge apple-llvm-split-commit: c0495eef34aff5b8aa91b3c8c40592f05d3206fc apple-llvm-split-dir: clang/ --- clang/unittests/Tooling/RefactoringTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/unittests/Tooling/RefactoringTest.cpp b/clang/unittests/Tooling/RefactoringTest.cpp index 432319b9013ce..9a86b00bae5e9 100644 --- a/clang/unittests/Tooling/RefactoringTest.cpp +++ b/clang/unittests/Tooling/RefactoringTest.cpp @@ -1360,7 +1360,7 @@ class RefactoringOperationTest { ResultHandler(std::move(ResultHandler)) {} bool runOver(StringRef Code) { - return runToolOnCode(new TestAction(this), Code); + return runToolOnCode(std::make_unique<TestAction>(this), Code); } bool succeeded() const { return Success; } From 0972e5e1b3e2a915fb3d3e69a4828020e3ff19dd Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Tue, 3 Sep 2019 11:16:14 -0700 Subject: [PATCH 474/582] Revert "Revert a commit series that leads to a tricky merge until Jan can take a look." Preserved old implementation of IndexASTConsumer (renamed to IndexRecordASTConsumer). This code is soon (TM) to be impacted by the index-while-building feature upstreaming anyway. TODO (as part of i-w-b upstreaming): - remove IndexRecordASTConsumer - remove IndexRecordActionBase - consider taking advantage of ShouldSkipFunctionBody for index serialization This reverts commit 96b6383be3e03fea6d7d498931163b49bf824ce3. # Conflicts: # include/clang/Index/IndexingAction.h # lib/Index/IndexingAction.cpp apple-llvm-split-commit: 91759ce6dec5cc71434dced0ddad2bde2845e811 apple-llvm-split-dir: clang/ --- clang/include/clang/Index/IndexingAction.h | 19 ++- clang/lib/Index/IndexingAction.cpp | 188 ++++++++++----------- clang/tools/c-index-test/core_main.cpp | 5 +- clang/tools/libclang/Indexing.cpp | 91 +++++----- 4 files changed, 151 insertions(+), 152 deletions(-) diff --git a/clang/include/clang/Index/IndexingAction.h b/clang/include/clang/Index/IndexingAction.h index a1c57cfe30df0..0e7667f2c2379 100644 --- a/clang/include/clang/Index/IndexingAction.h +++ b/clang/include/clang/Index/IndexingAction.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_INDEX_INDEXINGACTION_H #define LLVM_CLANG_INDEX_INDEXINGACTION_H +#include "clang/AST/ASTConsumer.h" #include "clang/Basic/LLVM.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" @@ -18,6 +19,7 @@ namespace clang { class ASTContext; + class ASTConsumer; class ASTReader; class ASTUnit; class CompilerInstance; @@ -66,13 +68,24 @@ struct RecordingOptions { bool RecordSystemDependencies = true; IncludesRecordingKind RecordIncludes = IncludesRecordingKind::UserOnly; }; +/// Creates an ASTConsumer that indexes all symbols (macros and AST decls). +std::unique_ptr<ASTConsumer> createIndexingASTConsumer( + std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP, + std::function<bool(const Decl *)> ShouldSkipFunctionBody); + +inline std::unique_ptr<ASTConsumer> createIndexingASTConsumer( + std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP) { + return createIndexingASTConsumer( + std::move(DataConsumer), Opts, std::move(PP), + /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); +} /// Creates a frontend action that indexes all symbols (macros and AST decls). -/// \param WrappedAction another frontend action to wrap over or null. std::unique_ptr<FrontendAction> createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts, - std::unique_ptr<FrontendAction> WrappedAction); + const IndexingOptions &Opts); /// Recursively indexes all decls in the AST. void indexASTUnit(ASTUnit &Unit, IndexDataConsumer &DataConsumer, diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 44ba1e073aafc..8275859aafeed 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -29,38 +29,7 @@ using namespace clang::index; namespace { -class IndexASTConsumer : public ASTConsumer { - std::shared_ptr<Preprocessor> PP; - std::shared_ptr<IndexingContext> IndexCtx; - -public: - IndexASTConsumer(std::shared_ptr<Preprocessor> PP, - std::shared_ptr<IndexingContext> IndexCtx) - : PP(std::move(PP)), IndexCtx(std::move(IndexCtx)) {} - -protected: - void Initialize(ASTContext &Context) override { - IndexCtx->setASTContext(Context); - IndexCtx->getDataConsumer().initialize(Context); - IndexCtx->getDataConsumer().setPreprocessor(PP); - } - - bool HandleTopLevelDecl(DeclGroupRef DG) override { - return IndexCtx->indexDeclGroupRef(DG); - } - - void HandleInterestingDecl(DeclGroupRef DG) override { - // Ignore deserialized decls. - } - - void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { - IndexCtx->indexDeclGroupRef(DG); - } - - void HandleTranslationUnit(ASTContext &Ctx) override {} -}; - -class IndexPPCallbacks : public PPCallbacks { +class IndexPPCallbacks final : public PPCallbacks { std::shared_ptr<IndexingContext> IndexCtx; public: @@ -90,103 +59,88 @@ class IndexPPCallbacks : public PPCallbacks { } }; -class IndexActionBase { -protected: +class IndexASTConsumer final : public ASTConsumer { std::shared_ptr<IndexDataConsumer> DataConsumer; std::shared_ptr<IndexingContext> IndexCtx; + std::shared_ptr<Preprocessor> PP; + std::function<bool(const Decl *)> ShouldSkipFunctionBody; - IndexActionBase(std::shared_ptr<IndexDataConsumer> dataConsumer, - IndexingOptions Opts) - : DataConsumer(std::move(dataConsumer)), - IndexCtx(new IndexingContext(Opts, *DataConsumer)) {} - - std::unique_ptr<IndexASTConsumer> - createIndexASTConsumer(CompilerInstance &CI) { - IndexCtx->setSysrootPath(CI.getHeaderSearchOpts().Sysroot); - return std::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), - IndexCtx); +public: + IndexASTConsumer(std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts, + std::shared_ptr<Preprocessor> PP, + std::function<bool(const Decl *)> ShouldSkipFunctionBody) + : DataConsumer(std::move(DataConsumer)), + IndexCtx(new IndexingContext(Opts, *this->DataConsumer)), + PP(std::move(PP)), + ShouldSkipFunctionBody(std::move(ShouldSkipFunctionBody)) { + assert(this->DataConsumer != nullptr); + assert(this->PP != nullptr); } - std::unique_ptr<PPCallbacks> createIndexPPCallbacks() { - return std::make_unique<IndexPPCallbacks>(IndexCtx); +protected: + void Initialize(ASTContext &Context) override { + IndexCtx->setASTContext(Context); + IndexCtx->getDataConsumer().initialize(Context); + IndexCtx->getDataConsumer().setPreprocessor(PP); + PP->addPPCallbacks(std::make_unique<IndexPPCallbacks>(IndexCtx)); } - void finish() { - DataConsumer->finish(); + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return IndexCtx->indexDeclGroupRef(DG); } -}; -class IndexAction : public ASTFrontendAction, IndexActionBase { -public: - IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts) - : IndexActionBase(std::move(DataConsumer), Opts) {} + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } -protected: - std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) override { - return createIndexASTConsumer(CI); + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx->indexDeclGroupRef(DG); } - bool BeginSourceFileAction(clang::CompilerInstance &CI) override { - CI.getPreprocessor().addPPCallbacks(createIndexPPCallbacks()); - return true; + void HandleTranslationUnit(ASTContext &Ctx) override { + DataConsumer->finish(); } - void EndSourceFileAction() override { - FrontendAction::EndSourceFileAction(); - finish(); + bool shouldSkipFunctionBody(Decl *D) override { + return ShouldSkipFunctionBody(D); } }; -class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { - bool CreatedASTConsumer = false; +class IndexAction final : public ASTFrontendAction { + std::shared_ptr<IndexDataConsumer> DataConsumer; + IndexingOptions Opts; public: - WrappingIndexAction(std::unique_ptr<FrontendAction> WrappedAction, - std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts) - : WrapperFrontendAction(std::move(WrappedAction)), - IndexActionBase(std::move(DataConsumer), Opts) {} + IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts) + : DataConsumer(std::move(DataConsumer)), Opts(Opts) { + assert(this->DataConsumer != nullptr); + } protected: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { - auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); - if (!OtherConsumer) - return nullptr; - - CreatedASTConsumer = true; - std::vector<std::unique_ptr<ASTConsumer>> Consumers; - Consumers.push_back(std::move(OtherConsumer)); - Consumers.push_back(createIndexASTConsumer(CI)); - return std::make_unique<MultiplexConsumer>(std::move(Consumers)); - } - - bool BeginSourceFileAction(clang::CompilerInstance &CI) override { - WrapperFrontendAction::BeginSourceFileAction(CI); - CI.getPreprocessor().addPPCallbacks(createIndexPPCallbacks()); - return true; - } - - void EndSourceFileAction() override { - // Invoke wrapped action's method. - WrapperFrontendAction::EndSourceFileAction(); - if (CreatedASTConsumer) - finish(); + return std::make_unique<IndexASTConsumer>( + DataConsumer, Opts, CI.getPreprocessorPtr(), + /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); } }; } // anonymous namespace +std::unique_ptr<ASTConsumer> index::createIndexingASTConsumer( + std::shared_ptr<IndexDataConsumer> DataConsumer, + const IndexingOptions &Opts, std::shared_ptr<Preprocessor> PP, + std::function<bool(const Decl *)> ShouldSkipFunctionBody) { + return std::make_unique<IndexASTConsumer>(DataConsumer, Opts, PP, + ShouldSkipFunctionBody); +} + std::unique_ptr<FrontendAction> index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer, - IndexingOptions Opts, - std::unique_ptr<FrontendAction> WrappedAction) { - if (WrappedAction) - return std::make_unique<WrappingIndexAction>(std::move(WrappedAction), - std::move(DataConsumer), - Opts); + const IndexingOptions &Opts) { + assert(DataConsumer != nullptr); return std::make_unique<IndexAction>(std::move(DataConsumer), Opts); } @@ -498,6 +452,38 @@ class SourceFilesIndexDependencyCollector : public DependencyCollector, } }; + +class IndexRecordASTConsumer : public ASTConsumer { + std::shared_ptr<Preprocessor> PP; + std::shared_ptr<IndexingContext> IndexCtx; + +public: + IndexRecordASTConsumer(std::shared_ptr<Preprocessor> PP, + std::shared_ptr<IndexingContext> IndexCtx) + : PP(std::move(PP)), IndexCtx(std::move(IndexCtx)) {} + +protected: + void Initialize(ASTContext &Context) override { + IndexCtx->setASTContext(Context); + IndexCtx->getDataConsumer().initialize(Context); + IndexCtx->getDataConsumer().setPreprocessor(PP); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return IndexCtx->indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx->indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override {} +}; + class IndexRecordActionBase { protected: RecordingOptions RecordOpts; @@ -510,7 +496,7 @@ class IndexRecordActionBase { IndexCtx(new IndexingContext(IndexOpts, Recorder)), DepCollector(*IndexCtx, RecordOpts) {} - std::unique_ptr<IndexASTConsumer> + std::unique_ptr<IndexRecordASTConsumer> createIndexASTConsumer(CompilerInstance &CI) { IndexCtx->setSysrootPath(CI.getHeaderSearchOpts().Sysroot); Recorder.init(IndexCtx.get(), CI); @@ -520,7 +506,7 @@ class IndexRecordActionBase { DepCollector.setSysrootPath(IndexCtx->getSysrootPath()); DepCollector.attachToPreprocessor(PP); - return std::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(), + return std::make_unique<IndexRecordASTConsumer>(CI.getPreprocessorPtr(), IndexCtx); } diff --git a/clang/tools/c-index-test/core_main.cpp b/clang/tools/c-index-test/core_main.cpp index d7c47cacfd87c..bdda3beea2f77 100644 --- a/clang/tools/c-index-test/core_main.cpp +++ b/clang/tools/c-index-test/core_main.cpp @@ -255,9 +255,8 @@ static bool printSourceSymbols(const char *Executable, auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS); IndexingOptions IndexOpts; IndexOpts.IndexFunctionLocals = indexLocals; - std::unique_ptr<FrontendAction> IndexAction; - IndexAction = createIndexingAction(DataConsumer, IndexOpts, - /*WrappedAction=*/nullptr); + std::unique_ptr<FrontendAction> IndexAction = + createIndexingAction(DataConsumer, IndexOpts); auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( diff --git a/clang/tools/libclang/Indexing.cpp b/clang/tools/libclang/Indexing.cpp index ce1e6b3828fc0..5e567b3276aad 100644 --- a/clang/tools/libclang/Indexing.cpp +++ b/clang/tools/libclang/Indexing.cpp @@ -19,6 +19,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/Utils.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/HeaderSearch.h" @@ -296,54 +297,20 @@ class IndexPPCallbacks : public PPCallbacks { class IndexingConsumer : public ASTConsumer { CXIndexDataConsumer &DataConsumer; - ParsedSrcLocationsTracker *ParsedLocsTracker; public: IndexingConsumer(CXIndexDataConsumer &dataConsumer, ParsedSrcLocationsTracker *parsedLocsTracker) - : DataConsumer(dataConsumer), ParsedLocsTracker(parsedLocsTracker) {} - - // ASTConsumer Implementation + : DataConsumer(dataConsumer) {} void Initialize(ASTContext &Context) override { DataConsumer.setASTContext(Context); DataConsumer.startedTranslationUnit(); } - void HandleTranslationUnit(ASTContext &Ctx) override { - if (ParsedLocsTracker) - ParsedLocsTracker->syncWithStorage(); - } - bool HandleTopLevelDecl(DeclGroupRef DG) override { return !DataConsumer.shouldAbort(); } - - bool shouldSkipFunctionBody(Decl *D) override { - if (!ParsedLocsTracker) { - // Always skip bodies. - return true; - } - - const SourceManager &SM = DataConsumer.getASTContext().getSourceManager(); - SourceLocation Loc = D->getLocation(); - if (Loc.isMacroID()) - return false; - if (SM.isInSystemHeader(Loc)) - return true; // always skip bodies from system headers. - - FileID FID; - unsigned Offset; - std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); - // Don't skip bodies from main files; this may be revisited. - if (SM.getMainFileID() == FID) - return false; - const FileEntry *FE = SM.getFileEntryForID(FID); - if (!FE) - return false; - - return ParsedLocsTracker->hasAlredyBeenParsed(Loc, FID, FE); - } }; //===----------------------------------------------------------------------===// @@ -367,14 +334,16 @@ class CaptureDiagnosticConsumer : public DiagnosticConsumer { class IndexingFrontendAction : public ASTFrontendAction { std::shared_ptr<CXIndexDataConsumer> DataConsumer; + IndexingOptions Opts; SharedParsedRegionsStorage *SKData; std::unique_ptr<ParsedSrcLocationsTracker> ParsedLocsTracker; public: IndexingFrontendAction(std::shared_ptr<CXIndexDataConsumer> dataConsumer, + const IndexingOptions &Opts, SharedParsedRegionsStorage *skData) - : DataConsumer(std::move(dataConsumer)), SKData(skData) {} + : DataConsumer(std::move(dataConsumer)), Opts(Opts), SKData(skData) {} std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { @@ -398,8 +367,39 @@ class IndexingFrontendAction : public ASTFrontendAction { std::make_unique<ParsedSrcLocationsTracker>(*SKData, *PPRec, PP); } - return std::make_unique<IndexingConsumer>(*DataConsumer, - ParsedLocsTracker.get()); + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + Consumers.push_back(std::make_unique<IndexingConsumer>( + *DataConsumer, ParsedLocsTracker.get())); + Consumers.push_back(createIndexingASTConsumer( + DataConsumer, Opts, CI.getPreprocessorPtr(), + [this](const Decl *D) { return this->shouldSkipFunctionBody(D); })); + return std::make_unique<MultiplexConsumer>(std::move(Consumers)); + } + + bool shouldSkipFunctionBody(const Decl *D) { + if (!ParsedLocsTracker) { + // Always skip bodies. + return true; + } + + const SourceManager &SM = D->getASTContext().getSourceManager(); + SourceLocation Loc = D->getLocation(); + if (Loc.isMacroID()) + return false; + if (SM.isInSystemHeader(Loc)) + return true; // always skip bodies from system headers. + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + // Don't skip bodies from main files; this may be revisited. + if (SM.getMainFileID() == FID) + return false; + const FileEntry *FE = SM.getFileEntryForID(FID); + if (!FE) + return false; + + return ParsedLocsTracker->hasAlredyBeenParsed(Loc, FID, FE); } TranslationUnitKind getTranslationUnitKind() override { @@ -409,6 +409,11 @@ class IndexingFrontendAction : public ASTFrontendAction { return TU_Prefix; } bool hasCodeCompletionSupport() const override { return false; } + + void EndSourceFileAction() override { + if (ParsedLocsTracker) + ParsedLocsTracker->syncWithStorage(); + } }; //===----------------------------------------------------------------------===// @@ -569,12 +574,9 @@ static CXErrorCode clang_indexSourceFile_Impl( auto DataConsumer = std::make_shared<CXIndexDataConsumer>(client_data, CB, index_options, CXTU->getTU()); - auto InterAction = std::make_unique<IndexingFrontendAction>(DataConsumer, - SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); - std::unique_ptr<FrontendAction> IndexAction; - IndexAction = createIndexingAction(DataConsumer, - getIndexingOptionsFromCXOptions(index_options), - std::move(InterAction)); + auto IndexAction = std::make_unique<IndexingFrontendAction>( + DataConsumer, getIndexingOptionsFromCXOptions(index_options), + SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<FrontendAction> @@ -995,4 +997,3 @@ CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]); return cxloc::translateSourceLocation(DataConsumer.getASTContext(), Loc); } - From 2b2c20fb1757df154cfc2c23e48104004dbbf44a Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Fri, 6 Sep 2019 14:41:28 -0700 Subject: [PATCH 475/582] [clang][Index] Replace CodegenNameGenerator with ASTNameGenerator vol. III Follow-up to upstream commit: 7e36ecd66dbe528edf67d1a412c4dea24019f178 [clang][Index] Replace CodegenNameGenerator with ASTNameGenerator apple-llvm-split-commit: 70ad01ba0cfec839f4d5a16a8891764888dfe943 apple-llvm-split-dir: clang/ --- clang/lib/Index/ClangIndexRecordWriter.cpp | 6 +++--- clang/lib/Index/ClangIndexRecordWriter.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/lib/Index/ClangIndexRecordWriter.cpp b/clang/lib/Index/ClangIndexRecordWriter.cpp index 41d45a0360d42..a8c8bd953c40c 100644 --- a/clang/lib/Index/ClangIndexRecordWriter.cpp +++ b/clang/lib/Index/ClangIndexRecordWriter.cpp @@ -44,7 +44,7 @@ ClangIndexRecordWriter::ClangIndexRecordWriter(ASTContext &Ctx, : Impl(Opts.DataDirPath), Ctx(Ctx), RecordOpts(std::move(Opts)), Hasher(Ctx) { if (Opts.RecordSymbolCodeGenName) - CGNameGen.reset(new CodegenNameGenerator(Ctx)); + ASTNameGen.reset(new ASTNameGenerator(Ctx)); } ClangIndexRecordWriter::~ClangIndexRecordWriter() {} @@ -109,9 +109,9 @@ bool ClangIndexRecordWriter::writeRecord(StringRef Filename, Sym.USR = getUSR(D); assert(!Sym.USR.empty() && "Recorded decl without USR!"); - if (CGNameGen && ND) { + if (ASTNameGen && ND) { llvm::raw_svector_ostream OS(Scratch); - CGNameGen->writeName(ND, OS); + ASTNameGen->writeName(ND, OS); } unsigned CGNameLen = Scratch.size() - NameLen; Sym.CodeGenName = StringRef(Scratch.data() + NameLen, CGNameLen); diff --git a/clang/lib/Index/ClangIndexRecordWriter.h b/clang/lib/Index/ClangIndexRecordWriter.h index b68f9875fb34b..81ab6a662745e 100644 --- a/clang/lib/Index/ClangIndexRecordWriter.h +++ b/clang/lib/Index/ClangIndexRecordWriter.h @@ -11,9 +11,9 @@ #define LLVM_CLANG_LIB_INDEX_CLANGINDEXRECORDWRITER_H #include "IndexRecordHasher.h" +#include "clang/AST/Mangle.h" #include "clang/Index/IndexRecordWriter.h" #include "clang/Index/IndexingAction.h" -#include "clang/Index/CodegenNameGenerator.h" #include "llvm/ADT/SmallString.h" namespace clang { @@ -29,7 +29,7 @@ class ClangIndexRecordWriter { ASTContext &Ctx; RecordingOptions RecordOpts; - std::unique_ptr<CodegenNameGenerator> CGNameGen; + std::unique_ptr<ASTNameGenerator> ASTNameGen; llvm::BumpPtrAllocator Allocator; llvm::DenseMap<const Decl *, StringRef> USRByDecl; IndexRecordHasher Hasher; @@ -39,7 +39,7 @@ class ClangIndexRecordWriter { ~ClangIndexRecordWriter(); ASTContext &getASTContext() { return Ctx; } - CodegenNameGenerator *getCGNameGen() { return CGNameGen.get(); } + ASTNameGenerator *getASTNameGen() { return ASTNameGen.get(); } bool writeRecord(StringRef Filename, const FileIndexRecord &Record, std::string &Error, std::string *RecordFile = nullptr); From f3e88483aa80a23d63b470bfb4f6f0599a7a404f Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Mon, 17 Jun 2019 13:09:39 -0700 Subject: [PATCH 476/582] [indexstore] Handle enum value SymbolRole::NameReference rdar://problem/51747249 apple-llvm-split-commit: 795397d5c407920ff754e2131acd93bf4a7a8895 apple-llvm-split-dir: clang/ --- clang/lib/Index/IndexDataStoreUtils.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index d88f8ffa17ce5..96f700340b25c 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -511,6 +511,9 @@ uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) { case SymbolRole::RelationSpecializationOf: storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF; break; + case SymbolRole::NameReference: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REFERENCE; + break; } }); return storeRoles; From a0f879077039bc0d93cf91e69f9867110d5ffe3b Mon Sep 17 00:00:00 2001 From: Jan Korous <jkorous@apple.com> Date: Mon, 17 Jun 2019 14:09:14 -0700 Subject: [PATCH 477/582] [indexstore] Add INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE rdar://problem/51747249 apple-llvm-split-commit: 60b9b9e6903314251949c1c63d84befd7d85b78b apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 1 + clang/lib/Index/IndexDataStoreUtils.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 1d671fefefc76..5c1e47d81bfde 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -294,6 +294,7 @@ typedef enum { INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7, INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8, INDEXSTORE_SYMBOL_ROLE_UNDEFINITION = 1 << 19, + INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE = 1 << 20, // Relation roles. INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 9, diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index 96f700340b25c..b2cb7da01108c 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -275,6 +275,8 @@ SymbolRoleSet index::getSymbolRoles(uint64_t Roles) { SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationIBTypeOf; if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF) SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationSpecializationOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::NameReference; return SymbolRoles; } @@ -512,7 +514,7 @@ uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) { storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF; break; case SymbolRole::NameReference: - storeRoles |= INDEXSTORE_SYMBOL_ROLE_REFERENCE; + storeRoles |= INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE; break; } }); From 20faea56992ffdf3a70975903181a90883011e74 Mon Sep 17 00:00:00 2001 From: Tim Northover <t.p.northover@gmail.com> Date: Thu, 19 Sep 2019 13:18:04 +0100 Subject: [PATCH 478/582] Fix regression tests. apple-llvm-split-commit: 57636b4001bdfb62bcc5ed3f8ab3d66adcbb94bd apple-llvm-split-dir: llvm/ --- llvm/test/Object/mri2.test | 2 +- llvm/test/Object/mri3.test | 2 +- llvm/test/Object/mri4.test | 2 +- llvm/test/Reduce/Inputs/remove-instructions.py | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/llvm/test/Object/mri2.test b/llvm/test/Object/mri2.test index c4dadf13f268b..2e90171c6acb8 100644 --- a/llvm/test/Object/mri2.test +++ b/llvm/test/Object/mri2.test @@ -4,4 +4,4 @@ ; RUN: echo end >> %t.mri ; RUN: not llvm-ar -M < %t.mri 2>&1 | FileCheck %s -; CHECK: error: editing multiple archives not supported +; CHECK: error: script line 2: editing multiple archives not supported diff --git a/llvm/test/Object/mri3.test b/llvm/test/Object/mri3.test index 163de53f8e57a..697878bfff4b7 100644 --- a/llvm/test/Object/mri3.test +++ b/llvm/test/Object/mri3.test @@ -3,4 +3,4 @@ ; RUN: echo end >> %t.mri ; RUN: not llvm-ar -M < %t.mri 2>&1 | FileCheck %s -; CHECK: error: file already saved +; CHECK: error: script line 2: file already saved diff --git a/llvm/test/Object/mri4.test b/llvm/test/Object/mri4.test index 26a3733affb27..bdca3c9d57e13 100644 --- a/llvm/test/Object/mri4.test +++ b/llvm/test/Object/mri4.test @@ -1,4 +1,4 @@ ; RUN: echo abc > %t.mri ; RUN: not llvm-ar -M < %t.mri 2>&1 | FileCheck %s -; CHECK: error: unknown command: abc +; CHECK: error: script line 1: unknown command: abc diff --git a/llvm/test/Reduce/Inputs/remove-instructions.py b/llvm/test/Reduce/Inputs/remove-instructions.py index df4d85dd4f46a..a24f194b0b82a 100755 --- a/llvm/test/Reduce/Inputs/remove-instructions.py +++ b/llvm/test/Reduce/Inputs/remove-instructions.py @@ -1,3 +1,4 @@ +from __future__ import print_function import sys InterestingInstructions = 0 @@ -9,7 +10,7 @@ line = line[:i] if "%interesting" in line: InterestingInstructions += 1 - print InterestingInstructions + print(InterestingInstructions) if InterestingInstructions == 5: sys.exit(0) # interesting! From f47480963e8028ae7704b4723c6e8f0ff2671247 Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee.com@gmail.com> Date: Sat, 30 Mar 2019 10:46:53 -0700 Subject: [PATCH 479/582] Mark indexstore enum flag types with flag_enum attribute apple-llvm-split-commit: 674466424e911212fd19cc6cab6f29a65ba441ea apple-llvm-split-dir: clang/ --- .../clang/Index/IndexDataStoreSymbolUtils.h | 4 ++-- clang/include/indexstore/indexstore.h | 20 ++++++++++++------- clang/lib/Index/IndexDataStoreUtils.cpp | 8 ++++---- clang/tools/IndexStore/IndexStore.cpp | 10 +++++----- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/clang/include/clang/Index/IndexDataStoreSymbolUtils.h b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h index e1d982de094cd..4db55f68bbf17 100644 --- a/clang/include/clang/Index/IndexDataStoreSymbolUtils.h +++ b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h @@ -42,10 +42,10 @@ indexstore_symbol_subkind_t getIndexStoreSubKind(SymbolSubKind K); indexstore_symbol_language_t getIndexStoreLang(SymbolLanguage L); /// Map a SymbolPropertySet to its indexstore representation. -uint64_t getIndexStoreProperties(SymbolPropertySet Props); +indexstore_symbol_property_t getIndexStoreProperties(SymbolPropertySet Props); /// Map a SymbolRoleSet to its indexstore representation. -uint64_t getIndexStoreRoles(SymbolRoleSet Roles); +indexstore_symbol_role_t getIndexStoreRoles(SymbolRoleSet Roles); } // end namespace index } // end namespace clang diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index 5c1e47d81bfde..a872fcb3c5fc7 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -84,6 +84,12 @@ # define INDEXSTORE_NOESCAPE #endif +#if __has_attribute(flag_enum) +# define INDEXSTORE_OPTIONS __attribute__((flag_enum)) +#else +# define INDEXSTORE_OPTIONS +#endif + INDEXSTORE_BEGIN_DECLS typedef void *indexstore_error_t; @@ -263,7 +269,7 @@ typedef enum { INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY = 1015, } indexstore_symbol_subkind_t; -typedef enum { +typedef enum INDEXSTORE_OPTIONS { INDEXSTORE_SYMBOL_PROPERTY_GENERIC = 1 << 0, INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION = 1 << 1, INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION = 1 << 2, @@ -283,7 +289,7 @@ typedef enum { INDEXSTORE_SYMBOL_LANG_SWIFT = 100, } indexstore_symbol_language_t; -typedef enum { +typedef enum INDEXSTORE_OPTIONS { INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, @@ -318,13 +324,13 @@ indexstore_symbol_get_kind(indexstore_symbol_t); INDEXSTORE_PUBLIC indexstore_symbol_subkind_t indexstore_symbol_get_subkind(indexstore_symbol_t); -INDEXSTORE_PUBLIC uint64_t +INDEXSTORE_PUBLIC indexstore_symbol_property_t indexstore_symbol_get_properties(indexstore_symbol_t); -INDEXSTORE_PUBLIC uint64_t +INDEXSTORE_PUBLIC indexstore_symbol_role_t indexstore_symbol_get_roles(indexstore_symbol_t); -INDEXSTORE_PUBLIC uint64_t +INDEXSTORE_PUBLIC indexstore_symbol_role_t indexstore_symbol_get_related_roles(indexstore_symbol_t); INDEXSTORE_PUBLIC indexstore_string_ref_t @@ -338,7 +344,7 @@ indexstore_symbol_get_codegen_name(indexstore_symbol_t); typedef void *indexstore_symbol_relation_t; -INDEXSTORE_PUBLIC uint64_t +INDEXSTORE_PUBLIC indexstore_symbol_role_t indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t); INDEXSTORE_PUBLIC indexstore_symbol_t @@ -360,7 +366,7 @@ indexstore_occurrence_relations_apply_f(indexstore_occurrence_t, void *context, INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)); -INDEXSTORE_PUBLIC uint64_t +INDEXSTORE_PUBLIC indexstore_symbol_role_t indexstore_occurrence_get_roles(indexstore_occurrence_t); INDEXSTORE_PUBLIC void diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp index b2cb7da01108c..f27ffa6a54e10 100644 --- a/clang/lib/Index/IndexDataStoreUtils.cpp +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -412,7 +412,7 @@ indexstore_symbol_language_t index::getIndexStoreLang(SymbolLanguage L) { } /// Map a SymbolPropertySet to its indexstore representation. -uint64_t index::getIndexStoreProperties(SymbolPropertySet Props) { +indexstore_symbol_property_t index::getIndexStoreProperties(SymbolPropertySet Props) { uint64_t storeProp = 0; applyForEachSymbolProperty(Props, [&](SymbolProperty prop) { switch (prop) { @@ -445,11 +445,11 @@ uint64_t index::getIndexStoreProperties(SymbolPropertySet Props) { break; } }); - return storeProp; + return static_cast<indexstore_symbol_property_t>(storeProp); } /// Map a SymbolRoleSet to its indexstore representation. -uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) { +indexstore_symbol_role_t index::getIndexStoreRoles(SymbolRoleSet Roles) { uint64_t storeRoles = 0; applyForEachSymbolRole(Roles, [&](SymbolRole role) { switch (role) { @@ -518,5 +518,5 @@ uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) { break; } }); - return storeRoles; + return static_cast<indexstore_symbol_role_t>(storeRoles); } diff --git a/clang/tools/IndexStore/IndexStore.cpp b/clang/tools/IndexStore/IndexStore.cpp index 8fda780489887..7ab7142ac2343 100644 --- a/clang/tools/IndexStore/IndexStore.cpp +++ b/clang/tools/IndexStore/IndexStore.cpp @@ -315,17 +315,17 @@ indexstore_symbol_get_language(indexstore_symbol_t sym) { return getIndexStoreLang(static_cast<IndexRecordDecl *>(sym)->SymInfo.Lang); } -uint64_t +indexstore_symbol_property_t indexstore_symbol_get_properties(indexstore_symbol_t sym) { return getIndexStoreProperties(static_cast<IndexRecordDecl *>(sym)->SymInfo.Properties); } -uint64_t +indexstore_symbol_role_t indexstore_symbol_get_roles(indexstore_symbol_t sym) { return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->Roles); } -uint64_t +indexstore_symbol_role_t indexstore_symbol_get_related_roles(indexstore_symbol_t sym) { return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->RelatedRoles); } @@ -348,7 +348,7 @@ indexstore_symbol_get_codegen_name(indexstore_symbol_t sym) { return toIndexStoreString(D->CodeGenName); } -uint64_t +indexstore_symbol_role_t indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t sym_rel) { return getIndexStoreRoles(static_cast<IndexRecordRelation *>(sym_rel)->Roles); } @@ -388,7 +388,7 @@ indexstore_occurrence_relations_apply_f(indexstore_occurrence_t occur, return true; } -uint64_t +indexstore_symbol_role_t indexstore_occurrence_get_roles(indexstore_occurrence_t occur) { return getIndexStoreRoles(static_cast<IndexRecordOccurrence*>(occur)->Roles); } From a591786fa7a2aac014c0f0424f36111f5f0d3348 Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee.com@gmail.com> Date: Sat, 17 Aug 2019 16:26:53 -0700 Subject: [PATCH 480/582] Use sized enums or fallback to typedef apple-llvm-split-commit: c3b0e6b00cb1fcfd132b844547c506453bd943ea apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index a872fcb3c5fc7..d18d5a57089cf 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -85,9 +85,25 @@ #endif #if __has_attribute(flag_enum) -# define INDEXSTORE_OPTIONS __attribute__((flag_enum)) +# define _INDEXSTORE_FLAG_ENUM __attribute__((flag_enum)) #else -# define INDEXSTORE_OPTIONS +# define _INDEXSTORE_FLAG_ENUM +#endif + +#if __has_attribute(enum_extensibility) +# define _INDEXSTORE_OPEN_ENUM __attribute__((enum_extensibility(open))) +#else +# define _INDEXSTORE_OPEN_ENUM +#endif + +#define _INDEXSTORE_OPTIONS_ATTRS _INDEXSTORE_OPEN_ENUM _INDEXSTORE_FLAG_ENUM + +#if __has_extension(cxx_strong_enums) +# define INDEXSTORE_OPTIONS(_name, _type) enum _INDEXSTORE_OPTIONS_ATTRS _name : _type +#elif __has_feature(objc_fixed_enum) +# define INDEXSTORE_OPTIONS(_name, _type) typedef enum _INDEXSTORE_OPTIONS_ATTRS _name : _type _name; enum _INDEXSTORE_OPTIONS_ATTRS _name : _type +#else +# define INDEXSTORE_OPTIONS(_name, _type) typedef _type _name; enum _INDEXSTORE_OPTIONS_ATTRS #endif INDEXSTORE_BEGIN_DECLS @@ -269,7 +285,7 @@ typedef enum { INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY = 1015, } indexstore_symbol_subkind_t; -typedef enum INDEXSTORE_OPTIONS { +INDEXSTORE_OPTIONS(indexstore_symbol_property_t, uint64_t) { INDEXSTORE_SYMBOL_PROPERTY_GENERIC = 1 << 0, INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION = 1 << 1, INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION = 1 << 2, @@ -279,7 +295,7 @@ typedef enum INDEXSTORE_OPTIONS { INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE = 1 << 6, INDEXSTORE_SYMBOL_PROPERTY_LOCAL = 1 << 7, INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE = 1 << 8, -} indexstore_symbol_property_t; +}; typedef enum { INDEXSTORE_SYMBOL_LANG_C = 0, @@ -289,7 +305,7 @@ typedef enum { INDEXSTORE_SYMBOL_LANG_SWIFT = 100, } indexstore_symbol_language_t; -typedef enum INDEXSTORE_OPTIONS { +INDEXSTORE_OPTIONS(indexstore_symbol_role_t, uint64_t) { INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, @@ -313,7 +329,7 @@ typedef enum INDEXSTORE_OPTIONS { INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 16, INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 17, INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 18, -} indexstore_symbol_role_t; +}; INDEXSTORE_PUBLIC indexstore_symbol_language_t indexstore_symbol_get_language(indexstore_symbol_t); From 710d052c56c6f0857893d04d80121c446143bb91 Mon Sep 17 00:00:00 2001 From: Dave Lee <davelee.com@gmail.com> Date: Mon, 19 Aug 2019 15:01:27 -0700 Subject: [PATCH 481/582] Move typedef keyword out of macro; Remove _ prefixes apple-llvm-split-commit: 728d86f1f243edad8fdedc18105a44148d7a73cc apple-llvm-split-dir: clang/ --- clang/include/indexstore/indexstore.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index d18d5a57089cf..ac365e770116a 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -85,25 +85,23 @@ #endif #if __has_attribute(flag_enum) -# define _INDEXSTORE_FLAG_ENUM __attribute__((flag_enum)) +# define INDEXSTORE_FLAG_ENUM_ATTR __attribute__((flag_enum)) #else -# define _INDEXSTORE_FLAG_ENUM +# define INDEXSTORE_FLAG_ENUM_ATTR #endif #if __has_attribute(enum_extensibility) -# define _INDEXSTORE_OPEN_ENUM __attribute__((enum_extensibility(open))) +# define INDEXSTORE_OPEN_ENUM_ATTR __attribute__((enum_extensibility(open))) #else -# define _INDEXSTORE_OPEN_ENUM +# define INDEXSTORE_OPEN_ENUM_ATTR #endif -#define _INDEXSTORE_OPTIONS_ATTRS _INDEXSTORE_OPEN_ENUM _INDEXSTORE_FLAG_ENUM +#define INDEXSTORE_OPTIONS_ATTRS INDEXSTORE_OPEN_ENUM_ATTR INDEXSTORE_FLAG_ENUM_ATTR -#if __has_extension(cxx_strong_enums) -# define INDEXSTORE_OPTIONS(_name, _type) enum _INDEXSTORE_OPTIONS_ATTRS _name : _type -#elif __has_feature(objc_fixed_enum) -# define INDEXSTORE_OPTIONS(_name, _type) typedef enum _INDEXSTORE_OPTIONS_ATTRS _name : _type _name; enum _INDEXSTORE_OPTIONS_ATTRS _name : _type +#if __has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum) +# define INDEXSTORE_OPTIONS(_type, _name) enum INDEXSTORE_OPTIONS_ATTRS _name : _type _name; enum INDEXSTORE_OPTIONS_ATTRS _name : _type #else -# define INDEXSTORE_OPTIONS(_name, _type) typedef _type _name; enum _INDEXSTORE_OPTIONS_ATTRS +# define INDEXSTORE_OPTIONS(_type, _name) _type _name; enum INDEXSTORE_OPTIONS_ATTRS #endif INDEXSTORE_BEGIN_DECLS @@ -285,7 +283,7 @@ typedef enum { INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY = 1015, } indexstore_symbol_subkind_t; -INDEXSTORE_OPTIONS(indexstore_symbol_property_t, uint64_t) { +typedef INDEXSTORE_OPTIONS(uint64_t, indexstore_symbol_property_t) { INDEXSTORE_SYMBOL_PROPERTY_GENERIC = 1 << 0, INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION = 1 << 1, INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION = 1 << 2, @@ -305,7 +303,7 @@ typedef enum { INDEXSTORE_SYMBOL_LANG_SWIFT = 100, } indexstore_symbol_language_t; -INDEXSTORE_OPTIONS(indexstore_symbol_role_t, uint64_t) { +typedef INDEXSTORE_OPTIONS(uint64_t, indexstore_symbol_role_t) { INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, From 16b09e5828b999ac127eec7e8cae4156e45d5e60 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 11 Oct 2019 12:53:35 -0700 Subject: [PATCH 482/582] Update disclaimer in the README apple-llvm-split-commit: cda19fc54358e2f9d34e9dc78f102e03d70e49db apple-llvm-split-dir: - --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c01872a6f62c7..f59f15045ec74 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # Disclaimer The [llvm-monorepo-root](https://github.com/apple/llvm-monorepo-root), -[llvm-project-v2](https://github.com/apple/llvm-project-v2), -[llvm-project-v2-split](https://github.com/apple/llvm-project-v2-split), -[llvm-project-v1](https://github.com/apple/llvm-project-v1), and -[llvm-project-v1-split](https://github.com/apple/llvm-project-v1-split) repositories are +[llvm-project-v3](https://github.com/apple/llvm-project-v3), +[llvm-project-v3-split](https://github.com/apple/llvm-project-v3-split), +[llvm-project-v2](https://github.com/apple/llvm-project-v2), and +[llvm-project-v2-split](https://github.com/apple/llvm-project-v2-split) +repositories are WIP repositories that are used for development and prototyping of an llvm-project monorepo that will be used for Apple's compiler releases and for the open source Swift project. Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition-update/27079) From 758ff8bbe9bea566811afdad2ef545359c2a1d79 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Sat, 12 Oct 2019 13:15:38 -0700 Subject: [PATCH 483/582] [apple-llvm-config] add an AM config file for apple/master test merge branch apple-llvm-split-commit: 0c933c28b8fd3ac452a99de60f83852d14dea597 apple-llvm-split-dir: - --- apple-llvm-config/am/am-test-apple-master.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 apple-llvm-config/am/am-test-apple-master.json diff --git a/apple-llvm-config/am/am-test-apple-master.json b/apple-llvm-config/am/am-test-apple-master.json new file mode 100644 index 0000000000000..e2d7498341261 --- /dev/null +++ b/apple-llvm-config/am/am-test-apple-master.json @@ -0,0 +1,3 @@ +{ + "upstream": "am-test/llvm.org/master" +} From 5cd6761fd6d051a5049ef5059c53befb7ad47baf Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner <gwenm@oculus.com> Date: Fri, 11 Oct 2019 10:01:09 -0700 Subject: [PATCH 484/582] Fix Assumption-cache-invalidation 3b55056633a1fe19f313f1837c60be414798e81d modified the penalty calculation which makes this test fail due to the hotcold splitting no longer activating. This re-enables hotcold splitting and fixes the test. apple-llvm-split-commit: 8bbd1edb5fd1f38bf1abb8f5b32db3f363e2cb73 apple-llvm-split-dir: llvm/ --- .../HotColdSplit/assumption-cache-invalidation.ll | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/llvm/test/Transforms/HotColdSplit/assumption-cache-invalidation.ll b/llvm/test/Transforms/HotColdSplit/assumption-cache-invalidation.ll index fbf2061ff650a..849745a6d13ef 100644 --- a/llvm/test/Transforms/HotColdSplit/assumption-cache-invalidation.ll +++ b/llvm/test/Transforms/HotColdSplit/assumption-cache-invalidation.ll @@ -1,5 +1,5 @@ ; REQUIRES: asserts -; RUN: opt -S -instsimplify -hotcoldsplit -debug < %s 2>&1 | FileCheck %s +; RUN: opt -S -instsimplify -hotcoldsplit -hotcoldsplit-threshold=-1 -debug < %s 2>&1 | FileCheck %s ; RUN: opt -instcombine -hotcoldsplit -instsimplify %s -o /dev/null target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" @@ -13,7 +13,10 @@ target triple = "aarch64" ; CHECK-NOT: @llvm.assume ; CHECK: } ; CHECK: declare {{.*}}@llvm.assume -; CHECK: define {{.*}}@f.cold.1(i64 %0) +; CHECK: define {{.*}}@f.cold.1() +; CHECK-LABEL: newFuncRoot: +; CHECK: } +; CHECK: define {{.*}}@f.cold.2(i64 %0) ; CHECK-LABEL: newFuncRoot: ; CHECK: %1 = icmp eq i64 %0, 0 ; CHECK: call void @llvm.assume(i1 %1) From 138aecc9738cce45bb4319faa475fa954ee96269 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Mon, 14 Oct 2019 16:54:38 -0700 Subject: [PATCH 485/582] Disable lsan tests for apple/master branch to unblock PR testing for swift-ci apple-llvm-split-commit: 4df46b1b6debc910bc17a37a9e53edfb05cd3ac1 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/test/lsan/lit.common.cfg.py | 5 +++++ compiler-rt/test/sanitizer_common/lit.common.cfg.py | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/compiler-rt/test/lsan/lit.common.cfg.py b/compiler-rt/test/lsan/lit.common.cfg.py index e1a2c4e087753..67f3a0741c808 100644 --- a/compiler-rt/test/lsan/lit.common.cfg.py +++ b/compiler-rt/test/lsan/lit.common.cfg.py @@ -79,3 +79,8 @@ def build_invocation(compile_flags): config.unsupported = True config.suffixes = ['.c', '.cpp', '.mm'] + +# Apple-Clang: Disable LSan +if config.host_os == 'Darwin': + lit_config.note('Disabling LSan tests on Darwin') + config.unsupported = True diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg.py b/compiler-rt/test/sanitizer_common/lit.common.cfg.py index c5cc9ed38c96d..6c29d7185f6c0 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg.py +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg.py @@ -75,3 +75,9 @@ def build_invocation(compile_flags): if not config.parallelism_group: config.parallelism_group = 'shadow-memory' + +# Disable LSan sanitizer_common tests +# because AppleClang doesn't support LSan. +if config.tool_name == 'lsan' and config.host_os == 'Darwin': + lit_config.note('LSan sanitizer_common tests disabled') + config.unsupported = True From 05687dc86c533890b63de3232717a02132645a48 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Tue, 13 Dec 2016 17:29:36 -0800 Subject: [PATCH 486/582] Reapply: [profile] Add support for the exit-on-signal '%Nx' specifier The exit-on-signal specifier tells the runtime to handle a signal by writing out a profile and then exiting the program. It can be specified in the LLVM_PROFILE_FILE environment variable or by calling __llvm_profile_set_filename. Here is how you might force a program to exit when it's sent SIGTERM (sig 15): LLVM_PROFILE_FILE="default.profraw%15x" <program> When an instance of <program> is sent SIGTERM, it will write out a profile to "default.profraw" (the specifier is stripped out of the filename) and exit with return code 0. It's possible to use multiple exit-on-signal specifiers (up to 16). rdar://problem/24098975 apple-llvm-split-commit: e1adefa39464c5c039c13406e255db6932e8b923 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/profile/InstrProfilingFile.c | 68 +++++++++++++++++-- .../test/profile/instrprof-exit-on-signal.c | 16 +++++ 2 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 compiler-rt/test/profile/instrprof-exit-on-signal.c diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index e7996e2828946..a169a8b85467f 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -12,6 +12,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <signal.h> #ifdef _MSC_VER /* For _alloca. */ #include <malloc.h> @@ -62,6 +63,7 @@ static const char *getPNSStr(ProfileNameSpecifier PNS) { } #define MAX_PID_SIZE 16 +#define MAX_SIGNAL_HANDLERS 16 /* Data structure holding the result of parsed filename pattern. */ typedef struct lprofFilename { /* File name string possibly with %p or %h specifiers. */ @@ -83,11 +85,13 @@ typedef struct lprofFilename { * 2 profile data files. %1m is equivalent to %m. Also %m specifier * can only appear once at the end of the name pattern. */ unsigned MergePoolSize; + char ExitOnSignals[MAX_SIGNAL_HANDLERS]; + unsigned NumExitSignals; ProfileNameSpecifier PNS; } lprofFilename; COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0}, - {0}, 0, 0, 0, PNS_unknown}; + {0}, 0, 0, 0, {0}, 0, PNS_unknown}; static int ProfileMergeRequested = 0; static int isProfileMergeRequested() { return ProfileMergeRequested; } @@ -357,6 +361,26 @@ static void truncateCurrentFile(void) { fclose(File); } +static void exitSignalHandler(int sig) { + (void)sig; + exit(0); +} + +static void installExitSignalHandlers(void) { + unsigned I; + struct sigaction sigact; + int err; + for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = exitSignalHandler; + err = sigaction(lprofCurFilename.ExitOnSignals[I], &sigact, NULL); + if (err) + PROF_WARN( + "Unable to install an exit signal handler for %d (errno = %d).\n", + lprofCurFilename.ExitOnSignals[I], err); + } +} + static const char *DefaultProfileName = "default.profraw"; static void resetFilenameToDefault(void) { if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { @@ -367,14 +391,23 @@ static void resetFilenameToDefault(void) { lprofCurFilename.PNS = PNS_default; } +static int isNonZeroDigit(char C) { return C >= '1' && C <= '9'; } + static int containsMergeSpecifier(const char *FilenamePat, int I) { return (FilenamePat[I] == 'm' || - (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' && + (isNonZeroDigit(FilenamePat[I]) && /* If FilenamePat[I] is not '\0', the next byte is guaranteed * to be in-bound as the string is null terminated. */ FilenamePat[I + 1] == 'm')); } +static int containsExitOnSignalSpecifier(const char *FilenamePat, int I) { + if (!isNonZeroDigit(FilenamePat[I])) + return 0; + return (FilenamePat[I + 1] == 'x') || + (isNonZeroDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x'); +} + /* Parses the pattern string \p FilenamePat and stores the result to * lprofcurFilename structure. */ static int parseFilenamePattern(const char *FilenamePat, @@ -383,6 +416,7 @@ static int parseFilenamePattern(const char *FilenamePat, char *PidChars = &lprofCurFilename.PidChars[0]; char *Hostname = &lprofCurFilename.Hostname[0]; int MergingEnabled = 0; + char SignalNo; /* Clean up cached prefix and filename. */ if (lprofCurFilename.ProfilePathPrefix) @@ -435,6 +469,22 @@ static int parseFilenamePattern(const char *FilenamePat, lprofCurFilename.MergePoolSize = FilenamePat[I] - '0'; I++; /* advance to 'm' */ } + } else if (containsExitOnSignalSpecifier(FilenamePat, I)) { + if (lprofCurFilename.NumExitSignals == MAX_SIGNAL_HANDLERS) { + PROF_WARN("%%x specifier has been specified too many times in %s.\n", + FilenamePat); + return -1; + } + /* Grab the signal number. */ + SignalNo = FilenamePat[I] - '0'; + I++; /* advance to either another digit, or 'x' */ + if (FilenamePat[I] != 'x') { + SignalNo = (SignalNo * 10) + (FilenamePat[I] - '0'); + I++; /* advance to 'x' */ + } + lprofCurFilename.ExitOnSignals[lprofCurFilename.NumExitSignals] = + SignalNo; + ++lprofCurFilename.NumExitSignals; } } @@ -478,6 +528,7 @@ static void parseAndSetFilename(const char *FilenamePat, } truncateCurrentFile(); + installExitSignalHandlers(); } /* Return buffer length that is required to store the current profile @@ -486,11 +537,12 @@ static void parseAndSetFilename(const char *FilenamePat, #define SIGLEN 24 static int getCurFilenameLength() { int Len; + unsigned I; if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize)) + lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) return strlen(lprofCurFilename.FilenamePat); Len = strlen(lprofCurFilename.FilenamePat) + @@ -498,6 +550,11 @@ static int getCurFilenameLength() { lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); if (lprofCurFilename.MergePoolSize) Len += SIGLEN; + for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { + Len -= 3; /* Drop the '%', signal number, and the 'x'. */ + if (lprofCurFilename.ExitOnSignals[I] >= 10) + --Len; /* Drop the second digit of the signal number. */ + } return Len; } @@ -514,7 +571,7 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || - lprofCurFilename.MergePoolSize)) { + lprofCurFilename.MergePoolSize || lprofCurFilename.NumExitSignals)) { if (!ForceUseBuf) return lprofCurFilename.FilenamePat; @@ -547,6 +604,9 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) { J += S; if (FilenamePat[I] != 'm') I++; + } else if (containsExitOnSignalSpecifier(FilenamePat, I)) { + while (FilenamePat[I] != 'x') + ++I; } /* Drop any unknown substitutions. */ } else diff --git a/compiler-rt/test/profile/instrprof-exit-on-signal.c b/compiler-rt/test/profile/instrprof-exit-on-signal.c new file mode 100644 index 0000000000000..cf23b68003b80 --- /dev/null +++ b/compiler-rt/test/profile/instrprof-exit-on-signal.c @@ -0,0 +1,16 @@ +// RUN: %clang_profgen -o %t %s +// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t +// RUN: llvm-profdata show --all-functions %t.profraw | FileCheck %s +// CHECK: Total functions: 1 + +#include <signal.h> +#include <unistd.h> + +int main() { + kill(getpid(), SIGTERM); + + while (1) { + /* loop forever */ + } + return 1; +} From ace982bbdff1270649cc1582e0532a8d2f374bc9 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Tue, 13 Dec 2016 17:44:32 -0800 Subject: [PATCH 487/582] Reapply: [profile] Fix bug in exit-on-signal specifier parsing The parsing logic for the '%Nx' specifier had a bug which made it impossible to specify handling of two-digit signals where the second digit is 0. Fix it and add a test. rdar://problem/24098975 (cherry picked from commit 29107a0e2baacf42e4f4baa0a83d557a3fc7aa88) apple-llvm-split-commit: d1bad55cf3a8b2b533c0e7d41e436f307d66e56c apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/profile/InstrProfilingFile.c | 4 +++- .../test/profile/instrprof-exit-on-signal.c | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index a169a8b85467f..9a47293f68c3b 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -391,6 +391,8 @@ static void resetFilenameToDefault(void) { lprofCurFilename.PNS = PNS_default; } +static int isDigit(char C) { return C >= '0' && C <= '9'; } + static int isNonZeroDigit(char C) { return C >= '1' && C <= '9'; } static int containsMergeSpecifier(const char *FilenamePat, int I) { @@ -405,7 +407,7 @@ static int containsExitOnSignalSpecifier(const char *FilenamePat, int I) { if (!isNonZeroDigit(FilenamePat[I])) return 0; return (FilenamePat[I + 1] == 'x') || - (isNonZeroDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x'); + (isDigit(FilenamePat[I + 1]) && FilenamePat[I + 2] == 'x'); } /* Parses the pattern string \p FilenamePat and stores the result to diff --git a/compiler-rt/test/profile/instrprof-exit-on-signal.c b/compiler-rt/test/profile/instrprof-exit-on-signal.c index cf23b68003b80..efe1013f4ba28 100644 --- a/compiler-rt/test/profile/instrprof-exit-on-signal.c +++ b/compiler-rt/test/profile/instrprof-exit-on-signal.c @@ -1,13 +1,23 @@ // RUN: %clang_profgen -o %t %s -// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t -// RUN: llvm-profdata show --all-functions %t.profraw | FileCheck %s -// CHECK: Total functions: 1 +// +// Verify SIGTERM handling. +// RUN: %run LLVM_PROFILE_FILE="%15x%t.profraw" %t 15 +// RUN: llvm-profdata show %t.profraw | FileCheck %s +// +// Verify SIGUSR1 handling. +// RUN: %run LLVM_PROFILE_FILE="%30x%t.profraw" %t 30 +// RUN: llvm-profdata show %t.profraw | FileCheck %s +#include <stdlib.h> #include <signal.h> #include <unistd.h> -int main() { - kill(getpid(), SIGTERM); +// CHECK: Total functions: 1 +int main(int argc, char **argv) { + (void)argc; + + int sig = atoi(argv[1]); + kill(getpid(), sig); while (1) { /* loop forever */ From 5836e747498c4cc88132d60dcf63d7cb93fe2a19 Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Wed, 12 Dec 2018 13:16:30 -0800 Subject: [PATCH 488/582] Reland "Fix TSan check failure" This commit has been accidentally reverted by: 48588f96676d4242b7fb280e8b074f86db815c3b Reland is tracked by radar: rdar://56045793 Original commit message: rdar://problem/39400035: Fix TSan check failure which aborts the process This is an internal fix until a more principled solution is implemented upstream: https://reviews.llvm.org/D45646 Committing on behalf of Kuba Mracek apple-llvm-split-commit: 79c839a60ed1416fce7a2270d1ce32a2d7ce94a1 apple-llvm-split-dir: compiler-rt/ --- compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp index 0ac1ee99c4701..892fa8041f94d 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp @@ -277,10 +277,14 @@ void ThreadStart(ThreadState *thr, int tid, tid_t os_id, void ThreadFinish(ThreadState *thr) { ThreadCheckIgnore(thr); StatInc(thr, StatThreadFinish); - if (thr->stk_addr && thr->stk_size) + if (thr->stk_addr && thr->stk_size) { + MemoryResetRange(thr, /*pc=*/ 1, thr->stk_addr, thr->stk_size); DontNeedShadowFor(thr->stk_addr, thr->stk_size); - if (thr->tls_addr && thr->tls_size) + } + if (thr->tls_addr && thr->tls_size) { + MemoryResetRange(thr, /*pc=*/ 1, thr->tls_addr, thr->tls_size); DontNeedShadowFor(thr->tls_addr, thr->tls_size); + } thr->is_dead = true; ctx->thread_registry->FinishThread(thr->tid); } From d89cb48a7c7fece0a9aa9626e14d726038b3ee58 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 15 Oct 2019 13:54:32 -0700 Subject: [PATCH 489/582] Update readme to mention llvm-project-v4 apple-llvm-split-commit: cf3cf1f57c9403c7db5c74983ddf55fa118b5e2c apple-llvm-split-dir: - --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f59f15045ec74..4a7fc9481fc4a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Disclaimer The [llvm-monorepo-root](https://github.com/apple/llvm-monorepo-root), +[llvm-project-v4](https://github.com/apple/llvm-project-v4), +[llvm-project-v4-split](https://github.com/apple/llvm-project-v4-split), [llvm-project-v3](https://github.com/apple/llvm-project-v3), [llvm-project-v3-split](https://github.com/apple/llvm-project-v3-split), [llvm-project-v2](https://github.com/apple/llvm-project-v2), and From e631f0957b832d16a812f9b78fc3d6124f6dad8b Mon Sep 17 00:00:00 2001 From: Julian Lettner <jlettner@apple.com> Date: Tue, 15 Oct 2019 18:10:10 -0700 Subject: [PATCH 490/582] Fixup "[compiler-rt] Rename test file to ensure it gets executed" Fixup for e942dfe6c440d4253b8f76faa5d415312677031c. apple-llvm-split-commit: fdc0bbf369091e677c7ef2e96935ead0f78ef0c9 apple-llvm-split-dir: compiler-rt/ --- .../{ignore-noninstrumented.ccp => ignore-noninstrumented.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler-rt/test/tsan/{ignore-noninstrumented.ccp => ignore-noninstrumented.cpp} (100%) diff --git a/compiler-rt/test/tsan/ignore-noninstrumented.ccp b/compiler-rt/test/tsan/ignore-noninstrumented.cpp similarity index 100% rename from compiler-rt/test/tsan/ignore-noninstrumented.ccp rename to compiler-rt/test/tsan/ignore-noninstrumented.cpp From 8420e306852677dd3645a058a4c26f22c3cc9fff Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 15 Oct 2019 18:58:37 -0700 Subject: [PATCH 491/582] Create am-test-swift-master-next.json apple-llvm-split-commit: c2412dc57a589c91d30ff409759ae90e29e0e258 apple-llvm-split-dir: - --- apple-llvm-config/am/am-test-swift-master-next.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 apple-llvm-config/am/am-test-swift-master-next.json diff --git a/apple-llvm-config/am/am-test-swift-master-next.json b/apple-llvm-config/am/am-test-swift-master-next.json new file mode 100644 index 0000000000000..d92b59854c540 --- /dev/null +++ b/apple-llvm-config/am/am-test-swift-master-next.json @@ -0,0 +1,3 @@ +{ + "upstream": "am-test/apple/master" +} From dde37d52bc71d1c45c0bb652a0398d3237a5fb81 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Tue, 22 Jan 2019 09:57:14 -0800 Subject: [PATCH 492/582] [Modules] Fix implicit modules 'could not read module signature' problem This fixes a problem that happens while creating / using the same PCH file (with -fmodules enabled) from different compiler invocations concurrently. One of the invocations might fail with the error message: 'truncated or malformed object: could not read module signature' In addModule(), we populate a FileEntry for the PCM by calling lookupModuleFile(), where clang opens the file immediately to ensure there is no race between stat'ing and opening the file. At this point, we cache the size for SomeModule.pcm. Another process might come around at this point, re-write the PCM, changing its size. When we resume, next thing clang does is to call getBufferForFile to populate the memory buffer: -- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile, bool ShouldCloseOpenFile) { uint64_t FileSize = Entry->getSize(); // If there's a high enough chance that the file have changed since we // got its size, force a stat before opening it. if (isVolatile) FileSize = -1; StringRef Filename = Entry->getName(); // If the file is already open, use the open file descriptor. if (Entry->File) { auto Result = Entry->File->getBuffer(Filename, FileSize, /*RequiresNullTerminator=*/true, isVolatile); .... -- If we don't force the reload above, we might end up getting a buffer with a smaller size than we need, since the signature lives at the end of the PCM, and potentialy past the size of the buffer, we hit the malformed message rdar://problem/45337549 (cherry picked from commit a113643bc4fc0547e6b559c469bd3ddc86dae2c0) (cherry picked from commit 236fe10a35f721171909ca3a225bcdf40d26040e) Conflicts: lib/Serialization/ModuleManager.cpp apple-llvm-split-commit: 8e3c120528af69869469a938c53799e7688eea1c apple-llvm-split-dir: clang/ --- clang/lib/Serialization/ModuleManager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index 4b9f20fca4f80..0bd61617b0c82 100644 --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -184,8 +184,12 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, if (FileName == "-") { Buf = llvm::MemoryBuffer::getSTDIN(); } else { - // Get a buffer of the file and close the file descriptor when done. - Buf = FileMgr.getBufferForFile(NewModule->File, /*isVolatile=*/false); + // Get a buffer of the file and close the file descriptor when done. Use + // IsVolatile=true since PCMs with same signature can have different sizes + // due to different content in the unhashed control block (e.g. diagnostic + // options). Tha said, concurrent creation & access of the same PCM + // filename can lead to reading past the buffer size otherwise. + Buf = FileMgr.getBufferForFile(NewModule->File, /*IsVolatile=*/true); } if (!Buf) { From 3856e3d1c0be50916d7f1d9a8edb3bca4984a962 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 17 Oct 2019 17:18:14 -0700 Subject: [PATCH 493/582] apple/master: enable automerger from llvm.org/master --- .../am/{am-test-apple-master.json => apple-master.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apple-llvm-config/am/{am-test-apple-master.json => apple-master.json} (100%) diff --git a/apple-llvm-config/am/am-test-apple-master.json b/apple-llvm-config/am/apple-master.json similarity index 100% rename from apple-llvm-config/am/am-test-apple-master.json rename to apple-llvm-config/am/apple-master.json From ed35c18e6ca5ba7c6825af963d7872c4907cb243 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 17 Oct 2019 17:28:19 -0700 Subject: [PATCH 494/582] apple/master: fix the upstream branch name in the am config to remove the am-test prefix --- apple-llvm-config/am/apple-master.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple-llvm-config/am/apple-master.json b/apple-llvm-config/am/apple-master.json index e2d7498341261..427b8b4bbf218 100644 --- a/apple-llvm-config/am/apple-master.json +++ b/apple-llvm-config/am/apple-master.json @@ -1,3 +1,3 @@ { - "upstream": "am-test/llvm.org/master" + "upstream": "llvm.org/master" } From c9fbd40c721cf1e9a8e47abdf2bdf55ee28ba425 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 17 Oct 2019 18:17:42 -0700 Subject: [PATCH 495/582] Restore original llvm.org readme to remove the transition disclaimer --- README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/README.md b/README.md index 4a7fc9481fc4a..de0891a7044a9 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,3 @@ -# Disclaimer - -The [llvm-monorepo-root](https://github.com/apple/llvm-monorepo-root), -[llvm-project-v4](https://github.com/apple/llvm-project-v4), -[llvm-project-v4-split](https://github.com/apple/llvm-project-v4-split), -[llvm-project-v3](https://github.com/apple/llvm-project-v3), -[llvm-project-v3-split](https://github.com/apple/llvm-project-v3-split), -[llvm-project-v2](https://github.com/apple/llvm-project-v2), and -[llvm-project-v2-split](https://github.com/apple/llvm-project-v2-split) -repositories are -WIP repositories that are used for development and prototyping of an llvm-project monorepo -that will be used for Apple's compiler releases and for the open source Swift project. -Please see [this forum post](https://forums.swift.org/t/llvm-monorepo-transition-update/27079) -for more details. - -Original readme follows: - # The LLVM Compiler Infrastructure This directory and its subdirectories contain source code for LLVM, From b4f68846a225b880a9c8fe12641daf52bf1d92d3 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 17 Oct 2019 18:40:05 -0700 Subject: [PATCH 496/582] [apple-llvm-config] remove test am config --- apple-llvm-config/am/am-test-swift-master-next.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 apple-llvm-config/am/am-test-swift-master-next.json diff --git a/apple-llvm-config/am/am-test-swift-master-next.json b/apple-llvm-config/am/am-test-swift-master-next.json deleted file mode 100644 index d92b59854c540..0000000000000 --- a/apple-llvm-config/am/am-test-swift-master-next.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "upstream": "am-test/apple/master" -} From f5d89c97042014d8c15d78f84565041161053694 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 17 Oct 2019 23:25:17 -0700 Subject: [PATCH 497/582] [apple-llvm-config] remove the old push configs --- apple-llvm-config/push/swift-master-next.json | 8 -------- apple-llvm-config/push/swift-master.json | 8 -------- 2 files changed, 16 deletions(-) delete mode 100644 apple-llvm-config/push/swift-master-next.json delete mode 100644 apple-llvm-config/push/swift-master.json diff --git a/apple-llvm-config/push/swift-master-next.json b/apple-llvm-config/push/swift-master-next.json deleted file mode 100644 index 1e4a8ced439f1..0000000000000 --- a/apple-llvm-config/push/swift-master-next.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "branch_to_dest_branch_mapping": { - "swift/master-next:lldb": "upstream-with-swift" - }, - "repo_mapping": { - "lldb": "git@github.com:apple/swift-lldb.git" - } -} diff --git a/apple-llvm-config/push/swift-master.json b/apple-llvm-config/push/swift-master.json deleted file mode 100644 index b025c19d07299..0000000000000 --- a/apple-llvm-config/push/swift-master.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "branch_to_dest_branch_mapping": { - "swift/master:lldb": "stable" - }, - "repo_mapping": { - "lldb": "https://github.com/apple/swift-lldb.git" - } -} From 938e3002f3477600a0cb140768e24cfccb39c5d1 Mon Sep 17 00:00:00 2001 From: Vedant Kumar <vsk@apple.com> Date: Fri, 25 Oct 2019 14:48:20 -0700 Subject: [PATCH 498/582] Reapply: Port swift specific compiler-rt code to Windows Patch by Hugh Bellamy. https://github.com/apple/swift-compiler-rt/pull/5 --- compiler-rt/lib/profile/InstrProfilingFile.c | 11 ++--------- compiler-rt/lib/profile/InstrProfilingUtil.c | 19 +++++++++++++++++++ compiler-rt/lib/profile/InstrProfilingUtil.h | 2 ++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c index 55e11ef3e2db0..fa4328ec9da02 100644 --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -367,16 +367,9 @@ static void exitSignalHandler(int sig) { static void installExitSignalHandlers(void) { unsigned I; - struct sigaction sigact; - int err; for (I = 0; I < lprofCurFilename.NumExitSignals; ++I) { - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = exitSignalHandler; - err = sigaction(lprofCurFilename.ExitOnSignals[I], &sigact, NULL); - if (err) - PROF_WARN( - "Unable to install an exit signal handler for %d (errno = %d).\n", - lprofCurFilename.ExitOnSignals[I], err); + lprofInstallSignalHandler(lprofCurFilename.ExitOnSignals[I], + exitSignalHandler); } } diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.c b/compiler-rt/lib/profile/InstrProfilingUtil.c index 13301f341fc5a..9a8607c66c110 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.c +++ b/compiler-rt/lib/profile/InstrProfilingUtil.c @@ -24,6 +24,7 @@ #include <sys/utsname.h> #endif +#include <signal.h> #include <stdlib.h> #include <string.h> @@ -308,6 +309,24 @@ COMPILER_RT_VISIBILITY const char *lprofFindLastDirSeparator(const char *Path) { return Sep; } +COMPILER_RT_VISIBILITY void lprofInstallSignalHandler(int sig, + void (*handler)(int)) { +#ifdef _WIN32 + void (*err)(int) = signal(sig, handler); + if (err == SIG_ERR) + PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n", + sig, errno); +#else + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = handler; + int err = sigaction(sig, &sigact, NULL); + if (err) + PROF_WARN("Unable to install an exit signal handler for %d (errno = %d).\n", + sig, err); +#endif +} + COMPILER_RT_VISIBILITY int lprofSuspendSigKill() { #if defined(__linux__) int PDeachSig = 0; diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.h b/compiler-rt/lib/profile/InstrProfilingUtil.h index efba94ca76396..6f23a98695027 100644 --- a/compiler-rt/lib/profile/InstrProfilingUtil.h +++ b/compiler-rt/lib/profile/InstrProfilingUtil.h @@ -61,6 +61,8 @@ int lprofGetHostName(char *Name, int Len); unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV); void *lprofPtrFetchAdd(void **Mem, long ByteIncr); +void lprofInstallSignalHandler(int sig, void(*handler)(int)); + /* Temporarily suspend SIGKILL. Return value of 1 means a restore is needed. * Other return values mean no restore is needed. */ From 9e702b117ff6d8e82b01a0c6645e99b00df7a320 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Tue, 25 Jun 2019 18:38:20 -0700 Subject: [PATCH 499/582] [AArch64][v8.3a] Add LDRA '[xN]!' alias. The instruction definition has been retroactively expanded to allow for an alias for '[xN, 0]!' as '[xN]!'. That wouldn't make sense on LDR, but does for LDRA. --- .../lib/Target/AArch64/AArch64InstrFormats.td | 3 +++ .../test/MC/AArch64/armv8.3a-signed-pointer.s | 8 +++++++ .../AArch64/armv8.3a-signed-pointer.txt | 22 ++++++++++++------- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td index f555e41233072..24aa54bdd81e5 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -1463,6 +1463,9 @@ multiclass AuthLoad<bit M, string asm, Operand opr> { def : InstAlias<asm # "\t$Rt, [$Rn]", (!cast<Instruction>(NAME # "indexed") GPR64:$Rt, GPR64sp:$Rn, 0)>; + + def : InstAlias<asm # "\t$Rt, [$wback]!", + (!cast<Instruction>(NAME # "writeback") GPR64sp:$wback, GPR64:$Rt, 0)>; } //--- diff --git a/llvm/test/MC/AArch64/armv8.3a-signed-pointer.s b/llvm/test/MC/AArch64/armv8.3a-signed-pointer.s index fe34002680e3c..2ca15fceccc8f 100644 --- a/llvm/test/MC/AArch64/armv8.3a-signed-pointer.s +++ b/llvm/test/MC/AArch64/armv8.3a-signed-pointer.s @@ -306,3 +306,11 @@ // CHECK-NEXT: ldrab x0, [x1] // encoding: [0x20,0x04,0xa0,0xf8] // CHECK-REQ: error: instruction requires: pa // CHECK-REQ-NEXT: ldrab x0, [x1] + ldraa x0, [x1]! +// CHECK-NEXT: ldraa x0, [x1]! // encoding: [0x20,0x0c,0x20,0xf8] +// CHECK-REQ: error: instruction requires: pa +// CHECK-REQ-NEXT: ldraa x0, [x1]! + ldrab x0, [x1]! +// CHECK-NEXT: ldrab x0, [x1]! // encoding: [0x20,0x0c,0xa0,0xf8] +// CHECK-REQ: error: instruction requires: pa +// CHECK-REQ-NEXT: ldrab x0, [x1]! diff --git a/llvm/test/MC/Disassembler/AArch64/armv8.3a-signed-pointer.txt b/llvm/test/MC/Disassembler/AArch64/armv8.3a-signed-pointer.txt index 18b376631c4b2..d11056044fa48 100644 --- a/llvm/test/MC/Disassembler/AArch64/armv8.3a-signed-pointer.txt +++ b/llvm/test/MC/Disassembler/AArch64/armv8.3a-signed-pointer.txt @@ -83,14 +83,6 @@ # CHECK: retab # CHECK: eretaa # CHECK: eretab -# CHECK: ldraa x0, [x1, #4088] -# CHECK: ldraa x0, [x1, #-4096] -# CHECK: ldrab x0, [x1, #4088] -# CHECK: ldrab x0, [x1, #-4096] -# CHECK: ldraa x0, [x1, #4088]! -# CHECK: ldraa x0, [x1, #-4096]! -# CHECK: ldrab x0, [x1, #4088]! -# CHECK: ldrab x0, [x1, #-4096]! [0x1f,0x08,0x1f,0xd6] [0x1f,0x0c,0x1f,0xd6] [0x1f,0x08,0x3f,0xd6] @@ -99,6 +91,15 @@ [0xff,0x0f,0x5f,0xd6] [0xff,0x0b,0x9f,0xd6] [0xff,0x0f,0x9f,0xd6] + +# CHECK: ldraa x0, [x1, #4088] +# CHECK: ldraa x0, [x1, #-4096] +# CHECK: ldrab x0, [x1, #4088] +# CHECK: ldrab x0, [x1, #-4096] +# CHECK: ldraa x0, [x1, #4088]! +# CHECK: ldraa x0, [x1, #-4096]! +# CHECK: ldrab x0, [x1, #4088]! +# CHECK: ldrab x0, [x1, #-4096]! [0x20,0xf4,0x3f,0xf8] [0x20,0x04,0x60,0xf8] [0x20,0xf4,0xbf,0xf8] @@ -112,3 +113,8 @@ # CHECK: ldrab x0, [x1] [0x20,0x04,0x20,0xf8] [0x20,0x04,0xa0,0xf8] + +# CHECK: ldraa x0, [x1]! +# CHECK: ldrab x0, [x1]! +[0x20,0x0c,0x20,0xf8] +[0x20,0x0c,0xa0,0xf8] From 5cb5475e1a06b41aadb0be5419dc2f119658b2af Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Tue, 25 Jun 2019 18:44:39 -0700 Subject: [PATCH 500/582] [AArch64][v8.3a] Add missing imp-defs on RETA*. --- llvm/lib/Target/AArch64/AArch64InstrFormats.td | 1 + llvm/test/CodeGen/AArch64/branch-target-enforcment.mir | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td index 24aa54bdd81e5..36f0f61004abb 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -1427,6 +1427,7 @@ class AuthOneOperand<bits<3> opc, bits<1> M, string asm> let Inst{9-5} = Rn; } +let Uses = [LR,SP] in class AuthReturn<bits<3> op, bits<1> M, string asm> : AuthBase<M, (outs), (ins), asm, "", []> { let Inst{24} = 0; diff --git a/llvm/test/CodeGen/AArch64/branch-target-enforcment.mir b/llvm/test/CodeGen/AArch64/branch-target-enforcment.mir index 5db503ddcee95..e0eff1e2de8b2 100644 --- a/llvm/test/CodeGen/AArch64/branch-target-enforcment.mir +++ b/llvm/test/CodeGen/AArch64/branch-target-enforcment.mir @@ -142,7 +142,7 @@ body: | INLINEASM &"", 1, 12, implicit-def dead early-clobber $lr $w0 = ORRWrs $wzr, $wzr, 0 early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load 8 from %stack.0) - RETAA implicit killed $w0 + RETAA implicit $sp, implicit $lr, implicit killed $w0 --- # Function starts with PACIBSP, which implicitly acts as BTI JC, so no change @@ -166,7 +166,7 @@ body: | INLINEASM &"", 1, 12, implicit-def dead early-clobber $lr $w0 = ORRWrs $wzr, $wzr, 0 early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load 8 from %stack.0) - RETAB implicit killed $w0 + RETAB implicit $sp, implicit $lr, implicit killed $w0 --- # Function contains a jump table, so every target of the jump table must start From 11d7d3398f42d991b304837b9e1c0262d23a716f Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 21 Oct 2019 12:35:54 -0700 Subject: [PATCH 501/582] [AArch64] Add 'vortex' CPU. This is an Apple core that's similar to Cyclone, with a couple major ISA changes: it supports v8.3a, and fullfp16. --- clang/lib/Driver/ToolChains/Arch/AArch64.cpp | 6 +++++- clang/test/Driver/aarch64-cpus.c | 7 +++++++ .../llvm/Support/AArch64TargetParser.def | 2 ++ llvm/lib/Target/AArch64/AArch64.td | 20 ++++++++++++++++++- llvm/lib/Target/AArch64/AArch64Subtarget.cpp | 1 + llvm/lib/Target/AArch64/AArch64Subtarget.h | 3 ++- llvm/unittests/Support/TargetParserTest.cpp | 7 ++++++- 7 files changed, 42 insertions(+), 4 deletions(-) diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp index 3a5fe6ddeaed5..0ee2819be1d9c 100644 --- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp +++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp @@ -139,10 +139,14 @@ getAArch64MicroArchFeaturesFromMtune(const Driver &D, StringRef Mtune, // Handle CPU name is 'native'. if (MtuneLowerCase == "native") MtuneLowerCase = llvm::sys::getHostCPUName(); - if (MtuneLowerCase == "cyclone") { + + // 'cyclone' and later have zero-cycle register moves and zeroing. + if (MtuneLowerCase == "cyclone" || + MtuneLowerCase == "vortex") { Features.push_back("+zcm"); Features.push_back("+zcz"); } + return true; } diff --git a/clang/test/Driver/aarch64-cpus.c b/clang/test/Driver/aarch64-cpus.c index 32920ea2edd4a..18602f4b88fb2 100644 --- a/clang/test/Driver/aarch64-cpus.c +++ b/clang/test/Driver/aarch64-cpus.c @@ -282,6 +282,13 @@ // ARM64-THUNDERX2T99-TUNE: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "generic" // ARM64-THUNDERX2T99-TUNE-NOT: +v8.1a +// RUN: %clang -target arm64-apple-darwin -arch arm64 -mcpu=vortex -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-VORTEX %s +// ARM64-VORTEX: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "vortex" "-target-feature" "+v8.3a" "-target-feature" "+fp-armv8" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" "-target-feature" "+fullfp16" "-target-feature" "+ras" "-target-feature" "+lse" "-target-feature" "+rdm" "-target-feature" "+rcpc" "-target-feature" "+zcm" "-target-feature" "+zcz" "-target-feature" "+sha2" "-target-feature" "+aes" + +// Check that we also support -march, which overrides -mcpu (same for e.g. -march=v8.1a -mcpu=cyclone not enabling crc). +// RUN: %clang -target arm64-apple-darwin -arch arm64 -march=armv8.3a -mcpu=vortex -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-VORTEX-V83 %s +// ARM64-VORTEX-V83: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "vortex" "-target-feature" "+neon" "-target-feature" "+v8.3a" "-target-feature" "+zcm" "-target-feature" "+zcz" + // RUN: %clang -target aarch64_be -### -c %s 2>&1 | FileCheck -check-prefix=GENERIC-BE %s // RUN: %clang -target aarch64 -mbig-endian -### -c %s 2>&1 | FileCheck -check-prefix=GENERIC-BE %s // RUN: %clang -target aarch64_be -mbig-endian -### -c %s 2>&1 | FileCheck -check-prefix=GENERIC-BE %s diff --git a/llvm/include/llvm/Support/AArch64TargetParser.def b/llvm/include/llvm/Support/AArch64TargetParser.def index 15737265dfc3b..1f274929cb802 100644 --- a/llvm/include/llvm/Support/AArch64TargetParser.def +++ b/llvm/include/llvm/Support/AArch64TargetParser.def @@ -120,6 +120,8 @@ AARCH64_CPU_NAME("neoverse-n1", ARMV8_2A, FK_CRYPTO_NEON_FP_ARMV8, false, AArch64::AEK_SSBS)) AARCH64_CPU_NAME("cyclone", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_NONE)) +AARCH64_CPU_NAME("vortex", ARMV8_3A, FK_CRYPTO_NEON_FP_ARMV8, false, + (AArch64::AEK_FP16)) AARCH64_CPU_NAME("exynos-m1", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) AARCH64_CPU_NAME("exynos-m2", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, diff --git a/llvm/lib/Target/AArch64/AArch64.td b/llvm/lib/Target/AArch64/AArch64.td index 5b4c9e2149dae..9e7b3a07ff1f3 100644 --- a/llvm/lib/Target/AArch64/AArch64.td +++ b/llvm/lib/Target/AArch64/AArch64.td @@ -582,6 +582,23 @@ def ProcCyclone : SubtargetFeature<"cyclone", "ARMProcFamily", "Cyclone", FeatureZCZeroingFPWorkaround ]>; +def ProcVortex : SubtargetFeature<"vortex", "ARMProcFamily", "Vortex", + "Vortex", [ + FeatureAlternateSExtLoadCVTF32Pattern, + FeatureArithmeticBccFusion, + FeatureArithmeticCbzFusion, + FeatureCrypto, + FeatureDisableLatencySchedHeuristic, + FeatureFullFP16, + FeatureFuseAES, + FeatureFuseCryptoEOR, + FeatureNEON, + FeaturePerfMon, + FeatureZCRegMove, + FeatureZCZeroing, + HasV8_3aOps + ]>; + def ProcExynosM1 : SubtargetFeature<"exynosm1", "ARMProcFamily", "ExynosM1", "Samsung Exynos-M1 processors", [FeatureSlowPaired128, @@ -816,6 +833,7 @@ def : ProcessorModel<"cortex-a76ae", CortexA57Model, [ProcA76]>; def : ProcessorModel<"neoverse-e1", CortexA53Model, [ProcNeoverseE1]>; def : ProcessorModel<"neoverse-n1", CortexA57Model, [ProcNeoverseN1]>; def : ProcessorModel<"cyclone", CycloneModel, [ProcCyclone]>; +def : ProcessorModel<"vortex", CycloneModel, [ProcVortex]>; def : ProcessorModel<"exynos-m1", ExynosM1Model, [ProcExynosM1]>; def : ProcessorModel<"exynos-m2", ExynosM1Model, [ProcExynosM2]>; def : ProcessorModel<"exynos-m3", ExynosM3Model, [ProcExynosM3]>; @@ -835,7 +853,7 @@ def : ProcessorModel<"thunderx2t99", ThunderX2T99Model, [ProcThunderX2T99]>; def : ProcessorModel<"tsv110", CortexA57Model, [ProcTSV110]>; // Alias for the latest Apple processor model supported by LLVM. -def : ProcessorModel<"apple-latest", CycloneModel, [ProcCyclone]>; +def : ProcessorModel<"apple-latest", CycloneModel, [ProcVortex]>; //===----------------------------------------------------------------------===// // Assembly parser diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp index 558bea368eff2..1ee6b7d8a158a 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp @@ -89,6 +89,7 @@ void AArch64Subtarget::initializeProperties() { PrefFunctionLogAlignment = 4; break; case Cyclone: + case Vortex: CacheLineSize = 64; PrefetchDistance = 280; MinPrefetchStride = 2048; diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h index f3212fae8e5e1..01a19436afc40 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.h +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -60,7 +60,8 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo { ThunderXT81, ThunderXT83, ThunderXT88, - TSV110 + TSV110, + Vortex }; protected: diff --git a/llvm/unittests/Support/TargetParserTest.cpp b/llvm/unittests/Support/TargetParserTest.cpp index 64052519226b3..4cd7508d10e5c 100644 --- a/llvm/unittests/Support/TargetParserTest.cpp +++ b/llvm/unittests/Support/TargetParserTest.cpp @@ -829,6 +829,11 @@ TEST(TargetParserTest, testAArch64CPU) { EXPECT_TRUE(testAArch64CPU( "cyclone", "armv8-a", "crypto-neon-fp-armv8", AArch64::AEK_CRYPTO | AArch64::AEK_FP | AArch64::AEK_SIMD, "8-A")); + EXPECT_TRUE(testAArch64CPU( + "vortex", "armv8.3-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | + AArch64::AEK_SIMD | AArch64::AEK_LSE | AArch64::AEK_RAS | + AArch64::AEK_RDM | AArch64::AEK_RCPC | AArch64::AEK_FP16, "8.3-A")); EXPECT_TRUE(testAArch64CPU( "exynos-m1", "armv8-a", "crypto-neon-fp-armv8", AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | @@ -908,7 +913,7 @@ TEST(TargetParserTest, testAArch64CPU) { "8.2-A")); } -static constexpr unsigned NumAArch64CPUArchs = 28; +static constexpr unsigned NumAArch64CPUArchs = 29; TEST(TargetParserTest, testAArch64CPUArchList) { SmallVector<StringRef, NumAArch64CPUArchs> List; From 5ed200f04dcba955cbe54375565a7524d5c38fd5 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 21 Oct 2019 12:37:06 -0700 Subject: [PATCH 502/582] [AArch64] Add 'lightning' CPU. This is an Apple core that's similar to Vortex, with one major ISA changes: it supports v8.4a (and, with that, extends the FullFP16 support to add v8.4a FP16FML). --- clang/lib/Driver/ToolChains/Arch/AArch64.cpp | 3 ++- clang/test/Driver/aarch64-cpus.c | 3 +++ .../llvm/Support/AArch64TargetParser.def | 2 ++ llvm/lib/Target/AArch64/AArch64.td | 23 ++++++++++++++++++- llvm/lib/Target/AArch64/AArch64Subtarget.cpp | 1 + llvm/lib/Target/AArch64/AArch64Subtarget.h | 1 + llvm/unittests/Support/TargetParserTest.cpp | 8 ++++++- 7 files changed, 38 insertions(+), 3 deletions(-) diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp index 0ee2819be1d9c..ff9239185c029 100644 --- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp +++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp @@ -142,7 +142,8 @@ getAArch64MicroArchFeaturesFromMtune(const Driver &D, StringRef Mtune, // 'cyclone' and later have zero-cycle register moves and zeroing. if (MtuneLowerCase == "cyclone" || - MtuneLowerCase == "vortex") { + MtuneLowerCase == "vortex" || + MtuneLowerCase == "lightning") { Features.push_back("+zcm"); Features.push_back("+zcz"); } diff --git a/clang/test/Driver/aarch64-cpus.c b/clang/test/Driver/aarch64-cpus.c index 18602f4b88fb2..74c5ee3395c42 100644 --- a/clang/test/Driver/aarch64-cpus.c +++ b/clang/test/Driver/aarch64-cpus.c @@ -289,6 +289,9 @@ // RUN: %clang -target arm64-apple-darwin -arch arm64 -march=armv8.3a -mcpu=vortex -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-VORTEX-V83 %s // ARM64-VORTEX-V83: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "vortex" "-target-feature" "+neon" "-target-feature" "+v8.3a" "-target-feature" "+zcm" "-target-feature" "+zcz" +// RUN: %clang -target arm64-apple-darwin -arch arm64 -mcpu=lightning -### -c %s 2>&1 | FileCheck -check-prefix=ARM64-LIGHTNING %s +// ARM64-LIGHTNING: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "lightning" "-target-feature" "+v8.4a" "-target-feature" "+fp-armv8" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" "-target-feature" "+dotprod" "-target-feature" "+fullfp16" "-target-feature" "+ras" "-target-feature" "+lse" "-target-feature" "+rdm" "-target-feature" "+rcpc" "-target-feature" "+zcm" "-target-feature" "+zcz" "-target-feature" "+fp16fml" "-target-feature" "+sm4" "-target-feature" "+sha3" "-target-feature" "+sha2" "-target-feature" "+aes" + // RUN: %clang -target aarch64_be -### -c %s 2>&1 | FileCheck -check-prefix=GENERIC-BE %s // RUN: %clang -target aarch64 -mbig-endian -### -c %s 2>&1 | FileCheck -check-prefix=GENERIC-BE %s // RUN: %clang -target aarch64_be -mbig-endian -### -c %s 2>&1 | FileCheck -check-prefix=GENERIC-BE %s diff --git a/llvm/include/llvm/Support/AArch64TargetParser.def b/llvm/include/llvm/Support/AArch64TargetParser.def index 1f274929cb802..74a6a0bbbdedc 100644 --- a/llvm/include/llvm/Support/AArch64TargetParser.def +++ b/llvm/include/llvm/Support/AArch64TargetParser.def @@ -122,6 +122,8 @@ AARCH64_CPU_NAME("cyclone", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_NONE)) AARCH64_CPU_NAME("vortex", ARMV8_3A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_FP16)) +AARCH64_CPU_NAME("lightning", ARMV8_4A, FK_CRYPTO_NEON_FP_ARMV8, false, + (AArch64::AEK_FP16 | AArch64::AEK_FP16FML)) AARCH64_CPU_NAME("exynos-m1", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, (AArch64::AEK_CRC)) AARCH64_CPU_NAME("exynos-m2", ARMV8A, FK_CRYPTO_NEON_FP_ARMV8, false, diff --git a/llvm/lib/Target/AArch64/AArch64.td b/llvm/lib/Target/AArch64/AArch64.td index 9e7b3a07ff1f3..ac618031775d0 100644 --- a/llvm/lib/Target/AArch64/AArch64.td +++ b/llvm/lib/Target/AArch64/AArch64.td @@ -599,6 +599,26 @@ def ProcVortex : SubtargetFeature<"vortex", "ARMProcFamily", "Vortex", HasV8_3aOps ]>; +def ProcLightning : SubtargetFeature<"lightning", "ARMProcFamily", "Lightning", + "Lightning", [ + FeatureAlternateSExtLoadCVTF32Pattern, + FeatureArithmeticBccFusion, + FeatureArithmeticCbzFusion, + FeatureCrypto, + FeatureDisableLatencySchedHeuristic, + FeatureFP16FML, + FeatureFullFP16, + FeatureFuseAES, + FeatureFuseCryptoEOR, + FeatureNEON, + FeaturePerfMon, + FeatureSHA3, + FeatureSM4, + FeatureZCRegMove, + FeatureZCZeroing, + HasV8_4aOps + ]>; + def ProcExynosM1 : SubtargetFeature<"exynosm1", "ARMProcFamily", "ExynosM1", "Samsung Exynos-M1 processors", [FeatureSlowPaired128, @@ -834,6 +854,7 @@ def : ProcessorModel<"neoverse-e1", CortexA53Model, [ProcNeoverseE1]>; def : ProcessorModel<"neoverse-n1", CortexA57Model, [ProcNeoverseN1]>; def : ProcessorModel<"cyclone", CycloneModel, [ProcCyclone]>; def : ProcessorModel<"vortex", CycloneModel, [ProcVortex]>; +def : ProcessorModel<"lightning", CycloneModel, [ProcLightning]>; def : ProcessorModel<"exynos-m1", ExynosM1Model, [ProcExynosM1]>; def : ProcessorModel<"exynos-m2", ExynosM1Model, [ProcExynosM2]>; def : ProcessorModel<"exynos-m3", ExynosM3Model, [ProcExynosM3]>; @@ -853,7 +874,7 @@ def : ProcessorModel<"thunderx2t99", ThunderX2T99Model, [ProcThunderX2T99]>; def : ProcessorModel<"tsv110", CortexA57Model, [ProcTSV110]>; // Alias for the latest Apple processor model supported by LLVM. -def : ProcessorModel<"apple-latest", CycloneModel, [ProcVortex]>; +def : ProcessorModel<"apple-latest", CycloneModel, [ProcLightning]>; //===----------------------------------------------------------------------===// // Assembly parser diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp index 1ee6b7d8a158a..3bb7208ee06d9 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp @@ -90,6 +90,7 @@ void AArch64Subtarget::initializeProperties() { break; case Cyclone: case Vortex: + case Lightning: CacheLineSize = 64; PrefetchDistance = 280; MinPrefetchStride = 2048; diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h index 01a19436afc40..bbd5adfae09aa 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.h +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -52,6 +52,7 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo { ExynosM3, Falkor, Kryo, + Lightning, NeoverseE1, NeoverseN1, Saphira, diff --git a/llvm/unittests/Support/TargetParserTest.cpp b/llvm/unittests/Support/TargetParserTest.cpp index 4cd7508d10e5c..b93e454ad6ef1 100644 --- a/llvm/unittests/Support/TargetParserTest.cpp +++ b/llvm/unittests/Support/TargetParserTest.cpp @@ -834,6 +834,12 @@ TEST(TargetParserTest, testAArch64CPU) { AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | AArch64::AEK_SIMD | AArch64::AEK_LSE | AArch64::AEK_RAS | AArch64::AEK_RDM | AArch64::AEK_RCPC | AArch64::AEK_FP16, "8.3-A")); + EXPECT_TRUE(testAArch64CPU( + "lightning", "armv8.4-a", "crypto-neon-fp-armv8", + AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | + AArch64::AEK_SIMD | AArch64::AEK_LSE | AArch64::AEK_RAS | + AArch64::AEK_RDM | AArch64::AEK_RCPC | AArch64::AEK_DOTPROD | + AArch64::AEK_FP16 | AArch64::AEK_FP16FML, "8.4-A")); EXPECT_TRUE(testAArch64CPU( "exynos-m1", "armv8-a", "crypto-neon-fp-armv8", AArch64::AEK_CRC | AArch64::AEK_CRYPTO | AArch64::AEK_FP | @@ -913,7 +919,7 @@ TEST(TargetParserTest, testAArch64CPU) { "8.2-A")); } -static constexpr unsigned NumAArch64CPUArchs = 29; +static constexpr unsigned NumAArch64CPUArchs = 30; TEST(TargetParserTest, testAArch64CPUArchList) { SmallVector<StringRef, NumAArch64CPUArchs> List; From 7bda9ff4778d647ae49ad19c80f5fb8f5ede0b28 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 21 Oct 2019 12:45:35 -0700 Subject: [PATCH 503/582] [Triple] Add 'arm64e', an AArch64 sub-architecture. --- clang/lib/Basic/Targets/AArch64.cpp | 3 +++ clang/lib/Driver/ToolChain.cpp | 4 ++++ clang/lib/Driver/ToolChains/Arch/AArch64.cpp | 13 ++++++++++++- clang/lib/Driver/ToolChains/Darwin.cpp | 16 ++++++++++++---- clang/test/Driver/aarch64-cpus.c | 3 +++ clang/test/Driver/arch-arm64e.c | 8 ++++++++ clang/test/Preprocessor/arm64e.c | 5 +++++ llvm/include/llvm/ADT/Triple.h | 2 ++ llvm/lib/LTO/LTOCodeGenerator.cpp | 2 ++ llvm/lib/LTO/LTOModule.cpp | 2 ++ llvm/lib/Support/ARMTargetParser.cpp | 2 ++ llvm/lib/Support/Triple.cpp | 4 ++++ llvm/lib/Target/AArch64/AArch64TargetMachine.cpp | 10 +++++++++- .../AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp | 6 +++++- llvm/test/MC/AArch64/arm64e.s | 9 +++++++++ llvm/unittests/ADT/TripleTest.cpp | 5 +++++ 16 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 clang/test/Driver/arch-arm64e.c create mode 100644 clang/test/Preprocessor/arm64e.c create mode 100644 llvm/test/MC/AArch64/arm64e.s diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index c86cc63e3d84c..bc36e0d46f867 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -650,6 +650,9 @@ void DarwinAArch64TargetInfo::getOSDefines(const LangOptions &Opts, Builder.defineMacro("__arm64", "1"); Builder.defineMacro("__arm64__", "1"); + if (Triple.getArchName() == "arm64e") + Builder.defineMacro("__arm64e__", "1"); + getDarwinDefines(Builder, Opts, Triple, PlatformName, PlatformMinVersion); } diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 357a5106ab393..77387e53356cf 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -614,6 +614,10 @@ std::string ToolChain::ComputeLLVMTriple(const ArgList &Args, if (!Triple.isOSBinFormatMachO()) return getTripleString(); + StringRef Arch = Triple.getArchName(); + if (Arch == "arm64e") + return Triple.getTriple(); + // FIXME: older versions of ld64 expect the "arm64" component in the actual // triple string and query it to determine whether an LTO file can be // handled. Remove this when we don't care any more. diff --git a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp index ff9239185c029..4f926426bf51e 100644 --- a/clang/lib/Driver/ToolChains/Arch/AArch64.cpp +++ b/clang/lib/Driver/ToolChains/Arch/AArch64.cpp @@ -40,7 +40,18 @@ std::string aarch64::getAArch64TargetCPU(const ArgList &Args, // Handle CPU name is 'native'. if (CPU == "native") return llvm::sys::getHostCPUName(); - else if (CPU.size()) + + // arm64e requires v8.3a and only runs on vortex and later CPUs. + if (Triple.getArchName() == "arm64e") { + // Honor -mcpu as long it doesn't specify an older CPU than "vortex". + if (CPU.size() && (CPU != "cyclone")) + return CPU; + + // Otherwise default to "vortex". + return "vortex"; + } + + if (CPU.size()) return CPU; // Make sure we pick "cyclone" if -arch is used or when targetting a Darwin diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index f31633779bed9..339dadded89e1 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -56,7 +56,7 @@ llvm::Triple::ArchType darwin::getArchTypeForMachOArchName(StringRef Str) { .Cases("arm", "armv4t", "armv5", "armv6", "armv6m", llvm::Triple::arm) .Cases("armv7", "armv7em", "armv7k", "armv7m", llvm::Triple::arm) .Cases("armv7s", "xscale", llvm::Triple::arm) - .Case("arm64", llvm::Triple::aarch64) + .Cases("arm64", "arm64e", llvm::Triple::aarch64) .Case("r600", llvm::Triple::r600) .Case("amdgcn", llvm::Triple::amdgcn) .Case("nvptx", llvm::Triple::nvptx) @@ -71,8 +71,13 @@ void darwin::setTripleTypeForMachOArchName(llvm::Triple &T, StringRef Str) { llvm::ARM::ArchKind ArchKind = llvm::ARM::parseArch(Str); T.setArch(Arch); - if (Str == "x86_64h") + // Preserve the original string for those -arch options that aren't + // reflected in ArchKind but still affect code generation. It's not + // clear why these aren't just reflected in ArchKind, though. + if (Str == "x86_64h" || Str == "arm64e") T.setArchName(Str); + + // These arches aren't really Darwin even if we're using a Darwin toolchain. else if (ArchKind == llvm::ARM::ArchKind::ARMV6M || ArchKind == llvm::ARM::ArchKind::ARMV7M || ArchKind == llvm::ARM::ArchKind::ARMV7EM) { @@ -835,8 +840,11 @@ StringRef MachO::getMachOArchName(const ArgList &Args) const { default: return getDefaultUniversalArchName(); - case llvm::Triple::aarch64: + case llvm::Triple::aarch64: { + if (getTriple().getArchName() == "arm64e") + return "arm64e"; return "arm64"; + } case llvm::Triple::thumb: case llvm::Triple::arm: @@ -1609,7 +1617,7 @@ inferDeploymentTargetFromArch(DerivedArgList &Args, const Darwin &Toolchain, StringRef MachOArchName = Toolchain.getMachOArchName(Args); if (MachOArchName == "armv7" || MachOArchName == "armv7s" || - MachOArchName == "arm64") + MachOArchName == "arm64" || MachOArchName == "arm64e") OSTy = llvm::Triple::IOS; else if (MachOArchName == "armv7k") OSTy = llvm::Triple::WatchOS; diff --git a/clang/test/Driver/aarch64-cpus.c b/clang/test/Driver/aarch64-cpus.c index 74c5ee3395c42..93250e169849f 100644 --- a/clang/test/Driver/aarch64-cpus.c +++ b/clang/test/Driver/aarch64-cpus.c @@ -26,6 +26,9 @@ // ARM64-DARWIN: "-cc1"{{.*}} "-triple" "arm64{{.*}}" "-target-cpu" "cyclone" // ARM64-DARWIN-SAME: "-target-feature" "+aes" +// RUN: %clang -target x86_64-apple-darwin -arch arm64e -### -c %s 2>&1 | FileCheck -check-prefix=ARM64E-DARWIN %s +// ARM64E-DARWIN: "-cc1"{{.*}} "-triple" "arm64e{{.*}}" "-target-cpu" "vortex" + // RUN: %clang -target aarch64 -mcpu=cortex-a35 -### -c %s 2>&1 | FileCheck -check-prefix=CA35 %s // RUN: %clang -target aarch64 -mlittle-endian -mcpu=cortex-a35 -### -c %s 2>&1 | FileCheck -check-prefix=CA35 %s // RUN: %clang -target aarch64_be -mlittle-endian -mcpu=cortex-a35 -### -c %s 2>&1 | FileCheck -check-prefix=CA35 %s diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c new file mode 100644 index 0000000000000..bf4b34fba51e5 --- /dev/null +++ b/clang/test/Driver/arch-arm64e.c @@ -0,0 +1,8 @@ +// Check the CPU defaults and overrides. + +// RUN: %clang -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -arch arm64e -mcpu=vortex -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -arch arm64e -mcpu=cyclone -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -arch arm64e -mcpu=lightning -c %s -### 2>&1 | FileCheck %s --check-prefix LIGHTNING +// VORTEX: "-cc1"{{.*}} "-target-cpu" "vortex" +// LIGHTNING: "-cc1"{{.*}} "-target-cpu" "lightning" diff --git a/clang/test/Preprocessor/arm64e.c b/clang/test/Preprocessor/arm64e.c new file mode 100644 index 0000000000000..26653ace5852b --- /dev/null +++ b/clang/test/Preprocessor/arm64e.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -E -dM -ffreestanding -triple=arm64e-apple-ios < /dev/null | FileCheck %s + +// CHECK: #define __ARM64_ARCH_8__ 1 +// CHECK: #define __arm64__ 1 +// CHECK: #define __arm64e__ 1 diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index edeb31efab801..b66d663ae7c14 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -124,6 +124,8 @@ class Triple { ARMSubArch_v5te, ARMSubArch_v4t, + AArch64SubArch_E, + KalimbaSubArch_v3, KalimbaSubArch_v4, KalimbaSubArch_v5, diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp index 8821928928672..27f4ad4a06a38 100644 --- a/llvm/lib/LTO/LTOCodeGenerator.cpp +++ b/llvm/lib/LTO/LTOCodeGenerator.cpp @@ -365,6 +365,8 @@ bool LTOCodeGenerator::determineTarget() { MCpu = "core2"; else if (Triple.getArch() == llvm::Triple::x86) MCpu = "yonah"; + else if (Triple.getArchName() == "arm64e") + MCpu = "vortex"; else if (Triple.getArch() == llvm::Triple::aarch64 || Triple.getArch() == llvm::Triple::aarch64_32) MCpu = "cyclone"; diff --git a/llvm/lib/LTO/LTOModule.cpp b/llvm/lib/LTO/LTOModule.cpp index 587b332e70649..7689269c3f097 100644 --- a/llvm/lib/LTO/LTOModule.cpp +++ b/llvm/lib/LTO/LTOModule.cpp @@ -220,6 +220,8 @@ LTOModule::makeLTOModule(MemoryBufferRef Buffer, const TargetOptions &options, CPU = "core2"; else if (Triple.getArch() == llvm::Triple::x86) CPU = "yonah"; + else if (Triple.getArchName() == "arm64e") + CPU = "vortex"; else if (Triple.getArch() == llvm::Triple::aarch64 || Triple.getArch() == llvm::Triple::aarch64_32) CPU = "cyclone"; diff --git a/llvm/lib/Support/ARMTargetParser.cpp b/llvm/lib/Support/ARMTargetParser.cpp index ce5daa7fe58c0..08317284c03bf 100644 --- a/llvm/lib/Support/ARMTargetParser.cpp +++ b/llvm/lib/Support/ARMTargetParser.cpp @@ -278,6 +278,8 @@ StringRef ARM::getCanonicalArchName(StringRef Arch) { // Begins with "arm" / "thumb", move past it. if (A.startswith("arm64_32")) offset = 8; + else if (A.startswith("arm64e")) + offset = 6; else if (A.startswith("arm64")) offset = 5; else if (A.startswith("aarch64_32")) diff --git a/llvm/lib/Support/Triple.cpp b/llvm/lib/Support/Triple.cpp index d419463e6a5e6..04f4ef214471a 100644 --- a/llvm/lib/Support/Triple.cpp +++ b/llvm/lib/Support/Triple.cpp @@ -400,6 +400,7 @@ static Triple::ArchType parseArch(StringRef ArchName) { .Case("arc", Triple::arc) .Case("arm64", Triple::aarch64) .Case("arm64_32", Triple::aarch64_32) + .Case("arm64e", Triple::aarch64) .Case("arm", Triple::arm) .Case("armeb", Triple::armeb) .Case("thumb", Triple::thumb) @@ -565,6 +566,9 @@ static Triple::SubArchType parseSubArch(StringRef SubArchName) { StringRef ARMSubArch = ARM::getCanonicalArchName(SubArchName); + if (SubArchName == "arm64e") + return Triple::AArch64SubArch_E; + // For now, this is the small part. Early return. if (ARMSubArch.empty()) return StringSwitch<Triple::SubArchType>(SubArchName) diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index 0ec1e667d6920..40817fc0b4072 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -216,6 +216,12 @@ static std::string computeDataLayout(const Triple &TT, return "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"; } +static StringRef computeDefaultCPU(const Triple &TT, StringRef CPU) { + if (CPU.empty() && TT.getArchName() == "arm64e") + return "vortex"; + return CPU; +} + static Reloc::Model getEffectiveRelocModel(const Triple &TT, Optional<Reloc::Model> RM) { // AArch64 Darwin and Windows are always PIC. @@ -264,7 +270,8 @@ AArch64TargetMachine::AArch64TargetMachine(const Target &T, const Triple &TT, bool LittleEndian) : LLVMTargetMachine(T, computeDataLayout(TT, Options.MCOptions, LittleEndian), - TT, CPU, FS, Options, getEffectiveRelocModel(TT, RM), + TT, computeDefaultCPU(TT, CPU), FS, Options, + getEffectiveRelocModel(TT, RM), getEffectiveAArch64CodeModel(TT, CM, JIT), OL), TLOF(createTLOF(getTargetTriple())), isLittle(LittleEndian) { initAsmInfo(); @@ -288,6 +295,7 @@ AArch64TargetMachine::AArch64TargetMachine(const Target &T, const Triple &TT, // MachO/CodeModel::Large, which GlobalISel does not support. if (getOptLevel() <= EnableGlobalISelAtO && TT.getArch() != Triple::aarch64_32 && + TT.getArchName() != "arm64e" && !(getCodeModel() == CodeModel::Large && TT.isOSBinFormatMachO())) { setGlobalISel(true); setGlobalISelAbort(GlobalISelAbortMode::Disable); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp index 0cafd5dd12fa6..a7ce00d6b90ab 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp @@ -50,9 +50,13 @@ static MCInstrInfo *createAArch64MCInstrInfo() { static MCSubtargetInfo * createAArch64MCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS) { - if (CPU.empty()) + if (CPU.empty()) { CPU = "generic"; + if (TT.getArchName() == "arm64e") + CPU = "vortex"; + } + return createAArch64MCSubtargetInfoImpl(TT, CPU, FS); } diff --git a/llvm/test/MC/AArch64/arm64e.s b/llvm/test/MC/AArch64/arm64e.s new file mode 100644 index 0000000000000..d034f9196aca1 --- /dev/null +++ b/llvm/test/MC/AArch64/arm64e.s @@ -0,0 +1,9 @@ +// RUN: not llvm-mc -triple arm64-- -show-encoding < %s 2> %t +// RUN: FileCheck --check-prefix=CHECK-GENERIC < %t %s + +// RUN: llvm-mc -triple arm64e-- -show-encoding < %s |\ +// RUN: FileCheck %s --check-prefix=CHECK-ARM64E + +// CHECK-GENERIC: error: instruction requires: pa +// CHECK-ARM64E: pacia x0, x1 // encoding: [0x20,0x00,0xc1,0xda] + pacia x0, x1 diff --git a/llvm/unittests/ADT/TripleTest.cpp b/llvm/unittests/ADT/TripleTest.cpp index d8123bbbfdf7a..b9e18d0bcea46 100644 --- a/llvm/unittests/ADT/TripleTest.cpp +++ b/llvm/unittests/ADT/TripleTest.cpp @@ -1497,5 +1497,10 @@ TEST(TripleTest, ParseARMArch) { Triple T = Triple("aarch64_be"); EXPECT_EQ(Triple::aarch64_be, T.getArch()); } + { + Triple T = Triple("arm64e"); + EXPECT_EQ(Triple::aarch64, T.getArch()); + EXPECT_EQ(Triple::AArch64SubArch_E, T.getSubArch()); + } } } // end anonymous namespace From ed270adc93db51b82460724861d4bb0ab8357d65 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Fri, 6 Sep 2019 13:41:14 -0700 Subject: [PATCH 504/582] [Object] Add MachO CPU_SUBTYPE_ARM64E. This is an ARM64 subarchitecture (cpusubtype 2), that requires a 'vortex' CPU. --- llvm/lib/Object/MachOObjectFile.cpp | 12 ++++++++++-- llvm/test/MC/AArch64/arm64e-subtype.s | 12 ++++++++++++ .../AArch64/nm-trivial-object-arm64e.test | 4 ++++ .../Inputs/trivial-object-test.macho-arm64e | Bin 0 -> 312 bytes .../tools/llvm-dwarfdump/AArch64/arm64e.ll | 17 +++++++++++++++++ .../Inputs/trivial.obj.macho-arm64e | Bin 0 -> 312 bytes llvm/test/tools/llvm-readobj/macho-arm64e.test | 5 +++++ 7 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 llvm/test/MC/AArch64/arm64e-subtype.s create mode 100644 llvm/test/Object/AArch64/nm-trivial-object-arm64e.test create mode 100644 llvm/test/Object/Inputs/trivial-object-test.macho-arm64e create mode 100644 llvm/test/tools/llvm-dwarfdump/AArch64/arm64e.ll create mode 100644 llvm/test/tools/llvm-readobj/Inputs/trivial.obj.macho-arm64e create mode 100644 llvm/test/tools/llvm-readobj/macho-arm64e.test diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp index c0c873f973545..e651f9e6fb7f3 100644 --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -2557,6 +2557,8 @@ StringRef MachOObjectFile::getFileFormatName() const { case MachO::CPU_TYPE_X86_64: return "Mach-O 64-bit x86-64"; case MachO::CPU_TYPE_ARM64: + if (getHeader().cpusubtype == MachO::CPU_SUBTYPE_ARM64E) + return "Mach-O arm64e"; return "Mach-O arm64"; case MachO::CPU_TYPE_POWERPC64: return "Mach-O 64-bit ppc64"; @@ -2680,6 +2682,12 @@ Triple MachOObjectFile::getArchTriple(uint32_t CPUType, uint32_t CPUSubType, if (ArchFlag) *ArchFlag = "arm64"; return Triple("arm64-apple-darwin"); + case MachO::CPU_SUBTYPE_ARM64E: + if (McpuDefault) + *McpuDefault = "vortex"; + if (ArchFlag) + *ArchFlag = "arm64e"; + return Triple("arm64e-apple-darwin"); default: return Triple(); } @@ -2727,10 +2735,10 @@ bool MachOObjectFile::isValidArch(StringRef ArchFlag) { } ArrayRef<StringRef> MachOObjectFile::getValidArchs() { - static const std::array<StringRef, 17> validArchs = {{ + static const std::array<StringRef, 18> validArchs = {{ "i386", "x86_64", "x86_64h", "armv4t", "arm", "armv5e", "armv6", "armv6m", "armv7", "armv7em", "armv7k", "armv7m", - "armv7s", "arm64", "arm64_32", "ppc", "ppc64", + "armv7s", "arm64", "arm64e", "arm64_32","ppc", "ppc64", }}; return validArchs; diff --git a/llvm/test/MC/AArch64/arm64e-subtype.s b/llvm/test/MC/AArch64/arm64e-subtype.s new file mode 100644 index 0000000000000..03c5c8d30287a --- /dev/null +++ b/llvm/test/MC/AArch64/arm64e-subtype.s @@ -0,0 +1,12 @@ +; RUN: llvm-mc -triple=arm64e-apple-ios -filetype=obj %s -o - | llvm-objdump -macho -d -p - | FileCheck %s + +; CHECK: _foo: +; CHECK: 0: c0 03 5f d6 ret + +; CHECK: Mach header +; CHECK: magic cputype cpusubtype caps filetype ncmds sizeofcmds flags +; CHECK: MH_MAGIC_64 ARM64 E 0x00 OBJECT 3 256 0x00000000 + +.globl _foo +_foo: + ret diff --git a/llvm/test/Object/AArch64/nm-trivial-object-arm64e.test b/llvm/test/Object/AArch64/nm-trivial-object-arm64e.test new file mode 100644 index 0000000000000..c44b1550ef2f0 --- /dev/null +++ b/llvm/test/Object/AArch64/nm-trivial-object-arm64e.test @@ -0,0 +1,4 @@ +RUN: llvm-nm -arch arm64e %p/../Inputs/trivial-object-test.macho-arm64e \ +RUN: | FileCheck %s + +CHECK: 00000000 t ltmp0 diff --git a/llvm/test/Object/Inputs/trivial-object-test.macho-arm64e b/llvm/test/Object/Inputs/trivial-object-test.macho-arm64e new file mode 100644 index 0000000000000000000000000000000000000000..5813378c8b1320ef0112db25ea9544544908ea0c GIT binary patch literal 312 zcmX^A>+L@t1_nkZ1|R{%%s>ogfM_6)1mYQB7Jfnjq!|gY1KA)96ONBBNv$YBD2R^_ zag7K;@X=Z5<^km!kkx?PD*?nHbs#$ofCLAS<_6*bAO-=DI0%CPjE3-tCqer7kTvI& K<Q5n(FaQ9=4Gf_G literal 0 HcmV?d00001 diff --git a/llvm/test/tools/llvm-dwarfdump/AArch64/arm64e.ll b/llvm/test/tools/llvm-dwarfdump/AArch64/arm64e.ll new file mode 100644 index 0000000000000..2626caa916bac --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/AArch64/arm64e.ll @@ -0,0 +1,17 @@ +; RUN: llc -O0 %s -filetype=obj -o - \ +; RUN: | llvm-dwarfdump -arch arm64e - | FileCheck %s +; CHECK: file format Mach-O arm64e + +source_filename = "/tmp/empty.c" +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64e-apple-ios" + +!llvm.module.flags = !{!1, !2, !3, !4} +!llvm.dbg.cu = !{!5} + +!1 = !{i32 2, !"Dwarf Version", i32 4} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !{i32 1, !"wchar_size", i32 4} +!4 = !{i32 7, !"PIC Level", i32 2} +!5 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6, producer: "Apple clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +!6 = !DIFile(filename: "/tmp/empty.c", directory: "/Volumes/Data/llvm-project") diff --git a/llvm/test/tools/llvm-readobj/Inputs/trivial.obj.macho-arm64e b/llvm/test/tools/llvm-readobj/Inputs/trivial.obj.macho-arm64e new file mode 100644 index 0000000000000000000000000000000000000000..5813378c8b1320ef0112db25ea9544544908ea0c GIT binary patch literal 312 zcmX^A>+L@t1_nkZ1|R{%%s>ogfM_6)1mYQB7Jfnjq!|gY1KA)96ONBBNv$YBD2R^_ zag7K;@X=Z5<^km!kkx?PD*?nHbs#$ofCLAS<_6*bAO-=DI0%CPjE3-tCqer7kTvI& K<Q5n(FaQ9=4Gf_G literal 0 HcmV?d00001 diff --git a/llvm/test/tools/llvm-readobj/macho-arm64e.test b/llvm/test/tools/llvm-readobj/macho-arm64e.test new file mode 100644 index 0000000000000..3e0ef2d6622e3 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/macho-arm64e.test @@ -0,0 +1,5 @@ +RUN: llvm-readobj -h %p/Inputs/trivial.obj.macho-arm64e | FileCheck %s + +CHECK: Magic: Magic64 (0xFEEDFACF) +CHECK: CpuType: Arm64 (0x100000C) +CHECK: CpuSubType: CPU_SUBTYPE_ARM64E (0x2) From b79512654fa5b0ed30bb2858cd87561f06c495d1 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Tue, 25 Jun 2019 18:37:45 -0700 Subject: [PATCH 505/582] [utils] Teach UpdateTestChecks about arm64e. --- llvm/utils/UpdateTestChecks/asm.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py index 81556d65802c9..d999f3a41c6ca 100644 --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -35,6 +35,14 @@ class string: r'.Lfunc_end[0-9]+:\n', flags=(re.M | re.S)) +ASM_FUNCTION_ARM64_RE = re.compile( + r'^_?(?P<func>[^:]+):[ \t]*;[ \t]*@(?P=func)\n' + r'(?:[ \t]+.cfi_startproc\n)?' + r'(?P<body>.*?)\n' + # Darwin doesn't rely on function end labels, so we look for cfi instead. + r'^[ \t]+.cfi_endproc\n', + flags=(re.M | re.S)) + ASM_FUNCTION_AMDGPU_RE = re.compile( r'^_?(?P<func>[^:]+):[ \t]*;+[ \t]*@(?P=func)\n[^:]*?' r'(?P<body>.*?)\n' # (body of the function) @@ -317,6 +325,8 @@ def build_function_body_dictionary_for_triple(args, raw_tool_output, triple, pre 'x86': (scrub_asm_x86, ASM_FUNCTION_X86_RE), 'i386': (scrub_asm_x86, ASM_FUNCTION_X86_RE), 'aarch64': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_RE), + 'arm64': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM64_RE), + 'arm64e': (scrub_asm_arm_eabi, ASM_FUNCTION_ARM64_RE), 'aarch64-apple-darwin': (scrub_asm_arm_eabi, ASM_FUNCTION_AARCH64_DARWIN_RE), 'hexagon': (scrub_asm_hexagon, ASM_FUNCTION_HEXAGON_RE), 'r600': (scrub_asm_amdgpu, ASM_FUNCTION_AMDGPU_RE), From 700888c06556e81839c6de92e251d70d901c95ae Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 06:50:08 -0700 Subject: [PATCH 506/582] [AArch64] Support emitting arm64e MachO in Darwin AsmBackend. --- .../AArch64/MCTargetDesc/AArch64AsmBackend.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp index 21ce5785ea5e1..14f5ecc70cd30 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp @@ -529,6 +529,7 @@ enum CompactUnwindEncodings { class DarwinAArch64AsmBackend : public AArch64AsmBackend { const MCRegisterInfo &MRI; bool IsILP32; + bool IsARM64E; /// Encode compact unwind stack adjustment for frameless functions. /// See UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK in compact_unwind_encoding.h. @@ -539,18 +540,22 @@ class DarwinAArch64AsmBackend : public AArch64AsmBackend { public: DarwinAArch64AsmBackend(const Target &T, const Triple &TT, - const MCRegisterInfo &MRI, bool IsILP32) + const MCRegisterInfo &MRI, + bool IsILP32, bool IsARM64E) : AArch64AsmBackend(T, TT, /*IsLittleEndian*/ true), MRI(MRI), - IsILP32(IsILP32) {} + IsILP32(IsILP32), IsARM64E(IsARM64E) {} std::unique_ptr<MCObjectTargetWriter> createObjectTargetWriter() const override { if (IsILP32) return createAArch64MachObjectWriter( MachO::CPU_TYPE_ARM64_32, MachO::CPU_SUBTYPE_ARM64_32_V8, true); + else if (IsARM64E) + return createAArch64MachObjectWriter( + MachO::CPU_TYPE_ARM64, MachO::CPU_SUBTYPE_ARM64E, false); else - return createAArch64MachObjectWriter(MachO::CPU_TYPE_ARM64, - MachO::CPU_SUBTYPE_ARM64_ALL, false); + return createAArch64MachObjectWriter( + MachO::CPU_TYPE_ARM64, MachO::CPU_SUBTYPE_ARM64_ALL, false); } /// Generate the compact unwind encoding from the CFI directives. @@ -734,7 +739,8 @@ MCAsmBackend *llvm::createAArch64leAsmBackend(const Target &T, const Triple &TheTriple = STI.getTargetTriple(); if (TheTriple.isOSBinFormatMachO()) { const bool IsILP32 = TheTriple.isArch32Bit(); - return new DarwinAArch64AsmBackend(T, TheTriple, MRI, IsILP32); + const bool IsARM64E = TheTriple.getArchName() == "arm64e"; + return new DarwinAArch64AsmBackend(T, TheTriple, MRI, IsILP32, IsARM64E); } if (TheTriple.isOSBinFormatCOFF()) From d3d83e5c68d0a656b304170855b2ac6c84951807 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 06:47:48 -0700 Subject: [PATCH 507/582] [AArch64] Add helper functions for mapping PAC keys and instructions. --- llvm/lib/Target/AArch64/AArch64InstrInfo.h | 28 +++++++++++++ .../Target/AArch64/Utils/AArch64BaseInfo.h | 39 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h index 1688045e4fb86..a46d70aee6f22 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h @@ -360,6 +360,34 @@ static inline bool isIndirectBranchOpcode(int Opc) { return Opc == AArch64::BR; } +static inline unsigned getXPACOpcodeForKey(AArch64PACKey::ID K) { + using namespace AArch64PACKey; + switch (K) { + case IA: case IB: return AArch64::XPACI; + case DA: case DB: return AArch64::XPACD; + } +} + +static inline unsigned getAUTOpcodeForKey(AArch64PACKey::ID K, bool Zero) { + using namespace AArch64PACKey; + switch (K) { + case IA: return Zero ? AArch64::AUTIZA : AArch64::AUTIA; + case IB: return Zero ? AArch64::AUTIZB : AArch64::AUTIB; + case DA: return Zero ? AArch64::AUTDZA : AArch64::AUTDA; + case DB: return Zero ? AArch64::AUTDZB : AArch64::AUTDB; + } +} + +static inline unsigned getPACOpcodeForKey(AArch64PACKey::ID K, bool Zero) { + using namespace AArch64PACKey; + switch (K) { + case IA: return Zero ? AArch64::PACIZA : AArch64::PACIA; + case IB: return Zero ? AArch64::PACIZB : AArch64::PACIB; + case DA: return Zero ? AArch64::PACDZA : AArch64::PACDA; + case DB: return Zero ? AArch64::PACDZB : AArch64::PACDB; + } +} + // struct TSFlags { #define TSFLAG_ELEMENT_SIZE_TYPE(X) (X) // 3-bits #define TSFLAG_DESTRUCTIVE_INST_TYPE(X) ((X) << 3) // 1-bit diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h index 7a4fcac09ec4d..48c363738dd08 100644 --- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h +++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h @@ -643,6 +643,45 @@ namespace AArch64II { }; } // end namespace AArch64II +//===----------------------------------------------------------------------===// +// v8.3a Pointer Authentication +// + +namespace AArch64PACKey { +enum ID : uint8_t { + IA = 0, + IB = 1, + DA = 2, + DB = 3 +}; +}; + +inline static StringRef AArch64PACKeyIDToString(AArch64PACKey::ID KeyID) { + switch (KeyID) { + case AArch64PACKey::IA: + return StringRef("ia"); + case AArch64PACKey::IB: + return StringRef("ib"); + case AArch64PACKey::DA: + return StringRef("da"); + case AArch64PACKey::DB: + return StringRef("db"); + } +} + +inline static Optional<AArch64PACKey::ID> +AArch64StringToPACKeyID(StringRef Name) { + if (Name == "ia") + return AArch64PACKey::IA; + if (Name == "ib") + return AArch64PACKey::IB; + if (Name == "da") + return AArch64PACKey::DA; + if (Name == "db") + return AArch64PACKey::DB; + return None; +} + } // end namespace llvm #endif From 13d4276f5472b3c31157c6d569283a82fef852f1 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Fri, 6 Sep 2019 13:44:25 -0700 Subject: [PATCH 508/582] [AArch64] Support @AUTH symbol variant and MachO reloc. This adds support for a new symbol variant, "@AUTH". This describes a symbol reference that needs to be signed at load-time: .quad _sym@AUTH(ib,123[,addr]) .quad (_sym + 5)@AUTH(ib,123[,addr]) .quad (_sym - 5)@AUTH(ib,123[,addr]) At the asm level, it requires parameters, which makes it slightly more complicated than existing variants: because of that, it's implemented separately, in an early parsePrimaryExpr hook. When emitting MachO, it maps to the ARM64_RELOC_AUTHENTICATED_POINTER relocation. It's not currently supported in other object writers. --- llvm/include/llvm/BinaryFormat/MachO.h | 2 + llvm/lib/Object/MachOObjectFile.cpp | 3 +- .../AArch64/AsmParser/AArch64AsmParser.cpp | 111 ++++++++++++++++ .../AArch64/MCTargetDesc/AArch64MCExpr.cpp | 51 ++++++++ .../AArch64/MCTargetDesc/AArch64MCExpr.h | 51 ++++++++ .../MCTargetDesc/AArch64MachObjectWriter.cpp | 41 ++++++ llvm/test/MC/AArch64/arm64e-ptrauth-reloc.s | 118 ++++++++++++++++++ 7 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 llvm/test/MC/AArch64/arm64e-ptrauth-reloc.s diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h index fb50e549cb9da..169bcffd5a4a6 100644 --- a/llvm/include/llvm/BinaryFormat/MachO.h +++ b/llvm/include/llvm/BinaryFormat/MachO.h @@ -460,6 +460,8 @@ enum RelocationInfoType { ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9, // Must be followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12. ARM64_RELOC_ADDEND = 10, + // An authenticated pointer. + ARM64_RELOC_AUTHENTICATED_POINTER = 11, // Constant values for the r_type field in an x86_64 architecture // llvm::MachO::relocation_info or llvm::MachO::scattered_relocation_info diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp index e651f9e6fb7f3..1d602aa2dba1a 100644 --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -2220,7 +2220,8 @@ void MachOObjectFile::getRelocationTypeName( "ARM64_RELOC_PAGEOFF12", "ARM64_RELOC_GOT_LOAD_PAGE21", "ARM64_RELOC_GOT_LOAD_PAGEOFF12", "ARM64_RELOC_POINTER_TO_GOT", "ARM64_RELOC_TLVP_LOAD_PAGE21", "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", - "ARM64_RELOC_ADDEND" + "ARM64_RELOC_ADDEND", + "ARM64_RELOC_AUTHENTICATED_POINTER" }; if (RType >= array_lengthof(Table)) diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index 4fb409f020d91..d6091ef5b8b83 100644 --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -164,6 +164,8 @@ class AArch64AsmParser : public MCTargetAsmParser { bool showMatchError(SMLoc Loc, unsigned ErrCode, uint64_t ErrorInfo, OperandVector &Operands); + bool parseAuthExpr(const MCExpr *&Res, SMLoc &EndLoc); + bool parseDirectiveArch(SMLoc L); bool parseDirectiveArchExtension(SMLoc L); bool parseDirectiveCPU(SMLoc L); @@ -264,6 +266,8 @@ class AArch64AsmParser : public MCTargetAsmParser { unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; + bool parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) override; + static bool classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, MCSymbolRefExpr::VariantKind &DarwinRefKind, @@ -5466,6 +5470,113 @@ bool AArch64AsmParser::parseDirectiveCFIBKeyFrame() { return false; } +bool AArch64AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) { + // Try @AUTH expressions: they're more complex than the usual symbol variants. + if (!parseAuthExpr(Res, EndLoc)) + return false; + return getParser().parsePrimaryExpr(Res, EndLoc); +} + +/// parseAuthExpr +/// ::= _sym@AUTH(ib,123[,addr]) +/// ::= (_sym + 5)@AUTH(ib,123[,addr]) +/// ::= (_sym - 5)@AUTH(ib,123[,addr]) +bool AArch64AsmParser::parseAuthExpr(const MCExpr *&Res, SMLoc &EndLoc) { + MCAsmParser &Parser = getParser(); + MCContext &Ctx = getContext(); + + AsmToken Tok = Parser.getTok(); + + // Look for '_sym@AUTH' ... + if (Tok.is(AsmToken::Identifier) && Tok.getIdentifier().endswith("@AUTH")) { + StringRef SymName = Tok.getIdentifier().drop_back(strlen("@AUTH")); + Res = MCSymbolRefExpr::create(Ctx.getOrCreateSymbol(SymName), Ctx); + + Parser.Lex(); // Eat the identifier. + } else { + // ... or look for a more complex symbol reference, such as ... + SmallVector<AsmToken, 6> Tokens; + + // ... '"_long sym"@AUTH' ... + if (Tok.is(AsmToken::String)) + Tokens.resize(2); + // ... or '(_sym + 5)@AUTH'. + else if (Tok.is(AsmToken::LParen)) + Tokens.resize(6); + else + return true; + + if (Parser.getLexer().peekTokens(Tokens) != Tokens.size()) + return true; + + // In either case, the expression ends with '@' 'AUTH'. + if (Tokens[Tokens.size()-2].isNot(AsmToken::At) || + Tokens[Tokens.size()-1].isNot(AsmToken::Identifier) || + Tokens[Tokens.size()-1].getIdentifier() != "AUTH") + return true; + + if (Tok.is(AsmToken::String)) { + StringRef SymName; + if (Parser.parseIdentifier(SymName)) + return true; + Res = MCSymbolRefExpr::create(Ctx.getOrCreateSymbol(SymName), Ctx); + } else { + if (Parser.parsePrimaryExpr(Res, EndLoc)) + return true; + } + + Parser.Lex(); // '@' + Parser.Lex(); // 'AUTH' + } + + // At this point, we encountered "<id>@AUTH". There is no fallback anymore. + if (parseToken(AsmToken::LParen, "expected '(' after @AUTH expression")) + return true; + + if (Parser.getTok().isNot(AsmToken::Identifier)) + return TokError("expected key name in @AUTH expression"); + + StringRef KeyStr = Parser.getTok().getIdentifier(); + auto KeyIDOrNone = AArch64StringToPACKeyID(KeyStr); + if (!KeyIDOrNone) + return TokError("invalid key '" + KeyStr + "' in @AUTH expression"); + Parser.Lex(); + + if (parseToken(AsmToken::Comma, "expected ',' after key in @AUTH expression")) + return true; + + if (Parser.getTok().isNot(AsmToken::Integer)) + return TokError( + "expected integer discriminator after key in @AUTH expression"); + int64_t Discriminator = Parser.getTok().getIntVal(); + + if (!isUInt<16>(Discriminator)) + return TokError("too wide integer discriminator '" + itostr(Discriminator) + + "'in @AUTH expression"); + Parser.Lex(); + + bool UseAddressDiversity = false; + if (Parser.getTok().is(AsmToken::Comma)) { + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::Identifier) || + Parser.getTok().getIdentifier() != "addr") + return TokError( + "expected 'addr' after discriminator in @AUTH expression"); + UseAddressDiversity = true; + Parser.Lex(); + } + + EndLoc = Parser.getTok().getEndLoc(); + if (parseToken(AsmToken::RParen, + "expected ')' at the end of @AUTH expression")) + return true; + + Res = AArch64AuthMCExpr::create(Res, Discriminator, *KeyIDOrNone, + UseAddressDiversity, Ctx); + + return false; +} + bool AArch64AsmParser::classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp index 548e399e05a3f..ece2c26d1e7d3 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.cpp @@ -152,3 +152,54 @@ void AArch64MCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); } + +//===----------------------------------------------------------------------===// + +const AArch64AuthMCExpr *AArch64AuthMCExpr::create(const MCExpr *Expr, + uint16_t Discriminator, + AArch64PACKey::ID Key, + bool HasAddressDiversity, + MCContext &Ctx) { + return new (Ctx) + AArch64AuthMCExpr(Expr, Discriminator, Key, HasAddressDiversity); +} + +void AArch64AuthMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + bool WrapSubExprInParens = !isa<MCSymbolRefExpr>(getSubExpr()); + if (WrapSubExprInParens) + OS << '('; + getSubExpr()->print(OS, MAI); + if (WrapSubExprInParens) + OS << ')'; + + OS << "@AUTH(" << AArch64PACKeyIDToString(Key) << ',' << Discriminator; + if (hasAddressDiversity()) + OS << ",addr"; + OS << ')'; +} + +void AArch64AuthMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +MCFragment *AArch64AuthMCExpr::findAssociatedFragment() const { + llvm_unreachable("FIXME: what goes here?"); +} + +bool AArch64AuthMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + if (Res.getSymB()) + report_fatal_error("Auth relocation can't reference two symbols"); + + Res = MCValue::get(Res.getSymA(), nullptr, Res.getConstant(), getKind()); + + return true; +} + +void AArch64AuthMCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { + llvm_unreachable("FIXME"); +} diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h index a82ff2e914268..04808c9f820e6 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCExpr.h @@ -14,6 +14,7 @@ #ifndef LLVM_LIB_TARGET_AARCH64_MCTARGETDESC_AARCH64MCEXPR_H #define LLVM_LIB_TARGET_AARCH64_MCTARGETDESC_AARCH64MCEXPR_H +#include "Utils/AArch64BaseInfo.h" #include "llvm/MC/MCExpr.h" #include "llvm/Support/ErrorHandling.h" @@ -34,6 +35,8 @@ class AArch64MCExpr : public MCTargetExpr { VK_TPREL = 0x007, VK_TLSDESC = 0x008, VK_SECREL = 0x009, + VK_AUTH = 0x00a, + VK_AUTHADDR = 0x00b, VK_SymLocBits = 0x00f, // Variants specifying which part of the final address calculation is @@ -114,6 +117,7 @@ class AArch64MCExpr : public MCTargetExpr { const MCExpr *Expr; const VariantKind Kind; +protected: explicit AArch64MCExpr(const MCExpr *Expr, VariantKind Kind) : Expr(Expr), Kind(Kind) {} @@ -171,6 +175,53 @@ class AArch64MCExpr : public MCTargetExpr { static bool classof(const AArch64MCExpr *) { return true; } }; + +class AArch64AuthMCExpr : public AArch64MCExpr { + uint16_t Discriminator; + AArch64PACKey::ID Key; + + explicit AArch64AuthMCExpr(const MCExpr *Expr, uint16_t Discriminator, + AArch64PACKey::ID Key, bool HasAddressDiversity) + : AArch64MCExpr(Expr, HasAddressDiversity ? VK_AUTHADDR : VK_AUTH), + Discriminator(Discriminator), Key(Key) {} + +public: + /// @name Construction + /// @{ + + static const AArch64AuthMCExpr * + create(const MCExpr *Expr, uint16_t Discriminator, AArch64PACKey::ID Key, + bool HasAddressDiversity, MCContext &Ctx); + + /// @} + /// @name Accessors + /// @{ + + AArch64PACKey::ID getKey() const { return Key; } + uint16_t getDiscriminator() const { return Discriminator; } + bool hasAddressDiversity() const { return getKind() == VK_AUTHADDR; } + + /// @} + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + + void visitUsedExpr(MCStreamer &Streamer) const override; + + MCFragment *findAssociatedFragment() const override; + + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override; + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + + static bool classof(const AArch64MCExpr *E) { + return E->getKind() == VK_AUTH || E->getKind() == VK_AUTHADDR; + } +}; } // end namespace llvm #endif diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MachObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MachObjectWriter.cpp index fc04d37eb3623..2c505b2c59b1e 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MachObjectWriter.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MachObjectWriter.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/AArch64FixupKinds.h" +#include "MCTargetDesc/AArch64MCExpr.h" #include "MCTargetDesc/AArch64MCTargetDesc.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/MachO.h" @@ -392,6 +393,46 @@ void AArch64MachObjectWriter::recordRelocation( Value = 0; } + if (Target.getRefKind() == AArch64MCExpr::VK_AUTH || + Target.getRefKind() == AArch64MCExpr::VK_AUTHADDR) { + auto *Expr = cast<AArch64AuthMCExpr>(Fixup.getValue()); + + assert(Type == MachO::ARM64_RELOC_UNSIGNED); + + if (IsPCRel) { + Asm.getContext().reportError( + Fixup.getLoc(), "invalid PC relative auth relocation"); + return; + } + + if (Log2Size != 3) { + Asm.getContext().reportError( + Fixup.getLoc(), "invalid auth relocation size, must be 8 bytes"); + return; + } + + if (Target.getSymB()) { + Asm.getContext().reportError( + Fixup.getLoc(), "invalid auth relocation, can't reference two symbols"); + return; + } + + uint16_t Discriminator = Expr->getDiscriminator(); + AArch64PACKey::ID Key = Expr->getKey(); + + if (!isInt<32>(Value)) { + Asm.getContext().reportError(Fixup.getLoc(), "too wide addend '" + + itostr(Value) + + "' in auth relocation"); + return; + } + + Type = MachO::ARM64_RELOC_AUTHENTICATED_POINTER; + Value = (uint32_t(Value)) | (uint64_t(Discriminator) << 32) | + (uint64_t(Expr->hasAddressDiversity()) << 48) | + (uint64_t(Key) << 49) | (1ULL << 63); + } + // If there's any addend left to handle, encode it in the instruction. FixedValue = Value; diff --git a/llvm/test/MC/AArch64/arm64e-ptrauth-reloc.s b/llvm/test/MC/AArch64/arm64e-ptrauth-reloc.s new file mode 100644 index 0000000000000..b70c5ba8de3e8 --- /dev/null +++ b/llvm/test/MC/AArch64/arm64e-ptrauth-reloc.s @@ -0,0 +1,118 @@ +// RUN: llvm-mc -triple=arm64-apple-ios < %s | \ +// RUN: FileCheck %s --check-prefix=ASM + +// RUN: llvm-mc -triple=arm64-apple-ios -filetype=obj < %s | \ +// RUN: llvm-readobj --expand-relocs -sections -section-relocations -section-data | \ +// RUN: FileCheck %s --check-prefix=RELOC + + + +// RELOC: Sections [ +// RELOC-LABEL: Section { +// RELOC-LABEL: Section { +// RELOC-NEXT: Index: 1 +// RELOC-NEXT: Name: __const (5F 5F 63 6F 6E 73 74 00 00 00 00 00 00 00 00 00) +// RELOC-NEXT: Segment: __DATA (5F 5F 44 41 54 41 00 00 00 00 00 00 00 00 00 00) + +.section __DATA,__const +.p2align 3 + +// RELOC-LABEL: Relocations [ +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x70 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g 7 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x60 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g 6 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x50 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g5 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x40 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g4 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x30 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g3 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x20 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g2 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x10 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g1 +// RELOC-NEXT: } +// RELOC-NEXT: Relocation { +// RELOC-NEXT: Offset: 0x0 +// RELOC-NEXT: PCRel: 0 +// RELOC-NEXT: Length: 3 +// RELOC-NEXT: Type: ARM64_RELOC_AUTHENTICATED_POINTER (11) +// RELOC-NEXT: Symbol: _g0 +// RELOC-NEXT: } +// RELOC-NEXT: ] +// RELOC-NEXT: SectionData ( + +// RELOC-NEXT: 0000: 00000000 2A000080 +// ASM: .quad _g0@AUTH(ia,42) +.quad _g0@AUTH(ia,42) +.quad 0 + +// RELOC-NEXT: 0010: 00000000 00000280 +// ASM: .quad _g1@AUTH(ib,0) +.quad _g1@AUTH(ib,0) +.quad 0 + +// RELOC-NEXT: 0020: 00000000 05000580 +// ASM: .quad _g2@AUTH(da,5,addr) +.quad _g2@AUTH(da,5,addr) +.quad 0 + +// RELOC-NEXT: 0030: 00000000 FFFF0780 +// ASM: .quad _g3@AUTH(db,65535,addr) +.quad _g3@AUTH(db,0xffff,addr) +.quad 0 + +// RELOC-NEXT: 0040: 07000000 00000080 +// ASM: .quad (_g4+7)@AUTH(ia,0) +.quad (_g4 + 7)@AUTH(ia,0) +.quad 0 + +// RELOC-NEXT: 0050: FDFFFFFF 00DE0280 +// ASM: .quad (_g5-3)@AUTH(ib,56832) +.quad (_g5 - 3)@AUTH(ib,0xde00) +.quad 0 + +// RELOC-NEXT: 0060: 00000000 FF000780 +// ASM: .quad "_g 6"@AUTH(db,255,addr) +.quad "_g 6"@AUTH(db,0xff,addr) +.quad 0 + +// RELOC-NEXT: 0070: 07000000 10000080 +// ASM: .quad ("_g 7"+7)@AUTH(ia,16) +.quad ("_g 7" + 7)@AUTH(ia,16) +.quad 0 From 481f2fd5699ea40947d76be6b2cc90003e9d3015 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 06:20:25 -0700 Subject: [PATCH 509/582] [Docs] Document Pointer Authentication IR additions. --- llvm/docs/LangRef.rst | 14 ++ llvm/docs/PointerAuth.md | 469 +++++++++++++++++++++++++++++++++++++++ llvm/docs/Reference.rst | 5 + 3 files changed, 488 insertions(+) create mode 100644 llvm/docs/PointerAuth.md diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 6c86c27ea4662..08a34a1ed61b8 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1981,6 +1981,13 @@ site, these bundles may contain any values that are needed by the generated code. For more details, see :ref:`GC Transitions <gc_transition_args>`. +Pointer Authentication Operand Bundles +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Pointer Authentication operand bundles are characterized by the +``"ptrauth"`` operand bundle tag. They are described in the +`Pointer Authentication <PointerAuth.html#operand-bundle>`_ document. + .. _moduleasm: Module-Level Inline Assembly @@ -14453,6 +14460,13 @@ The LLVM exception handling intrinsics (which all start with ``llvm.eh.`` prefix), are described in the `LLVM Exception Handling <ExceptionHandling.html#format-common-intrinsics>`_ document. +Pointer Authentication Intrinsics +--------------------------------- + +The LLVM pointer authentication intrinsics (which all start with +``llvm.ptrauth.`` prefix), are described in the `Pointer Authentication +<PointerAuth.html#intrinsics>`_ document. + .. _int_trampoline: Trampoline Intrinsics diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md new file mode 100644 index 0000000000000..972ad7ce74cda --- /dev/null +++ b/llvm/docs/PointerAuth.md @@ -0,0 +1,469 @@ +# Pointer Authentication + +## Introduction + +Pointer Authentication is a mechanism by which certain pointers are signed, +are modified to embed that signature in their unused bits, and are +authenticated (have their signature checked) when used, to prevent pointers +of unknown origin from being injected into a process. + +To enforce Control Flow Integrity (CFI), this is mostly used for all code +pointers (function pointers, vtables, ...), but certain data pointers specified +by the ABI (vptr, ...) are also authenticated. + +Additionally, with clang extensions, users can specify that a given pointer +be signed/authenticated. + +At the IR level, it is represented using: + +* a [set of intrinsics](#intrinsics) (to sign/authenticate pointers) +* a [special section and relocation](#authenticated-global-relocation) + (to sign globals) +* a [call operand bundle](#operand-bundle) (to authenticate called pointers) + +It is implemented by the [AArch64 target](#aarch64-support), using the +[ARMv8.3 Pointer Authentication Code](#armv8-3-pointer-authentication-code) +instructions, to support the Darwin [arm64e](#arm64e) ABI. + + +## Concepts + +### Operations + +Pointer Authentication is based on three fundamental operations: + +#### Sign +* compute a cryptographic signature of a given pointer value +* embed it within the value +* return the signed value + +#### Auth +* compute a cryptographic signature of a given value +* compare it against the embedded signature +* remove the embedded signature +* return the raw, unauthenticated, value + +#### Strip +* remove the embedded signature +* return the unauthenticated value + + +### Diversity + +To prevent any signed pointer from being used instead of any other signed +pointer, the signatures are diversified, using additional inputs: + +* a key: one of a small, fixed set. The value of the key itself is not + directly accessible, but is referenced by ptrauth operations via an + identifier. + +* salt, or extra diversity data: additional data mixed in with the value and + used by the ptrauth operations. + A concrete value is called a "discriminator", and, in the special case where + the diversity data is a pointer to the storage location of the signed value, + the value is said to be "address-discriminated". + Additionally, an arbitrary small integer can be blended into an address + discriminator to produce a blended address discriminator. + +Keys are not necessarily interchangeable, and keys can be specified to be +incompatible with certain kinds of pointers (e.g., code vs data keys/pointers). +Which keys are appropriate for a given kind of pointer is defined by the +target implementation. + +## LLVM IR Representation + +### Intrinsics + +These intrinsics are provided by LLVM to expose pointer authentication +operations. + + +#### '``llvm.ptrauth.sign``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.sign.i64(i64 <value>, i32 <key>, i64 <extra data>) +``` + +##### Overview: + +The '``llvm.ptrauth.sign``' intrinsic signs an unauthenticated pointer. + + +##### Arguments: + +The ``value`` argument is the unauthenticated (raw) pointer value to be signed. +The ``key`` argument is the identifier of the key to be used to generate the +signed value. +The ``extra data`` argument is the additional diversity data to be used as a +discriminator. + +##### Semantics: + +The '``llvm.ptrauth.sign``' intrinsic implements the `sign`_ operation. +It returns a signed value. + +If ``value`` is already a signed value, the behavior is undefined. + +If ``value`` is not a pointer value for which ``key`` is appropriate, the +behavior is undefined. + + +#### '``llvm.ptrauth.auth``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.auth.i64(i64 <value>, i32 <key>, i64 <extra data>) +``` + +##### Overview: + +The '``llvm.ptrauth.auth``' intrinsic authenticates a signed pointer. + +##### Arguments: + +The ``value`` argument is the signed pointer value to be authenticated. +The ``key`` argument is the identifier of the key that was used to generate +the signed value. +The ``extra data`` argument is the additional diversity data to be used as a +discriminator. + +##### Semantics: + +The '``llvm.ptrauth.auth``' intrinsic implements the `auth`_ operation. +It returns a raw, unauthenticated value. +If ``value`` does not have a correct signature for ``key`` and ``extra data``, +the returned value is an invalid, poison pointer. + + +#### '``llvm.ptrauth.strip``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.strip.i64(i64 <value>, i32 <key>) +``` + +##### Overview: + +The '``llvm.ptrauth.strip``' intrinsic strips the embedded signature out of a +possibly-signed pointer. + + +##### Arguments: + +The ``value`` argument is the signed pointer value to be stripped. +The ``key`` argument is the identifier of the key that was used to generate +the signed value. + +##### Semantics: + +The '``llvm.ptrauth.strip``' intrinsic implements the `strip`_ operation. +It returns an unauthenticated value. It does **not** check that the +signature is valid. + +If ``value`` is an unauthenticated pointer value, it is returned as-is, +provided the ``key`` is appropriate for the pointer. + +If ``value`` is not a pointer value for which ``key`` is appropriate, the +behavior is undefined. + +If ``value`` is a signed pointer value, but ``key`` does not identify the +same ``key`` that was used to generate ``value``, the behavior is undefined. + + +#### '``llvm.ptrauth.resign``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.resign.i64(i64 <value>, + i32 <old key>, i64 <old extra data>, + i32 <new key>, i64 <new extra data>) +``` + +##### Overview: + +The '``llvm.ptrauth.resign``' intrinsic re-signs a signed pointer using +a different key and diversity data. + +##### Arguments: + +The ``value`` argument is the signed pointer value to be authenticated. +The ``old key`` argument is the identifier of the key that was used to generate +the signed value. +The ``old extra data`` argument is the additional diversity data to be used as a +discriminator in the auth operation. +The ``new key`` argument is the identifier of the key to use to generate the +resigned value. +The ``new extra data`` argument is the additional diversity data to be used as a +discriminator in the sign operation. + +##### Semantics: + +The '``llvm.ptrauth.resign``' intrinsic performs a combined `auth`_ and `sign`_ +operation, without exposing the intermediate unauthenticated pointer. +It returns a signed value. +If ``value`` does not have a correct signature for ``old key`` and +``old extra data``, the returned value is an invalid, poison pointer. + +#### '``llvm.ptrauth.sign_generic``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.sign_generic.i64(i64 <value>, i64 <extra data>) +``` + +##### Overview: + +The '``llvm.ptrauth.sign_generic``' intrinsic computes a generic signature of +arbitrary data. + +##### Arguments: + +The ``value`` argument is the arbitrary data value to be signed. +The ``extra data`` argument is the additional diversity data to be used as a +discriminator. + +##### Semantics: + +The '``llvm.ptrauth.sign_generic``' intrinsic computes the signature of a given +combination of value and additional diversity data. + +It returns a full signature value (as opposed to a signed pointer value, with +an embedded signature). + +As opposed to [``llvm.ptrauth.sign``](#llvm-ptrauth-sign), it does not interpret +``value`` as a pointer value. Instead, it is an arbitrary data value. + + +#### '``llvm.ptrauth.blend``' + +##### Syntax: + +```llvm +declare i64 @llvm.ptrauth.blend.i64(i64 <address discriminator>, i64 <integer discriminator>) +``` + +##### Overview: + +The '``llvm.ptrauth.blend``' intrinsic blends a pointer address discriminator +with a small integer discriminator to produce a new discriminator. + +##### Arguments: + +The ``address discriminator`` argument is a pointer. +The ``integer discriminator`` argument is a small integer. + +##### Semantics: + +The '``llvm.ptrauth.blend``' intrinsic combines a small integer discriminator +with a pointer address discriminator, in a way that is specified by the target +implementation. + + +### Operand Bundle + +As a way to enforce CFI, function pointers used as indirect call targets are +signed when materialized, and authenticated before calls. + +To prevent the intermediate, unauthenticated pointer from being exposed to +attackers (similar to [``llvm.ptrauth.resign``](#llvm-ptrauth-resign)), the +representation guarantees that the intermediate call target is never attackable +(e.g., by being spilled to memory), using the ``ptrauth`` operand bundle. + +```llvm +define void @f(void ()* %fp) { + call void %fp() [ "ptrauth"(i32 <key>, i64 <data>) ] + ret void +} +``` + +is functionally equivalent to: + +```llvm +define void @f(void ()* %fp) { + %fp_i = ptrtoint void ()* %fp to i64 + %fp_auth = call i64 @llvm.ptrauth.auth.i64(i64 %fp_i, i32 <key>, i64 <data>) + %fp_auth_p = inttoptr i64 %fp_auth to void ()* + call void %fp_auth_p() + ret void +} +``` + +but with the added guarantee that ``%fp_i``, ``%fp_auth``, and ``%fp_auth_p`` +are never attackable. + + +### Function Attributes + +Two function attributes are used to describe other pointer authentication +operations that are not otherwise explicitly expressed in IR. + +#### ``ptrauth-returns`` + +``ptrauth-returns`` specifies that returns from functions should be +authenticated, and that saved return addresses should be signed. + +Note that this describes the execution environment that can be assumed by +this function, not the semantics of return instructions in this function alone. + +The semantics of +[``llvm.returnaddress``](LangRef.html#llvm-returnaddress-intrinsic) are not +changed (it still returns a raw, unauthenticated, return address), so it might +require an implicit strip/authenticate operation. This applies to return +addresses stored in deeper stack frames. + +#### ``ptrauth-calls`` + +``ptrauth-calls`` specifies that calls emitted in this function should be +authenticated according to the platform ABI. + +Calls represented by ``call``/``invoke`` instructions in IR are not affected by +this attribute, as they should already be annotated with the +[``ptrauth`` operand bundle](#operand-bundle). + +The ``ptrauth-calls`` attribute only describes calls emitted by the backend, +as part of target-specific lowering (e.g., runtime calls for TLS accesses). + + +### Authenticated Global Relocation + +[Intrinsics](#intrinsics) can be used to produce signed pointers dynamically, +in code, but not for signed pointers referenced by constants, in, e.g., global +initializers. + +The latter are represented using a special kind of global describing an +authenticated relocation (producing a signed pointer). + +These special global must live in section '``llvm.ptrauth``', and have a +specific type. + +```llvm +@fp.ptrauth = constant { i8*, i32, i64, i64 } + { i8* <value>, + i32 <key>, + i64 <address discriminator>, + i64 <integer discriminator> + }, section "llvm.ptrauth" +``` + +is equivalent to ``@fp.ptrauth`` being initialized with: + +```llvm + %disc = call i64 @llvm.ptrauth.blend.i64(i64 <address discriminator>, i64 <integer discriminator>) + %signed_fp = call i64 @llvm.ptrauth.sign.i64(i64 bitcast (i8* <value> to i64), i32 <key>, i64 %disc) + %fp_p_loc = bitcast { i8*, i32, i64, i64 }* @fp.ptrauth to i64* + store i64 %signed_fp, i8* %fp_p_loc +``` + +Note that this is a temporary representation, chosen to minimize divergence with +upstream. Ideally, this would simply be a new kind of ConstantExpr. + + + +## AArch64 Support + +AArch64 is currently the only target with full support of the pointer +authentication primitives, based on ARMv8.3 instructions. + +### ARMv8.3 Pointer Authentication Code + +[ARMv8.3] is an ISA extension that includes Pointer Authentication Code (PAC) +instructions. + +[ARMv8.3]: https://developer.arm.com/products/architecture/cpu-architecture/a-profile/docs/ddi0487/latest + +#### Keys + +5 keys are supported by ARMv8.3. + +Of those, 4 keys are interchangeably usable to specify the key used in IR +constructs: +* ``ASIA``/``ASIB`` are instruction keys (encoded as respectively 0 and 1). +* ``ASDA``/``ASDB`` are data keys (encoded as respectively 2 and 3). + +``ASGA`` is a special key that cannot be explicitly specified, and is only ever +used implicitly, to implement the +[``llvm.ptrauth.sign_generic``](#llvm-ptrauth-sign-generic) intrinsic. + +#### Instructions + +The IR [Intrinsics](#intrinsics) described above map onto these +instructions as such: +* [``llvm.ptrauth.sign``](#llvm-ptrauth-sign): ``PAC{I,D}{A,B}{Z,SP,}`` +* [``llvm.ptrauth.auth``](#llvm-ptrauth-auth): ``AUT{I,D}{A,B}{Z,SP,}`` +* [``llvm.ptrauth.strip``](#llvm-ptrauth-strip): ``XPAC{I,D}`` +* [``llvm.ptrauth.blend``](#llvm-ptrauth-blend): The semantics of the + blend operation are, in effect, specified by the ABI. arm64e specifies it as + a ``MOVK`` into the high 16-bits. +* [``llvm.ptrauth.sign_generic``](#llvm-ptrauth-sign-generic): ``PACGA`` +* [``llvm.ptrauth.resign``](#llvm-ptrauth-resign): ``AUT*+PAC*``. These are + represented as a single pseudo-instruction in the backend to guarantee that + the intermediate unauthenticated value is not spilled and attackable. + +### arm64e + +Darwin supports ARMv8.3 Pointer Authentication Codes via the arm64e MachO +architecture slice. + +#### CPU Subtype + +The arm64e slice is an extension of the ``arm64`` slice (so uses the same +MachO ``cpu_type``, ``CPU_TYPE_ARM64``). + +It is mainly represented using the ``cpu_subtype`` 2, or ``CPU_SUBTYPE_ARM64E``. + +The subtype also encodes the version of the pointer authentication ABI used in +the object: + +``` +| 31-28 | 28-25 | 24-0 | +| ----- | ------------ | -------------- | +| 0000 | ABI version | 0000 0000 0010 | +``` + + +#### Assembly Representation + +At the assembly level, +[Authenticated Relocations](#authenticated-global-relocation) are represented +using the ``@AUTH`` modifier: + +```asm + .quad _target@AUTH(<key>,<discriminator>[,addr]) +``` + +where: +* ``key`` is the ARMv8.3 key identifier (``ia``, ``ib``, ``da``, ``db``) +* ``discriminator`` is the 16-bit unsigned discriminator value +* ``addr`` signifies that the authenticated pointer is address-discriminated + (that is, that the relocation's target address is to be blended into the + ``discriminator`` before it is used in the sign operation. + +For example: +```asm + _authenticated_reference_to_sym: + .quad _sym@AUTH(db,0) + + _authenticated_reference_to_sym_addr_disc: + .quad _sym@AUTH(ia,12,addr) +``` + +#### Object File Representation + +At the binary object file level, +[Authenticated Relocations](#authenticated-global-relocation) are represented +using the ``ARM64_RELOC_AUTHENTICATED_POINTER`` relocation kind (with value +``11``). + +The pointer authentication information is encoded into the addend, as such: + +``` +| 63 | 62 | 61-51 | 50-49 | 48 | 47 - 32 | 31 - 0 | +| -- | -- | ----- | ----- | ------ | --------------- | -------- | +| 1 | 0 | 0 | key | addr | discriminator | addend | +``` diff --git a/llvm/docs/Reference.rst b/llvm/docs/Reference.rst index 4c421a209274e..1b2d0772e53ae 100644 --- a/llvm/docs/Reference.rst +++ b/llvm/docs/Reference.rst @@ -35,6 +35,7 @@ LLVM and API reference documentation. OptBisect ORCv2 PDB/index + PointerAuth ScudoHardenedAllocator SegmentedStacks StackMaps @@ -207,5 +208,9 @@ Additional Topics :doc:`Coroutines` LLVM support for coroutines. +:doc:`PointerAuth` + A description of pointer authentication, its LLVM IR representation, and its + support in the backend. + :doc:`YamlIO` A reference guide for using LLVM's YAML I/O library. From e652bd4482824b605080c480b731252dd25294b9 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 06:21:21 -0700 Subject: [PATCH 510/582] [IR] Define new llvm.ptrauth intrinsics. These expose pointer authentication operations to IR. There are 3 primitive operations: - sign - authenticate - strip the signature that operate on: - pointer values (signed, or unauthenticated raw pointers) - key (a small, target-specific integer immediate) - discriminator (an additional value used to diversify the signature) This also defines 3 additional intrinsics: - resign, which combines sign and authenticate with added guarantees - blend, which creates a discriminator out of an integer and a pointer - sign_generic, which produces a full 64 bit signature and can be used to sign arbitrary non-pointer values. --- llvm/include/llvm/IR/Intrinsics.td | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index 7a0263f88c2a6..e2dbee6acf12c 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1279,6 +1279,58 @@ def int_preserve_struct_access_index : Intrinsic<[llvm_anyptr_ty], [IntrNoMem, ImmArg<1>, ImmArg<2>]>; +//===----------------- Pointer Authentication Intrinsics ------------------===// +// + +// Sign an unauthenticated pointer (with a raw address as its value), using +// the specified key and discriminator. +// Returns the first operand, with a signature embedded instead of known bits. +def int_ptrauth_sign : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, llvm_i32_ty, + LLVMMatchType<0>], + [IntrNoMem,ImmArg<1>]>; + +// Authenticate a signed pointer, using the specified key and discriminator. +// Returns the first operand, with the signature bits removed. +// If the signature isn't valid, this returns an invalid, poisoned pointer. +def int_ptrauth_auth : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, llvm_i32_ty, + LLVMMatchType<0>], + [IntrNoMem,ImmArg<1>]>; + +// Authenticate a signed pointer (using the first set of key/discriminator), +// and resign it (using the second). +// This is a combined form of @llvm.ptrauth.sign and @llvm.ptrauth.auth, with +// an additional integrity guarantee on the intermediate value. +def int_ptrauth_resign : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, llvm_i32_ty, + LLVMMatchType<0>, llvm_i32_ty, + LLVMMatchType<0>], + [IntrNoMem,ImmArg<1>,ImmArg<3>]>; + +// Strip the embedded signature out of a signed pointer. +// This behaves like @llvm.ptrauth.auth, but doesn't check for the validity of +// the embedded signature. +def int_ptrauth_strip : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, llvm_i32_ty], + [IntrNoMem,ImmArg<1>]>; + +// Blend a small integer discriminator with an address discriminator, producing +// a new discriminator value. +def int_ptrauth_blend : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem]>; + +// Compute the signature of a value, using a given discriminator. +// This differs from @llvm.ptrauth.sign in that it doesn't embed the computed +// signature in the pointer, but instead returns it as a standalone value. +// That allows it to be used to sign non-pointer data: in that sense, it is +// generic. There is no generic @llvm.ptrauth.auth: instead, the signature +// can be computed using @llvm.ptrauth.sign_generic, and compared separately. +def int_ptrauth_sign_generic : Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem]>; + //===----------------------------------------------------------------------===// // Target-specific intrinsics //===----------------------------------------------------------------------===// From aad63485c1e9fc406c21e16cf445df05f636d1f7 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 16 Sep 2019 08:50:34 -0700 Subject: [PATCH 511/582] [AArch64] Lower llvm.ptrauth.sign intrinsics. The sign intrinsic can be simply selected to PAC, which has reg-zero and reg-reg variants. --- .../lib/Target/AArch64/AArch64InstrFormats.td | 14 ++- llvm/lib/Target/AArch64/AArch64InstrInfo.td | 26 ++--- .../AArch64/arm64e-ptrauth-intrinsics.ll | 100 ++++++++++++++++++ 3 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td index 36f0f61004abb..11bd86c492063 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -1696,9 +1696,10 @@ class OneXRegData<bits<3> opc, string asm, SDPatternOperator node> let Inst{31} = 1; } -class SignAuthOneData<bits<3> opcode_prefix, bits<2> opcode, string asm> - : I<(outs GPR64:$Rd), (ins GPR64sp:$Rn), asm, "\t$Rd, $Rn", "", - []>, +class SignAuthOneData<bits<3> opcode_prefix, bits<2> opcode, string asm, + Intrinsic op> + : I<(outs GPR64:$dst), (ins GPR64:$Rd, GPR64sp:$Rn), asm, "\t$Rd, $Rn", + "$dst = $Rd", [(set GPR64:$dst, (op GPR64:$Rd, opcode, GPR64sp:$Rn))]>, Sched<[WriteI, ReadI]> { bits<5> Rd; bits<5> Rn; @@ -1709,8 +1710,11 @@ class SignAuthOneData<bits<3> opcode_prefix, bits<2> opcode, string asm> let Inst{4-0} = Rd; } -class SignAuthZero<bits<3> opcode_prefix, bits<2> opcode, string asm> - : I<(outs GPR64:$Rd), (ins), asm, "\t$Rd", "", []>, Sched<[]> { +class SignAuthZero<bits<3> opcode_prefix, bits<2> opcode, string asm, + SDPatternOperator op> + : I<(outs GPR64:$dst), (ins GPR64:$Rd), asm, "\t$Rd", "$dst = $Rd", + [(set GPR64:$dst, (op GPR64:$Rd, opcode, (i64 0)))]>, + Sched<[]> { bits<5> Rd; let Inst{31-15} = 0b11011010110000010; let Inst{14-12} = opcode_prefix; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 4e1be2696d59c..d0b539695900e 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -752,23 +752,25 @@ let Uses = [LR], Defs = [LR], CRm = 0b0000 in { // These pointer authentication isntructions require armv8.3a let Predicates = [HasPA] in { - multiclass SignAuth<bits<3> prefix, bits<3> prefix_z, string asm> { - def IA : SignAuthOneData<prefix, 0b00, !strconcat(asm, "ia")>; - def IB : SignAuthOneData<prefix, 0b01, !strconcat(asm, "ib")>; - def DA : SignAuthOneData<prefix, 0b10, !strconcat(asm, "da")>; - def DB : SignAuthOneData<prefix, 0b11, !strconcat(asm, "db")>; - def IZA : SignAuthZero<prefix_z, 0b00, !strconcat(asm, "iza")>; - def DZA : SignAuthZero<prefix_z, 0b10, !strconcat(asm, "dza")>; - def IZB : SignAuthZero<prefix_z, 0b01, !strconcat(asm, "izb")>; - def DZB : SignAuthZero<prefix_z, 0b11, !strconcat(asm, "dzb")>; + multiclass SignAuth<bits<3> prefix, bits<3> prefix_z, string asm, + Intrinsic op> { + def IA : SignAuthOneData<prefix, 0b00, !strconcat(asm, "ia"), op>; + def IB : SignAuthOneData<prefix, 0b01, !strconcat(asm, "ib"), op>; + def DA : SignAuthOneData<prefix, 0b10, !strconcat(asm, "da"), op>; + def DB : SignAuthOneData<prefix, 0b11, !strconcat(asm, "db"), op>; + def IZA : SignAuthZero<prefix_z, 0b00, !strconcat(asm, "iza"), op>; + def DZA : SignAuthZero<prefix_z, 0b10, !strconcat(asm, "dza"), op>; + def IZB : SignAuthZero<prefix_z, 0b01, !strconcat(asm, "izb"), op>; + def DZB : SignAuthZero<prefix_z, 0b11, !strconcat(asm, "dzb"), op>; } - defm PAC : SignAuth<0b000, 0b010, "pac">; - defm AUT : SignAuth<0b001, 0b011, "aut">; + defm PAC : SignAuth<0b000, 0b010, "pac", int_ptrauth_sign>; + defm AUT : SignAuth<0b001, 0b011, "aut", null_frag>; def XPACI : SignAuthZero<0b100, 0b00, "xpaci">; def XPACD : SignAuthZero<0b100, 0b01, "xpacd">; - def PACGA : SignAuthTwoOperand<0b1100, "pacga", null_frag>; + + def PACGA : SignAuthTwoOperand<0b1100, "pacga", int_ptrauth_sign_generic>; // Combined Instructions def BRAA : AuthBranchTwoOperands<0, 0, "braa">; diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll new file mode 100644 index 0000000000000..ac3588530769a --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll @@ -0,0 +1,100 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs | FileCheck %s --check-prefixes=ALL + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +define i64 @test_sign_ia(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_sign_ia: +; ALL: ; %bb.0: +; ALL-NEXT: pacia x0, x1 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 0, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_sign_ia_zero(i64 %arg) { +; ALL-LABEL: test_sign_ia_zero: +; ALL: ; %bb.0: +; ALL-NEXT: paciza x0 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 0, i64 0) + ret i64 %tmp +} + +define i64 @test_sign_ib(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_sign_ib: +; ALL: ; %bb.0: +; ALL-NEXT: pacib x0, x1 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 1, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_sign_ib_zero(i64 %arg) { +; ALL-LABEL: test_sign_ib_zero: +; ALL: ; %bb.0: +; ALL-NEXT: pacizb x0 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 1, i64 0) + ret i64 %tmp +} + +define i64 @test_sign_da(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_sign_da: +; ALL: ; %bb.0: +; ALL-NEXT: pacda x0, x1 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 2, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_sign_da_zero(i64 %arg) { +; ALL-LABEL: test_sign_da_zero: +; ALL: ; %bb.0: +; ALL-NEXT: pacdza x0 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 2, i64 0) + ret i64 %tmp +} + +define i64 @test_sign_db(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_sign_db: +; ALL: ; %bb.0: +; ALL-NEXT: pacdb x0, x1 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 3, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_sign_db_zero(i64 %arg) { +; ALL-LABEL: test_sign_db_zero: +; ALL: ; %bb.0: +; ALL-NEXT: pacdzb x0 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 3, i64 0) + ret i64 %tmp +} + +define i64 @test_sign_generic(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_sign_generic: +; ALL: ; %bb.0: +; ALL-NEXT: pacga x0, x0, x1 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.sign.generic.i64(i64 %arg, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_sign_cse(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_sign_cse: +; ALL: ; %bb.0: +; ALL-NEXT: pacia x0, x1 +; ALL-NEXT: add x0, x0, x0 +; ALL-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 0, i64 %arg1) + %tmp1 = call i64 @llvm.ptrauth.sign.i64(i64 %arg, i32 0, i64 %arg1) + %tmp2 = add i64 %tmp0, %tmp1 + ret i64 %tmp2 +} + +declare i64 @llvm.ptrauth.sign.i64(i64, i32, i64) +declare i64 @llvm.ptrauth.sign.generic.i64(i64, i64) From 9622ba45c67056c5b78cecdbc7fb3f45c757ba88 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 16 Sep 2019 09:08:12 -0700 Subject: [PATCH 512/582] [AArch64] Lower llvm.ptrauth.auth/resign intrinsics. Auth/resign are lowered late, to one of three sequences: - poison result on auth failure - trap on auth failure - unchecked The sequence is lowered in AsmPrinter to guarantee the integrity of the intermediate (unauthenticated) values against code motion or spills. The intermediate values are also only help in x16/x17, which, on arm64e Darwin, have additional integrity guarantees on context switch. --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 172 +++++ .../Target/AArch64/AArch64ISelDAGToDAG.cpp | 61 ++ llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 6 + llvm/lib/Target/AArch64/AArch64InstrInfo.td | 26 + .../AArch64/arm64e-ptrauth-intrinsics.ll | 660 +++++++++++++++++- 5 files changed, 924 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 7ea7915c2ca6a..a5dd7b6e5b6a9 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -62,6 +62,16 @@ using namespace llvm; +enum PtrauthCheckMode { Default, Unchecked, Poison, Trap }; +static cl::opt<PtrauthCheckMode> +PtrauthAuthChecks("aarch64-ptrauth-auth-checks", cl::Hidden, + cl::values( + clEnumValN(Unchecked, "none", "don't test for failure"), + clEnumValN(Poison, "poison", "poison on failure"), + clEnumValN(Trap, "trap", "trap on failure")), + cl::desc("Check pointer authentication auth/resign failures"), + cl::init(Default)); + #define DEBUG_TYPE "asm-printer" namespace { @@ -987,6 +997,168 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) { } } + case AArch64::AUT: + case AArch64::AUTPAC: { + const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC; + + // We can expand AUT/AUTPAC into 3 possible sequences: + // - unchecked: + // autia x16, x0 + // pacib x16, x1 ; if AUTPAC + // + // - checked and clearing: + // mov x17, x16 + // autia x16, x0 + // xpaci x17 + // cmp x16, x17 + // pacib x16, x1 + // csel x16, x16, x17, eq + // Where we only emit the AUT if we started with an AUT. + // + // - checked and trapping: + // mov x17, x16 + // autia x16, x0 + // xpaci x17 + // cmp x16, x17 + // b.eq Lsuccess + // brk #<0xc470 + aut key> + // Lsuccess: + // pacib x16, x1 ; if AUTPAC + // Where the b.eq skips over the trap if the PAC is valid. + // + // This sequence is expensive, but we need more information to be able to + // do better. + // + // We can't TBZ the poison bit because EnhancedPAC2 XORs the PAC bits + // on failure. + // We can't TST the PAC bits because we don't always know how the address + // space is setup for the target environment (and the bottom PAC bit is + // based on that). + // Either way, we also don't always know whether TBI is enabled or not for + // the specific target environment. + // + // FIXME: we could re-use AUTReg as a temporary register, but that would + // require splitting the XZR cases into separate opcodes. + + // By default, auth/resign sequences check for auth failures. + bool ShouldCheck = true; + // In the checked sequence, we only trap if explicitly requested. + bool ShouldTrap = MF->getFunction().hasFnAttribute("ptrauth-auth-traps"); + + // However, command-line flags can override this, for experimentation. + switch (PtrauthAuthChecks) { + case PtrauthCheckMode::Default: break; + case PtrauthCheckMode::Unchecked: + ShouldCheck = ShouldTrap = false; + break; + case PtrauthCheckMode::Poison: + ShouldCheck = true; + ShouldTrap = false; + break; + case PtrauthCheckMode::Trap: + ShouldCheck = ShouldTrap = true; + break; + } + + const auto AUTKey = (AArch64PACKey::ID)MI->getOperand(0).getImm(); + const unsigned AUTReg = MI->getOperand(1).getReg(); + + const unsigned XPACOpc = getXPACOpcodeForKey(AUTKey); + const bool AUTZero = AUTReg == AArch64::XZR; + const unsigned AUTOpc = getAUTOpcodeForKey(AUTKey, AUTZero); + + // Checked AUTPACs and trapping AUTs need a temporary copy of the input: x17 + if ((IsAUTPAC && ShouldCheck) || ShouldTrap) { + // mov x17, x16 + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::ORRXrs) + .addReg(AArch64::X17) + .addReg(AArch64::XZR) + .addReg(AArch64::X16) + .addImm(0)); + } + + // autia x16, x0 + MCInst AUTInst; + AUTInst.setOpcode(AUTOpc); + AUTInst.addOperand(MCOperand::createReg(AArch64::X16)); + AUTInst.addOperand(MCOperand::createReg(AArch64::X16)); + if (!AUTZero) + AUTInst.addOperand(MCOperand::createReg(AUTReg)); + EmitToStreamer(*OutStreamer, AUTInst); + + // Unchecked or checked-but-non-trapping AUT is just an "AUT": we're done. + if (!IsAUTPAC && (!ShouldCheck || !ShouldTrap)) + return; + + // Checked sequences do an additional strip-and-compare. + if (ShouldCheck) { + // xpaci x17 + EmitToStreamer(*OutStreamer, + MCInstBuilder(XPACOpc) + .addReg(AArch64::X17) + .addReg(AArch64::X17)); + + // cmp x16, x17 + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::SUBSXrs) + .addReg(AArch64::XZR) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0)); + + // Trapping sequences do a 'brk'. + if (ShouldTrap) { + // b.eq Lsuccess + // where Lsuccess is encoded as 2 (the offset from this instruction to + // what's after the brk, divided by 4) + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::Bcc) + .addImm(AArch64CC::EQ) + .addImm(2)); + + // brk #<0xc470 + aut key> + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::BRK) + .addImm(0xc470 | AUTKey)); + } + } + + // We already emitted unchecked and checked-but-non-trapping AUTs. + // That left us with trapping AUTs, and AUTPACs. + // Trapping AUTs don't need PAC: we're done. + if (!IsAUTPAC) + return; + + const auto PACKey = (AArch64PACKey::ID)MI->getOperand(2).getImm(); + const unsigned PACReg = MI->getOperand(3).getReg(); + const bool PACZero = PACReg == AArch64::XZR; + const unsigned PACOpc = getPACOpcodeForKey(PACKey, PACZero); + + // pacib x16, x9 + MCInst PACInst; + PACInst.setOpcode(PACOpc); + PACInst.addOperand(MCOperand::createReg(AArch64::X16)); + PACInst.addOperand(MCOperand::createReg(AArch64::X16)); + if (!PACZero) + PACInst.addOperand(MCOperand::createReg(PACReg)); + EmitToStreamer(*OutStreamer, PACInst); + + // Non-trapping AUTPAC selects the result based on the xpac check. + // Trapping AUTPAC already trapped; unchecked AUTPAC didn't even check. + if (ShouldTrap || !ShouldCheck) + return; + + // csel x16, x16, x17, eq + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::CSELXr) + .addReg(AArch64::X16) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0)); + return; + } + // Tail calls use pseudo instructions so they have the proper code-gen // attributes (isCall, isReturn, etc.). We lower them to the real // instruction here. diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp index 4d4f76e8e9128..62301e5ef0c36 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -3354,6 +3354,67 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) { if (tryMULLV64LaneV128(IntNo, Node)) return; break; + + case Intrinsic::ptrauth_resign: { + SDLoc DL(Node); + // IntrinsicID is operand #0 + SDValue Val = Node->getOperand(1); + SDValue AUTKey = Node->getOperand(2); + SDValue AUTDisc = Node->getOperand(3); + SDValue PACKey = Node->getOperand(4); + SDValue PACDisc = Node->getOperand(5); + + unsigned AUTKeyC = cast<ConstantSDNode>(AUTKey)->getZExtValue(); + unsigned PACKeyC = cast<ConstantSDNode>(PACKey)->getZExtValue(); + + AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64); + PACKey = CurDAG->getTargetConstant(PACKeyC, DL, MVT::i64); + + SDValue ImpDef = SDValue( + CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, DL, MVT::i64), 0); + SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, + AArch64::X16, Val, SDValue()); + SDValue X17Copy = + CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X17, + ImpDef, X16Copy.getValue(1)); + + SDValue Ops[] = {AUTKey, AUTDisc, PACKey, PACDisc, X17Copy.getValue(1)}; + SDVTList VTs = CurDAG->getVTList(MVT::Other, MVT::Glue); + SDNode *N = CurDAG->getMachineNode(AArch64::AUTPAC, DL, VTs, Ops); + N = CurDAG->getCopyFromReg(SDValue(N, 0), DL, AArch64::X16, MVT::i64, + SDValue(N, 1)).getNode(); + ReplaceNode(Node, N); + return; + } + + case Intrinsic::ptrauth_auth: { + SDLoc DL(Node); + // IntrinsicID is operand #0 + SDValue Val = Node->getOperand(1); + SDValue AUTKey = Node->getOperand(2); + SDValue AUTDisc = Node->getOperand(3); + + unsigned AUTKeyC = cast<ConstantSDNode>(AUTKey)->getZExtValue(); + AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64); + + SDValue ImpDef = SDValue( + CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, DL, MVT::i64), 0); + SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, + AArch64::X16, Val, SDValue()); + SDValue X17Copy = + CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X17, + ImpDef, X16Copy.getValue(1)); + + SDValue Ops[] = {AUTKey, AUTDisc, X17Copy.getValue(1)}; + + SDVTList VTs = CurDAG->getVTList(MVT::Other, MVT::Glue); + SDNode *N = CurDAG->getMachineNode(AArch64::AUT, DL, VTs, Ops); + N = CurDAG->getCopyFromReg(SDValue(N, 0), DL, AArch64::X16, MVT::i64, + SDValue(N, 1)).getNode(); + ReplaceNode(Node, N); + return; + } + } break; } diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 5c35e5bcdd30e..245c1aa8cdacc 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -110,6 +110,12 @@ unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { // This gets lowered to an instruction sequence which takes 16 bytes NumBytes = 16; break; + case AArch64::AUT: + NumBytes = 24; + break; + case AArch64::AUTPAC: + NumBytes = 28; + break; case AArch64::JumpTableDest32: case AArch64::JumpTableDest16: case AArch64::JumpTableDest8: diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index d0b539695900e..931b07da65335 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -793,6 +793,32 @@ let Predicates = [HasPA] in { defm LDRAA : AuthLoad<0, "ldraa", simm10Scaled>; defm LDRAB : AuthLoad<1, "ldrab", simm10Scaled>; + // AUT pseudo. + // This directly manipulates x16/x17, which are the only registers the OS + // guarantees are safe to use for sensitive operations. + def AUT : Pseudo<(outs), (ins i32imm:$Key, GPR64all:$Rn), []>, + Sched<[WriteI, ReadI]> { + let isCodeGenOnly = 1; + let hasSideEffects = 1; + let mayStore = 0; + let mayLoad = 0; + let Defs = [X16,X17,NZCV]; + let Uses = [X16,X17]; + } + + // AUT and re-PAC a value, using different keys/data. + // This directly manipulates x16/x17, which are the only registers the OS + // guarantees are safe to use for sensitive operations. + def AUTPAC : Pseudo<(outs), (ins i32imm:$AUTKey, GPR64:$AUTRn, + i32imm:$PACKey, GPR64:$PACRn), []>, + Sched<[WriteI, ReadI]> { + let isCodeGenOnly = 1; + let hasSideEffects = 1; + let mayStore = 0; + let mayLoad = 0; + let Defs = [X16,X17,NZCV]; + let Uses = [X16,X17]; + } } // v8.3a floating point conversion for javascript diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll index ac3588530769a..b349d96751f9b 100644 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll @@ -1,8 +1,250 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs | FileCheck %s --check-prefixes=ALL +; RUN: llc < %s -mtriple arm64e-apple-darwin -aarch64-ptrauth-auth-checks=none -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,UNCHECKED +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,CHECKED +; RUN: llc < %s -mtriple arm64e-apple-darwin -aarch64-ptrauth-auth-checks=trap -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,TRAP target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +define i64 @test_auth_ia(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_auth_ia: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autia x16, x1 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_ia: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autia x16, x1 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_ia: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autia x16, x1 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 0, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_ia_zero(i64 %arg) { +; UNCHECKED-LABEL: test_auth_ia_zero: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autiza x16 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_ia_zero: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autiza x16 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_ia_zero: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autiza x16 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 0, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_ib(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_auth_ib: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autib x16, x1 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_ib: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autib x16, x1 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_ib: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autib x16, x1 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc471 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 1, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_ib_zero(i64 %arg) { +; UNCHECKED-LABEL: test_auth_ib_zero: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autizb x16 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_ib_zero: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autizb x16 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_ib_zero: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autizb x16 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc471 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 1, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_da(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_auth_da: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autda x16, x1 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_da: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autda x16, x1 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_da: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autda x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 2, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_da_zero(i64 %arg) { +; UNCHECKED-LABEL: test_auth_da_zero: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autdza x16 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_da_zero: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autdza x16 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_da_zero: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autdza x16 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 2, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_db(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_auth_db: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autdb x16, x1 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_db: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_db: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 3, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_db_zero(i64 %arg) { +; UNCHECKED-LABEL: test_auth_db_zero: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autdzb x16 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_db_zero: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autdzb x16 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_db_zero: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autdzb x16 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 3, i64 0) + ret i64 %tmp +} + define i64 @test_sign_ia(i64 %arg, i64 %arg1) { ; ALL-LABEL: test_sign_ia: ; ALL: ; %bb.0: @@ -84,6 +326,373 @@ define i64 @test_sign_generic(i64 %arg, i64 %arg1) { ret i64 %tmp } +; Note that this might seem like a no-op but is actually a valid way to enforce +; the validity of a signature. +define i64 @test_resign_ia_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_ia_ia: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autia x16, x1 +; UNCHECKED-NEXT: pacia x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_ia_ia: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autia x16, x1 +; CHECKED-NEXT: xpaci x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacia x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_ia_ia: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autia x16, x1 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: pacia x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 0, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_ib_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_ib_ia: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autib x16, x1 +; UNCHECKED-NEXT: pacia x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_ib_ia: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autib x16, x1 +; CHECKED-NEXT: xpaci x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacia x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_ib_ia: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autib x16, x1 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc471 +; TRAP-NEXT: pacia x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 1, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_da_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_da_ia: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autda x16, x1 +; UNCHECKED-NEXT: pacia x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_da_ia: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autda x16, x1 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacia x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_da_ia: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autda x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: pacia x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 2, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_db_ia: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autdb x16, x1 +; UNCHECKED-NEXT: pacia x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_db_ia: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacia x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_db_ia: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: pacia x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 3, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_ib(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_db_ib: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autdb x16, x1 +; UNCHECKED-NEXT: pacib x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_db_ib: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacib x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_db_ib: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: pacib x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 3, i64 %arg1, i32 1, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_da(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_db_da: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autdb x16, x1 +; UNCHECKED-NEXT: pacda x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_db_da: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacda x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_db_da: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: pacda x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 3, i64 %arg1, i32 2, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_db(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_db_db: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autdb x16, x1 +; UNCHECKED-NEXT: pacdb x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_db_db: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacdb x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_db_db: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: pacdb x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 3, i64 %arg1, i32 3, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_iza_db(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_iza_db: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autiza x16 +; UNCHECKED-NEXT: pacdb x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_iza_db: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autiza x16 +; CHECKED-NEXT: xpaci x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacdb x16, x2 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_iza_db: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autiza x16 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: pacdb x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 0, i64 0, i32 3, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_da_dzb(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_da_dzb: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autda x16, x1 +; UNCHECKED-NEXT: pacdzb x16 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_da_dzb: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autda x16, x1 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: pacdzb x16 +; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_da_dzb: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autda x16, x1 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: pacdzb x16 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign.i64(i64 %arg, i32 2, i64 %arg1, i32 3, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_cse(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_auth_cse: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autia x16, x1 +; UNCHECKED-NEXT: add x0, x16, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_cse: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autia x16, x1 +; CHECKED-NEXT: add x0, x16, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_cse: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autia x16, x1 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: add x0, x16, x16 +; TRAP-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 0, i64 %arg1) + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 0, i64 %arg1) + %tmp2 = add i64 %tmp0, %tmp1 + ret i64 %tmp2 +} + define i64 @test_sign_cse(i64 %arg, i64 %arg1) { ; ALL-LABEL: test_sign_cse: ; ALL: ; %bb.0: @@ -96,5 +705,54 @@ define i64 @test_sign_cse(i64 %arg, i64 %arg1) { ret i64 %tmp2 } +define i64 @test_blend_cse(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_blend_cse: +; ALL: ; %bb.0: +; ALL-NEXT: bfi x0, x1, #48, #16 +; ALL-NEXT: add x0, x0, x0 +; ALL-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend.i64(i64 %arg, i64 %arg1) + %tmp1 = call i64 @llvm.ptrauth.blend.i64(i64 %arg, i64 %arg1) + %tmp2 = add i64 %tmp0, %tmp1 + ret i64 %tmp2 +} + +define i64 @test_auth_trap_attribute(i64 %arg, i64 %arg1) "ptrauth-auth-traps" { +; UNCHECKED-LABEL: test_auth_trap_attribute: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autia x16, x1 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_trap_attribute: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: autia x16, x1 +; CHECKED-NEXT: xpaci x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq #8 +; CHECKED-NEXT: brk #0xc470 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_trap_attribute: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: autia x16, x1 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth.i64(i64 %arg, i32 0, i64 %arg1) + ret i64 %tmp +} + +declare i64 @llvm.ptrauth.auth.i64(i64, i32, i64) declare i64 @llvm.ptrauth.sign.i64(i64, i32, i64) declare i64 @llvm.ptrauth.sign.generic.i64(i64, i64) +declare i64 @llvm.ptrauth.resign.i64(i64, i32, i64, i32, i64) From 2a06c80a657a14a548d1deecad83e3774206690d Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 16 Sep 2019 08:54:16 -0700 Subject: [PATCH 513/582] [AArch64] Lower llvm.ptrauth.strip intrinsics. D-key strip is straightforward, but, as an optimization, XPACI goes through an untied pseudo, which avoids the need to save/restore LR in certain fast-path users of return address builtins (at the cost of additional copies in the less critical functions that already had a stack frame). --- .../AArch64/AArch64ExpandPseudoInsts.cpp | 31 +++++++++++++++++++ llvm/lib/Target/AArch64/AArch64InstrInfo.td | 13 ++++++-- .../AArch64/arm64e-ptrauth-intrinsics.ll | 11 +++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp index 082e17e44d043..7b0c7b7cbd2e8 100644 --- a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp @@ -662,6 +662,37 @@ bool AArch64ExpandPseudo::expandMI(MachineBasicBlock &MBB, MI.eraseFromParent(); return true; } + case AArch64::XPACIuntied: { + const MachineOperand &LHS = MI.getOperand(0); + const MachineOperand &RHS = MI.getOperand(1); + // If the registrs are the same, just lower to the "tied" version. + // $x0 = XPACIuntied $x0 -> $x0 = XPACI $x0. + if (LHS.getReg() == RHS.getReg()) { + MachineInstrBuilder DefMIB = + BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::XPACI)) + .add(LHS) + .add(RHS); + transferImpOps(MI, DefMIB, DefMIB); + } else { + // $x0 = XPACIuntied $x1 + // -> + // mov $x0, $x1 + // XPACI $x0. + MachineInstrBuilder DefMIB = + BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ORRXrs)) + .addReg(LHS.getReg()) + .addReg(AArch64::XZR) + .add(RHS) + .addImm(0); + MachineInstrBuilder UseMIB = + BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::XPACI), + LHS.getReg()) + .addReg(LHS.getReg()); + transferImpOps(MI, UseMIB, DefMIB); + } + MI.eraseFromParent(); + return true; + } case AArch64::IRGstack: { MachineFunction &MF = *MBB.getParent(); const AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>(); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 931b07da65335..84b44c03a99e0 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -767,8 +767,17 @@ let Predicates = [HasPA] in { defm PAC : SignAuth<0b000, 0b010, "pac", int_ptrauth_sign>; defm AUT : SignAuth<0b001, 0b011, "aut", null_frag>; - def XPACI : SignAuthZero<0b100, 0b00, "xpaci">; - def XPACD : SignAuthZero<0b100, 0b01, "xpacd">; + def XPACI : SignAuthZero<0b100, 0b00, "xpaci", null_frag>; + // Pseudo of the previous instruction with untied operands. Lowers to: + // mov $dst, $src + // xpaci $dst + def XPACIuntied : Pseudo<(outs GPR64:$dst), (ins GPR64:$src), []>, Sched<[]>; + def : Pat<(int_ptrauth_strip GPR64:$Rd, 0), (XPACIuntied GPR64:$Rd)>; + def : Pat<(int_ptrauth_strip GPR64:$Rd, 1), (XPACIuntied GPR64:$Rd)>; + + def XPACD : SignAuthZero<0b100, 0b01, "xpacd", null_frag>; + def : Pat<(int_ptrauth_strip GPR64:$Rd, 2), (XPACD GPR64:$Rd)>; + def : Pat<(int_ptrauth_strip GPR64:$Rd, 3), (XPACD GPR64:$Rd)>; def PACGA : SignAuthTwoOperand<0b1100, "pacga", int_ptrauth_sign_generic>; diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll index b349d96751f9b..1262db57b6a03 100644 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll @@ -661,6 +661,16 @@ define i64 @test_resign_da_dzb(i64 %arg, i64 %arg1, i64 %arg2) { ret i64 %tmp } +define i64 @test_strip(i64 %arg) { +; ALL-LABEL: test_strip: +; ALL: ; %bb.0: +; ALL-NEXT: xpaci x0 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.strip.i64(i64 %arg, i32 0) + ret i64 %tmp +} + + define i64 @test_auth_cse(i64 %arg, i64 %arg1) { ; UNCHECKED-LABEL: test_auth_cse: ; UNCHECKED: ; %bb.0: @@ -756,3 +766,4 @@ declare i64 @llvm.ptrauth.auth.i64(i64, i32, i64) declare i64 @llvm.ptrauth.sign.i64(i64, i32, i64) declare i64 @llvm.ptrauth.sign.generic.i64(i64, i64) declare i64 @llvm.ptrauth.resign.i64(i64, i32, i64, i32, i64) +declare i64 @llvm.ptrauth.strip.i64(i64, i32) From 08678efdde2f6dc1f8bca5d264afebfcb92b92c4 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 16 Sep 2019 08:55:06 -0700 Subject: [PATCH 514/582] [AArch64] Lower llvm.ptrauth.blend intrinsics. Blends are implemented using a simple: movk Xn, Xn, #<discriminator> when the discriminator is a 16-bit integer constant, or: bfmxxx Xn, Xn, Xd Note that this is technically an ABI decision: this really only implements the 'blend' operation used in the arm64e ABI. --- .../lib/Target/AArch64/AArch64InstrFormats.td | 7 +++++++ llvm/lib/Target/AArch64/AArch64InstrInfo.td | 10 ++++++++++ .../AArch64/arm64e-ptrauth-intrinsics.ll | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td index 11bd86c492063..10d6bd68ff9ab 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -694,6 +694,13 @@ def i64_imm0_65535 : Operand<i64>, TImmLeaf<i64, [{ }]>; } +def imm64_0_65535 : Operand<i64>, ImmLeaf<i64, [{ + return Imm < 65536; +}]> { + let ParserMatchClass = AsmImmRange<0, 65535>; + let PrintMethod = "printImmHex"; +} + // imm0_255 predicate - True if the immediate is in the range [0,255]. def Imm0_255Operand : AsmImmRange<0,255>; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 84b44c03a99e0..e2581e99d1101 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -7212,5 +7212,15 @@ let AddedComplexity = 10 in { // FIXME: add SVE dot-product patterns. } +//----------------------------------------------------------------------------- +// v8.3 Pointer Authentication late patterns + +let Predicates = [HasPA] in { +def : Pat<(int_ptrauth_blend GPR64:$Rd, imm64_0_65535:$imm), + (MOVKXi GPR64:$Rd, (trunc_imm imm64_0_65535:$imm), 48)>; +def : Pat<(int_ptrauth_blend GPR64:$Rd, GPR64:$Rn), + (BFMXri GPR64:$Rd, GPR64:$Rn, 16, 15)>; +} + include "AArch64InstrAtomics.td" include "AArch64SVEInstrInfo.td" diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll index 1262db57b6a03..9a192037eb3fe 100644 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll @@ -670,6 +670,24 @@ define i64 @test_strip(i64 %arg) { ret i64 %tmp } +define i64 @test_blend(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_blend: +; ALL: ; %bb.0: +; ALL-NEXT: bfi x0, x1, #48, #16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.blend.i64(i64 %arg, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_blend_constant(i64 %arg) { +; ALL-LABEL: test_blend_constant: +; ALL: ; %bb.0: +; ALL-NEXT: movk x0, #12345, lsl #48 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.blend.i64(i64 %arg, i64 12345) + ret i64 %tmp +} + define i64 @test_auth_cse(i64 %arg, i64 %arg1) { ; UNCHECKED-LABEL: test_auth_cse: @@ -767,3 +785,4 @@ declare i64 @llvm.ptrauth.sign.i64(i64, i32, i64) declare i64 @llvm.ptrauth.sign.generic.i64(i64, i64) declare i64 @llvm.ptrauth.resign.i64(i64, i32, i64, i32, i64) declare i64 @llvm.ptrauth.strip.i64(i64, i32) +declare i64 @llvm.ptrauth.blend.i64(i64, i64) From 9f1a12cffd5f3e4a9ffcd03dd50a0d2fc65accac Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Mon, 16 Sep 2019 08:57:11 -0700 Subject: [PATCH 515/582] [AArch64] Lower auth calls/tail-calls. --- llvm/lib/Target/AArch64/AArch64InstrInfo.td | 61 ++++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index e2581e99d1101..655b3df37c827 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -724,6 +724,17 @@ defm FCADD : SIMDThreeSameVectorComplexHSD<1, 0b111, complexrotateopodd, defm FCMLA : SIMDIndexedTiedComplexHSD<1, 0, 1, complexrotateop, "fcmla", null_frag>; +def AArch64authcall : SDNode<"AArch64ISD::AUTH_CALL", + SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; + +def AArch64authtcret: SDNode<"AArch64ISD::AUTH_TC_RETURN", + SDTypeProfile<0, 4, [SDTCisPtrTy<0>, + SDTCisVT<2, i32>, + SDTCisVT<3, i64>]>, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + // v8.3a Pointer Authentication // These instructions inhabit part of the hint space and so can be used for // armv8 targets @@ -782,15 +793,29 @@ let Predicates = [HasPA] in { def PACGA : SignAuthTwoOperand<0b1100, "pacga", int_ptrauth_sign_generic>; // Combined Instructions - def BRAA : AuthBranchTwoOperands<0, 0, "braa">; - def BRAB : AuthBranchTwoOperands<0, 1, "brab">; - def BLRAA : AuthBranchTwoOperands<1, 0, "blraa">; - def BLRAB : AuthBranchTwoOperands<1, 1, "blrab">; + let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in { + def BRAA : AuthBranchTwoOperands<0, 0, "braa">; + def BRAB : AuthBranchTwoOperands<0, 1, "brab">; + def BRAAZ : AuthOneOperand<0b000, 0, "braaz">; + def BRABZ : AuthOneOperand<0b000, 1, "brabz">; + } + + let isCall = 1, Defs = [LR], Uses = [SP] in { + def BLRAA : AuthBranchTwoOperands<1, 0, "blraa">; + def BLRAB : AuthBranchTwoOperands<1, 1, "blrab">; + + def BLRAAZ : AuthOneOperand<0b001, 0, "blraaz">; + def BLRABZ : AuthOneOperand<0b001, 1, "blrabz">; + } - def BRAAZ : AuthOneOperand<0b000, 0, "braaz">; - def BRABZ : AuthOneOperand<0b000, 1, "brabz">; - def BLRAAZ : AuthOneOperand<0b001, 0, "blraaz">; - def BLRABZ : AuthOneOperand<0b001, 1, "blrabz">; + def : Pat<(AArch64authcall GPR64:$Rn, (i32 0), GPR64sp:$Rm), + (BLRAA GPR64:$Rn, GPR64:$Rm)>; + def : Pat<(AArch64authcall GPR64:$Rn, (i32 1), GPR64sp:$Rm), + (BLRAB GPR64:$Rn, GPR64:$Rm)>; + def : Pat<(AArch64authcall GPR64:$Rn, (i32 0), (i64 0)), + (BLRAAZ GPR64:$Rn)>; + def : Pat<(AArch64authcall GPR64:$Rn, (i32 1), (i64 0)), + (BLRABZ GPR64:$Rn)>; let isReturn = 1, isTerminator = 1, isBarrier = 1 in { def RETAA : AuthReturn<0b010, 0, "retaa">; @@ -828,6 +853,26 @@ let Predicates = [HasPA] in { let Defs = [X16,X17,NZCV]; let Uses = [X16,X17]; } + + let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, + Uses = [SP] in { + def AUTH_TCRETURNriri : Pseudo<(outs), (ins tcGPR64:$dst, i32imm:$FPDiff, + i32imm:$Key, tcGPR64:$Rn), + []>, Sched<[WriteBrReg]>; + def AUTH_TCRETURNrii : Pseudo<(outs), (ins tcGPR64:$dst, i32imm:$FPDiff, + i32imm:$Key), + []>, Sched<[WriteBrReg]>; + } + + def : Pat<(AArch64authtcret tcGPR64:$dst, (i32 timm:$FPDiff), (i32 timm:$Key), + tcGPR64:$Rn), + (AUTH_TCRETURNriri tcGPR64:$dst, imm:$FPDiff, imm:$Key, + tcGPR64:$Rn)>; + + def : Pat<(AArch64authtcret tcGPR64:$dst, (i32 timm:$FPDiff), (i32 timm:$Key), + (i64 0)), + (AUTH_TCRETURNrii tcGPR64:$dst, imm:$FPDiff, imm:$Key)>; + } // v8.3a floating point conversion for javascript From cf672d9e35443495eadd3039658d96e53527a298 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 07:39:38 -0700 Subject: [PATCH 516/582] [AArch64] Fold auth + load sequences into LDRA. LDRAA/LDRAB are able to authenticate the pointer used as the base address in the load address calculation. Because of encoding constraints, they assume that: - the keys are data keys (DA/DB) - the discriminator is zero. Despite these constraints, this is common enough in the arm64e ABI that this is worth combining. This piggy-backs on the existing indexed/writeback load logic, and additionally matches an "llvm.ptrauth.auth" intrinsic. We can select both w/ and w/o writeback, but there are complications when we do: see the tests and comments for the interesting cases. --- .../Target/AArch64/AArch64ISelDAGToDAG.cpp | 133 +++++++- .../CodeGen/AArch64/arm64e-ptrauth-load.ll | 298 ++++++++++++++++++ 2 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp index 62301e5ef0c36..f5f94fd76dc0c 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -178,6 +178,8 @@ class AArch64DAGToDAGISel : public SelectionDAGISel { bool tryIndexedLoad(SDNode *N); + bool tryAuthLoad(SDNode *N); + bool trySelectStackSlotTagP(SDNode *N); void SelectTagP(SDNode *N); @@ -1240,6 +1242,133 @@ bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) { return true; } +bool AArch64DAGToDAGISel::tryAuthLoad(SDNode *N) { + LoadSDNode *LD = cast<LoadSDNode>(N); + EVT VT = LD->getMemoryVT(); + if (VT != MVT::i64) + return false; + + assert(LD->getExtensionType() == ISD::NON_EXTLOAD && "invalid 64bit extload"); + + ISD::MemIndexedMode AM = LD->getAddressingMode(); + bool isPre = AM == ISD::PRE_INC; + if (!isPre && AM != ISD::UNINDEXED) + return false; + + SDValue Chain = LD->getChain(); + SDValue Ptr = LD->getBasePtr(); + + SDValue Base = Ptr; + + int64_t OffsetVal = 0; + if (isPre) { + OffsetVal = cast<ConstantSDNode>(LD->getOffset())->getSExtValue(); + } else if (CurDAG->isBaseWithConstantOffset(Base)) { + // We support both 'base' and 'base + constant offset' modes. + ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Base.getOperand(1)); + if (!RHS) + return false; + OffsetVal = RHS->getSExtValue(); + Base = Base.getOperand(0); + } + + if (!isShiftedInt<10, 3>(OffsetVal)) + return false; + + // The base must be of the form: + // (int_ptrauth_auth <signedbase>, da/db, 0) + if (Base.getOpcode() != ISD::INTRINSIC_WO_CHAIN) + return false; + + unsigned IntID = cast<ConstantSDNode>(Base.getOperand(0))->getZExtValue(); + if (IntID != Intrinsic::ptrauth_auth) + return false; + unsigned IntKey = cast<ConstantSDNode>(Base.getOperand(2))->getZExtValue(); + if (!isNullConstant(Base.getOperand(3))) + return false; + + // If the pointer is an address computation based on an intermediate auth + // that's used more than once, it's not worth folding the auth, as we can't + // writeback just the auth result (without the address computation). + // + // FIXME: we can turn it into an unchecked auth though. + if (OffsetVal && !Base.hasOneUse()) + return false; + + Base = Base.getOperand(1); + + // If this is an indexed pre-inc load, we obviously need the writeback form. + bool needsWriteback = isPre; + // If not, but the base authenticated pointer has any other use, it's + // beneficial to use the writeback form, to "writeback" the auth, even if + // there is no base+offset addition. + if (!Ptr.hasOneUse()) { + needsWriteback = true; + + // However, we can only do that if we don't introduce cycles between the + // load node and any other user of the pointer computation nodes. That can + // happen if the load node uses any of said other users. + // In other words: we can only do this transformation if none of the other + // uses of the pointer computation to be folded are predecessors of the load + // we're folding into. + // + // Visited is a cache containing nodes that are known predecessors of N. + // Worklist is the set of nodes we're looking for predecessors of. + // For the first lookup, that only contains the load node N. Each call to + // hasPredecessorHelper adds any of the potential predecessors of N to the + // Worklist. + SmallPtrSet<const SDNode *, 32> Visited; + SmallVector<const SDNode *, 16> Worklist; + Worklist.push_back(N); + for (SDNode *U : Ptr.getNode()->uses()) + if (SDNode::hasPredecessorHelper(U, Visited, Worklist, /*Max=*/32, + /*TopologicalPrune=*/true)) + return false; + } + + unsigned Opc = 0; + switch (IntKey) { + case AArch64PACKey::DA: + Opc = needsWriteback ? AArch64::LDRAAwriteback : AArch64::LDRAAindexed; + break; + case AArch64PACKey::DB: + Opc = needsWriteback ? AArch64::LDRABwriteback : AArch64::LDRABindexed; + break; + default: + return false; + } + + SDLoc DL(N); + // The offset is encoded as scaled, for an element size of 8 bytes. + SDValue Offset = CurDAG->getTargetConstant(OffsetVal / 8, DL, MVT::i64); + SDValue Ops[] = { Base, Offset, Chain }; + SDNode *Res = needsWriteback ? + CurDAG->getMachineNode(Opc, DL, MVT::i64, MVT::i64, MVT::Other, Ops) : + CurDAG->getMachineNode(Opc, DL, MVT::i64, MVT::Other, Ops); + + if (isPre) { + // If the original load was pre-inc, the resulting LDRA is writeback. + assert(needsWriteback && "preinc loads can't be selected into non-wb ldra"); + ReplaceUses(SDValue(N, 1), SDValue(Res, 0)); // writeback + ReplaceUses(SDValue(N, 0), SDValue(Res, 1)); // loaded value + ReplaceUses(SDValue(N, 2), SDValue(Res, 2)); // chain + } else if (needsWriteback) { + // If the original load was unindexed, but we emitted a writeback form, + // we need to replace the uses of the original auth(signedbase)[+offset] + // computation. + ReplaceUses(Ptr, SDValue(Res, 0)); // writeback + ReplaceUses(SDValue(N, 0), SDValue(Res, 1)); // loaded value + ReplaceUses(SDValue(N, 1), SDValue(Res, 2)); // chain + } else { + // Otherwise, we selected a simple load to a simple non-wb ldra. + assert(Ptr.hasOneUse() && "reused auth ptr should be folded into ldra"); + ReplaceUses(SDValue(N, 0), SDValue(Res, 0)); // loaded value + ReplaceUses(SDValue(N, 1), SDValue(Res, 1)); // chain + } + CurDAG->RemoveDeadNode(N); + return true; +} + void AArch64DAGToDAGISel::SelectLoad(SDNode *N, unsigned NumVecs, unsigned Opc, unsigned SubRegIdx) { SDLoc dl(N); @@ -2902,8 +3031,8 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) { break; case ISD::LOAD: { - // Try to select as an indexed load. Fall through to normal processing - // if we can't. + if (tryAuthLoad(Node)) + return; if (tryIndexedLoad(Node)) return; break; diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll new file mode 100644 index 0000000000000..d7ed3c78e2b42 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll @@ -0,0 +1,298 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +define i64 @test_load_auth_da(i64* %ptr) { +; CHECK-LABEL: test_load_auth_da: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x0, [x0] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = inttoptr i64 %tmp1 to i64* + %tmp3 = load i64, i64* %tmp2 + ret i64 %tmp3 +} + +define i64 @test_load_auth_db(i64* %ptr) { +; CHECK-LABEL: test_load_auth_db: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x0, [x0] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = inttoptr i64 %tmp1 to i64* + %tmp3 = load i64, i64* %tmp2 + ret i64 %tmp3 +} + +; Offset. + +define i64 @test_load_auth_da_8(i64* %ptr) { +; CHECK-LABEL: test_load_auth_da_8: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x0, [x0, #8] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 8 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + ret i64 %tmp4 +} + +define i64 @test_load_auth_da_m8(i64* %ptr) { +; CHECK-LABEL: test_load_auth_da_m8: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x0, [x0, #-8] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, -8 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + ret i64 %tmp4 +} + +define i64 @test_load_auth_db_4088(i64* %ptr) { +; CHECK-LABEL: test_load_auth_db_4088: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x0, [x0, #4088] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 4088 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + ret i64 %tmp4 +} + +; Offset invalid cases. + +define i64 @test_load_auth_da_4(i64* %ptr) { +; CHECK-LABEL: test_load_auth_da_4: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: ldur x0, [x16, #4] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 4 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + ret i64 %tmp4 +} + +define i64 @test_load_auth_da_4096(i64* %ptr) { +; CHECK-LABEL: test_load_auth_da_4096: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: ldr x0, [x16, #4096] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 4096 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + ret i64 %tmp4 +} + +; Pre-indexed variant. + +define i64* @test_load_auth_da_8_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_da_8_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x8, [x0, #8]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 8 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + ret i64* %tmp3 +} + +define i64* @test_load_auth_db_248_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_db_248_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x8, [x0, #248]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 248 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + ret i64* %tmp3 +} + +define i64* @test_load_auth_db_m256_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_db_m256_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x8, [x0, #-256]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, -256 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + ret i64* %tmp3 +} + +; "Pre-indexed" with index 0: writeback the auth result. + +define i64* @test_load_auth_da_0_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_da_0_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x8, [x0]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = inttoptr i64 %tmp1 to i64* + %tmp3 = load i64, i64* %tmp2 + store i64 %tmp3, i64* %dst + ret i64* %tmp2 +} + +; "Pre-indexed" with index 0, with a potential cycle. + +define void @test_load_auth_da_0_pre_cycle(i64* %ptr, i64* %dst, i64* %dst2, i64* %dst3) { +; CHECK-LABEL: test_load_auth_da_0_pre_cycle: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: str x16, [x2] +; CHECK-NEXT: ldr x8, [x16] +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = inttoptr i64 %tmp1 to i64* + store i64 %tmp1, i64* %dst2 + %tmp3 = load i64, i64* %tmp2 + store i64 %tmp3, i64* %dst + ret void +} + +; Pre-indexed invalid offsets. + +define i64* @test_load_auth_db_4_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_db_4_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdzb x16 +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: ldr x8, [x0, #4]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 4 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + ret i64* %tmp3 +} + +define i64* @test_load_auth_db_4096_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_db_4096_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdzb x16 +; CHECK-NEXT: add x0, x16, #1, lsl #12 ; =4096 +; CHECK-NEXT: ldr x8, [x16, #4096] +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 4096 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + ret i64* %tmp3 +} + +define i64* @test_load_auth_db_256_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_db_256_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x8, [x0, #256]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 256 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + ret i64* %tmp3 +} + +define i64* @test_load_auth_db_m264_pre(i64* %ptr, i64* %dst) { +; CHECK-LABEL: test_load_auth_db_m264_pre: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x8, [x0, #-264]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, -264 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + ret i64* %tmp3 +} + +; Pre-indexed multiple-use of the auth. + +define i64* @test_load_auth_da_8_pre_use(i64* %ptr, i64* %dst, i64* %dst2) { +; CHECK-LABEL: test_load_auth_da_8_pre_use: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: ldr x8, [x0, #8]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: str x16, [x2] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 8 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + store i64 %tmp1, i64* %dst2 + ret i64* %tmp3 +} + +; Pre-indexed multiple-use of the auth, invalid offset. + +define i64* @test_load_auth_da_256_pre_use(i64* %ptr, i64* %dst, i64* %dst2) { +; CHECK-LABEL: test_load_auth_da_256_pre_use: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: ldr x8, [x16, #256] +; CHECK-NEXT: add x0, x16, #256 ; =256 +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: str x16, [x2] +; CHECK-NEXT: ret + %tmp0 = ptrtoint i64* %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 256 + %tmp3 = inttoptr i64 %tmp2 to i64* + %tmp4 = load i64, i64* %tmp3 + store i64 %tmp4, i64* %dst + store i64 %tmp1, i64* %dst2 + ret i64* %tmp3 +} + +declare i64 @llvm.ptrauth.auth.i64(i64, i32, i64) From 8198c132fd6cac08c87dce427017dd5fa7cfd572 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 07:26:31 -0700 Subject: [PATCH 517/582] [AArch64] Support emitting "hardened" jump-tables. These are used in a similar manner to existing jump-tables, but they guarantee the integrity of the intermediate values used to compute the final branch target, by using a combined compute-address-and-jump pseudo, as well as holding temporaries in x16/x17, which on arm64e Darwin have additional integrity guarantees. This uses a new 'expand hardened pseudo' pass, which runs almost as late as AsmPrinter (for integrity guarantees), but before the LOH collector (to allow some of the address materialization sequences to be optimized). This isn't strictly related to pointer authentication, but allows for preserving the arm64e CFI guarantees, while avoiding the cost of jump-table entry authentication. --- llvm/lib/Target/AArch64/AArch64.h | 2 + .../AArch64/AArch64ExpandHardenedPseudos.cpp | 181 ++++++++++++++++++ .../Target/AArch64/AArch64ISelLowering.cpp | 15 ++ llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 5 + llvm/lib/Target/AArch64/AArch64InstrInfo.td | 11 ++ .../Target/AArch64/AArch64TargetMachine.cpp | 5 + llvm/lib/Target/AArch64/CMakeLists.txt | 1 + llvm/test/CodeGen/AArch64/O0-pipeline.ll | 1 + llvm/test/CodeGen/AArch64/O3-pipeline.ll | 1 + .../AArch64/arm64e-jump-table-hardening.ll | 49 +++++ 10 files changed, 271 insertions(+) create mode 100644 llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp create mode 100644 llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll diff --git a/llvm/lib/Target/AArch64/AArch64.h b/llvm/lib/Target/AArch64/AArch64.h index ac765ebcddc04..f2113888e44d9 100644 --- a/llvm/lib/Target/AArch64/AArch64.h +++ b/llvm/lib/Target/AArch64/AArch64.h @@ -48,6 +48,7 @@ FunctionPass *createAArch64A53Fix835769(); FunctionPass *createFalkorHWPFFixPass(); FunctionPass *createFalkorMarkStridedAccessesPass(); FunctionPass *createAArch64BranchTargetsPass(); +FunctionPass *createAArch64ExpandHardenedPseudosPass(); FunctionPass *createAArch64CleanupLocalDynamicTLSPass(); @@ -69,6 +70,7 @@ void initializeAArch64CompressJumpTablesPass(PassRegistry&); void initializeAArch64ConditionalComparesPass(PassRegistry&); void initializeAArch64ConditionOptimizerPass(PassRegistry&); void initializeAArch64DeadRegisterDefinitionsPass(PassRegistry&); +void initializeAArch64ExpandHardenedPseudosPass(PassRegistry&); void initializeAArch64ExpandPseudoPass(PassRegistry&); void initializeAArch64SpeculationHardeningPass(PassRegistry&); void initializeAArch64LoadStoreOptPass(PassRegistry&); diff --git a/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp b/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp new file mode 100644 index 0000000000000..4ce8e54ae6c67 --- /dev/null +++ b/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp @@ -0,0 +1,181 @@ +//===- AArch64ExpandHardenedPseudos.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "AArch64InstrInfo.h" +#include "AArch64Subtarget.h" +#include "AArch64MachineFunctionInfo.h" +#include "MCTargetDesc/AArch64AddressingModes.h" +#include "Utils/AArch64BaseInfo.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/Pass.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetMachine.h" +#include <cassert> + +using namespace llvm; + +#define DEBUG_TYPE "aarch64-expand-hardened-pseudos" + +#define PASS_NAME "AArch64 Expand Hardened Pseudos" + +namespace { + +class AArch64ExpandHardenedPseudos : public MachineFunctionPass { +public: + static char ID; + + AArch64ExpandHardenedPseudos() : MachineFunctionPass(ID) { + initializeAArch64ExpandHardenedPseudosPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &Fn) override; + + StringRef getPassName() const override { + return PASS_NAME; + } + +private: + bool expandMI(MachineInstr &MI); +}; + +} // end anonymous namespace + +char AArch64ExpandHardenedPseudos::ID = 0; + +INITIALIZE_PASS(AArch64ExpandHardenedPseudos, DEBUG_TYPE, PASS_NAME, false, false); + +bool AArch64ExpandHardenedPseudos::expandMI(MachineInstr &MI) { + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + DebugLoc DL = MI.getDebugLoc(); + auto MBBI = MI.getIterator(); + + const AArch64Subtarget &STI = MF.getSubtarget<AArch64Subtarget>(); + const AArch64InstrInfo *TII = STI.getInstrInfo(); + + if (MI.getOpcode() == AArch64::BR_JumpTable) { + LLVM_DEBUG(dbgs() << "Expanding: " << MI << "\n"); + const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); + assert(MJTI && "Can't lower jump-table dispatch without JTI"); + + const std::vector<MachineJumpTableEntry> &JTs = MJTI->getJumpTables(); + assert(!JTs.empty() && "Invalid JT index for jump-table dispatch"); + + // Emit: + // adrp xTable, Ltable@PAGE + // add xTable, Ltable@PAGEOFF + // mov xEntry, #<size of table> ; depending on table size, with MOVKs + // cmp xEntry, #<size of table> ; if table size fits in 12-bit immediate + // csel xEntry, xEntry, xzr, ls + // ldrsw xScratch, [xTable, xEntry, lsl #2] ; kill xEntry, xScratch = xEntry + // add xDest, xTable, xScratch ; kill xTable, xDest = xTable + // br xDest + + MachineOperand JTOp = MI.getOperand(0); + + unsigned JTI = JTOp.getIndex(); + const uint64_t NumTableEntries = JTs[JTI].MBBs.size(); + + // cmp only supports a 12-bit immediate. If we need more, materialize the + // immediate, using TableReg as a scratch register. + uint64_t MaxTableEntry = NumTableEntries - 1; + if (isUInt<12>(MaxTableEntry)) { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SUBSXri), AArch64::XZR) + .addReg(AArch64::X16) + .addImm(MaxTableEntry) + .addImm(0); + } else { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVZXi), AArch64::X17) + .addImm(static_cast<uint16_t>(MaxTableEntry)) + .addImm(0); + // It's sad that we have to manually materialize instructions, but we can't + // trivially reuse the main pseudo expansion logic. + // A MOVK sequence is easy enough to generate and handles the general case. + for (int Offset = 16; Offset < 64; Offset += 16) { + if ((MaxTableEntry >> Offset) == 0) + break; + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVKXi), AArch64::X17) + .addReg(AArch64::X17) + .addImm(static_cast<uint16_t>(MaxTableEntry >> Offset)) + .addImm(Offset); + } + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0); + } + + // This picks entry #0 on failure. + // We might want to trap instead. + BuildMI(MBB, MBBI, DL, TII->get(AArch64::CSELXr), AArch64::X16) + .addReg(AArch64::X16) + .addReg(AArch64::XZR) + .addImm(AArch64CC::LS); + + MachineOperand JTHiOp(JTOp); + MachineOperand JTLoOp(JTOp); + JTHiOp.setTargetFlags(AArch64II::MO_PAGE); + JTLoOp.setTargetFlags(AArch64II::MO_PAGEOFF); + + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADRP), AArch64::X17) + .add(JTHiOp); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXri), AArch64::X17) + .addReg(AArch64::X17) + .add(JTLoOp) + .addImm(0); + + BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRSWroX), AArch64::X16) + .addReg(AArch64::X17) + .addReg(AArch64::X16) + .addImm(0) + .addImm(1); + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXrs), AArch64::X16) + .addReg(AArch64::X17) + .addReg(AArch64::X16) + .addImm(0); + + BuildMI(MBB, MBBI, DL, TII->get(AArch64::BR)) + .addReg(AArch64::X16); + + MI.eraseFromParent(); + return true; + } + + return false; +} + + +bool AArch64ExpandHardenedPseudos::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG(dbgs() << "***** AArch64ExpandHardenedPseudos *****\n"); + + bool Modified = false; + for (auto &MBB : MF) { + for (auto MBBI = MBB.begin(), MBBE = MBB.end(); MBBI != MBBE; ) { + auto &MI = *MBBI++; + Modified |= expandMI(MI); + } + } + return Modified; +} + +FunctionPass *llvm::createAArch64ExpandHardenedPseudosPass() { + return new AArch64ExpandHardenedPseudos(); +} diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 8e3a524ed2cb1..9fcfdbd42aca8 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -5274,6 +5274,21 @@ SDValue AArch64TargetLowering::LowerBR_JT(SDValue Op, SDValue Entry = Op.getOperand(2); int JTI = cast<JumpTableSDNode>(JT.getNode())->getIndex(); + // With aarch64-hardened-codegen, we only expand the full jump table dispatch + // sequence later, to guarantee the integrity of the intermediate values. + if (DAG.getMachineFunction().getFunction() + .hasFnAttribute("jump-table-hardening") || + Subtarget->getTargetTriple().getArchName() == "arm64e") { + if (getTargetMachine().getCodeModel() != CodeModel::Small) + report_fatal_error("Unsupported code-model for hardened jump-table"); + SDValue Chain = DAG.getCopyToReg(DAG.getEntryNode(), DL, AArch64::X16, + Entry, SDValue()); + SDNode *B = DAG.getMachineNode(AArch64::BR_JumpTable, DL, MVT::Other, + DAG.getTargetJumpTable(JTI, MVT::i32), + Chain.getValue(0), Chain.getValue(1)); + return SDValue(B, 0); + } + SDNode *Dest = DAG.getMachineNode(AArch64::JumpTableDest32, DL, MVT::i64, MVT::i64, JT, Entry, DAG.getTargetJumpTable(JTI, MVT::i32)); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 245c1aa8cdacc..9eb31174ba3b7 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -116,6 +116,11 @@ unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { case AArch64::AUTPAC: NumBytes = 28; break; + case AArch64::BR_JumpTable: + // 28 fixed + 16 variable, for table size materialization + // We could potentially model the variable size overhead more accurately. + NumBytes = 44; + break; case AArch64::JumpTableDest32: case AArch64::JumpTableDest16: case AArch64::JumpTableDest8: diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 655b3df37c827..a05979e9c871b 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -604,6 +604,17 @@ def JumpTableDest8 : Pseudo<(outs GPR64:$dst, GPR64sp:$scratch), Sched<[]>; } +def BR_JumpTable : Pseudo<(outs), (ins i32imm:$jti), []>, + Sched<[]> { + let isBranch = 1; + let isTerminator = 1; + let isIndirectBranch = 1; + let isBarrier = 1; + let Defs = [X16,X17,NZCV]; + let Uses = [X16]; + let Size = 44; // 28 fixed + 16 variable, for table size materialization +} + // Space-consuming pseudo to aid testing of placement and reachability // algorithms. Immediate operand is the number of bytes this "instruction" // occupies; register operands can be used to enforce dependency and constrain diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index 40817fc0b4072..4df314aadc2fa 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -184,6 +184,7 @@ extern "C" void LLVMInitializeAArch64Target() { initializeAArch64SpeculationHardeningPass(*PR); initializeAArch64StackTaggingPass(*PR); initializeAArch64StackTaggingPreRAPass(*PR); + initializeAArch64ExpandHardenedPseudosPass(*PR); } //===----------------------------------------------------------------------===// @@ -637,6 +638,10 @@ void AArch64PassConfig::addPreEmitPass() { if (TM->getOptLevel() != CodeGenOpt::None && EnableCompressJumpTables) addPass(createAArch64CompressJumpTablesPass()); + // Expand hardened pseudo-instructions. + // Do this now to enable LOH emission. + addPass(createAArch64ExpandHardenedPseudosPass()); + if (TM->getOptLevel() != CodeGenOpt::None && EnableCollectLOH && TM->getTargetTriple().isOSBinFormatMachO()) addPass(createAArch64CollectLOHPass()); diff --git a/llvm/lib/Target/AArch64/CMakeLists.txt b/llvm/lib/Target/AArch64/CMakeLists.txt index 103925d45d510..ae68830f6871d 100644 --- a/llvm/lib/Target/AArch64/CMakeLists.txt +++ b/llvm/lib/Target/AArch64/CMakeLists.txt @@ -33,6 +33,7 @@ add_llvm_target(AArch64CodeGen AArch64CondBrTuning.cpp AArch64ConditionalCompares.cpp AArch64DeadRegisterDefinitionsPass.cpp + AArch64ExpandHardenedPseudos.cpp AArch64ExpandImm.cpp AArch64ExpandPseudoInsts.cpp AArch64FalkorHWPFFix.cpp diff --git a/llvm/test/CodeGen/AArch64/O0-pipeline.ll b/llvm/test/CodeGen/AArch64/O0-pipeline.ll index 20214fb83f4e1..7d9e6b9a5a52e 100644 --- a/llvm/test/CodeGen/AArch64/O0-pipeline.ll +++ b/llvm/test/CodeGen/AArch64/O0-pipeline.ll @@ -60,6 +60,7 @@ ; CHECK-NEXT: Analyze Machine Code For Garbage Collection ; CHECK-NEXT: Branch relaxation pass ; CHECK-NEXT: AArch64 Branch Targets +; CHECK-NEXT: AArch64 Expand Hardened Pseudos ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis diff --git a/llvm/test/CodeGen/AArch64/O3-pipeline.ll b/llvm/test/CodeGen/AArch64/O3-pipeline.ll index f483d631167c0..c6fbf8fc236eb 100644 --- a/llvm/test/CodeGen/AArch64/O3-pipeline.ll +++ b/llvm/test/CodeGen/AArch64/O3-pipeline.ll @@ -166,6 +166,7 @@ ; CHECK-NEXT: Branch relaxation pass ; CHECK-NEXT: AArch64 Branch Targets ; CHECK-NEXT: AArch64 Compress Jump Tables +; CHECK-NEXT: AArch64 Expand Hardened Pseudos ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis diff --git a/llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll b/llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll new file mode 100644 index 0000000000000..48f37b856b589 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll @@ -0,0 +1,49 @@ +; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-enable-atomic-cfg-tidy=0 | FileCheck %s + +; CHECK-LABEL: test_jumptable: +; CHECK: mov w[[INDEX:[0-9]+]], w0 +; CHECK: cmp x[[INDEX]], #5 +; CHECK: csel [[INDEX2:x[0-9]+]], x[[INDEX]], xzr, ls +; CHECK: adrp [[JTPAGE:x[0-9]+]], LJTI0_0@PAGE +; CHECK: add x[[JT:[0-9]+]], [[JTPAGE]], LJTI0_0@PAGEOFF +; CHECK: ldrsw [[OFFSET:x[0-9]+]], [x[[JT]], [[INDEX2]], lsl #2] +; CHECK: add [[DEST:x[0-9]+]], x[[JT]], [[OFFSET]] +; CHECK: br [[DEST]] + +define i32 @test_jumptable(i32 %in) "jump-table-hardening" { + + switch i32 %in, label %def [ + i32 0, label %lbl1 + i32 1, label %lbl2 + i32 2, label %lbl3 + i32 4, label %lbl4 + i32 5, label %lbl5 + ] + +def: + ret i32 0 + +lbl1: + ret i32 1 + +lbl2: + ret i32 2 + +lbl3: + ret i32 4 + +lbl4: + ret i32 8 + +lbl5: + ret i32 10 + +} + +; CHECK: LJTI0_0: +; CHECK-NEXT: .long LBB{{[0-9_]+}}-LJTI0_0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-LJTI0_0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-LJTI0_0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-LJTI0_0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-LJTI0_0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-LJTI0_0 From 5b022b21037a0ff4bb034cec0bb4786f48102f0f Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 06:27:13 -0700 Subject: [PATCH 518/582] [IR] Define "ptrauth" operand bundles. ptrauth operand bundles, when added to a call instruction, describe a combined llvm.ptrauth.auth + call operation. Like llvm.ptrauth.auth, ptrauth operand bundles use: - a key identifier (a 32-bit integer immediate constant) - a discriminator (a 64-bit value, that can be the result of a blend) --- llvm/include/llvm/IR/InstrTypes.h | 9 ++++---- llvm/include/llvm/IR/LLVMContext.h | 1 + llvm/lib/IR/LLVMContext.cpp | 5 ++++ llvm/lib/IR/Verifier.cpp | 16 +++++++++++-- .../Scalar/TailRecursionElimination.cpp | 3 ++- .../Bitcode/operand-bundles-bc-analyzer.ll | 1 + .../Transforms/TailCallElim/ptrauth-bundle.ll | 12 ++++++++++ llvm/test/Verifier/ptrauth-operand-bundles.ll | 23 +++++++++++++++++++ 8 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 llvm/test/Transforms/TailCallElim/ptrauth-bundle.ll create mode 100644 llvm/test/Verifier/ptrauth-operand-bundles.ll diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index faf58cf190146..7789f047ea01f 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1868,9 +1868,9 @@ class CallBase : public Instruction { /// may read from the heap. bool hasReadingOperandBundles() const { // Implementation note: this is a conservative implementation of operand - // bundle semantics, where *any* operand bundle forces a callsite to be at - // least readonly. - return hasOperandBundles(); + // bundle semantics, where any operand bundle (other than ptrauth) forces + // a callsite to be at least readonly. + return hasOperandBundlesOtherThan(LLVMContext::OB_ptrauth); } /// Return true if this operand bundle user has operand bundles that @@ -1878,7 +1878,8 @@ class CallBase : public Instruction { bool hasClobberingOperandBundles() const { for (auto &BOI : bundle_op_infos()) { if (BOI.Tag->second == LLVMContext::OB_deopt || - BOI.Tag->second == LLVMContext::OB_funclet) + BOI.Tag->second == LLVMContext::OB_funclet || + BOI.Tag->second == LLVMContext::OB_ptrauth) continue; // This instruction has an operand bundle that is not known to us. diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h index ea272740ba9dc..9c7b818a14d4c 100644 --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -86,6 +86,7 @@ class LLVMContext { OB_funclet = 1, // "funclet" OB_gc_transition = 2, // "gc-transition" OB_cfguardtarget = 3, // "cfguardtarget" + OB_ptrauth = 4, // "ptrauth" }; /// getMDKindID - Return a unique non-zero ID for the specified metadata kind. diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp index cb13b27aa50f4..46dd48f8512d1 100644 --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -67,6 +67,11 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) { "cfguardtarget operand bundle id drifted!"); (void)CFGuardTargetEntry; + auto *PtrauthEntry = pImpl->getOrInsertBundleTag("ptrauth"); + assert(PtrauthEntry->second == LLVMContext::OB_ptrauth && + "ptrauth operand bundle id drifted!"); + (void)PtrauthEntry; + SyncScope::ID SingleThreadSSID = pImpl->getOrInsertSyncScopeID("singlethread"); assert(SingleThreadSSID == SyncScope::SingleThread && diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 497dc394d9607..5319c3c579166 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2976,9 +2976,11 @@ void Verifier::visitCallBase(CallBase &Call) { visitIntrinsicCall(ID, Call); // Verify that a callsite has at most one "deopt", at most one "funclet", at - // most one "gc-transition", and at most one "cfguardtarget" operand bundle. + // most one "gc-transition", at most one "cfguardtarget", and at most one + // "ptrauth" operand bundle. bool FoundDeoptBundle = false, FoundFuncletBundle = false, - FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false; + FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false, + FoundPtrauthBundle = false; for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) { OperandBundleUse BU = Call.getOperandBundleAt(i); uint32_t Tag = BU.getTagID(); @@ -3003,6 +3005,16 @@ void Verifier::visitCallBase(CallBase &Call) { FoundCFGuardTargetBundle = true; Assert(BU.Inputs.size() == 1, "Expected exactly one cfguardtarget bundle operand", Call); + } else if (Tag == LLVMContext::OB_ptrauth) { + Assert(!FoundPtrauthBundle, "Multiple ptrauth operand bundles", Call); + FoundPtrauthBundle = true; + Assert(BU.Inputs.size() == 2, + "Expected exactly two ptrauth bundle operands", Call); + Assert(isa<ConstantInt>(BU.Inputs[0]) && + BU.Inputs[0]->getType()->isIntegerTy(32), + "Ptrauth bundle key operand must be an i32 constant", Call); + Assert(BU.Inputs[1]->getType()->isIntegerTy(64), + "Ptrauth bundle discriminator operand must be an i64", Call); } } diff --git a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp index b27a36b67d62e..4529567da05b9 100644 --- a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp +++ b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp @@ -240,7 +240,8 @@ static bool markTails(Function &F, bool &AllCallsAreTailCalls, if (!CI || CI->isTailCall() || isa<DbgInfoIntrinsic>(&I)) continue; - bool IsNoTail = CI->isNoTailCall() || CI->hasOperandBundles(); + bool IsNoTail = CI->isNoTailCall() || + CI->hasOperandBundlesOtherThan({LLVMContext::OB_ptrauth}); if (!IsNoTail && CI->doesNotAccessMemory()) { // A call to a readnone function whose arguments are all things computed diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll index ac7feec77aa1b..6d7d4c0a912c3 100644 --- a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll +++ b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll @@ -7,6 +7,7 @@ ; CHECK-NEXT: <OPERAND_BUNDLE_TAG ; CHECK-NEXT: <OPERAND_BUNDLE_TAG ; CHECK-NEXT: <OPERAND_BUNDLE_TAG +; CHECK-NEXT: <OPERAND_BUNDLE_TAG ; CHECK-NEXT: </OPERAND_BUNDLE_TAGS_BLOCK ; CHECK: <FUNCTION_BLOCK diff --git a/llvm/test/Transforms/TailCallElim/ptrauth-bundle.ll b/llvm/test/Transforms/TailCallElim/ptrauth-bundle.ll new file mode 100644 index 0000000000000..578d4d500fa3c --- /dev/null +++ b/llvm/test/Transforms/TailCallElim/ptrauth-bundle.ll @@ -0,0 +1,12 @@ +; RUN: opt < %s -tailcallelim -verify-dom-info -S | FileCheck %s +; Check that the "ptrauth" operand bundle doesn't prevent tail calls. + +declare i64 @f_0(i64 %x) + +define i64 @f_1(i64 %x) { +; CHECK-LABEL: @f_1( +entry: +; CHECK: tail call i64 @f_0(i64 %x) [ "ptrauth"(i32 42, i64 %x) ] + %tmp = call i64 @f_0(i64 %x) [ "ptrauth"(i32 42, i64 %x) ] + ret i64 0 +} diff --git a/llvm/test/Verifier/ptrauth-operand-bundles.ll b/llvm/test/Verifier/ptrauth-operand-bundles.ll new file mode 100644 index 0000000000000..311cea20922ee --- /dev/null +++ b/llvm/test/Verifier/ptrauth-operand-bundles.ll @@ -0,0 +1,23 @@ +; RUN: not opt -verify < %s 2>&1 | FileCheck %s + +declare void @g() + +define void @f_deopt(i64 %arg0, i32 %arg1) { +; CHECK: Multiple ptrauth operand bundles +; CHECK-NEXT: call void @g() [ "ptrauth"(i32 42, i64 100), "ptrauth"(i32 42, i64 %arg0) ] +; CHECK: Ptrauth bundle key operand must be an i32 constant +; CHECK-NEXT: call void @g() [ "ptrauth"(i32 %arg1, i64 120) ] +; CHECK: Ptrauth bundle key operand must be an i32 constant +; CHECK-NEXT: call void @g() [ "ptrauth"(i64 42, i64 120) ] +; CHECK: Ptrauth bundle discriminator operand must be an i64 +; CHECK-NEXT: call void @g() [ "ptrauth"(i32 42, i32 120) ] +; CHECK-NOT: call void @g() [ "ptrauth"(i32 42, i64 120, i32 %x) ] + + entry: + call void @g() [ "ptrauth"(i32 42, i64 100), "ptrauth"(i32 42, i64 %arg0) ] + call void @g() [ "ptrauth"(i32 %arg1, i64 120) ] + call void @g() [ "ptrauth"(i64 42, i64 120) ] + call void @g() [ "ptrauth"(i32 42, i32 120) ] + call void @g() [ "ptrauth"(i32 42, i64 120) ] ;; The verifier should not complain about this one + ret void +} From a90507b2d353b243e3ee755f981ff4544b3908be Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 07:06:58 -0700 Subject: [PATCH 519/582] [InstCombine] Combine ptrauth intrinsics into call operand bundles. Since the "ptrauth" call operand bundles really describe a folded "llvm.ptrauth.auth" operation, there are optimizations we can make to elide redundant sign/auth pairs. Note that this needs to be careful about preserving the integrity of the intermediate unauthenticated values, as that's the sole purpose of having operand bundles in the first place. --- .../InstCombine/InstCombineCalls.cpp | 80 ++++++++++++++++++ .../InstCombine/InstCombineInternal.h | 1 + .../Transforms/InstCombine/ptrauth-call.ll | 82 +++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 llvm/test/Transforms/InstCombine/ptrauth-call.ll diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 654de3ee0172d..fe32a3b50a453 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -4214,6 +4214,81 @@ static IntrinsicInst *findInitTrampoline(Value *Callee) { return nullptr; } +Instruction *InstCombiner::tryCombinePtrAuthCall(CallBase &Call) { + Value *Callee = Call.getCalledValue(); + auto *IPC = dyn_cast<IntToPtrInst>(Callee); + if (!IPC || !IPC->isNoopCast(DL)) + return nullptr; + + IntrinsicInst *II = dyn_cast<IntrinsicInst>(IPC->getOperand(0)); + if (!II) + return nullptr; + + auto PtrAuthBundleOrNone = Call.getOperandBundle(LLVMContext::OB_ptrauth); + assert(Call.getNumOperandBundles() <= 1 && + "unimplemented support for ptrauth and other bundle"); + + Value *NewCallee = nullptr; + SmallVector<OperandBundleDef, 1> NewBundles; + switch (II->getIntrinsicID()) { + default: + return nullptr; + + // call(ptrauth_resign(p)), ["ptrauth"()] -> call p, ["ptrauth"()] + // assuming the call bundle and the sign operands match. + case Intrinsic::ptrauth_resign: { + if (!PtrAuthBundleOrNone) + return nullptr; + auto PtrAuthBundle = *PtrAuthBundleOrNone; + if (II->getOperand(3) != PtrAuthBundle.Inputs[0] || + II->getOperand(4) != PtrAuthBundle.Inputs[1]) + return nullptr; + + Value *NewBundleOps[] = {II->getOperand(1), II->getOperand(2)}; + NewBundles.emplace_back("ptrauth", NewBundleOps); + NewCallee = II->getOperand(0); + break; + } + + // call(ptrauth_sign(p)), ["ptrauth"()] -> call p + // assuming the call bundle and the sign operands match. + case Intrinsic::ptrauth_sign: { + if (!PtrAuthBundleOrNone) + return nullptr; + auto PtrAuthBundle = *PtrAuthBundleOrNone; + if (II->getOperand(1) != PtrAuthBundle.Inputs[0] || + II->getOperand(2) != PtrAuthBundle.Inputs[1]) + return nullptr; + NewCallee = II->getOperand(0); + break; + } + + // call(ptrauth_auth(p)) -> call p, ["ptrauth"()] + case Intrinsic::ptrauth_auth: { + if (PtrAuthBundleOrNone) + return nullptr; + Value *NewBundleOps[] = {II->getOperand(1), II->getOperand(2)}; + NewBundles.emplace_back("ptrauth", NewBundleOps); + NewCallee = II->getOperand(0); + break; + } + } + + if (!NewCallee) + return nullptr; + + NewCallee = Builder.CreateBitOrPointerCast(NewCallee, Callee->getType()); + CallBase *NewCall = nullptr; + if (auto *CI = dyn_cast<CallInst>(&Call)) { + NewCall = CallInst::Create(CI, NewBundles); + } else { + auto *IKI = cast<InvokeInst>(&Call); + NewCall = InvokeInst::Create(IKI, NewBundles); + } + NewCall->setCalledOperand(NewCallee); + return NewCall; +} + static void annotateAnyAllocSite(CallBase &Call, const TargetLibraryInfo *TLI) { unsigned NumArgs = Call.getNumArgOperands(); ConstantInt *Op0C = dyn_cast<ConstantInt>(Call.getOperand(0)); @@ -4356,6 +4431,11 @@ Instruction *InstCombiner::visitCallBase(CallBase &Call) { if (IntrinsicInst *II = findInitTrampoline(Callee)) return transformCallThroughTrampoline(Call, *II); + // Combine calls involving pointer authentication + if (Instruction *NewCall = tryCombinePtrAuthCall(Call)) + return NewCall; + + PointerType *PTy = cast<PointerType>(Callee->getType()); FunctionType *FTy = cast<FunctionType>(PTy->getElementType()); if (FTy->isVarArg()) { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 1dbc06d92e7ae..0a3bfc501ff40 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -505,6 +505,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner bool transformConstExprCastCall(CallBase &Call); Instruction *transformCallThroughTrampoline(CallBase &Call, IntrinsicInst &Tramp); + Instruction *tryCombinePtrAuthCall(CallBase &Call); Value *simplifyMaskedLoad(IntrinsicInst &II); Instruction *simplifyMaskedStore(IntrinsicInst &II); diff --git a/llvm/test/Transforms/InstCombine/ptrauth-call.ll b/llvm/test/Transforms/InstCombine/ptrauth-call.ll new file mode 100644 index 0000000000000..ce272e2552925 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/ptrauth-call.ll @@ -0,0 +1,82 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +define i32 @test_ptrauth_call_resign(i8* %p) { +; CHECK-LABEL: @test_ptrauth_call_resign( +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8* [[P:%.*]] to i32 ()* +; CHECK-NEXT: [[TMP3:%.*]] = call i32 [[TMP1]]() [ "ptrauth"(i32 0, i64 1234) ] +; CHECK-NEXT: ret i32 [[TMP3]] +; + %tmp0 = ptrtoint i8* %p to i64 + %tmp1 = call i64 @llvm.ptrauth.resign.i64(i64 %tmp0, i32 0, i64 1234, i32 2, i64 5678) + %tmp2 = inttoptr i64 %tmp1 to i32()* + %tmp3 = call i32 %tmp2() [ "ptrauth"(i32 2, i64 5678) ] + ret i32 %tmp3 +} + +define i32 @test_ptrauth_call_resign_blend(i8** %pp) { +; CHECK-LABEL: @test_ptrauth_call_resign_blend( +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8** [[PP:%.*]] to i32 ()** +; CHECK-NEXT: [[TMP012:%.*]] = load i32 ()*, i32 ()** [[TMP1]], align 8 +; CHECK-NEXT: [[TMP6:%.*]] = call i32 [[TMP012]]() [ "ptrauth"(i32 0, i64 1234) ] +; CHECK-NEXT: ret i32 [[TMP6]] +; + %tmp0 = load i8*, i8** %pp, align 8 + %tmp1 = ptrtoint i8** %pp to i64 + %tmp2 = ptrtoint i8* %tmp0 to i64 + %tmp3 = call i64 @llvm.ptrauth.blend.i64(i64 %tmp1, i64 5678) + %tmp4 = call i64 @llvm.ptrauth.resign.i64(i64 %tmp2, i32 0, i64 1234, i32 1, i64 %tmp3) + %tmp5 = inttoptr i64 %tmp4 to i32()* + %tmp6 = call i32 %tmp5() [ "ptrauth"(i32 1, i64 %tmp3) ] + ret i32 %tmp6 +} + +define i32 @test_ptrauth_call_resign_blend_2(i8** %pp) { +; CHECK-LABEL: @test_ptrauth_call_resign_blend_2( +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8** [[PP:%.*]] to i32 ()** +; CHECK-NEXT: [[TMP012:%.*]] = load i32 ()*, i32 ()** [[TMP1]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint i8** [[PP]] to i64 +; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[TMP1]], i64 5678) +; CHECK-NEXT: [[TMP6:%.*]] = call i32 [[TMP012]]() [ "ptrauth"(i32 1, i64 [[TMP3]]) ] +; CHECK-NEXT: ret i32 [[TMP6]] +; + %tmp0 = load i8*, i8** %pp, align 8 + %tmp1 = ptrtoint i8** %pp to i64 + %tmp2 = ptrtoint i8* %tmp0 to i64 + %tmp3 = call i64 @llvm.ptrauth.blend.i64(i64 %tmp1, i64 5678) + %tmp4 = call i64 @llvm.ptrauth.resign.i64(i64 %tmp2, i32 1, i64 %tmp3, i32 0, i64 1234) + %tmp5 = inttoptr i64 %tmp4 to i32()* + %tmp6 = call i32 %tmp5() [ "ptrauth"(i32 0, i64 1234) ] + ret i32 %tmp6 +} + +define i32 @test_ptrauth_call_auth(i8* %p) { +; CHECK-LABEL: @test_ptrauth_call_auth( +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8* [[P:%.*]] to i32 ()* +; CHECK-NEXT: [[TMP3:%.*]] = call i32 [[TMP1]]() [ "ptrauth"(i32 2, i64 5678) ] +; CHECK-NEXT: ret i32 [[TMP3]] +; + %tmp0 = ptrtoint i8* %p to i64 + %tmp1 = call i64 @llvm.ptrauth.auth.i64(i64 %tmp0, i32 2, i64 5678) + %tmp2 = inttoptr i64 %tmp1 to i32()* + %tmp3 = call i32 %tmp2() + ret i32 %tmp3 +} + +define i32 @test_ptrauth_call_sign(i8* %p) { +; CHECK-LABEL: @test_ptrauth_call_sign( +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8* [[P:%.*]] to i32 ()* +; CHECK-NEXT: [[TMP3:%.*]] = call i32 [[TMP1]]() +; CHECK-NEXT: ret i32 [[TMP3]] +; + %tmp0 = ptrtoint i8* %p to i64 + %tmp1 = call i64 @llvm.ptrauth.sign.i64(i64 %tmp0, i32 2, i64 5678) + %tmp2 = inttoptr i64 %tmp1 to i32()* + %tmp3 = call i32 %tmp2() [ "ptrauth"(i32 2, i64 5678) ] + ret i32 %tmp3 +} + +declare i64 @llvm.ptrauth.auth.i64(i64, i32, i64) +declare i64 @llvm.ptrauth.sign.i64(i64, i32, i64) +declare i64 @llvm.ptrauth.resign.i64(i64, i32, i64, i32, i64) +declare i64 @llvm.ptrauth.blend.i64(i64, i64) From 41c9f473fb3d4623aaf3a97f56a7e1374cbd7c80 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 06:19:57 -0700 Subject: [PATCH 520/582] [AArch64] Lower "llvm.ptrauth" signed global wrappers. This is a large patch, and basically adds end-to-end support for generating authenticated global relocation. At the IR level, this currently uses a flawed representation (because of downstream compatibility concerns): instead of a "ptrauth sign" ConstantExpr, a signed global pointer is represented using a global variable wrapper, in the special section "llvm.ptrauth". This has some ramifications at the IR level, but is mostly straightforward. In data, signed global references are lowered to the arm64e AUTHENTICATED_POINTER relocation, via AArch64AuthMCExpr. In code, signed global pointers can be materialized using: - a code sequence, via the MOVaddrPAC (hardened) pseudo - a compiler GOT section, __DATA,__auth_ptr. At the ISel level, the reference is represented using a "PtrAuthGlobalAddress" node. --- llvm/include/llvm/CodeGen/AsmPrinter.h | 12 + llvm/include/llvm/CodeGen/ISDOpcodes.h | 4 + .../llvm/CodeGen/MachineModuleInfoImpls.h | 33 ++ llvm/include/llvm/IR/GlobalPtrAuthInfo.h | 122 +++++++ llvm/include/llvm/IR/GlobalValue.h | 4 +- llvm/include/llvm/module.modulemap | 1 + llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 105 +++++- .../SelectionDAG/SelectionDAGBuilder.cpp | 16 +- .../SelectionDAG/SelectionDAGDumper.cpp | 1 + llvm/lib/IR/CMakeLists.txt | 1 + llvm/lib/IR/GlobalPtrAuthInfo.cpp | 152 ++++++++ llvm/lib/IR/Globals.cpp | 9 + llvm/lib/IR/Verifier.cpp | 9 + llvm/lib/MC/MCMachOStreamer.cpp | 3 + llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 78 ++++ .../AArch64/AArch64ExpandHardenedPseudos.cpp | 71 +++- llvm/lib/Target/AArch64/AArch64FastISel.cpp | 6 + .../Target/AArch64/AArch64ISelLowering.cpp | 123 +++++++ llvm/lib/Target/AArch64/AArch64ISelLowering.h | 6 + llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 5 + llvm/lib/Target/AArch64/AArch64InstrInfo.td | 12 + .../lib/Target/AArch64/AArch64MCInstLower.cpp | 63 ++++ .../Instrumentation/AddressSanitizer.cpp | 2 + .../CodeGen/AArch64/arm64e-ptrauth-reloc.ll | 343 ++++++++++++++++++ .../do-not-instrument-ptrauth-globals.ll | 11 + llvm/test/Verifier/ptrauth-global.ll | 21 ++ 26 files changed, 1206 insertions(+), 7 deletions(-) create mode 100644 llvm/include/llvm/IR/GlobalPtrAuthInfo.h create mode 100644 llvm/lib/IR/GlobalPtrAuthInfo.cpp create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-reloc.ll create mode 100644 llvm/test/Instrumentation/AddressSanitizer/do-not-instrument-ptrauth-globals.ll create mode 100644 llvm/test/Verifier/ptrauth-global.ll diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 16298ff7b7276..c9bd44b2ecbac 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -75,6 +75,8 @@ class StackMaps; class TargetLoweringObjectFile; class TargetMachine; +class GlobalPtrAuthInfo; + /// This class is intended to be used as a driving class for all asm writers. class AsmPrinter : public MachineFunctionPass { public: @@ -441,6 +443,16 @@ class AsmPrinter : public MachineFunctionPass { /// instructions in verbose mode. virtual void emitImplicitDef(const MachineInstr *MI) const; + /// Lower the specified "llvm.ptrauth" GlobalVariable to an MCExpr. + virtual const MCExpr * + lowerPtrAuthGlobalConstant(const GlobalPtrAuthInfo &PAI) { + report_fatal_error("llvm.ptrauth global lowering not implemented"); + } + + /// Lower the specified "llvm.ptrauth" GlobalVariable to an MCExpr. + virtual const MCExpr * + lowerBlockAddressConstant(const BlockAddress *BA); + //===------------------------------------------------------------------===// // Symbol Lowering Routines. //===------------------------------------------------------------------===// diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index 658ad31fa2a64..4169bb0bbe66d 100644 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -61,6 +61,10 @@ namespace ISD { GlobalAddress, GlobalTLSAddress, FrameIndex, JumpTable, ConstantPool, ExternalSymbol, BlockAddress, + /// A llvm.ptrauth global + /// wrapper llvm.ptrauth global, ptr, key, addr-disc, disc + PtrAuthGlobalAddress, + /// The address of the GOT GLOBAL_OFFSET_TABLE, diff --git a/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h b/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h index 746e922396130..dae5b1874bcaf 100644 --- a/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h +++ b/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h @@ -25,6 +25,13 @@ class MCSymbol; /// MachineModuleInfoMachO - This is a MachineModuleInfoImpl implementation /// for MachO targets. class MachineModuleInfoMachO : public MachineModuleInfoImpl { +public: + /// The information specific to a Darwin '$auth_ptr' stub. + struct AuthStubInfo { + const MCExpr *Pointer; + }; + +private: /// GVStubs - Darwin '$non_lazy_ptr' stubs. The key is something like /// "Lfoo$non_lazy_ptr", the value is something like "_foo". The extra bit /// is true if this GV is external. @@ -35,6 +42,11 @@ class MachineModuleInfoMachO : public MachineModuleInfoImpl { /// bit is true if this GV is external. DenseMap<MCSymbol *, StubValueTy> ThreadLocalGVStubs; + /// Darwin '$auth_ptr' stubs. The key is the stub symbol, like + /// "Lfoo$addend$auth_ptr$ib$12". The value is the MCExpr representing that + /// pointer, something like "_foo+addend@AUTH(ib, 12)". + DenseMap<MCSymbol *, AuthStubInfo> AuthGVStubs; + virtual void anchor(); // Out of line virtual method. public: @@ -50,11 +62,32 @@ class MachineModuleInfoMachO : public MachineModuleInfoImpl { return ThreadLocalGVStubs[Sym]; } + AuthStubInfo &getAuthGVStubEntry(MCSymbol *Sym) { + assert(Sym && "Key cannot be null"); + return AuthGVStubs[Sym]; + } + /// Accessor methods to return the set of stubs in sorted order. SymbolListTy GetGVStubList() { return getSortedStubs(GVStubs); } SymbolListTy GetThreadLocalGVStubList() { return getSortedStubs(ThreadLocalGVStubs); } + + typedef std::pair<MCSymbol *, AuthStubInfo> AuthStubPairTy; + typedef std::vector<AuthStubPairTy> AuthStubListTy; + + AuthStubListTy getAuthGVStubList() { + AuthStubListTy List(AuthGVStubs.begin(), AuthGVStubs.end()); + + if (!List.empty()) + std::sort(List.begin(), List.end(), + [](const AuthStubPairTy &LHS, const AuthStubPairTy &RHS) { + return LHS.first->getName() < RHS.first->getName(); + }); + + AuthGVStubs.clear(); + return List; + } }; /// MachineModuleInfoELF - This is a MachineModuleInfoImpl implementation diff --git a/llvm/include/llvm/IR/GlobalPtrAuthInfo.h b/llvm/include/llvm/IR/GlobalPtrAuthInfo.h new file mode 100644 index 0000000000000..f5d5e57f5b62a --- /dev/null +++ b/llvm/include/llvm/IR/GlobalPtrAuthInfo.h @@ -0,0 +1,122 @@ +//===- GlobalPtrAuthInfo.h - Analysis tools for ptrauth globals -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file contains a set of utilities to analyze llvm.ptrauth globals, and +/// to decompose them into key, discriminator, and base pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_GLOBALPTRAUTHINFO_H +#define LLVM_IR_GLOBALPTRAUTHINFO_H + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/Optional.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/Support/Error.h" + +namespace llvm { + +/// Helper class to access the information regarding an "llvm.ptrauth" global. +/// These globals are of the form: +/// @sg = constant { i8*, i32, i64, i64 } +/// { i8* bitcast (i32* @g to i8*), ; base pointer +/// i32 2, ; key ID +/// i64 ptrtoint (i8** @pg to i64), ; address discriminator +/// i64 42 ; discriminator +/// }, section "llvm.ptrauth" +/// +class GlobalPtrAuthInfo { + const GlobalVariable *GV; + + const ConstantStruct *getInitializer() const { + return cast<ConstantStruct>(GV->getInitializer()); + } + + GlobalPtrAuthInfo(const GlobalVariable *GV) : GV(GV) {} + +public: + /// A constant value for the address discriminator which has special + /// significance to coroutine lowering. + enum { AddrDiscriminator_UseCoroStorage = 1 }; + + /// Try to analyze \p V as an authenticated global reference, and return its + /// information if successful. + static Optional<GlobalPtrAuthInfo> analyze(const Value *V); + + /// Try to analyze \p V as an authenticated global reference, and return its + /// information if successful, or an error explaining the failure if not. + static Expected<GlobalPtrAuthInfo> tryAnalyze(const Value *V); + + /// Access the information contained in the "llvm.ptrauth" globals. + /// @{ + /// The "llvm.ptrauth" global itself. + const GlobalVariable *getGV() const { return GV; } + + /// The pointer that is authenticated in this authenticated global reference. + const Constant *getPointer() const { + return cast<Constant>(getInitializer()->getOperand(0)); + } + + Constant *getPointer() { + return cast<Constant>(getInitializer()->getOperand(0)); + } + + /// The Key ID, an i32 constant. + const ConstantInt *getKey() const { + return cast<ConstantInt>(getInitializer()->getOperand(1)); + } + + /// The address discriminator if any, or the null constant. + /// If present, this must be a value equivalent to the storage location of + /// the only user of the authenticated ptrauth global. + const Constant *getAddrDiscriminator() const { + return cast<Constant>(getInitializer()->getOperand(2)); + } + + /// Whether there is any non-null address discriminator. + bool hasAddressDiversity() const { + return !getAddrDiscriminator()->isNullValue(); + } + + /// Whether the address uses a special address discriminator. + /// These discriminators can't be used in real pointer-auth values; they + /// can only be used in "prototype" values that indicate how some real + /// schema is supposed to be produced. + bool hasSpecialAddressDiscriminator(uint64_t value) const { + if (auto intValue = dyn_cast<ConstantInt>(getAddrDiscriminator())) + return intValue->getValue() == value; + return false; + } + + /// The discriminator. + const ConstantInt *getDiscriminator() const { + return cast<ConstantInt>(getInitializer()->getOperand(3)); + } + /// @} + + /// Check whether an authentication operation with key \p KeyV and (possibly + /// blended) discriminator \p DiscriminatorV is compatible with this + /// authenticated global reference. + bool isCompatibleWith(const Value *Key, const Value *Discriminator, + const DataLayout &DL) const; + + /// Produce a "llvm.ptrauth" global that signs a value using the given + /// schema. The result will be casted to have the same type as the value. + static llvm::Constant *create(Module &M, Constant *Pointer, ConstantInt *Key, + Constant *AddrDiscriminator, + ConstantInt *Discriminator); + + /// Produce a new "llvm.ptrauth" global for signing the given value using + /// the same schema as is stored in this info. + llvm::Constant *createWithSameSchema(Module &M, Constant *Pointer) const; +}; + +} // end namespace llvm + +#endif diff --git a/llvm/include/llvm/IR/GlobalValue.h b/llvm/include/llvm/IR/GlobalValue.h index 2209881dbda62..fb6ce518b2e0f 100644 --- a/llvm/include/llvm/IR/GlobalValue.h +++ b/llvm/include/llvm/IR/GlobalValue.h @@ -443,9 +443,7 @@ class GlobalValue : public Constant { bool hasInternalLinkage() const { return isInternalLinkage(getLinkage()); } bool hasPrivateLinkage() const { return isPrivateLinkage(getLinkage()); } bool hasLocalLinkage() const { return isLocalLinkage(getLinkage()); } - bool hasExternalWeakLinkage() const { - return isExternalWeakLinkage(getLinkage()); - } + bool hasExternalWeakLinkage() const; bool hasCommonLinkage() const { return isCommonLinkage(getLinkage()); } bool hasValidDeclarationLinkage() const { return isValidDeclarationLinkage(getLinkage()); diff --git a/llvm/include/llvm/module.modulemap b/llvm/include/llvm/module.modulemap index ecb3b37004fd6..7648db1834332 100644 --- a/llvm/include/llvm/module.modulemap +++ b/llvm/include/llvm/module.modulemap @@ -207,6 +207,7 @@ module LLVM_intrinsic_gen { module IR_CallSite { header "IR/CallSite.h" export * } module IR_ConstantFolder { header "IR/ConstantFolder.h" export * } module IR_GlobalVariable { header "IR/GlobalVariable.h" export * } + module IR_GlobalPtrAuthInfo { header "IR/GlobalPtrAuthInfo.h" export * } module IR_NoFolder { header "IR/NoFolder.h" export * } module IR_Module { header "IR/Module.h" export * } module IR_ModuleSummaryIndex { header "IR/ModuleSummaryIndex.h" export * } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 6bff8de4a9cc0..4725b1dba6c50 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -70,6 +70,7 @@ #include "llvm/IR/GlobalIFunc.h" #include "llvm/IR/GlobalIndirectSymbol.h" #include "llvm/IR/GlobalObject.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instruction.h" @@ -1936,6 +1937,10 @@ bool AsmPrinter::EmitSpecialLLVMGlobal(const GlobalVariable *GV) { GV->hasAvailableExternallyLinkage()) return true; + // Ignore ptrauth globals: they only represent references to other globals. + if (GV->getSection() == "llvm.ptrauth") + return true; + if (!GV->hasAppendingLinkage()) return false; assert(GV->hasInitializer() && "Not a special LLVM global!"); @@ -2171,11 +2176,20 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) { if (const ConstantInt *CI = dyn_cast<ConstantInt>(CV)) return MCConstantExpr::create(CI->getZExtValue(), Ctx); - if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) + if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) { + // llvm.ptrauth globals should have been handled in emitGlobalConstantImpl, + // where we still had the offset of the reference. If we see them here, it + // means that they were obfuscated enough that we didn't detect them. + if (auto *GVB = dyn_cast<GlobalVariable>(GV)) + if (GVB->getSection() == "llvm.ptrauth") + report_fatal_error("Unsupported usage of llvm.ptrauth global '" + + GV->getName() + "'"); + return MCSymbolRefExpr::create(getSymbol(GV), Ctx); + } if (const BlockAddress *BA = dyn_cast<BlockAddress>(CV)) - return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), Ctx); + return lowerBlockAddressConstant(BA); const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV); if (!CE) { @@ -2665,6 +2679,70 @@ static void handleIndirectSymViaGOTPCRel(AsmPrinter &AP, const MCExpr **ME, AP.GlobalGOTEquivs[GOTEquivSym] = std::make_pair(GV, NumUses); } +static const GlobalVariable * +stripPtrAuthGlobalVariableCasts(const DataLayout &DL, const Value *V) { + bool FoundInvalidCast = false; + while (true) { + if (auto *CE = dyn_cast<ConstantExpr>(V)) { + if (CE->getOpcode() == Instruction::PtrToInt || + CE->getOpcode() == Instruction::IntToPtr) { + // If we found a size-changing cast, report it later, if we did find an + // llvm.ptrauth global. + if (DL.getTypeAllocSize(CE->getType()) != + DL.getTypeAllocSize(CE->getOperand(0)->getType())) + FoundInvalidCast = true; + V = CE->getOperand(0); + } + } + + if (auto *GV = dyn_cast<GlobalVariable>(V)) { + if (GV->getSection() != "llvm.ptrauth") + return nullptr; + if (FoundInvalidCast) + report_fatal_error("Invalid size-changing cast of llvm.ptrauth global"); + return GV; + } + + // If all else failed, and there are still casts to strip, try again. + // Otherwise, there's nothing else we can look through. + auto *Stripped = V->stripPointerCasts(); + if (Stripped != V) + V = Stripped; + else + return nullptr; + } + + llvm_unreachable("failed to strip ptrauth global casts"); +} + +static void emitPtrAuthGlobalConstant(const DataLayout &DL, + const GlobalVariable *GV, AsmPrinter &AP, + const Constant *BaseCV, uint64_t Offset, + uint64_t Size) { + auto PAI = *GlobalPtrAuthInfo::analyze(GV); + + // Check that the address discriminator matches. + // NOTE: This could in principle be a verifier, but for now this + // always-enabled very conservative check is preferable. + if (PAI.hasAddressDiversity()) { + APInt ComputedOffset(DL.getPointerSizeInBits(), 0); + const GlobalVariable *BaseGV = nullptr; + + auto *BaseCast = dyn_cast<PtrToIntOperator>(PAI.getAddrDiscriminator()); + if (BaseCast) + BaseGV = dyn_cast<GlobalVariable>( + BaseCast->getPointerOperand() + ->stripAndAccumulateInBoundsConstantOffsets(DL, ComputedOffset)); + if (!BaseGV || BaseCV != BaseGV || Offset != ComputedOffset.getZExtValue()) + GV->getContext().emitError( + "Mismatched address discriminator in llvm.ptrauth global '" + + GV->getName() + "'"); + } + + auto *ME = AP.lowerPtrAuthGlobalConstant(PAI); + AP.OutStreamer->EmitValue(ME, Size); +} + static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV, AsmPrinter &AP, const Constant *BaseCV, uint64_t Offset) { @@ -2714,6 +2792,25 @@ static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV, return emitGlobalConstantStruct(DL, CVS, AP, BaseCV, Offset); if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) { + + // Lower "llvm.ptrauth" global references directly, so that we can check + // the address discriminator ourselves, while we still have the offset of + // the constant into the global that contains the reference. + // Only pointer-sized constants could contain an authenticated pointer. + if (Size == DL.getPointerSize()) { + if (auto *GV = stripPtrAuthGlobalVariableCasts(DL, CE)) { + return emitPtrAuthGlobalConstant(DL, GV, AP, BaseCV, Offset, Size); + } + } +#ifndef NDEBUG + // But in asserts builds, check that we didn't let a ptrauth reference slip + // through in a non-pointer-sized constant. + else { + auto *GV = stripPtrAuthGlobalVariableCasts(DL, CE); + assert(!GV && "Invalid non-pointer-sized llvm.ptrauth global reference"); + } +#endif + // Look through bitcasts, which might not be able to be MCExpr'ized (e.g. of // vectors). if (CE->getOpcode() == Instruction::BitCast) @@ -2785,6 +2882,10 @@ MCSymbol *AsmPrinter::GetBlockAddressSymbol(const BasicBlock *BB) const { return MMI->getAddrLabelSymbol(BB); } +const MCExpr *AsmPrinter::lowerBlockAddressConstant(const BlockAddress *BA) { + return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), OutContext); +} + /// GetCPISymbol - Return the symbol for the specified constant pool entry. MCSymbol *AsmPrinter::GetCPISymbol(unsigned CPID) const { if (getSubtargetInfo().getTargetTriple().isWindowsMSVCEnvironment()) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index da96c9ac435ac..058280e6b878b 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -78,6 +78,8 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" @@ -1446,8 +1448,20 @@ SDValue SelectionDAGBuilder::getValueImpl(const Value *V) { if (const ConstantInt *CI = dyn_cast<ConstantInt>(C)) return DAG.getConstant(*CI, getCurSDLoc(), VT); - if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) + if (const GlobalValue *GV = dyn_cast<GlobalValue>(C)) { + if (const GlobalVariable *GVB = dyn_cast<GlobalVariable>(GV)) { + if (GVB->getSection() == "llvm.ptrauth") { + auto PAI = GlobalPtrAuthInfo::analyze(GVB); + return DAG.getNode(ISD::PtrAuthGlobalAddress, getCurSDLoc(), VT, + DAG.getGlobalAddress(GV, getCurSDLoc(), VT), + getValue(PAI->getPointer()), + getValue(PAI->getKey()), + getValue(PAI->getAddrDiscriminator()), + getValue(PAI->getDiscriminator())); + } + } return DAG.getGlobalAddress(GV, getCurSDLoc(), VT); + } if (isa<ConstantPointerNull>(C)) { unsigned AS = V->getType()->getPointerAddressSpace(); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp index bc10f76212394..323bcb15528f7 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -120,6 +120,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { case ISD::GlobalTLSAddress: return "GlobalTLSAddress"; case ISD::FrameIndex: return "FrameIndex"; case ISD::JumpTable: return "JumpTable"; + case ISD::PtrAuthGlobalAddress: return "PtrAuthGlobalAddress"; case ISD::GLOBAL_OFFSET_TABLE: return "GLOBAL_OFFSET_TABLE"; case ISD::RETURNADDR: return "RETURNADDR"; case ISD::ADDROFRETURNADDR: return "ADDROFRETURNADDR"; diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt index a9012637277bf..89fca1008d276 100644 --- a/llvm/lib/IR/CMakeLists.txt +++ b/llvm/lib/IR/CMakeLists.txt @@ -24,6 +24,7 @@ add_llvm_library(LLVMCore Dominators.cpp Function.cpp GVMaterializer.cpp + GlobalPtrAuthInfo.cpp Globals.cpp IRBuilder.cpp IRPrintingPasses.cpp diff --git a/llvm/lib/IR/GlobalPtrAuthInfo.cpp b/llvm/lib/IR/GlobalPtrAuthInfo.cpp new file mode 100644 index 0000000000000..c3a4df3d6c6cf --- /dev/null +++ b/llvm/lib/IR/GlobalPtrAuthInfo.cpp @@ -0,0 +1,152 @@ +//===- GlobalPtrAuthInfo.cpp - Analysis tools for ptrauth globals ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/GlobalPtrAuthInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" + +using namespace llvm; + +Expected<GlobalPtrAuthInfo> GlobalPtrAuthInfo::tryAnalyze(const Value *V) { + auto Invalid = [](const Twine &Reason) { + return make_error<StringError>(Reason, inconvertibleErrorCode()); + }; + + auto &Ctx = V->getContext(); + + V = V->stripPointerCasts(); + + auto *GV = dyn_cast<GlobalVariable>(V); + if (!GV) + return Invalid("value isn't a global"); + + if (GV->getSection() != "llvm.ptrauth") + return Invalid("global isn't in section \"llvm.ptrauth\""); + + if (!GV->hasInitializer()) + return Invalid("global doesn't have an initializer"); + + auto *Init = GV->getInitializer(); + + auto *Ty = dyn_cast<StructType>(GV->getInitializer()->getType()); + if (!Ty) + return Invalid("global isn't a struct"); + + auto *I64Ty = Type::getInt64Ty(Ctx); + auto *I32Ty = Type::getInt32Ty(Ctx); + auto *P0I8Ty = Type::getInt8PtrTy(Ctx); + // Check that the struct matches its expected shape: + // { i8*, i32, i64, i64 } + if (!Ty->isLayoutIdentical( + StructType::get(Ctx, {P0I8Ty, I32Ty, I64Ty, I64Ty}))) + return Invalid("global doesn't have type '{ i8*, i32, i64, i64 }'"); + + auto *Key = dyn_cast<ConstantInt>(Init->getOperand(1)); + if (!Key) + return Invalid("key isn't a constant integer"); + + auto *AddrDiscriminator = Init->getOperand(2); + if (!isa<ConstantInt>(AddrDiscriminator) && + !isa<ConstantExpr>(AddrDiscriminator)) + return Invalid("address discriminator isn't a constant integer or expr"); + + auto *Discriminator = dyn_cast<ConstantInt>(Init->getOperand(3)); + if (!Discriminator) + return Invalid("discriminator isn't a constant integer"); + + return GlobalPtrAuthInfo(GV); +} + +Optional<GlobalPtrAuthInfo> GlobalPtrAuthInfo::analyze(const Value *V) { + if (auto PAIOrErr = tryAnalyze(V)) { + return *PAIOrErr; + } else { + consumeError(PAIOrErr.takeError()); + return None; + } +} + +static bool areEquivalentAddrDiscriminators(const Value *V1, const Value *V2, + const DataLayout &DL) { + APInt V1Off(DL.getPointerSizeInBits(), 0); + APInt V2Off(DL.getPointerSizeInBits(), 0); + + if (auto *V1Cast = dyn_cast<PtrToIntOperator>(V1)) + V1 = V1Cast->getPointerOperand(); + if (auto *V2Cast = dyn_cast<PtrToIntOperator>(V2)) + V2 = V2Cast->getPointerOperand(); + auto *V1Base = V1->stripAndAccumulateInBoundsConstantOffsets(DL, V1Off); + auto *V2Base = V2->stripAndAccumulateInBoundsConstantOffsets(DL, V2Off); + return V1Base == V2Base && V1Off == V2Off; +} + +bool GlobalPtrAuthInfo::isCompatibleWith(const Value *Key, + const Value *Discriminator, + const DataLayout &DL) const { + // If the keys are different, there's no chance for this to be compatible. + if (Key != getKey()) + return false; + + // If the discriminators are the same, this is compatible iff there is no + // address discriminator. + if (Discriminator == getDiscriminator()) + return getAddrDiscriminator()->isNullValue(); + + // If we dynamically blend the discriminator with the address discriminator, + // this is compatible. + if (auto *DiscBlend = dyn_cast<IntrinsicInst>(Discriminator)) { + if (DiscBlend->getIntrinsicID() == Intrinsic::ptrauth_blend && + DiscBlend->getOperand(1) == getDiscriminator() && + areEquivalentAddrDiscriminators(DiscBlend->getOperand(0), + getAddrDiscriminator(), DL)) + return true; + } + + // If we don't have a non-address discriminator, we don't need a blend in + // the first place: accept the address discriminator as the discriminator. + if (getDiscriminator()->isNullValue() && + areEquivalentAddrDiscriminators(getAddrDiscriminator(), Discriminator, + DL)) + return true; + + // Otherwise, we don't know. + return false; +} + +Constant *GlobalPtrAuthInfo::createWithSameSchema(Module &M, + Constant *Pointer) const { + return create(M, Pointer, const_cast<ConstantInt*>(getKey()), + const_cast<Constant*>(getAddrDiscriminator()), + const_cast<ConstantInt*>(getDiscriminator())); +} + +Constant *GlobalPtrAuthInfo::create(Module &M, Constant *Pointer, + ConstantInt *Key, + Constant *AddrDiscriminator, + ConstantInt *Discriminator) { + auto CastPointer = + ConstantExpr::getBitCast(Pointer, Type::getInt8PtrTy(M.getContext())); + + auto Init = ConstantStruct::getAnon({CastPointer, Key, AddrDiscriminator, + Discriminator}, /*packed*/ false); + + // TODO: look for an existing global with the right setup? + auto GV = new GlobalVariable(M, Init->getType(), /*constant*/ true, + GlobalVariable::PrivateLinkage, Init); + GV->setSection("llvm.ptrauth"); + + auto Result = ConstantExpr::getBitCast(GV, Pointer->getType()); + + assert(analyze(Result).hasValue() && "invalid ptrauth constant"); + + return Result; +} diff --git a/llvm/lib/IR/Globals.cpp b/llvm/lib/IR/Globals.cpp index 46a9696b2944f..f0002118f1696 100644 --- a/llvm/lib/IR/Globals.cpp +++ b/llvm/lib/IR/Globals.cpp @@ -133,6 +133,15 @@ void GlobalObject::copyAttributesFrom(const GlobalObject *Src) { setSection(Src->getSection()); } +bool GlobalValue::hasExternalWeakLinkage() const { + // Be conservative with llvm.ptrauth wrappers. + // FIXME: this is gross but necessary with our current representation. + if (isa<GlobalVariable>(this) && + getSection() == "llvm.ptrauth") + return true; + return isExternalWeakLinkage(getLinkage()); +} + std::string GlobalValue::getGlobalIdentifier(StringRef Name, GlobalValue::LinkageTypes Linkage, StringRef FileName) { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 5319c3c579166..8fb448bc0921d 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -77,6 +77,7 @@ #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/InlineAsm.h" @@ -684,6 +685,14 @@ void Verifier::visitGlobalVariable(const GlobalVariable &GV) { } } + if (GV.getSection() == "llvm.ptrauth") { + if (auto Err = GlobalPtrAuthInfo::tryAnalyze(&GV).takeError()) { + CheckFailed("invalid llvm.ptrauth global: " + toString(std::move(Err)), + &GV); + return; + } + } + // Visit any debug info attachments. SmallVector<MDNode *, 1> MDs; GV.getMetadata(LLVMContext::MD_dbg, MDs); diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp index 8e558a36b7a1f..8dac4ff16a582 100644 --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -143,6 +143,9 @@ static bool canGoAfterDWARF(const MCSectionMachO &MSec) { SecName == "__thread_ptr")) return true; + if (SegName == "__DATA" && SecName == "__auth_ptr") + return true; + return false; } diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index a5dd7b6e5b6a9..91f8cd2fa03eb 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -29,6 +29,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" @@ -42,11 +43,13 @@ #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/Casting.h" @@ -94,6 +97,9 @@ class AArch64AsmPrinter : public AsmPrinter { return MCInstLowering.lowerOperand(MO, MCOp); } + const MCExpr * + lowerPtrAuthGlobalConstant(const GlobalPtrAuthInfo &PAI) override; + void EmitJumpTableInfo() override; void emitJumpTableEntry(const MachineJumpTableInfo *MJTI, const MachineBasicBlock *MBB, unsigned JTI); @@ -457,11 +463,39 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) { } } +static void +emitAuthenticatedPointer(MCStreamer &OutStreamer, MCSymbol *StubLabel, + const MachineModuleInfoMachO::AuthStubInfo &StubInfo) { + // L_foo$addend$auth_ptr$ib$23: + OutStreamer.EmitLabel(StubLabel); + OutStreamer.EmitValue(StubInfo.Pointer, /*size=*/8); +} + void AArch64AsmPrinter::EmitEndOfAsmFile(Module &M) { EmitHwasanMemaccessSymbols(M); const Triple &TT = TM.getTargetTriple(); if (TT.isOSBinFormatMachO()) { + + // Output authenticated pointers as indirect symbols, if we have any. + MachineModuleInfoMachO &MMIMacho = + MMI->getObjFileInfo<MachineModuleInfoMachO>(); + + auto Stubs = MMIMacho.getAuthGVStubList(); + + if (!Stubs.empty()) { + // Switch to the "__auth_ptr" section. + OutStreamer->SwitchSection( + OutContext.getMachOSection("__DATA", "__auth_ptr", MachO::S_REGULAR, + SectionKind::getMetadata())); + EmitAlignment(Align(8)); + + for (auto &Stub : Stubs) + emitAuthenticatedPointer(*OutStreamer, Stub.first, Stub.second); + + OutStreamer->AddBlankLine(); + } + // Funny Darwin hack: This flag tells the linker that no global symbols // contain code that falls through to other global symbols (e.g. the obvious // implementation of multiple entry points). If this doesn't occur, the @@ -909,6 +943,50 @@ void AArch64AsmPrinter::EmitFMov0(const MachineInstr &MI) { } } + +const MCExpr * +AArch64AsmPrinter::lowerPtrAuthGlobalConstant(const GlobalPtrAuthInfo &PAI) { + MCContext &Ctx = OutContext; + + // Figure out the base symbol and the addend, if any. + APInt Offset(64, 0); + const Value *BaseGV = + PAI.getPointer()->stripAndAccumulateInBoundsConstantOffsets( + getDataLayout(), Offset); + + auto *BaseGVB = dyn_cast<GlobalValue>(BaseGV); + + // If we can't understand the referenced ConstantExpr, there's nothing + // else we can do: emit an error. + if (!BaseGVB) { + BaseGVB = PAI.getGV(); + BaseGV->getContext().emitError( + "Couldn't resolve target base/addend of llvm.ptrauth global '" + + BaseGV->getName() + "'"); + } + + // If there is an addend, turn that into the appropriate MCExpr. + const MCExpr *Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx); + if (Offset.sgt(0)) + Sym = MCBinaryExpr::createAdd( + Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx); + else if (Offset.slt(0)) + Sym = MCBinaryExpr::createSub( + Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx); + + auto *Disc = PAI.getDiscriminator(); + uint64_t KeyID = PAI.getKey()->getZExtValue(); + if (!isUInt<2>(KeyID)) + BaseGV->getContext().emitError( + "Invalid AArch64 PAC Key ID '" + utostr(KeyID) + "' in llvm.ptrauth global '" + + BaseGV->getName() + "'"); + + // Finally build the complete @AUTH expr. + return AArch64AuthMCExpr::create(Sym, Disc->getZExtValue(), + AArch64PACKey::ID(KeyID), + PAI.hasAddressDiversity(), Ctx); +} + // Simple pseudo-instructions have their lowering (with expansion to real // instructions) auto-generated. #include "AArch64GenMCPseudoLowering.inc" diff --git a/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp b/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp index 4ce8e54ae6c67..8d540eedca514 100644 --- a/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp +++ b/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp @@ -159,7 +159,76 @@ bool AArch64ExpandHardenedPseudos::expandMI(MachineInstr &MI) { return true; } - return false; + if (MI.getOpcode() != AArch64::MOVaddrPAC) + return false; + + LLVM_DEBUG(dbgs() << "Expanding: " << MI << "\n"); + + + MachineOperand GAOp = MI.getOperand(0); + uint64_t Offset = MI.getOperand(1).getImm(); + auto Key = (AArch64PACKey::ID)MI.getOperand(2).getImm(); + unsigned AddrDisc = MI.getOperand(3).getReg(); + uint64_t Disc = MI.getOperand(4).getImm(); + + // Emit: + // target materialization: + // adrp x16, _target@GOTPAGE + // ldr x16, [x16, _target@GOTPAGEOFF] + // add x16, x16, #<offset> ; if offset != 0; up to 3 depending on width + // + // signing: + // - 0 discriminator: + // paciza x16 + // - Non-0 discriminator, no address discriminator: + // mov x17, #Disc + // pacia x16, x17 + // - address discriminator (with potentially folded immediate discriminator): + // pacia x16, xAddrDisc + + MachineOperand GAHiOp(GAOp); + MachineOperand GALoOp(GAOp); + GAHiOp.setTargetFlags(AArch64II::MO_GOT | AArch64II::MO_PAGE); + GALoOp.setTargetFlags(AArch64II::MO_GOT | AArch64II::MO_PAGEOFF); + + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADRP), AArch64::X16) + .add(GAHiOp); + + BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRXui), AArch64::X16) + .addReg(AArch64::X16) + .add(GALoOp); + + if (Offset) { + if (!isUInt<32>(Offset)) + report_fatal_error("ptrauth global offset too large, 32bit max encoding"); + + for (int BitPos = 0; BitPos < 32 && (Offset >> BitPos); BitPos += 12) { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXri), AArch64::X16) + .addReg(AArch64::X16) + .addImm((Offset >> BitPos) & 0xfff) + .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, BitPos)); + } + } + + unsigned DiscReg = AArch64::XZR; + if (Disc) { + DiscReg = AArch64::X17; + BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVZXi), AArch64::X17) + .addImm(Disc) + .addImm(0); + } else if (AddrDisc != AArch64::XZR) { + assert(Disc == 0 && "Non-0 discriminators should be folded into addr-disc"); + DiscReg = AddrDisc; + } + + unsigned PACOpc = getPACOpcodeForKey(Key, DiscReg == AArch64::XZR); + auto MIB = BuildMI(MBB, MBBI, DL, TII->get(PACOpc), AArch64::X16) + .addReg(AArch64::X16); + if (DiscReg != AArch64::XZR) + MIB.addReg(DiscReg); + + MI.eraseFromParent(); + return true; } diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index 98410c2e747ea..b35db2a422ed1 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -470,6 +470,12 @@ unsigned AArch64FastISel::materializeGV(const GlobalValue *GV) { unsigned ADRPReg = createResultReg(&AArch64::GPR64commonRegClass); unsigned ResultReg; + // Authenticated global references have special handling. + // Fallback to SDAG. + if (const GlobalVariable *GVB = dyn_cast<GlobalVariable>(GV)) + if (GVB->getSection() == "llvm.ptrauth") + return 0; + if (OpFlags & AArch64II::MO_GOT) { // ADRP + LDRX BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(AArch64::ADRP), diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 9fcfdbd42aca8..13e7e859379ec 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -117,6 +117,10 @@ EnableOptimizeLogicalImm("aarch64-enable-logical-imm", cl::Hidden, "optimization"), cl::init(true)); +static cl::opt<bool> AArch64PtrAuthGlobalDynamicMat( + "aarch64-ptrauth-global-dynamic-mat", cl::Hidden, cl::init(true), + cl::desc("Always materialize llvm.ptrauth global references dynamically")); + /// Value type used for condition codes. static const MVT MVT_CC = MVT::i32; @@ -217,6 +221,8 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::BR_JT, MVT::Other, Custom); setOperationAction(ISD::JumpTable, MVT::i64, Custom); + setOperationAction(ISD::PtrAuthGlobalAddress, MVT::i64, Custom); + setOperationAction(ISD::SHL_PARTS, MVT::i64, Custom); setOperationAction(ISD::SRA_PARTS, MVT::i64, Custom); setOperationAction(ISD::SRL_PARTS, MVT::i64, Custom); @@ -2971,6 +2977,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, return LowerGlobalAddress(Op, DAG); case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(Op, DAG); + case ISD::PtrAuthGlobalAddress: + return LowerPtrAuthGlobalAddress(Op, DAG); case ISD::SETCC: return LowerSETCC(Op, DAG); case ISD::BR_CC: @@ -4673,6 +4681,121 @@ SDValue AArch64TargetLowering::LowerGlobalTLSAddress(SDValue Op, llvm_unreachable("Unexpected platform trying to use TLS"); } +SDValue AArch64TargetLowering::LowerPtrAuthGlobalAddressViaGOT( + SDValue Wrapper, AArch64PACKey::ID Key, bool HasAddrDiversity, + GlobalAddressSDNode *PtrBaseGA, SelectionDAG &DAG) const { + + // Most llvm.ptrauth global references can be lowered using a stub. + // However, we cannot do that for address-diversified stub references: + // the stub relocation would be signed with the stub address, which + // is meaningless. + if (HasAddrDiversity) + return SDValue(); + + // Both key allocation and the wrapper usage support are target-specific. + if (!Subtarget->isTargetMachO()) + llvm_unreachable("Unimplemented ptrauth global lowering"); + + // Process-specific keys are dangerous when used in relocations. + if (Key == AArch64PACKey::IB || Key == AArch64PACKey::DB) { + // ..but that's our only way to implement weak references. Ban the combo. + if (PtrBaseGA->getGlobal()->hasExternalWeakLinkage()) + report_fatal_error("Unsupported weak B-key ptrauth global reference"); + return SDValue(); + } + + // In general, we don't want to abuse the pointer section, because + // that provides an attractive target, containing a cluster of + // already-signed pointers. + // + // FIXME: As an optimization, we could use the pointer section for + // global references that are used often. + // + // For now, materialize references dynamically, whenever possible. + // + // Don't do it for weak references, to avoid emitting another null-check. + if (AArch64PtrAuthGlobalDynamicMat && + !PtrBaseGA->getGlobal()->hasExternalWeakLinkage()) + return SDValue(); + + EVT VT = Wrapper.getValueType(); + SDLoc DL(Wrapper); + + // Use the wrapper directly, and let AsmPrinter turn it into $auth_ptr$ + // That's only implemented for MachO. + GlobalAddressSDNode *WrapperN = cast<GlobalAddressSDNode>(Wrapper.getNode()); + Wrapper = DAG.getTargetGlobalAddress(WrapperN->getGlobal(), DL, VT, + /*Offset=*/0, /*TargetFlags=*/0); + return DAG.getNode(AArch64ISD::LOADgot, DL, VT, Wrapper); +} + +SDValue +AArch64TargetLowering::LowerPtrAuthGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + SDValue Wrapper = Op.getOperand(0); + SDValue Ptr = Op.getOperand(1); + uint64_t KeyC = Op.getConstantOperandVal(2); + SDValue AddrDiscriminator = Op.getOperand(3); + SDValue Discriminator = Op.getOperand(4); + uint64_t DiscriminatorC = Op.getConstantOperandVal(4); + EVT VT = Op.getValueType(); + SDLoc DL(Op); + + bool HasAddrDiversity = !isNullConstant(AddrDiscriminator); + + uint64_t PtrOffsetC = 0; + if (Ptr.getOpcode() == ISD::ADD) { + PtrOffsetC = Ptr.getConstantOperandVal(1); + Ptr = Ptr.getOperand(0); + } + GlobalAddressSDNode *PtrN = cast<GlobalAddressSDNode>(Ptr.getNode()); + SDValue TPtr = DAG.getTargetGlobalAddress(PtrN->getGlobal(), DL, VT, + /*Offset=*/0, /*TargetFlags=*/0); + PtrOffsetC += PtrN->getOffset(); + assert(PtrN->getTargetFlags() == 0 && "Unsupported tflags on ptrauth global"); + + // Emit code to dynamically sign llvm.ptrauth global references. + // The alternative is to use a section of pointers (see ViaGOT helper). + // + // FIXME: An alternative lowering would be to simply use the address + // discriminator as a pointer and load the fully signed value from + // there. + if (SDValue Ld = LowerPtrAuthGlobalAddressViaGOT(Wrapper, + (AArch64PACKey::ID)KeyC, + HasAddrDiversity, PtrN, DAG)) + return Ld; + + + // If we're using an address discriminator, compute the full discriminator + // here, to avoid needing to carry a potentially complex constant expr all the + // way to the pseudo expansion. + if (HasAddrDiversity) { + SDValue BlendIntID = DAG.getTargetConstant(Intrinsic::ptrauth_blend, DL, VT); + AddrDiscriminator = + DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, VT, BlendIntID, + AddrDiscriminator, Discriminator); + DiscriminatorC = 0; + } + + SDValue PtrOffset = DAG.getTargetConstant(PtrOffsetC, DL, MVT::i64); + SDValue Key = DAG.getTargetConstant(KeyC, DL, MVT::i32); + Discriminator = DAG.getTargetConstant(DiscriminatorC, DL, VT); + + SDValue X16Copy = + DAG.getCopyToReg(DAG.getEntryNode(), DL, AArch64::X16, + DAG.getUNDEF(MVT::i64), SDValue()); + SDValue X17Copy = + DAG.getCopyToReg(DAG.getEntryNode(), DL, AArch64::X17, + DAG.getUNDEF(MVT::i64), X16Copy.getValue(1)); + + SDNode *MOV = + DAG.getMachineNode(AArch64::MOVaddrPAC, DL, {MVT::Other, MVT::Glue}, + {TPtr, PtrOffset, Key, AddrDiscriminator, + Discriminator, X17Copy.getValue(1)}); + return DAG.getCopyFromReg(SDValue(MOV, 0), DL, AArch64::X16, MVT::i64, + SDValue(MOV, 1)); +} + SDValue AArch64TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { SDValue Chain = Op.getOperand(0); ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get(); diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 5a76f0c467b77..99a7c2cb3df61 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -651,6 +651,12 @@ class AArch64TargetLowering : public TargetLowering { SDValue LowerELFTLSDescCallSeq(SDValue SymAddr, const SDLoc &DL, SelectionDAG &DAG) const; SDValue LowerWindowsGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerPtrAuthGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerPtrAuthGlobalAddressViaGOT(SDValue Wrapper, + AArch64PACKey::ID Key, + bool HasAddrDiversity, + GlobalAddressSDNode *PtrBaseGA, + SelectionDAG &DAG) const; SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 9eb31174ba3b7..d61dbface995f 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -116,6 +116,11 @@ unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { case AArch64::AUTPAC: NumBytes = 28; break; + case AArch64::MOVaddrPAC: + // 12 fixed + 16 variable, for pointer offset, and discriminator + // We could potentially model the variable size overhead more accurately. + NumBytes = 28; + break; case AArch64::BR_JumpTable: // 28 fixed + 16 variable, for table size materialization // We could potentially model the variable size overhead more accurately. diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index a05979e9c871b..bd3b32343bdf2 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -865,6 +865,18 @@ let Predicates = [HasPA] in { let Uses = [X16,X17]; } + // Materialize a signed global address. + def MOVaddrPAC : Pseudo<(outs), + (ins i64imm:$Addr, i64imm:$Offset, i32imm:$Key, + GPR64:$AddrDisc, i64imm:$Disc), []>, + Sched<[WriteI, ReadI]> { + let isReMaterializable = 1; + let isCodeGenOnly = 1; + let Size = 28; // ranges from 12 to 28 + let Defs = [X16,X17]; + let Uses = [X16,X17]; + } + let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [SP] in { def AUTH_TCRETURNriri : Pseudo<(outs), (ins tcGPR64:$dst, i32imm:$FPDiff, diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp index afd5ae6bcbf2c..c29396a01632b 100644 --- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp +++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp @@ -14,10 +14,14 @@ #include "AArch64MCInstLower.h" #include "MCTargetDesc/AArch64MCExpr.h" #include "Utils/AArch64BaseInfo.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/Mangler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" @@ -33,9 +37,68 @@ extern cl::opt<bool> EnableAArch64ELFLocalDynamicTLSGeneration; AArch64MCInstLower::AArch64MCInstLower(MCContext &ctx, AsmPrinter &printer) : Ctx(ctx), Printer(printer) {} +static MCSymbol *getAuthGVStub(const GlobalVariable *GVB, AsmPrinter &Printer) { + auto PAI = *GlobalPtrAuthInfo::analyze(GVB); + + // Figure out the base symbol and the addend, if any. + APInt Offset(64, 0); + const Value *BaseGV = + PAI.getPointer()->stripAndAccumulateInBoundsConstantOffsets( + Printer.getDataLayout(), Offset); + + auto *BaseGVB = dyn_cast<GlobalValue>(BaseGV); + + // If we can't understand the referenced ConstantExpr, there's nothing + // else we can do: emit an error. + if (!BaseGVB) { + BaseGVB = GVB; + BaseGV->getContext().emitError( + "Couldn't resolve target base/addend of llvm.ptrauth global '" + + BaseGV->getName() + "'"); + } + + uint16_t Discriminator = PAI.getDiscriminator()->getZExtValue(); + + auto *KeyC = PAI.getKey(); + assert(isUInt<2>(KeyC->getZExtValue()) && "Invalid PAC Key ID"); + AArch64PACKey::ID Key = AArch64PACKey::ID(KeyC->getZExtValue()); + + // Mangle the offset into the stub name. Avoid '-' in symbols and extra logic + // by using the uint64_t representation for negative numbers. + uint64_t OffsetV = Offset.getSExtValue(); + std::string Suffix = "$"; + if (OffsetV) + Suffix += utostr(OffsetV) + "$"; + Suffix += (Twine("auth_ptr$") + AArch64PACKeyIDToString(Key) + "$" + + utostr(Discriminator)) + .str(); + + if (PAI.hasAddressDiversity()) + report_fatal_error("Can't reference an address-diversified ptrauth global" + " in an instruction."); + + MCSymbol *MCSym = Printer.OutContext.getOrCreateSymbol( + Printer.getDataLayout().getLinkerPrivateGlobalPrefix() + "_" + + BaseGVB->getName() + Suffix); + + MachineModuleInfoMachO &MMIMachO = + Printer.MMI->getObjFileInfo<MachineModuleInfoMachO>(); + MachineModuleInfoMachO::AuthStubInfo &StubInfo = + MMIMachO.getAuthGVStubEntry(MCSym); + + if (!StubInfo.Pointer) + StubInfo.Pointer = Printer.lowerPtrAuthGlobalConstant(PAI); + return MCSym; +} + MCSymbol * AArch64MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { const GlobalValue *GV = MO.getGlobal(); + + if (const GlobalVariable *GVB = dyn_cast<GlobalVariable>(GV)) + if (GV->getSection() == "llvm.ptrauth") + return getAuthGVStub(GVB, Printer); + unsigned TargetFlags = MO.getTargetFlags(); const Triple &TheTriple = Printer.TM.getTargetTriple(); if (!TheTriple.isOSBinFormatCOFF()) diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index d92ee11c2e1ab..0ac8e2349ed98 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1848,6 +1848,8 @@ bool ModuleAddressSanitizer::ShouldInstrumentGlobal(GlobalVariable *G) { // Globals from llvm.metadata aren't emitted, do not instrument them. if (Section == "llvm.metadata") return false; + // Same for globals in llvm.ptrauth. + if (Section == "llvm.ptrauth") return false; // Do not instrument globals from special LLVM sections. if (Section.find("__llvm") != StringRef::npos || Section.find("__LLVM") != StringRef::npos) return false; diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-reloc.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-reloc.ll new file mode 100644 index 0000000000000..4199b0f6ed5b1 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-reloc.ll @@ -0,0 +1,343 @@ +; RUN: llc < %s -mtriple arm64e-apple-darwin -aarch64-ptrauth-global-dynamic-mat=1 | FileCheck %s --check-prefixes=CHECK,OPT,DYN-OPT +; RUN: llc < %s -mtriple arm64e-apple-darwin -O0 -aarch64-ptrauth-global-dynamic-mat=1 | FileCheck %s --check-prefixes=CHECK,O0,DYN-O0 +; RUN: llc < %s -mtriple arm64e-apple-darwin -aarch64-ptrauth-global-dynamic-mat=0 | FileCheck %s --check-prefixes=CHECK,OPT,LOAD-OPT +; RUN: llc < %s -mtriple arm64e-apple-darwin -O0 -aarch64-ptrauth-global-dynamic-mat=0 | FileCheck %s --check-prefixes=CHECK,O0,LOAD-O0 + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +; Check code references. + +; LOAD-LABEL: _test_global_zero_disc: +; LOAD-NEXT: ; %bb.0: +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: adrp x[[STUBPAGE:[0-9]+]], l_g$auth_ptr$ia$0@PAGE +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: ldr x0, [x[[STUBPAGE]], l_g$auth_ptr$ia$0@PAGEOFF] +; LOAD-NEXT: ret + +; DYN-O0-LABEL: _test_global_zero_disc: +; DYN-O0-NEXT: ; %bb.0: +; DYN-O0-NEXT: mov x8, #0 +; DYN-O0-NEXT: ; implicit-def: $x16 +; DYN-O0-NEXT: ; implicit-def: $x17 +; DYN-O0-NEXT: adrp x16, _g@GOTPAGE +; DYN-O0-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-O0-NEXT: pacia x16, x8 +; DYN-O0-NEXT: mov x0, x16 +; DYN-O0-NEXT: ret + +; DYN-OPT-LABEL: _test_global_zero_disc: +; DYN-OPT-NEXT: ; %bb.0: +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: adrp x16, _g@GOTPAGE +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-OPT-NEXT: paciza x16 +; DYN-OPT-NEXT: mov x0, x16 +; DYN-OPT-NEXT: ret + +define i8* @test_global_zero_disc() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @g.ptrauth.ia.0 to i8* + ret i8* %tmp0 +} + +; LOAD-LABEL: _test_global_offset_zero_disc: +; LOAD-NEXT: ; %bb.0: +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: adrp x[[STUBPAGE:[0-9]+]], l_g$148$auth_ptr$da$0@PAGE +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: ldr x0, [x[[STUBPAGE]], l_g$148$auth_ptr$da$0@PAGEOFF] +; LOAD-NEXT: ret + +; DYN-O0-LABEL: _test_global_offset_zero_disc: +; DYN-O0-NEXT: ; %bb.0: +; DYN-O0-NEXT: mov x8, #0 +; DYN-O0-NEXT: ; implicit-def: $x16 +; DYN-O0-NEXT: ; implicit-def: $x17 +; DYN-O0-NEXT: adrp x16, _g@GOTPAGE +; DYN-O0-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-O0-NEXT: add x16, x16, #148 +; DYN-O0-NEXT: pacda x16, x8 +; DYN-O0-NEXT: mov x0, x16 +; DYN-O0-NEXT: ret + +; DYN-OPT-LABEL: _test_global_offset_zero_disc: +; DYN-OPT-NEXT: ; %bb.0: +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: adrp x16, _g@GOTPAGE +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-OPT-NEXT: add x16, x16, #148 +; DYN-OPT-NEXT: pacdza x16 +; DYN-OPT-NEXT: mov x0, x16 +; DYN-OPT-NEXT: ret + +define i8* @test_global_offset_zero_disc() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @g.offset.ptrauth.da.0 to i8* + ret i8* %tmp0 +} + +; For large offsets, materializing it can take up to 3 add instructions. +; We limit the offset to 32-bits. We theoretically could support up to +; 64 bit offsets, but 32 bits Ought To Be Enough For Anybody, and that's +; the limit for the relocation addend anyway. + +; LOAD-LABEL: _test_global_big_offset_zero_disc: +; LOAD-NEXT: ; %bb.0: +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: adrp x[[STUBPAGE:[0-9]+]], l_g$2147549185$auth_ptr$da$0@PAGE +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: ldr x0, [x[[STUBPAGE]], l_g$2147549185$auth_ptr$da$0@PAGEOFF] +; LOAD-NEXT: ret + +; DYN-O0-LABEL: _test_global_big_offset_zero_disc: +; DYN-O0-NEXT: ; %bb.0: +; DYN-O0-NEXT: mov x8, #0 +; DYN-O0-NEXT: ; implicit-def: $x16 +; DYN-O0-NEXT: ; implicit-def: $x17 +; DYN-O0-NEXT: adrp x16, _g@GOTPAGE +; DYN-O0-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-O0-NEXT: add x16, x16, #1 +; DYN-O0-NEXT: add x16, x16, #16, lsl #12 +; DYN-O0-NEXT: add x16, x16, #128, lsl #24 +; DYN-O0-NEXT: pacda x16, x8 +; DYN-O0-NEXT: mov x0, x16 +; DYN-O0-NEXT: ret + +; DYN-OPT-LABEL: _test_global_big_offset_zero_disc: +; DYN-OPT-NEXT: ; %bb.0: +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: adrp x16, _g@GOTPAGE +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-OPT-NEXT: add x16, x16, #1 +; DYN-OPT-NEXT: add x16, x16, #16, lsl #12 +; DYN-OPT-NEXT: add x16, x16, #128, lsl #24 +; DYN-OPT-NEXT: pacdza x16 +; DYN-OPT-NEXT: mov x0, x16 +; DYN-OPT-NEXT: ret + +define i8* @test_global_big_offset_zero_disc() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @g.big_offset.ptrauth.da.0 to i8* + ret i8* %tmp0 +} + +; LOAD-LABEL: _test_global_disc: +; LOAD-NEXT: ; %bb.0: +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: adrp x[[STUBPAGE:[0-9]+]], l_g$auth_ptr$ia$42@PAGE +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: ldr x0, [x[[STUBPAGE]], l_g$auth_ptr$ia$42@PAGEOFF] +; LOAD-NEXT: ret + +; DYN-O0-LABEL: _test_global_disc: +; DYN-O0-NEXT: ; %bb.0: +; DYN-O0-NEXT: mov x8, #0 +; DYN-O0-NEXT: ; implicit-def: $x16 +; DYN-O0-NEXT: ; implicit-def: $x17 +; DYN-O0-NEXT: adrp x16, _g@GOTPAGE +; DYN-O0-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-O0-NEXT: mov x17, #42 +; DYN-O0-NEXT: pacia x16, x17 +; DYN-O0-NEXT: mov x0, x16 +; DYN-O0-NEXT: ret + +; DYN-OPT-LABEL: _test_global_disc: +; DYN-OPT-NEXT: ; %bb.0: +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: adrp x16, _g@GOTPAGE +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-OPT-NEXT: mov x17, #42 +; DYN-OPT-NEXT: pacia x16, x17 +; DYN-OPT-NEXT: mov x0, x16 +; DYN-OPT-NEXT: ret + +define i8* @test_global_disc() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @g.ptrauth.ia.42 to i8* + ret i8* %tmp0 +} + +; LOAD-LABEL: _test_global_addr_disc: +; LOAD-NEXT: ; %bb.0: +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: adrp x8, _g.ref.da.42.addr@PAGE +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: add x8, x8, _g.ref.da.42.addr@PAGEOFF +; LOAD-NEXT: movk x8, #42, lsl #48 +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: adrp x16, _g@GOTPAGE +; LOAD-NEXT: Lloh{{.*}}: +; LOAD-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; LOAD-NEXT: pacda x16, x8 +; LOAD-NEXT: mov x0, x16 +; LOAD-NEXT: ret + +; DYN-O0-LABEL: _test_global_addr_disc: +; DYN-O0-NEXT: ; %bb.0: +; DYN-O0-NEXT: adrp x8, _g.ref.da.42.addr@PAGE +; DYN-O0-NEXT: add x8, x8, _g.ref.da.42.addr@PAGEOFF +; DYN-O0-NEXT: movk x8, #42, lsl #48 +; DYN-O0-NEXT: ; implicit-def: $x16 +; DYN-O0-NEXT: ; implicit-def: $x17 +; DYN-O0-NEXT: adrp x16, _g@GOTPAGE +; DYN-O0-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-O0-NEXT: pacda x16, x8 +; DYN-O0-NEXT: mov x0, x16 +; DYN-O0-NEXT: ret + +; DYN-OPT-LABEL: _test_global_addr_disc: +; DYN-OPT-NEXT: ; %bb.0: +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: adrp x8, _g.ref.da.42.addr@PAGE +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: add x8, x8, _g.ref.da.42.addr@PAGEOFF +; DYN-OPT-NEXT: movk x8, #42, lsl #48 +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: adrp x16, _g@GOTPAGE +; DYN-OPT-NEXT: Lloh{{.*}}: +; DYN-OPT-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; DYN-OPT-NEXT: pacda x16, x8 +; DYN-OPT-NEXT: mov x0, x16 +; DYN-OPT-NEXT: ret + +define i8* @test_global_addr_disc() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @g.ptrauth.da.42.addr to i8* + ret i8* %tmp0 +} + +; Process-specific keys can't use __DATA,__auth_ptr + +; O0-LABEL: _test_global_process_specific: +; O0-NEXT: ; %bb.0: +; O0-NEXT: mov x8, #0 +; O0-NEXT: ; implicit-def: $x16 +; O0-NEXT: ; implicit-def: $x17 +; O0-NEXT: adrp x16, _g@GOTPAGE +; O0-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; O0-NEXT: pacib x16, x8 +; O0-NEXT: mov x0, x16 +; O0-NEXT: ret + +; OPT-LABEL: _test_global_process_specific: +; OPT-NEXT: ; %bb.0: +; OPT-NEXT: Lloh{{.*}}: +; OPT-NEXT: adrp x16, _g@GOTPAGE +; OPT-NEXT: Lloh{{.*}}: +; OPT-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; OPT-NEXT: pacizb x16 +; OPT-NEXT: mov x0, x16 +; OPT-NEXT: ret + +define i8* @test_global_process_specific() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @g.ptrauth.ib.0 to i8* + ret i8* %tmp0 +} + +; weak symbols can't be assumed to be non-nil. Use __DATA,__auth_ptr always. +; The alternative is to emit a null-check here, but that'd be redundant with +; whatever null-check follows in user code. + +; O0-LABEL: _test_global_weak: +; O0-NEXT: ; %bb.0: +; O0-NEXT: adrp x[[STUBPAGE:[0-9]+]], l_g_weak$auth_ptr$ia$42@PAGE +; O0-NEXT: ldr x8, [x[[STUBPAGE]], l_g_weak$auth_ptr$ia$42@PAGEOFF] +; O0-NEXT: mov x0, x8 +; O0-NEXT: ret + +; OPT-LABEL: _test_global_weak: +; OPT-NEXT: ; %bb.0: +; OPT-NEXT: Lloh{{.*}}: +; OPT-NEXT: adrp x[[STUBPAGE:[0-9]+]], l_g_weak$auth_ptr$ia$42@PAGE +; OPT-NEXT: Lloh{{.*}}: +; OPT-NEXT: ldr x0, [x[[STUBPAGE]], l_g_weak$auth_ptr$ia$42@PAGEOFF] +; OPT-NEXT: ret + +define i8* @test_global_weak() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @g_weak.ptrauth.ia.42 to i8* + ret i8* %tmp0 +} + +attributes #0 = { nounwind } + +; Check global references. + +@g = external global i32 + +@g_weak = extern_weak global i32 + +; CHECK-LABEL: .section __DATA,__const +; CHECK-NEXT: .globl _g.ref.ia.0 +; CHECK-NEXT: .p2align 4 +; CHECK-NEXT: _g.ref.ia.0: +; CHECK-NEXT: .quad 5 +; CHECK-NEXT: .quad _g@AUTH(ia,0) +; CHECK-NEXT: .quad 6 + +@g.ptrauth.ia.0 = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @g to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth" + +@g.ref.ia.0 = constant { i64, i8*, i64 } { i64 5, i8* bitcast ({ i8*, i32, i64, i64 }* @g.ptrauth.ia.0 to i8*), i64 6 } + +; CHECK-LABEL: .globl _g.ref.ia.42 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _g.ref.ia.42: +; CHECK-NEXT: .quad _g@AUTH(ia,42) + +@g.ptrauth.ia.42 = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @g to i8*), i32 0, i64 0, i64 42 }, section "llvm.ptrauth" + +@g.ref.ia.42 = constant i8* bitcast ({ i8*, i32, i64, i64 }* @g.ptrauth.ia.42 to i8*) + +; CHECK-LABEL: .globl _g.ref.ib.0 +; CHECK-NEXT: .p2align 4 +; CHECK-NEXT: _g.ref.ib.0: +; CHECK-NEXT: .quad 5 +; CHECK-NEXT: .quad _g@AUTH(ib,0) +; CHECK-NEXT: .quad 6 + +@g.ptrauth.ib.0 = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @g to i8*), i32 1, i64 0, i64 0 }, section "llvm.ptrauth" + +@g.ref.ib.0 = constant { i64, i8*, i64 } { i64 5, i8* bitcast ({ i8*, i32, i64, i64 }* @g.ptrauth.ib.0 to i8*), i64 6 } + + +; CHECK-LABEL: .globl _g.ref.da.42.addr +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _g.ref.da.42.addr: +; CHECK-NEXT: .quad _g@AUTH(da,42,addr) + +@g.ptrauth.da.42.addr = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @g to i8*), i32 2, i64 ptrtoint (i8** @g.ref.da.42.addr to i64), i64 42 }, section "llvm.ptrauth" + +@g.ref.da.42.addr = constant i8* bitcast ({ i8*, i32, i64, i64 }* @g.ptrauth.da.42.addr to i8*) + +; CHECK-LABEL: .globl _g.offset.ref.da.0 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _g.offset.ref.da.0: +; CHECK-NEXT: .quad (_g+148)@AUTH(da,0) + +@g.offset.ptrauth.da.0 = private constant { i8*, i32, i64, i64 } { i8* getelementptr inbounds (i8, i8* bitcast (i32* @g to i8*), i64 148), i32 2, i64 0, i64 0 }, section "llvm.ptrauth" + +@g.offset.ref.da.0 = constant i8* bitcast ({ i8*, i32, i64, i64 }* @g.offset.ptrauth.da.0 to i8*) + +; CHECK-LABEL: .globl _g.big_offset.ref.da.0 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _g.big_offset.ref.da.0: +; CHECK-NEXT: .quad (_g+2147549185)@AUTH(da,0) + +@g.big_offset.ptrauth.da.0 = private constant { i8*, i32, i64, i64 } { i8* getelementptr inbounds (i8, i8* bitcast (i32* @g to i8*), i64 add (i64 2147483648, i64 65537)), i32 2, i64 0, i64 0 }, section "llvm.ptrauth" + +@g.big_offset.ref.da.0 = constant i8* bitcast ({ i8*, i32, i64, i64 }* @g.big_offset.ptrauth.da.0 to i8*) + +; CHECK-LABEL: .globl _g.weird_ref.da.0 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _g.weird_ref.da.0: +; CHECK-NEXT: .quad (_g+148)@AUTH(da,0) + +@g.weird_ref.da.0 = constant i64 ptrtoint (i8* bitcast (i64* inttoptr (i64 ptrtoint (i8* bitcast ({ i8*, i32, i64, i64 }* @g.offset.ptrauth.da.0 to i8*) to i64) to i64*) to i8*) to i64) + +; CHECK-LABEL: .globl _g_weak.ref.ia.42 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _g_weak.ref.ia.42: +; CHECK-NEXT: .quad _g_weak@AUTH(ia,42) + +@g_weak.ptrauth.ia.42 = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @g_weak to i8*), i32 0, i64 0, i64 42 }, section "llvm.ptrauth" + +@g_weak.ref.ia.42 = constant i8* bitcast ({ i8*, i32, i64, i64 }* @g_weak.ptrauth.ia.42 to i8*) diff --git a/llvm/test/Instrumentation/AddressSanitizer/do-not-instrument-ptrauth-globals.ll b/llvm/test/Instrumentation/AddressSanitizer/do-not-instrument-ptrauth-globals.ll new file mode 100644 index 0000000000000..a2e3330a73e17 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/do-not-instrument-ptrauth-globals.ll @@ -0,0 +1,11 @@ +; This test checks that we are not instrumenting llvm.ptrauth globals. +; RUN: opt < %s -asan -asan-module -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +declare void @f() + +@f.ptrauth.ia.42 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @f to i8*), i32 0, i64 0, i64 42 }, section "llvm.ptrauth" + +; CHECK: @f.ptrauth.ia.42 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @f to i8*), i32 0, i64 0, i64 42 }, section "llvm.ptrauth" diff --git a/llvm/test/Verifier/ptrauth-global.ll b/llvm/test/Verifier/ptrauth-global.ll new file mode 100644 index 0000000000000..ef2f86c8f8c07 --- /dev/null +++ b/llvm/test/Verifier/ptrauth-global.ll @@ -0,0 +1,21 @@ +; RUN: not opt -S -verify < %s 2>&1 | FileCheck %s + +; CHECK: invalid llvm.ptrauth global: global doesn't have an initializer +@no_init = external global { i8*, i32, i64, i64 }, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: global isn't a struct +@not_struct = constant i8* null, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: global doesn't have type '{ i8*, i32, i64, i64 }' +@bad_type = constant { i8*, i32, i32, i32 } zeroinitializer, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: key isn't a constant integer +@bad_key = constant { i8*, i32, i64, i64 } { i8* null, i32 ptrtoint (i32* @g to i32), i64 0, i64 0}, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: discriminator isn't a constant integer +@bad_disc = constant { i8*, i32, i64, i64 } { i8* null, i32 0, i64 0, i64 ptrtoint (i32* @g to i64)}, section "llvm.ptrauth" + +; CHECK-NOT: invalid +@valid = private constant { i8*, i32, i64, i64 } { i8* getelementptr inbounds (i8, i8* bitcast (i32* @g to i8*), i64 2), i32 3, i64 0, i64 0 }, section "llvm.ptrauth" + +@g = external global i32 From 646d0c9d67c1d88236e970bef4447f74ad918790 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 06:21:11 -0700 Subject: [PATCH 521/582] [AArch64] Lower calls with ptrauth operand bundles. This is mostly about propagating the ptrauth operand bundle through SDAG call lowering logic, and, later, lowering it in AArch64 isel/PEI. --- llvm/include/llvm/CodeGen/TargetLowering.h | 20 ++ .../SelectionDAG/SelectionDAGBuilder.cpp | 64 ++++- .../SelectionDAG/SelectionDAGBuilder.h | 6 +- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 17 ++ .../Target/AArch64/AArch64FrameLowering.cpp | 3 + .../Target/AArch64/AArch64ISelLowering.cpp | 26 +- llvm/lib/Target/AArch64/AArch64ISelLowering.h | 10 + .../CodeGen/AArch64/arm64e-ptrauth-call.ll | 204 +++++++++++++++ .../AArch64/arm64e-ptrauth-direct-call.ll | 155 +++++++++++ .../CodeGen/AArch64/arm64e-ptrauth-invoke.ll | 247 ++++++++++++++++++ 10 files changed, 746 insertions(+), 6 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-call.ll create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-direct-call.ll create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-invoke.ll diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index b9bf19474ca93..0c8f5cbd63982 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -3382,6 +3382,11 @@ class TargetLowering : public TargetLoweringBase { return false; } + /// Return true if the target supports ptrauth operand bundles. + virtual bool supportPtrAuthBundles() const { + return false; + } + /// Perform necessary initialization to handle a subset of CSRs explicitly /// via copies. This function is called at the beginning of instruction /// selection. @@ -3427,6 +3432,14 @@ class TargetLowering : public TargetLoweringBase { llvm_unreachable("Not Implemented"); } + /// This structure contains the information necessary for lowering + /// pointer-authenticating indirect calls. It is equivalent to the "ptrauth" + /// operand bundle found on the call instruction, if any. + struct PtrAuthInfo { + uint64_t Key; + SDValue Discriminator; + }; + /// This structure contains all information that is necessary for lowering /// calls. It is passed to TLI::LowerCallTo when the SelectionDAG builder /// needs to lower a call, and targets will see this struct in their LowerCall @@ -3462,6 +3475,8 @@ class TargetLowering : public TargetLoweringBase { SmallVector<ISD::InputArg, 32> Ins; SmallVector<SDValue, 4> InVals; + Optional<PtrAuthInfo> PAI; + CallLoweringInfo(SelectionDAG &DAG) : RetSExt(false), RetZExt(false), IsVarArg(false), IsInReg(false), DoesNotReturn(false), IsReturnValueUsed(true), IsConvergent(false), @@ -3572,6 +3587,11 @@ class TargetLowering : public TargetLoweringBase { return *this; } + CallLoweringInfo &setPtrAuth(PtrAuthInfo Value) { + PAI = Value; + return *this; + } + CallLoweringInfo &setIsPostTypeLegalization(bool Value=true) { IsPostTypeLegalization = Value; return *this; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 058280e6b878b..99e9985679dea 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2758,11 +2758,12 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { MachineBasicBlock *Return = FuncInfo.MBBMap[I.getSuccessor(0)]; const BasicBlock *EHPadBB = I.getSuccessor(1); - // Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't + // Deopt and ptrauth bundles are lowered in helper functions, and we don't // have to do anything here to lower funclet bundles. assert(!I.hasOperandBundlesOtherThan({LLVMContext::OB_deopt, LLVMContext::OB_funclet, - LLVMContext::OB_cfguardtarget}) && + LLVMContext::OB_cfguardtarget, + LLVMContext::OB_ptrauth}) && "Cannot lower invokes with arbitrary operand bundles yet!"); const Value *Callee(I.getCalledValue()); @@ -2804,6 +2805,8 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { // intrinsic, and right now there are no plans to support other intrinsics // with deopt state. LowerCallSiteWithDeoptBundle(&I, getValue(Callee), EHPadBB); + } else if (I.countOperandBundlesOfType(LLVMContext::OB_ptrauth)) { + LowerCallSiteWithPtrAuthBundle(&I, EHPadBB); } else { LowerCallTo(&I, getValue(Callee), false, EHPadBB); } @@ -7109,7 +7112,8 @@ SelectionDAGBuilder::lowerInvokable(TargetLowering::CallLoweringInfo &CLI, void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, bool isTailCall, - const BasicBlock *EHPadBB) { + const BasicBlock *EHPadBB, + const TargetLowering::PtrAuthInfo *PAI) { auto &DL = DAG.getDataLayout(); FunctionType *FTy = CS.getFunctionType(); Type *RetTy = CS.getType(); @@ -7188,6 +7192,15 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, .setCallee(RetTy, FTy, Callee, std::move(Args), CS) .setTailCall(isTailCall) .setConvergent(CS.isConvergent()); + + // Set the pointer authentication info if we have it. + if (PAI) { + if (!TLI.supportPtrAuthBundles()) + report_fatal_error( + "This target doesn't support calls with ptrauth operand bundles."); + CLI.setPtrAuth(*PAI); + } + std::pair<SDValue, SDValue> Result = lowerInvokable(CLI, EHPadBB); if (Result.first.getNode()) { @@ -7711,6 +7724,11 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) { } } + if (I.countOperandBundlesOfType(LLVMContext::OB_ptrauth)) { + LowerCallSiteWithPtrAuthBundle(&I, /*EHPadBB=*/nullptr); + return; + } + // Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't // have to do anything here to lower funclet bundles. // CFGuardTarget bundles are lowered in LowerCallTo. @@ -7730,6 +7748,46 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) { LowerCallTo(&I, Callee, I.isTailCall()); } +void SelectionDAGBuilder::LowerCallSiteWithPtrAuthBundle( + ImmutableCallSite CS, const BasicBlock *EHPadBB) { + auto PAB = CS.getOperandBundle("ptrauth"); + auto *CalleeV = CS.getCalledValue(); + + // Gather the call ptrauth data from the operand bundle: + // [ i32 <key>, i64 <discriminator> ] + auto *Key = cast<ConstantInt>(PAB->Inputs[0]); + Value *Discriminator = PAB->Inputs[1]; + + assert(Key->getType()->isIntegerTy(32) && "Invalid ptrauth key"); + assert(Discriminator->getType()->isIntegerTy(64) && + "Invalid ptrauth discriminator"); + + // Look through ptrauth globals to find the raw callee. + // Do a direct unauthenticated call if we found it and everything matches. + if (auto CalleePAI = GlobalPtrAuthInfo::analyze(CalleeV)) { + // FIXME: bring back a static diagnostic when we can guarantee the mismatch + if (CalleePAI->isCompatibleWith(Key, Discriminator, DAG.getDataLayout())) { + LowerCallTo(CS, getValue(CalleePAI->getPointer()), CS.isTailCall(), + EHPadBB); + return; + } + } + + // Functions should never be ptrauth-called directly. + // We could lower these to direct unauthenticated calls, but for that to + // occur, there must have been a semantic mismatch somewhere leading to this + // arguably incorrect IR. + if (isa<Function>(CalleeV)) + report_fatal_error("Cannot lower direct authenticated call to" + " unauthenticated target"); + + // Otherwise, do an authenticated indirect call. + TargetLowering::PtrAuthInfo PAI = {Key->getZExtValue(), + getValue(Discriminator)}; + + LowerCallTo(CS, getValue(CalleeV), CS.isTailCall(), EHPadBB, &PAI); +} + namespace { /// AsmOperandInfo - This contains information for each constraint that we are diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h index bfcf30b430b6d..4effc4bf12843 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -538,7 +538,8 @@ class SelectionDAGBuilder { void CopyToExportRegsIfNeeded(const Value *V); void ExportFromCurrentBlock(const Value *V); void LowerCallTo(ImmutableCallSite CS, SDValue Callee, bool IsTailCall, - const BasicBlock *EHPadBB = nullptr); + const BasicBlock *EHPadBB = nullptr, + const TargetLowering::PtrAuthInfo *PAI = nullptr); // Lower range metadata from 0 to N to assert zext to an integer of nearest // floor power of two. @@ -621,6 +622,9 @@ class SelectionDAGBuilder { bool VarArgDisallowed, bool ForceVoidReturnTy); + void LowerCallSiteWithPtrAuthBundle(ImmutableCallSite CS, + const BasicBlock *EHPadBB); + /// Returns the type of FrameIndex and TargetFrameIndex nodes. MVT getFrameIndexTy() { return DAG.getTargetLoweringInfo().getFrameIndexTy(DAG.getDataLayout()); diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 91f8cd2fa03eb..edda23a22307a 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -1240,6 +1240,23 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) { // Tail calls use pseudo instructions so they have the proper code-gen // attributes (isCall, isReturn, etc.). We lower them to the real // instruction here. + case AArch64::AUTH_TCRETURNrii: + case AArch64::AUTH_TCRETURNriri: { + const bool isZero = MI->getOpcode() == AArch64::AUTH_TCRETURNrii; + const uint64_t Key = MI->getOperand(2).getImm(); + assert (Key < 2 && "Unknown key kind for authenticating tail-call return"); + + const unsigned Opcodes[2][2] = {{AArch64::BRAA, AArch64::BRAAZ}, + {AArch64::BRAB, AArch64::BRABZ}}; + + MCInst TmpInst; + TmpInst.setOpcode(Opcodes[Key][isZero]); + TmpInst.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); + if (!isZero) + TmpInst.addOperand(MCOperand::createReg(MI->getOperand(3).getReg())); + EmitToStreamer(*OutStreamer, TmpInst); + return; + } case AArch64::TCRETURNri: case AArch64::TCRETURNriBTI: case AArch64::TCRETURNriALL: { diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 042d8fdcc51d0..39005e758150c 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -1340,6 +1340,9 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, IsTailCallReturn = RetOpcode == AArch64::TCRETURNdi || RetOpcode == AArch64::TCRETURNri || RetOpcode == AArch64::TCRETURNriBTI; + + IsTailCallReturn |= RetOpcode == AArch64::AUTH_TCRETURNrii || + RetOpcode == AArch64::AUTH_TCRETURNriri; IsFunclet = isFuncletReturnInstr(*MBBI); } diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 13e7e859379ec..d7062b15755d0 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -1188,6 +1188,8 @@ const char *AArch64TargetLowering::getTargetNodeName(unsigned Opcode) const { switch ((AArch64ISD::NodeType)Opcode) { case AArch64ISD::FIRST_NUMBER: break; case AArch64ISD::CALL: return "AArch64ISD::CALL"; + case AArch64ISD::AUTH_CALL: return "AArch64ISD::AUTH_CALL"; + case AArch64ISD::AUTH_TC_RETURN: return "AArch64ISD::AUTH_TC_RETURN"; case AArch64ISD::ADRP: return "AArch64ISD::ADRP"; case AArch64ISD::ADR: return "AArch64ISD::ADR"; case AArch64ISD::ADDlow: return "AArch64ISD::ADDlow"; @@ -4039,6 +4041,13 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, assert(Subtarget->isTargetWindows() && "Windows is the only supported COFF target"); Callee = getGOT(G, DAG, AArch64II::MO_DLLIMPORT); + } else if (GV->getSection() == "llvm.ptrauth") { + // FIXME: this should deal with PtrAuthGlobalAddress instead + // If we're directly referencing a ptrauth wrapper, we need to materialize + // it from its __auth_ptr slot. + // We combine some of these into the call; ideally we'd catch them all. + Callee = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, /*TargetFlags=*/0); + Callee = DAG.getNode(AArch64ISD::LOADgot, DL, PtrVT, Callee); } else { const GlobalValue *GV = G->getGlobal(); Callee = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, 0); @@ -4065,6 +4074,8 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, InFlag = Chain.getValue(1); } + unsigned Opc = IsTailCall ? AArch64ISD::TC_RETURN : AArch64ISD::CALL; + std::vector<SDValue> Ops; Ops.push_back(Chain); Ops.push_back(Callee); @@ -4076,6 +4087,17 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, Ops.push_back(DAG.getTargetConstant(FPDiff, DL, MVT::i32)); } + if (CLI.PAI) { + const uint64_t Key = CLI.PAI->Key; + // Authenticated calls only support IA and IB. + if (Key > 1) + report_fatal_error("Unsupported key kind for authenticating call"); + + Opc = IsTailCall ? AArch64ISD::AUTH_TC_RETURN : AArch64ISD::AUTH_CALL; + Ops.push_back(DAG.getTargetConstant(Key, DL, MVT::i32)); + Ops.push_back(CLI.PAI->Discriminator); + } + // Add argument registers to the end of the list so that they are known live // into the call. for (auto &RegToPass : RegsToPass) @@ -4127,13 +4149,13 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, // actual call instruction. if (IsTailCall) { MF.getFrameInfo().setHasTailCall(); - SDValue Ret = DAG.getNode(AArch64ISD::TC_RETURN, DL, NodeTys, Ops); + SDValue Ret = DAG.getNode(Opc, DL, NodeTys, Ops); DAG.addCallSiteInfo(Ret.getNode(), std::move(CSInfo)); return Ret; } // Returns a chain and a flag for retval copy to use. - Chain = DAG.getNode(AArch64ISD::CALL, DL, NodeTys, Ops); + Chain = DAG.getNode(Opc, DL, NodeTys, Ops); InFlag = Chain.getValue(1); DAG.addCallSiteInfo(Chain.getNode(), std::move(CSInfo)); diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 99a7c2cb3df61..a83d6ce44092f 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -30,6 +30,12 @@ enum NodeType : unsigned { WrapperLarge, // 4-instruction MOVZ/MOVK sequence for 64-bit addresses. CALL, // Function call. + // Function call, authenticating the callee value first: + // AUTH_CALL chain, callee, auth key #, discriminator, operands. + AUTH_CALL, + // AUTH_TC_RETURN chain, callee, fpdiff, auth key #, discriminator, operands. + AUTH_TC_RETURN, + // Produces the full sequence of instructions for getting the thread pointer // offset of a variable into X0, using the TLSDesc model. TLSDESC_CALLSEQ, @@ -545,6 +551,10 @@ class AArch64TargetLowering : public TargetLowering { return true; } + bool supportPtrAuthBundles() const override { + return true; + } + /// Enable aggressive FMA fusion on targets that want it. bool enableAggressiveFMAFusion(EVT VT) const override; diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-call.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-call.ll new file mode 100644 index 0000000000000..3c5b0ad904dce --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-call.ll @@ -0,0 +1,204 @@ +; RUN: llc -mtriple arm64e-apple-darwin -asm-verbose=false -o - %s | FileCheck %s +; RUN: llc -mtriple arm64e-apple-darwin -fast-isel -asm-verbose=false -o - %s | FileCheck %s + +; CHECK-LABEL: _test_call_ia_0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: blraaz x0 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ia_0(i32 ()* %arg0) #0 { + %tmp0 = call i32 %arg0() [ "ptrauth"(i32 0, i64 0) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_call_ib_0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: blrabz x0 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ib_0(i32 ()* %arg0) #0 { + %tmp0 = call i32 %arg0() [ "ptrauth"(i32 1, i64 0) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_tailcall_ia_0: +; CHECK-NEXT: braaz x0 +define i32 @test_tailcall_ia_0(i32 ()* %arg0) #0 { + %tmp0 = tail call i32 %arg0() [ "ptrauth"(i32 0, i64 0) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_tailcall_ib_0: +; CHECK-NEXT: brabz x0 +define i32 @test_tailcall_ib_0(i32 ()* %arg0) #0 { + %tmp0 = tail call i32 %arg0() [ "ptrauth"(i32 1, i64 0) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_call_ia_imm: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: mov w8, #42 +; CHECK-NEXT: blraa x0, x8 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ia_imm(i32 ()* %arg0) #0 { + %tmp0 = call i32 %arg0() [ "ptrauth"(i32 0, i64 42) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_call_ib_imm: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: mov w8, #42 +; CHECK-NEXT: blrab x0, x8 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ib_imm(i32 ()* %arg0) #0 { + %tmp0 = call i32 %arg0() [ "ptrauth"(i32 1, i64 42) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_tailcall_ia_imm: +; CHECK-NEXT: mov w1, #42 +; CHECK-NEXT: braa x0, x1 +define i32 @test_tailcall_ia_imm(i32 ()* %arg0) #0 { + %tmp0 = tail call i32 %arg0() [ "ptrauth"(i32 0, i64 42) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_tailcall_ib_imm: +; CHECK-NEXT: mov w1, #42 +; CHECK-NEXT: brab x0, x1 +define i32 @test_tailcall_ib_imm(i32 ()* %arg0) #0 { + %tmp0 = tail call i32 %arg0() [ "ptrauth"(i32 1, i64 42) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_call_ia_var: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: ldr x8, [x1] +; CHECK-NEXT: blraa x0, x8 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ia_var(i32 ()* %arg0, i64* %arg1) #0 { + %tmp0 = load i64, i64* %arg1 + %tmp1 = call i32 %arg0() [ "ptrauth"(i32 0, i64 %tmp0) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_call_ib_var: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: ldr x8, [x1] +; CHECK-NEXT: blrab x0, x8 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ib_var(i32 ()* %arg0, i64* %arg1) #0 { + %tmp0 = load i64, i64* %arg1 + %tmp1 = call i32 %arg0() [ "ptrauth"(i32 1, i64 %tmp0) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_tailcall_ia_var: +; CHECK-NEXT: ldr x1, [x1] +; CHECK-NEXT: braa x0, x1 +define i32 @test_tailcall_ia_var(i32 ()* %arg0, i64* %arg1) #0 { + %tmp0 = load i64, i64* %arg1 + %tmp1 = tail call i32 %arg0() [ "ptrauth"(i32 0, i64 %tmp0) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_tailcall_ib_var: +; CHECK-NEXT: ldr x1, [x1] +; CHECK-NEXT: brab x0, x1 +define i32 @test_tailcall_ib_var(i32 ()* %arg0, i64* %arg1) #0 { + %tmp0 = load i64, i64* %arg1 + %tmp1 = tail call i32 %arg0() [ "ptrauth"(i32 1, i64 %tmp0) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_call_ia_arg: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: blraa x0, x1 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ia_arg(i32 ()* %arg0, i64 %arg1) #0 { + %tmp0 = call i32 %arg0() [ "ptrauth"(i32 0, i64 %arg1) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_call_ib_arg: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: blrab x0, x1 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ib_arg(i32 ()* %arg0, i64 %arg1) #0 { + %tmp0 = call i32 %arg0() [ "ptrauth"(i32 1, i64 %arg1) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_tailcall_ia_arg: +; CHECK-NEXT: braa x0, x1 +define i32 @test_tailcall_ia_arg(i32 ()* %arg0, i64 %arg1) #0 { + %tmp0 = tail call i32 %arg0() [ "ptrauth"(i32 0, i64 %arg1) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_tailcall_ib_arg: +; CHECK-NEXT: brab x0, x1 +define i32 @test_tailcall_ib_arg(i32 ()* %arg0, i64 %arg1) #0 { + %tmp0 = tail call i32 %arg0() [ "ptrauth"(i32 1, i64 %arg1) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_call_ia_arg_ind: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: ldr x8, [x0] +; CHECK-NEXT: blraa x8, x1 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ia_arg_ind(i32 ()** %arg0, i64 %arg1) #0 { + %tmp0 = load i32 ()*, i32 ()** %arg0 + %tmp1 = call i32 %tmp0() [ "ptrauth"(i32 0, i64 %arg1) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_call_ib_arg_ind: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: ldr x8, [x0] +; CHECK-NEXT: blrab x8, x1 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_call_ib_arg_ind(i32 ()** %arg0, i64 %arg1) #0 { + %tmp0 = load i32 ()*, i32 ()** %arg0 + %tmp1 = call i32 %tmp0() [ "ptrauth"(i32 1, i64 %arg1) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_tailcall_ia_arg_ind: +; CHECK-NEXT: ldr x0, [x0] +; CHECK-NEXT: braa x0, x1 +define i32 @test_tailcall_ia_arg_ind(i32 ()** %arg0, i64 %arg1) #0 { + %tmp0 = load i32 ()*, i32 ()** %arg0 + %tmp1 = tail call i32 %tmp0() [ "ptrauth"(i32 0, i64 %arg1) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_tailcall_ib_arg_ind: +; CHECK-NEXT: ldr x0, [x0] +; CHECK-NEXT: brab x0, x1 +define i32 @test_tailcall_ib_arg_ind(i32 ()** %arg0, i64 %arg1) #0 { + %tmp0 = load i32 ()*, i32 ()** %arg0 + %tmp1 = tail call i32 %tmp0() [ "ptrauth"(i32 1, i64 %arg1) ] + ret i32 %tmp1 +} + +attributes #0 = { nounwind "ptrauth-returns" } diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-direct-call.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-direct-call.ll new file mode 100644 index 0000000000000..c5268fd988222 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-direct-call.ll @@ -0,0 +1,155 @@ +; RUN: llc < %s -mtriple arm64e-apple-darwin | FileCheck %s +; RUN: llc < %s -mtriple arm64e-apple-darwin -fast-isel | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +; Check code references. + +; CHECK-LABEL: _test_direct_call_bitcast: +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _f +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_direct_call_bitcast() #0 { + %tmp0 = bitcast { i8*, i32, i64, i64 }* @f.ptrauth.ia.42 to i32 ()* + %tmp1 = call i32 %tmp0() [ "ptrauth"(i32 0, i64 42) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_direct_call: +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _f +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_direct_call() #0 { + %tmp0 = call i32 bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth.ia.42 to i32 ()*)() [ "ptrauth"(i32 0, i64 42) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_direct_call_mismatch: +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: Lloh{{.*}}: +; CHECK-NEXT: adrp x16, _f@GOTPAGE +; CHECK-NEXT: Lloh{{.*}}: +; CHECK-NEXT: ldr x16, [x16, _f@GOTPAGEOFF] +; CHECK-NEXT: mov x17, #42 +; CHECK-NEXT: pacia x16, x17 +; CHECK-NEXT: mov w8, #42 +; CHECK-NEXT: blrab x16, x8 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_direct_call_mismatch() #0 { + %tmp0 = call i32 bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth.ia.42 to i32 ()*)() [ "ptrauth"(i32 1, i64 42) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_direct_call_addr: +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _f +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_direct_call_addr() #0 { + %tmp0 = call i32 bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth.ib.0.addr to i32 ()*)() [ "ptrauth"(i32 1, i64 ptrtoint (i8** @f.ref.ib.0.addr to i64)) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_direct_call_addr_blend: +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _f +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_direct_call_addr_blend() #0 { + %tmp0 = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i8** @f.ref.ib.42.addr to i64), i64 42) + %tmp1 = call i32 bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth.ib.42.addr to i32 ()*)() [ "ptrauth"(i32 1, i64 %tmp0) ] + ret i32 %tmp1 +} + +; CHECK-LABEL: _test_direct_call_addr_gep_different_index_types: +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _f +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_direct_call_addr_gep_different_index_types() #0 { + %tmp0 = call i32 bitcast ({ i8*, i32, i64, i64 }* @f_struct.ptrauth.ib.0.addr to i32 ()*)() [ "ptrauth"(i32 1, i64 ptrtoint (i8** getelementptr ({ i8* }, { i8* }* @f_struct.ref.ib.0.addr, i32 0, i32 0) to i64)) ] + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_direct_call_addr_blend_gep_different_index_types: +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _f +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_direct_call_addr_blend_gep_different_index_types() #0 { + %tmp0 = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i8** getelementptr ({ i8* }, { i8* }* @f_struct.ref.ib.123.addr, i32 0, i32 0) to i64), i64 123) + %tmp1 = call i32 bitcast ({ i8*, i32, i64, i64 }* @f_struct.ptrauth.ib.123.addr to i32 ()*)() [ "ptrauth"(i32 1, i64 %tmp0) ] + ret i32 %tmp1 +} + +declare i64 @llvm.ptrauth.auth.i64(i64, i32, i64) #0 +declare i64 @llvm.ptrauth.blend.i64(i64, i64) #0 + +attributes #0 = { nounwind "ptrauth-returns" } + +; Check global references. + +declare void @f() + +; CHECK-LABEL: .section __DATA,__const +; CHECK-NEXT: .globl _f.ref.ia.42 +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _f.ref.ia.42: +; CHECK-NEXT: .quad _f@AUTH(ia,42) + +@f.ptrauth.ia.42 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void()* @f to i8*), i32 0, i64 0, i64 42 }, section "llvm.ptrauth" + +@f.ref.ia.42 = constant i8* bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth.ia.42 to i8*) + +; CHECK-LABEL: .globl _f.ref.ib.42.addr +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _f.ref.ib.42.addr: +; CHECK-NEXT: .quad _f@AUTH(ib,42,addr) + +@f.ptrauth.ib.42.addr = private constant { i8*, i32, i64, i64 } { i8* bitcast (void()* @f to i8*), i32 1, i64 ptrtoint (i8** @f.ref.ib.42.addr to i64), i64 42 }, section "llvm.ptrauth" + +@f.ref.ib.42.addr = constant i8* bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth.ib.42.addr to i8*) + +; CHECK-LABEL: .globl _f.ref.ib.0.addr +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _f.ref.ib.0.addr: +; CHECK-NEXT: .quad _f@AUTH(ib,0,addr) + +@f.ptrauth.ib.0.addr = private constant { i8*, i32, i64, i64 } { i8* bitcast (void()* @f to i8*), i32 1, i64 ptrtoint (i8** @f.ref.ib.0.addr to i64), i64 0 }, section "llvm.ptrauth" + +@f.ref.ib.0.addr = constant i8* bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth.ib.0.addr to i8*) + +; CHECK-LABEL: .globl _f_struct.ref.ib.0.addr +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _f_struct.ref.ib.0.addr: +; CHECK-NEXT: .quad _f@AUTH(ib,0,addr) + +@f_struct.ptrauth.ib.0.addr = private constant { i8*, i32, i64, i64 } { i8* bitcast (void()* @f to i8*), i32 1, i64 ptrtoint (i8** getelementptr ({ i8* }, { i8* }* @f_struct.ref.ib.0.addr, i64 0, i32 0) to i64), i64 0 }, section "llvm.ptrauth" + +@f_struct.ref.ib.0.addr = constant { i8* } { i8* bitcast ({ i8*, i32, i64, i64 }* @f_struct.ptrauth.ib.0.addr to i8*) } + +; CHECK-LABEL: .globl _f_struct.ref.ib.123.addr +; CHECK-NEXT: .p2align 3 +; CHECK-NEXT: _f_struct.ref.ib.123.addr: +; CHECK-NEXT: .quad _f@AUTH(ib,123,addr) + +@f_struct.ptrauth.ib.123.addr = private constant { i8*, i32, i64, i64 } { i8* bitcast (void()* @f to i8*), i32 1, i64 ptrtoint (i8** getelementptr ({ i8* }, { i8* }* @f_struct.ref.ib.123.addr, i64 0, i32 0) to i64), i64 123 }, section "llvm.ptrauth" + +@f_struct.ref.ib.123.addr = constant { i8* } { i8* bitcast ({ i8*, i32, i64, i64 }* @f_struct.ptrauth.ib.123.addr to i8*) } diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-invoke.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-invoke.ll new file mode 100644 index 0000000000000..3395493ce9bb4 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-invoke.ll @@ -0,0 +1,247 @@ +; RUN: llc -mtriple arm64e-apple-darwin -o - %s | FileCheck %s --check-prefixes=CHECK,SDAG +; RUN: llc -mtriple arm64e-apple-darwin -fast-isel -o - %s | FileCheck %s --check-prefixes=CHECK,FISEL + +; CHECK-LABEL: _test_invoke_ia_0: +; CHECK-NEXT: [[FNBEGIN:L.*]]: +; CHECK-NEXT: .cfi_startproc +; CHECK-NEXT: .cfi_personality 155, ___gxx_personality_v0 +; CHECK-NEXT: .cfi_lsda 16, [[EXCEPT:Lexception[0-9]+]] +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x20, x19, [sp, #-32]! +; CHECK-NEXT: stp x29, x30, [sp, #16] +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: .cfi_offset w30, -8 +; CHECK-NEXT: .cfi_offset w29, -16 +; CHECK-NEXT: .cfi_offset w19, -24 +; CHECK-NEXT: .cfi_offset w20, -32 +; CHECK-NEXT: [[PRECALL:L.*]]: +; CHECK-NEXT: blraaz x0 +; CHECK-NEXT: [[POSTCALL:L.*]]: +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: mov x19, x0 +; CHECK-NEXT: bl _foo +; CHECK-NEXT: mov x0, x19 +; CHECK-NEXT: [[EXITBB:LBB[0-9_]+]]: +; CHECK-NEXT: ldp x29, x30, [sp, #16] +; CHECK-NEXT: ldp x20, x19, [sp], #32 +; CHECK-NEXT: retab +; CHECK-NEXT: [[LPADBB:LBB[0-9_]+]]: +; CHECK-NEXT: [[LPAD:L.*]]: +; CHECK-NEXT: bl _foo +; CHECK-NEXT: mov w0, #-1 +; CHECK-NEXT: b [[EXITBB]] + +; CHECK-LABEL: GCC_except_table{{.*}}: +; CHECK-NEXT: [[EXCEPT]]: +; CHECK: .uleb128 [[POSTCALL]]-[[PRECALL]] ; Call between [[PRECALL]] and [[POSTCALL]] +; CHECK-NEXT: .uleb128 [[LPAD]]-[[FNBEGIN]] ; jumps to [[LPAD]] +; CHECK-NEXT: .byte 0 ; On action: cleanup + +define i32 @test_invoke_ia_0(i32 ()* %arg0) #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { + %tmp0 = invoke i32 %arg0() [ "ptrauth"(i32 0, i64 0) ] to label %continuebb + unwind label %unwindbb + +unwindbb: + %tmp1 = landingpad { i8*, i32 } cleanup + call void @foo() + ret i32 -1 + +continuebb: + call void @foo() + ret i32 %tmp0 +} + +; CHECK-LABEL: _test_invoke_ia_0_direct: +; CHECK-NEXT: [[FNBEGIN:L.*]]: +; CHECK-NEXT: .cfi_startproc +; CHECK-NEXT: .cfi_personality 155, ___gxx_personality_v0 +; CHECK-NEXT: .cfi_lsda 16, [[EXCEPT:Lexception[0-9]+]] +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x20, x19, [sp, #-32]! +; CHECK-NEXT: stp x29, x30, [sp, #16] +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: .cfi_offset w30, -8 +; CHECK-NEXT: .cfi_offset w29, -16 +; CHECK-NEXT: .cfi_offset w19, -24 +; CHECK-NEXT: .cfi_offset w20, -32 +; CHECK-NEXT: [[PRECALL:L.*]]: +; CHECK-NEXT: bl _baz +; CHECK-NEXT: [[POSTCALL:L.*]]: +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: mov x19, x0 +; CHECK-NEXT: bl _foo +; CHECK-NEXT: mov x0, x19 +; CHECK-NEXT: [[EXITBB:LBB[0-9_]+]]: +; CHECK-NEXT: ldp x29, x30, [sp, #16] +; CHECK-NEXT: ldp x20, x19, [sp], #32 +; CHECK-NEXT: retab +; CHECK-NEXT: [[LPADBB:LBB[0-9_]+]]: +; CHECK-NEXT: [[LPAD:L.*]]: +; CHECK-NEXT: bl _foo +; CHECK-NEXT: mov w0, #-1 +; CHECK-NEXT: b [[EXITBB]] + +; CHECK-LABEL: GCC_except_table{{.*}}: +; CHECK-NEXT: [[EXCEPT]]: +; CHECK: .uleb128 [[POSTCALL]]-[[PRECALL]] ; Call between [[PRECALL]] and [[POSTCALL]] +; CHECK-NEXT: .uleb128 [[LPAD]]-[[FNBEGIN]] ; jumps to [[LPAD]] +; CHECK-NEXT: .byte 0 ; On action: cleanup + +define i32 @test_invoke_ia_0_direct() #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { + %tmp0 = invoke i32 bitcast ({ i8*, i32, i64, i64 }* @baz.ptrauth to i32 ()*)() [ "ptrauth"(i32 0, i64 0) ] to label %continuebb + unwind label %unwindbb + +unwindbb: + %tmp1 = landingpad { i8*, i32 } cleanup + call void @foo() + ret i32 -1 + +continuebb: + call void @foo() + ret i32 %tmp0 +} + +@baz.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32()* @baz to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth" + +@_ZTIPKc = external constant i8* +@hello_str = private unnamed_addr constant [6 x i8] c"hello\00", align 1 + +; CHECK-LABEL: _test_invoke_ib_42_catch: +; CHECK-NEXT: [[FNBEGIN:L.*]]: +; CHECK-NEXT: .cfi_startproc +; CHECK-NEXT: .cfi_personality 155, ___gxx_personality_v0 +; CHECK-NEXT: .cfi_lsda 16, [[EXCEPT:Lexception[0-9]+]] +; CHECK-NEXT: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x20, x19, [sp, #-32]! +; CHECK-NEXT: stp x29, x30, [sp, #16] +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: .cfi_offset w30, -8 +; CHECK-NEXT: .cfi_offset w29, -16 +; CHECK-NEXT: .cfi_offset w19, -24 +; CHECK-NEXT: .cfi_offset w20, -32 +; CHECK-NEXT: mov x19, x0 +; CHECK-NEXT: mov w0, #8 +; CHECK-NEXT: bl ___cxa_allocate_exception +; CHECK-NEXT: Lloh{{.*}}: +; CHECK-NEXT: adrp x8, l_hello_str@PAGE +; CHECK-NEXT: Lloh{{.*}}: +; CHECK-NEXT: add x8, x8, l_hello_str@PAGEOFF +; CHECK-NEXT: str x8, [x0] +; CHECK-NEXT: [[PRECALL:L.*]]: +; CHECK-NEXT: Lloh{{.*}}: +; CHECK-NEXT: adrp x1, __ZTIPKc@GOTPAGE +; CHECK-NEXT: Lloh{{.*}}: +; CHECK-NEXT: ldr x1, [x1, __ZTIPKc@GOTPAGEOFF] +; CHECK-NEXT: mov w8, #42 +; CHECK-NEXT: mov x2, #0 +; CHECK-NEXT: blrab x19, x8 +; CHECK-NEXT: [[POSTCALL:L.*]]: +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: brk #0x1 +; CHECK-NEXT: [[LPADBB:LBB[0-9_]+]]: +; CHECK-NEXT: [[LPAD:L.*]]: +; CHECK-NEXT: mov x19, x1 + +; SDAG-NEXT: bl ___cxa_begin_catch +; SDAG-NEXT: cmp w19, #2 + +; FISEL-NEXT: mov w20, #2 +; FISEL-NEXT: bl ___cxa_begin_catch +; FISEL-NEXT: cmp w19, w20 + +; CHECK-NEXT: b.ne [[EXITBB:LBB[0-9_]+]] +; CHECK-NEXT: ; %bb.3: +; CHECK-NEXT: bl _bar +; CHECK-NEXT: [[EXITBB]]: +; CHECK-NEXT: bl _foo +; CHECK-NEXT: bl ___cxa_end_catch +; CHECK-NEXT: ldp x29, x30, [sp, #16] +; CHECK-NEXT: ldp x20, x19, [sp], #32 +; CHECK-NEXT: retab +; CHECK-NEXT: .loh {{.*}} +; CHECK-NEXT: .loh {{.*}} +; CHECK-NEXT: [[FNEND:L.*]]: + +; CHECK-LABEL: GCC_except_table{{.*}}: +; CHECK-NEXT: [[EXCEPT]]: +; CHECK-NEXT: .byte 255 ; @LPStart Encoding = omit +; CHECK-NEXT: .byte 155 ; @TType Encoding = indirect pcrel sdata4 +; CHECK-NEXT: .uleb128 [[TT:L.*]]-[[TTREF:L.*]] +; CHECK-NEXT: [[TTREF]]: +; CHECK-NEXT: .byte 1 ; Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 [[CSEND:L.*]]-[[CSBEGIN:L.*]] +; CHECK-NEXT: [[CSBEGIN]]: +; CHECK-NEXT: .uleb128 [[FNBEGIN]]-[[FNBEGIN]] ; >> Call Site 1 << +; CHECK-NEXT: .uleb128 [[PRECALL]]-[[FNBEGIN]] ; Call between [[FNBEGIN]] and [[PRECALL]] +; CHECK-NEXT: .byte 0 ; has no landing pad +; CHECK-NEXT: .byte 0 ; On action: cleanup +; CHECK-NEXT: .uleb128 [[PRECALL]]-[[FNBEGIN]] ; >> Call Site 2 << +; CHECK-NEXT: .uleb128 [[POSTCALL]]-[[PRECALL]] ; Call between [[PRECALL]] and [[POSTCALL]] +; CHECK-NEXT: .uleb128 [[LPAD]]-[[FNBEGIN]] ; jumps to [[LPAD]] +; CHECK-NEXT: .byte 3 ; On action: 2 +; CHECK-NEXT: .uleb128 [[POSTCALL]]-[[FNBEGIN]] ; >> Call Site 3 << +; CHECK-NEXT: .uleb128 [[FNEND]]-[[POSTCALL]] ; Call between [[POSTCALL]] and [[FNEND]] +; CHECK-NEXT: .byte 0 ; has no landing pad +; CHECK-NEXT: .byte 0 ; On action: cleanup +; CHECK-NEXT: [[CSEND]]: + +; CHECK-NEXT: .byte 1 ; >> Action Record 1 << +; CHECK-NEXT: ; Catch TypeInfo 1 +; CHECK-NEXT: .byte 0 ; No further actions +; CHECK-NEXT: .byte 2 ; >> Action Record 2 << +; CHECK-NEXT: ; Catch TypeInfo 2 +; CHECK-NEXT: .byte 125 ; Continue to action 1 +; CHECK-NEXT: .p2align 2 +; CHECK-NEXT: ; >> Catch TypeInfos << +; CHECK-NEXT: [[TI:L.*]]: ; TypeInfo 2 +; CHECK-NEXT: .long __ZTIPKc@GOT-[[TI]] +; CHECK-NEXT: .long 0 ; TypeInfo 1 + +; CHECK-NEXT: [[TT]]: + +define void @test_invoke_ib_42_catch(void(i8*, i8*, i8*)* %fptr) #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { + %tmp0 = call i8* @__cxa_allocate_exception(i64 8) + %tmp1 = bitcast i8* %tmp0 to i8** + store i8* getelementptr inbounds ([6 x i8], [6 x i8]* @hello_str, i64 0, i64 0), i8** %tmp1, align 8 + invoke void %fptr(i8* %tmp0, i8* bitcast (i8** @_ZTIPKc to i8*), i8* null) [ "ptrauth"(i32 1, i64 42) ] + to label %continuebb unwind label %catchbb + +catchbb: + %tmp2 = landingpad { i8*, i32 } + catch i8* bitcast (i8** @_ZTIPKc to i8*) + catch i8* null + %tmp3 = extractvalue { i8*, i32 } %tmp2, 0 + %tmp4 = extractvalue { i8*, i32 } %tmp2, 1 + %tmp5 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIPKc to i8*)) + %tmp6 = icmp eq i32 %tmp4, %tmp5 + %tmp7 = call i8* @__cxa_begin_catch(i8* %tmp3) + br i1 %tmp6, label %PKc_catchbb, label %any_catchbb + +PKc_catchbb: + call void @bar(i8* %tmp7) + br label %any_catchbb + +any_catchbb: + call void @foo() + call void @__cxa_end_catch() + ret void + +continuebb: + unreachable +} + +declare void @foo() +declare void @bar(i8*) +declare i32 @baz() + +declare i32 @__gxx_personality_v0(...) +declare i8* @__cxa_allocate_exception(i64) +declare void @__cxa_throw(i8*, i8*, i8*) +declare i32 @llvm.eh.typeid.for(i8*) +declare i8* @__cxa_begin_catch(i8*) +declare void @__cxa_end_catch() + +attributes #0 = { nounwind "ptrauth-returns" } From 4fdcd961441e97bae651d4795b3b53b21f090003 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 07:09:49 -0700 Subject: [PATCH 522/582] [AArch64] For arm64e Darwin TLS, sign the access thunk pointer. We synthesize a function pointer to the TLV access thunk: in arm64e, that function pointer needs to be signed. We currently use the ABI-specified function pointer signing scheme; but we could use something else, as this is dyld-codegen contract. --- .../Target/AArch64/AArch64ISelLowering.cpp | 20 ++++++++++++++++--- .../CodeGen/AArch64/arm64e-ptrauth-tls.ll | 17 ++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index d7062b15755d0..9236a36fe5250 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -4488,10 +4488,24 @@ AArch64TargetLowering::LowerDarwinGlobalTLSAddress(SDValue Op, // normal AArch64 call node: x0 takes the address of the descriptor, and // returns the address of the variable in this thread. Chain = DAG.getCopyToReg(Chain, DL, AArch64::X0, DescAddr, SDValue()); + + unsigned Opcode = AArch64ISD::CALL; + SmallVector<SDValue, 8> Ops; + Ops.push_back(Chain); + Ops.push_back(FuncTLVGet); + + // With ptrauth-calls, the tlv access thunk pointer is authenticated (IA, 0). + if (DAG.getMachineFunction().getFunction().hasFnAttribute("ptrauth-calls")) { + Opcode = AArch64ISD::AUTH_CALL; + Ops.push_back(DAG.getTargetConstant(AArch64PACKey::IA, DL, MVT::i32)); + Ops.push_back(DAG.getTargetConstant(0, DL, MVT::i64)); + } + + Ops.push_back(DAG.getRegister(AArch64::X0, MVT::i64)); + Ops.push_back(DAG.getRegisterMask(Mask)); + Ops.push_back(Chain.getValue(1)); Chain = - DAG.getNode(AArch64ISD::CALL, DL, DAG.getVTList(MVT::Other, MVT::Glue), - Chain, FuncTLVGet, DAG.getRegister(AArch64::X0, MVT::i64), - DAG.getRegisterMask(Mask), Chain.getValue(1)); + DAG.getNode(Opcode, DL, DAG.getVTList(MVT::Other, MVT::Glue), Ops); return DAG.getCopyFromReg(Chain, DL, AArch64::X0, PtrVT, Chain.getValue(1)); } diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll new file mode 100644 index 0000000000000..05904005563be --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll @@ -0,0 +1,17 @@ +; RUN: llc -mtriple=arm64e-apple-ios %s -o - | FileCheck %s + +@var = thread_local global i8 0 + +define i8 @get_var() #0 { +; CHECK-LABEL: get_var: +; CHECK: adrp x[[TLVPDESC_SLOT_HI:[0-9]+]], _var@TLVPPAGE +; CHECK: ldr x0, [x[[TLVPDESC_SLOT_HI]], _var@TLVPPAGEOFF] +; CHECK: ldr [[TLV_GET_ADDR:x[0-9]+]], [x0] +; CHECK: blraaz [[TLV_GET_ADDR]] +; CHECK: ldrb w0, [x0] + + %val = load i8, i8* @var, align 1 + ret i8 %val +} + +attributes #0 = { nounwind "ptrauth-calls" } From 0b6fe1595587168a1147cfed0a54bb0f759ea5b0 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 8 Aug 2019 07:43:10 -0700 Subject: [PATCH 523/582] [AArch64] Support return address authentication. This partially overlaps with the existing LR signing logic: I suggest unifying the two sets of function attributes and lowering logic separately. --- .../Target/AArch64/AArch64FrameLowering.cpp | 47 +++- .../lib/Target/AArch64/AArch64FrameLowering.h | 1 + .../Target/AArch64/AArch64ISelLowering.cpp | 26 ++- .../CodeGen/AArch64/arm64e-ptrauth-ret.ll | 221 ++++++++++++++++++ 4 files changed, 287 insertions(+), 8 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 39005e758150c..be95f08a4fbf9 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -228,6 +228,13 @@ bool AArch64FrameLowering::canUseRedZone(const MachineFunction &MF) const { getSVEStackSize(MF)); } +bool AArch64FrameLowering::shouldAuthenticateLR( + const MachineFunction &MF) const { + // Return address authentication can be enabled at the function level, using + // the "ptrauth-returns" attribute. + return MF.getFunction().hasFnAttribute("ptrauth-returns"); +} + /// hasFP - Return true if the specified function should have a dedicated frame /// pointer register. bool AArch64FrameLowering::hasFP(const MachineFunction &MF) const { @@ -880,6 +887,18 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, .setMIFlags(MachineInstr::FrameSetup); } + // If we're saving LR, sign it first. + if (shouldAuthenticateLR(MF)) { + if (LLVM_UNLIKELY(!Subtarget.hasPA())) + report_fatal_error("arm64e LR authentication requires ptrauth"); + for (const CalleeSavedInfo &Info : MFI.getCalleeSavedInfo()) { + if (Info.getReg() != AArch64::LR) + continue; + BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACIBSP)) + .setMIFlags(MachineInstr::FrameSetup); + } + } + // All calls are tail calls in GHC calling conv, and functions have no // prologue/epilogue. if (MF.getFunction().getCallingConv() == CallingConv::GHC) @@ -1294,7 +1313,7 @@ static void InsertReturnAddressAuth(MachineFunction &MF, // this instruction can safely used for any v8a architecture. // From v8.3a onwards there are optimised authenticate LR and return // instructions, namely RETA{A,B}, that can be used instead. - if (Subtarget.hasV8_3aOps() && MBBI != MBB.end() && + if (Subtarget.hasPA() && MBBI != MBB.end() && MBBI->getOpcode() == AArch64::RET_ReallyLR) { BuildMI(MBB, MBBI, DL, TII->get(ShouldSignWithAKey(MF) ? AArch64::RETAA : AArch64::RETAB)) @@ -1355,6 +1374,32 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, if (MF.getFunction().getCallingConv() == CallingConv::GHC) return; + // If we're restoring LR, authenticate it before returning. + // Use scope_exit to ensure we do that last on all return paths. + auto InsertAuthLROnExit = make_scope_exit([&]() { + if (shouldAuthenticateLR(MF)) { + if (LLVM_UNLIKELY(!Subtarget.hasPA())) + report_fatal_error("arm64e LR authentication requires ptrauth"); + for (const CalleeSavedInfo &Info : MFI.getCalleeSavedInfo()) { + if (Info.getReg() != AArch64::LR) + continue; + MachineBasicBlock::iterator TI = MBB.getFirstTerminator(); + if (TI != MBB.end() && TI->getOpcode() == AArch64::RET_ReallyLR) { + // If there is a terminator and it's a RET, we can fold AUTH into it. + // Be careful to keep the implicitly returned registers. + // By now, we don't need the ReallyLR pseudo, since it's only there + // to make it possible for LR to be used for non-RET purposes, and + // that happens in RA and PEI. + BuildMI(MBB, TI, DL, TII->get(AArch64::RETAB)).copyImplicitOps(*TI); + MBB.erase(TI); + } else { + // Otherwise, we could be in a shrink-wrapped or tail-calling block. + BuildMI(MBB, TI, DL, TII->get(AArch64::AUTIBSP)); + } + } + } + }); + // Initial and residual are named for consistency with the prologue. Note that // in the epilogue, the residual adjustment is executed first. uint64_t ArgumentPopSize = 0; diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.h b/llvm/lib/Target/AArch64/AArch64FrameLowering.h index ac150e86c9eb5..2022a9511f6ad 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.h +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.h @@ -102,6 +102,7 @@ class AArch64FrameLowering : public TargetFrameLowering { bool shouldCombineCSRLocalStackBump(MachineFunction &MF, unsigned StackBumpBytes) const; int64_t determineSVEStackSize(MachineFrameInfo &MF, unsigned &MaxAlign) const; + bool shouldAuthenticateLR(const MachineFunction &MF) const; }; } // End llvm namespace diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 9236a36fe5250..5b4de5e6db867 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -5750,18 +5750,30 @@ SDValue AArch64TargetLowering::LowerRETURNADDR(SDValue Op, EVT VT = Op.getValueType(); SDLoc DL(Op); + SDValue ReturnAddr; + SDValue FrameAddr; unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); if (Depth) { - SDValue FrameAddr = LowerFRAMEADDR(Op, DAG); + SDNodeFlags Flags; + Flags.setNoUnsignedWrap(true); + FrameAddr = LowerFRAMEADDR(Op, DAG); SDValue Offset = DAG.getConstant(8, DL, getPointerTy(DAG.getDataLayout())); - return DAG.getLoad(VT, DL, DAG.getEntryNode(), - DAG.getNode(ISD::ADD, DL, VT, FrameAddr, Offset), - MachinePointerInfo()); + ReturnAddr = DAG.getLoad(VT, DL, DAG.getEntryNode(), + DAG.getNode(ISD::ADD, DL, VT, FrameAddr, Offset, Flags), + MachinePointerInfo()); + } else { + unsigned LRReg = MF.addLiveIn(AArch64::LR, &AArch64::GPR64RegClass); + ReturnAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, LRReg, VT); } - // Return LR, which contains the return address. Mark it an implicit live-in. - unsigned Reg = MF.addLiveIn(AArch64::LR, &AArch64::GPR64RegClass); - return DAG.getCopyFromReg(DAG.getEntryNode(), DL, Reg, VT); + // If we're doing LR signing, we need to fixup ReturnAddr: strip it. + if (!MF.getFunction().hasFnAttribute("ptrauth-returns")) + return ReturnAddr; + + return DAG.getNode( + ISD::INTRINSIC_WO_CHAIN, DL, VT, + DAG.getConstant(Intrinsic::ptrauth_strip, DL, MVT::i32), ReturnAddr, + DAG.getConstant(AArch64PACKey::IB, DL, MVT::i32)); } /// LowerShiftRightParts - Lower SRA_PARTS, which returns two diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll new file mode 100644 index 0000000000000..d93ef83f2a2fa --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll @@ -0,0 +1,221 @@ +; RUN: llc -mtriple arm64e-apple-darwin -asm-verbose=false -disable-post-ra -o - %s | FileCheck %s + +; CHECK-LABEL: _test: +; CHECK-NEXT: stp x20, x19, [sp, #-16]! +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: ldp x20, x19, [sp], #16 +; CHECK-NEXT: ret +define i32 @test() #0 { + call void asm sideeffect "", "~{x19}"() + ret i32 0 +} + +; CHECK-LABEL: _test_alloca: +; CHECK-NEXT: sub sp, sp, #32 +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: add sp, sp, #32 +; CHECK-NEXT: ret +define i32 @test_alloca() #0 { + %p = alloca i8, i32 32 + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +; CHECK-LABEL: _test_realign_alloca: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: mov x29, sp +; CHECK-NEXT: sub x9, sp, #112 +; CHECK-NEXT: and sp, x9, #0xffffffffffffff80 +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: mov sp, x29 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_realign_alloca() #0 { + %p = alloca i8, i32 32, align 128 + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +; CHECK-LABEL: _test_big_alloca: +; CHECK-NEXT: stp x28, x27, [sp, #-16]! +; CHECK-NEXT: sub sp, sp, #1024 +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: add sp, sp, #1024 +; CHECK-NEXT: ldp x28, x27, [sp], #16 +; CHECK-NEXT: ret +define i32 @test_big_alloca() #0 { + %p = alloca i8, i32 1024 + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +; CHECK-LABEL: _test_var_alloca: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: mov x29, sp +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: mov w9, w0 +; CHECK-NEXT: add x9, x9, #15 +; CHECK-NEXT: and x9, x9, #0x1fffffff0 +; CHECK-NEXT: sub x8, x8, x9 +; CHECK-NEXT: mov sp, x8 +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: mov sp, x29 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i32 @test_var_alloca(i32 %s) #0 { + %p = alloca i8, i32 %s + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +; CHECK-LABEL: _test_noframe_saved: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x28, x27, [sp, #-96]! +; CHECK-NEXT: stp x26, x25, [sp, #16] +; CHECK-NEXT: stp x24, x23, [sp, #32] +; CHECK-NEXT: stp x22, x21, [sp, #48] +; CHECK-NEXT: stp x20, x19, [sp, #64] +; CHECK-NEXT: stp x29, x30, [sp, #80] +; CHECK-NEXT: ldr w30, [x0] +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov x0, x30 +; CHECK-NEXT: ldp x29, x30, [sp, #80] +; CHECK-NEXT: ldp x20, x19, [sp, #64] +; CHECK-NEXT: ldp x22, x21, [sp, #48] +; CHECK-NEXT: ldp x24, x23, [sp, #32] +; CHECK-NEXT: ldp x26, x25, [sp, #16] +; CHECK-NEXT: ldp x28, x27, [sp], #96 +; CHECK-NEXT: retab +define i32 @test_noframe_saved(i32* %p) #0 { + %v = load i32, i32* %p + call void asm sideeffect "", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28}"() + ret i32 %v +} + +; CHECK-LABEL: _test_noframe: +; CHECK-NEXT: ret +define void @test_noframe() #0 { + ret void +} + +; CHECK-LABEL: _test_returnaddress_0: +; CHECK-NEXT: mov x0, x30 +; CHECK-NEXT: xpaci x0 +; CHECK-NEXT: ret +define i8* @test_returnaddress_0() #0 { + %r = call i8* @llvm.returnaddress(i32 0) + ret i8* %r +} + +; CHECK-LABEL: _test_returnaddress_1: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: mov x29, sp +; CHECK-NEXT: ldr x8, [x29] +; CHECK-NEXT: ldr x8, [x8, #8] +; CHECK-NEXT: mov x0, x8 +; CHECK-NEXT: xpaci x0 +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define i8* @test_returnaddress_1() #0 { + %r = call i8* @llvm.returnaddress(i32 1) + ret i8* %r +} + +; CHECK-LABEL: _test_noframe_alloca: +; CHECK-NEXT: sub sp, sp, #16 +; CHECK-NEXT: add x8, sp, #15 +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: add sp, sp, #16 +; CHECK-NEXT: ret +define void @test_noframe_alloca() #0 { + %p = alloca i8, i32 1 + call void asm sideeffect "", "r"(i8* %p) + ret void +} + +; CHECK-LABEL: _test_call: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: retab +define void @test_call() #0 { + call i32 @bar() + ret void +} + +; CHECK-LABEL: _test_call_alloca: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: sub sp, sp, #32 +; CHECK-NEXT: stp x29, x30, [sp, #16] +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp, #16] +; CHECK-NEXT: add sp, sp, #32 +; CHECK-NEXT: retab +define void @test_call_alloca() #0 { + alloca i8 + call i32 @bar() + ret void +} + +; CHECK-LABEL: _test_call_shrinkwrapping: +; CHECK-NEXT: tbz w0, #0, [[RETBB:LBB[0-9_]+]] +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: autibsp +; CHECK-NEXT: [[RETBB]]: +; CHECK-NEXT: ret +define void @test_call_shrinkwrapping(i1 %c) #0 { + br i1 %c, label %tbb, label %fbb +tbb: + call i32 @bar() + br label %fbb +fbb: + ret void +} + +; CHECK-LABEL: _test_tailcall: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: autibsp +; CHECK-NEXT: b _bar +define i32 @test_tailcall() #0 { + call i32 @bar() + %c = tail call i32 @bar() + ret i32 %c +} + +; CHECK-LABEL: _test_tailcall_noframe: +; CHECK-NEXT: b _bar +define i32 @test_tailcall_noframe() #0 { + %c = tail call i32 @bar() + ret i32 %c +} + +declare i32 @bar() + +declare i8* @llvm.returnaddress(i32) + +attributes #0 = { nounwind "ptrauth-returns" } From af247a2114947590b95145098feab8f922946b03 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed.bougacha@gmail.com> Date: Tue, 25 Jun 2019 18:42:59 -0700 Subject: [PATCH 524/582] [Transforms] Add pass for software expansion of ptrauth. This pass enables lowering of the ptrauth IR additions on targets that don't have native ISA support for pointer authentication. The generated code uses a minimal runtime, provided separately. Patch by John McCall. --- llvm/include/llvm/InitializePasses.h | 1 + llvm/include/llvm/LinkAllPasses.h | 1 + .../include/llvm/Transforms/Instrumentation.h | 5 + .../Transforms/Instrumentation/CMakeLists.txt | 1 + .../Instrumentation/Instrumentation.cpp | 1 + .../Instrumentation/SoftPointerAuth.cpp | 879 ++++++++++++++++++ .../Transforms/SoftPointerAuth/intrinsics.ll | 35 + .../test/Transforms/SoftPointerAuth/relocs.ll | 23 + 8 files changed, 946 insertions(+) create mode 100644 llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp create mode 100644 llvm/test/Transforms/SoftPointerAuth/intrinsics.ll create mode 100644 llvm/test/Transforms/SoftPointerAuth/relocs.ll diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index b8108339abbf9..4495559050875 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -383,6 +383,7 @@ void initializeSingleLoopExtractorPass(PassRegistry&); void initializeSinkingLegacyPassPass(PassRegistry&); void initializeSjLjEHPreparePass(PassRegistry&); void initializeSlotIndexesPass(PassRegistry&); +void initializeSoftPointerAuthLegacyPassPass(PassRegistry&); void initializeSpeculativeExecutionLegacyPassPass(PassRegistry&); void initializeSpillPlacementPass(PassRegistry&); void initializeStackColoringPass(PassRegistry&); diff --git a/llvm/include/llvm/LinkAllPasses.h b/llvm/include/llvm/LinkAllPasses.h index ac88165845d3c..28a8c03814233 100644 --- a/llvm/include/llvm/LinkAllPasses.h +++ b/llvm/include/llvm/LinkAllPasses.h @@ -214,6 +214,7 @@ namespace { (void) llvm::createPartiallyInlineLibCallsPass(); (void) llvm::createScalarizerPass(); (void) llvm::createSeparateConstOffsetFromGEPPass(); + (void) llvm::createSoftPointerAuthPass(); (void) llvm::createSpeculativeExecutionPass(); (void) llvm::createSpeculativeExecutionIfHasBranchDivergencePass(); (void) llvm::createRewriteSymbolsPass(); diff --git a/llvm/include/llvm/Transforms/Instrumentation.h b/llvm/include/llvm/Transforms/Instrumentation.h index fcad1e11895fe..9854b12cdb229 100644 --- a/llvm/include/llvm/Transforms/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Instrumentation.h @@ -181,6 +181,11 @@ struct SanitizerCoverageOptions { SanitizerCoverageOptions() = default; }; +// SoftPointerAuth - This pass lowers the llvm.ptrauth intrinsics to use +// runtime function calls instead of relying on support from the +// backend, toolchain, loader, or hardware. +ModulePass *createSoftPointerAuthPass(); + /// Calculate what to divide by to scale counts. /// /// Given the maximum count, calculate a divisor that will scale all the diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt index 22190ad7a0ae9..570b7d556c342 100644 --- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt @@ -14,6 +14,7 @@ add_llvm_library(LLVMInstrumentation PGOMemOPSizeOpt.cpp PoisonChecking.cpp SanitizerCoverage.cpp + SoftPointerAuth.cpp ValueProfileCollector.cpp ThreadSanitizer.cpp HWAddressSanitizer.cpp diff --git a/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp b/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp index a6c2c9b464b63..15dd22eb080f6 100644 --- a/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp +++ b/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp @@ -119,6 +119,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) { initializeThreadSanitizerLegacyPassPass(Registry); initializeModuleSanitizerCoverageLegacyPassPass(Registry); initializeDataFlowSanitizerPass(Registry); + initializeSoftPointerAuthLegacyPassPass(Registry); } /// LLVMInitializeInstrumentation - C binding for diff --git a/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp b/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp new file mode 100644 index 0000000000000..486fe57122e6d --- /dev/null +++ b/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp @@ -0,0 +1,879 @@ +//===- SoftPointerAuth.cpp - Software lowering for ptrauth intrinsics -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass lowers the llvm.ptrauth intrinsics into something that can +// be supported (inefficiently) on an arbitrary target. +// +// The runtime functions you must define to use this pass are: +// /// Apply a signature to the given unsigned pointer value. +// void *__ptrauth_sign(void *pointer, int32_t key, uintptr_t discriminator); +// +// /// Remove the signature from the given signed pointer value. +// void *__ptrauth_strip(void *pointer, int32_t key); +// +// /// Authenticate and remove the signature on the given signed +// /// pointer value. Trap on authenticate failure. +// void *__ptrauth_auth(void *pointer, int32_t key, uintptr_t discriminator); +// +// /// Blend a small non-zero value into a primary discriminator, +// /// which is expected to resemble a pointer. +// uintptr_t __ptrauth_blend(uintptr_t primaryDiscriminator, +// uintptr_t secondaryDiscriminator); +// +// /// Compute a full, pointer-wide signature on a value. +// uintptr_t __ptrauth_sign_generic(uintptr_t data, uintptr_t discriminator); +// +// The resulting code pattern does not perfectly protect against the backend +// inserting code between authentications and uses, and so the result may +// be attackable. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/STLExtras.h" +#include <map> + +#define DEBUG_TYPE "soft-ptrauth" + +using namespace llvm; +using IRBuilderTy = llvm::IRBuilder<>; + +namespace { + +/// A structure for tracking uses of relocations within a Constant. +struct UseSite { + /// A map from operand index to the tracking sites of children. + /// If this is empty, the Constant is a GlobalVariable for a relocation. + /// Otherwise, the Constant is a ConstantAggregate or ConstantExpr, and + /// the relocation reference(s) appear further down in the tree. + std::map<unsigned, UseSite> Children; +}; + +/// A linked list down to the use of a relocation. +struct UsePath { + const UsePath *Next; + unsigned OperandIndex; +}; + +enum TypeTag { + IntPtr, // uintptr_t + Discriminator = IntPtr, // uintptr_t + Key, // uint32_t + VoidPtr, // i8* +}; + +class SoftPointerAuth { + // The module. + Module *M = nullptr; + + // Cached function pointers, initialized lazily. + Constant *SignPointerFn = nullptr; + Constant *AuthPointerFn = nullptr; + Constant *StripPointerFn = nullptr; + Constant *BlendDiscriminatorFn = nullptr; + Constant *SignGenericFn = nullptr; + + Optional<IRBuilderTy> GlobalConstructorBuilder; + +public: + SoftPointerAuth() {} + + bool runOnModule(Module &M); + +private: + bool isPointerAuthRelocation(GlobalVariable *global); + + bool transformRelocations(); + void transformGlobalInitializer(GlobalVariable *global, + const UseSite &usesToTransform); + Constant *transformInitializer(GlobalVariable *global, + SmallVectorImpl<Constant*> &pathToInitializer, + Constant *initializer, + const UseSite &usesToTransform); + void transformInstructionOperands(Instruction *user, + const UseSite &usesToTransform); + Value *emitTransformedConstant(IRBuilderTy &builder, Constant *constant, + const UseSite &usesToTransform); + IRBuilderTy &continueGlobalConstructor(); + + bool transformCalls(); + bool transformCall(CallInst *call); + bool transformInvoke(InvokeInst *call); + bool transformPointerAuthCall(CallSite oldCall, + const OperandBundleUse &bundle); + + Value *emitSign(IRBuilderTy &builder, Value *pointer, + Value *key, Value *discriminator); + Value *emitResign(IRBuilderTy &builder, Value *pointer, + Value *oldKey, Value *oldDiscriminator, + Value *newKey, Value *newDiscriminator); + Value *emitAuth(IRBuilderTy &builder, Value *pointer, + Value *key, Value *discriminator); + Value *emitStrip(IRBuilderTy &builder, Value *pointer, Value *key); + Value *emitBlend(IRBuilderTy &builder, Value *primary, Value *secondary); + Value *emitSignGeneric(IRBuilderTy &builder, + Value *value, Value *discriminator); + + /// Check whether the callee of a call has the right prototype. + bool hasExpectedPrototype(CallSite call, TypeTag resultTypeTag, + ArrayRef<TypeTag> argTypeTags) { + if (!hasType(call.getInstruction(), resultTypeTag)) + return false; + + if (call.getNumArgOperands() != argTypeTags.size()) + return false; + for (unsigned i = 0, e = argTypeTags.size(); i != e; ++i) { + if (!hasType(call.getArgOperand(i), argTypeTags[i])) + return false; + } + return true; + } + + /// Does the given value have its expected type? + bool hasType(Value *value, TypeTag tag) { + auto type = value->getType(); + switch (tag) { + case VoidPtr: + if (auto ptrType = dyn_cast<PointerType>(type)) + return ptrType->getAddressSpace() == 0 && + ptrType->getElementType()->isIntegerTy(8); + return false; + case Key: + return type->isIntegerTy(32); + case IntPtr: + return type->isIntegerTy(M->getDataLayout().getPointerSizeInBits(0)); + } + llvm_unreachable("unexpected type tag"); + } + /// Fetch an expected type. + Type *getType(TypeTag tag) { + switch (tag) { + case VoidPtr: return Type::getInt8PtrTy(M->getContext()); + case Key: return Type::getInt32Ty(M->getContext()); + case IntPtr: return Type::getIntNTy(M->getContext(), + M->getDataLayout().getPointerSizeInBits(0)); + } + llvm_unreachable("unexpected type tag"); + } + + ConstantInt *getInt32(unsigned value) { + return ConstantInt::get(Type::getInt32Ty(M->getContext()), value); + } + + /// Create a declaration for the given runtime function. + Constant *getOrInsertFunction(StringRef name, TypeTag resultTypeTag, + ArrayRef<TypeTag> argTypeTags) { + auto resultType = getType(resultTypeTag); + SmallVector<Type*, 4> argTypes; + for (auto argTypeTag : argTypeTags) + argTypes.push_back(getType(argTypeTag)); + auto functionType = FunctionType::get(resultType, argTypes, false); + return cast<Constant>( + M->getOrInsertFunction(name, functionType).getCallee()); + } + + Constant *getSignPointerFn() { + if (!SignPointerFn) + SignPointerFn = getOrInsertFunction("__ptrauth_sign", VoidPtr, + { VoidPtr, Key, Discriminator }); + return SignPointerFn; + } + + Constant *getAuthPointerFn() { + if (!AuthPointerFn) + AuthPointerFn = getOrInsertFunction("__ptrauth_auth", VoidPtr, + { VoidPtr, Key, Discriminator }); + return AuthPointerFn; + } + + Constant *getStripPointerFn() { + if (!StripPointerFn) + StripPointerFn = getOrInsertFunction("__ptrauth_strip", VoidPtr, + { VoidPtr, Key }); + return StripPointerFn; + } + + Constant *getBlendDiscriminatorFn() { + if (!BlendDiscriminatorFn) + BlendDiscriminatorFn = getOrInsertFunction("__ptrauth_blend", + Discriminator, + { Discriminator, Discriminator }); + return BlendDiscriminatorFn; + } + + Constant *getSignGenericFn() { + if (!SignGenericFn) + SignGenericFn = getOrInsertFunction("__ptrauth_sign_generic", IntPtr, + { IntPtr, Key, Discriminator }); + return SignPointerFn; + } +}; + +} // end anonymous namespace + +bool SoftPointerAuth::runOnModule(Module &M) { + assert(!GlobalConstructorBuilder); + + // Reset any existing caches. + SignPointerFn = nullptr; + AuthPointerFn = nullptr; + StripPointerFn = nullptr; + BlendDiscriminatorFn = nullptr; + SignGenericFn = nullptr; + + this->M = &M; + + bool changed = false; + + // Transform all of the intrinsic calls and operand bundles. + // Doing this before transforming the relocations doesn't deeply matter, + // but this pass has to walk all the functions and the relocation pass is + // based on use lists, so this order minimizes redundant work. + changed |= transformCalls(); + + // Next, transform all the uses of relocations. + changed |= transformRelocations(); + + return changed; +} + +/*****************************************************************************/ +/********************************** Common ***********************************/ +/*****************************************************************************/ + +Value *SoftPointerAuth::emitSign(IRBuilderTy &builder, Value *pointer, + Value *key, Value *discriminator) { + auto call = builder.CreateCall(getSignPointerFn(), + { pointer, key, discriminator }); + call->setDoesNotThrow(); + return call; +} + +Value *SoftPointerAuth::emitResign(IRBuilderTy &builder, Value *pointer, + Value *oldKey, Value *oldDiscriminator, + Value *newKey, Value *newDiscriminator) { + // This is not an unattackable code pattern, but we don't emit one for + // call operand bundles, either. + auto rawValue = emitAuth(builder, pointer, oldKey, oldDiscriminator); + return emitSign(builder, rawValue, newKey, newDiscriminator); +} + +Value *SoftPointerAuth::emitAuth(IRBuilderTy &builder, Value *pointer, + Value *key, Value *discriminator) { + auto call = builder.CreateCall(getAuthPointerFn(), + { pointer, key, discriminator }); + call->setDoesNotThrow(); + return call; +} + +Value *SoftPointerAuth::emitStrip(IRBuilderTy &builder, Value *pointer, + Value *key) { + auto call = builder.CreateCall(getStripPointerFn(), + { pointer, key }); + call->setDoesNotThrow(); + return call; +} + +Value *SoftPointerAuth::emitBlend(IRBuilderTy &builder, Value *primary, + Value *secondary) { + auto call = builder.CreateCall(getBlendDiscriminatorFn(), + { primary, secondary }); + call->setDoesNotThrow(); + return call; +} + +Value *SoftPointerAuth::emitSignGeneric(IRBuilderTy &builder, Value *value, + Value *discriminator) { + auto call = builder.CreateCall(getSignGenericFn(), + { value, discriminator }); + call->setDoesNotThrow(); + return call; +} + +bool SoftPointerAuth::isPointerAuthRelocation(GlobalVariable *global) { + // After checking the name, validate the type. + if (global->getSection() == "llvm.ptrauth") { + if (auto init = dyn_cast_or_null<ConstantStruct>( + global->getInitializer())) { + return (init->getNumOperands() == 4 && + hasType(init->getOperand(0), VoidPtr) && + hasType(init->getOperand(1), Key) && + hasType(init->getOperand(2), Discriminator) && + hasType(init->getOperand(3), Discriminator)); + } + } + + return false; +} + +/*****************************************************************************/ +/******************************** Relocations ********************************/ +/*****************************************************************************/ + +/// Find all the top-level uses of a constant (i.e. the uses that are not +/// ConstantAggregates or ConstantExprs) and call the given callback +/// function on them. +template <class Fn> +static void findTopLevelUsesOfConstant(Constant *constant, const UsePath *path, + const Fn &callback) { + for (auto i = constant->use_begin(), e = constant->use_end(); i != e; ++i) { + UsePath userPath = { path, i->getOperandNo() }; + auto user = i->getUser(); + + // If the user is a global variable, there's only one use we care about. + if (isa<GlobalVariable>(user)) { + assert(userPath.OperandIndex == 0 && "non-zero use index on global var"); + callback(user, path); + + // If the user is an instruction, remember the operand index. + } else if (isa<Instruction>(user)) { + callback(user, &userPath); + + // If the user is some other kind of context, recurse. + } else if (auto userConstant = dyn_cast<Constant>(user)) { + findTopLevelUsesOfConstant(userConstant, &userPath, callback); + } + + // TODO: metadata uses? + } +} + +bool SoftPointerAuth::transformRelocations() { + SmallVector<GlobalVariable *, 16> relocations; + SmallVector<User*, 16> rootUsers; + DenseMap<User*, UseSite> useSites; + + // Walk all the globals looking for relocations. + for (auto &global : M->globals()) { + if (!isPointerAuthRelocation(&global)) + continue; + + // Remember this relocation. + relocations.push_back(&global); + + // Remember all the top-level uses of the relocation, together with + // paths down to the use. + findTopLevelUsesOfConstant(&global, nullptr, + [&](User *user, const UsePath *path) { + // Look up an entry in the users map, adding one if necessary. + // We remember the order in which we encountered things to avoid + // non-deterministically walking over a DenseMap. This still leaves + // us vulnerable to use-list ordering, but that's harder to avoid. + auto result = useSites.try_emplace(user); + if (result.second) rootUsers.push_back(user); + + // Fill out the path down to the use. + UseSite *site = &result.first->second; + for (; path; path = path->Next) { + site = &site->Children[path->OperandIndex]; + } + (void) site; + }); + } + + // Bail out if we didn't find any uses. + if (relocations.empty()) + return false; + + // Rewrite all the root users. + for (auto user : rootUsers) { + const auto &uses = useSites.find(user)->second; + if (auto global = dyn_cast<GlobalVariable>(user)) { + transformGlobalInitializer(global, uses); + } else { + transformInstructionOperands(cast<Instruction>(user), uses); + } + } + + // Destroy all the relocations. + for (auto reloc : relocations) { + reloc->replaceAllUsesWith(ConstantPointerNull::get(reloc->getType())); + reloc->eraseFromParent(); + } + + // Finish the global initialization function if we started one. + if (GlobalConstructorBuilder) { + GlobalConstructorBuilder->CreateRetVoid(); + GlobalConstructorBuilder.reset(); + } + + return true; +} + +/// Transform a global initializer that contains signing relocations. +void SoftPointerAuth::transformGlobalInitializer(GlobalVariable *global, + const UseSite &usesToTransform) { + auto oldInitializer = global->getInitializer(); + assert(oldInitializer && "global has no initializer?"); + + // transformInitializer wants the indices of a GEP to the initializer + // that it's transforming. Seed that with a '0' to enter the global. + SmallVector<Constant*, 4> pathToInitializer; + pathToInitializer.push_back(getInt32(0)); + + auto newInitializer = transformInitializer(global, pathToInitializer, + oldInitializer, usesToTransform); + + assert(newInitializer != oldInitializer && "no changes?"); + assert(pathToInitializer.size() == 1 && "didn't balance push/pop"); + + global->setInitializer(newInitializer); + + // Make the global mutable; our constant initializer will change it. + global->setConstant(false); +} + +/// Transform part of a global initializer that contains signing relocations. +Constant *SoftPointerAuth::transformInitializer(GlobalVariable *global, + SmallVectorImpl<Constant*> &pathToInitializer, + Constant *initializer, + const UseSite &usesToTransform) { + auto aggregate = dyn_cast<ConstantAggregate>(initializer); + + // If the initializer is a simple reference to a relocation, or an + // expression in terms of same, compute it in the global construction. + if (!aggregate) { + auto &builder = continueGlobalConstructor(); + + // Compute the value. + auto transformedInitializer = + emitTransformedConstant(builder, initializer, usesToTransform); + + // Drill down to the current position. + Constant *addr = global; + if (pathToInitializer.size() != 1) + addr = ConstantExpr::getInBoundsGetElementPtr(global->getValueType(), + addr, pathToInitializer); + + // Store the transformed vlaue to this position. + builder.CreateStore(transformedInitializer, addr); + + // Use a null value for the global position. + return Constant::getNullValue(initializer->getType()); + } + + // Otherwise, the initializer is a constant aggregate. Recurse into it + // at the appropriate positions. The goal here is to avoid emitting the + // entire aggregate with stores. + assert(!usesToTransform.Children.empty() + && "walking into wrong initializer?"); + + // Copy the original elements. + SmallVector<Constant*, 16> elts; + elts.reserve(aggregate->getNumOperands()); + for (auto &op : aggregate->operands()) + elts.push_back(cast<Constant>(&*op)); + + // Modify just the elements that we decided to modify. + for (const auto &eltIndexAndUses : usesToTransform.Children) { + auto eltIndex = eltIndexAndUses.first; + + // Add an index to the GEP down to this position. + pathToInitializer.push_back(getInt32(eltIndex)); + + // Rewrite the element. + elts[eltIndex] = transformInitializer(global, pathToInitializer, + elts[eltIndex], eltIndexAndUses.second); + + // Pop the previously pushed path element. + pathToInitializer.pop_back(); + } + + // Rebuild the aggregate. + auto type = aggregate->getType(); + if (auto structType = dyn_cast<StructType>(type)) { + return ConstantStruct::get(structType, elts); + } else if (auto arrayType = dyn_cast<ArrayType>(type)) { + return ConstantArray::get(arrayType, elts); + } else { + return ConstantVector::get(elts); + } +} + +/// Continue emitting the global constructor function. +IRBuilderTy &SoftPointerAuth::continueGlobalConstructor() { + // Create the global initialization function if we haven't yet. + if (!GlobalConstructorBuilder) { + auto &context = M->getContext(); + + // Create the function. + auto fnType = FunctionType::get(Type::getVoidTy(context), + {}, false); + Function *fn = Function::Create(fnType, Function::PrivateLinkage, + "ptrauth_soft_init", M); + + // Add the function to the global initializers list. + appendToGlobalCtors(*M, fn, 0); + + auto entryBB = BasicBlock::Create(context, "", fn); + + GlobalConstructorBuilder.emplace(entryBB); + } + return *GlobalConstructorBuilder; +} + +void SoftPointerAuth::transformInstructionOperands(Instruction *user, + const UseSite &usesToTransform) { + assert(!usesToTransform.Children.empty() + && "no uses to transform for instruction"); + + // Handle PHIs differently because we have to insert code into the + // right predecessor(s). + if (auto phi = dyn_cast<PHINode>(user)) { + for (auto &useEntry : usesToTransform.Children) { + auto operandIndex = useEntry.first; + auto operand = cast<Constant>(phi->getOperand(operandIndex)); + + // Figure out the block this edge corresponds to. + auto incomingValueIndex = + PHINode::getIncomingValueNumForOperand(operandIndex); + auto incomingBlock = phi->getIncomingBlock(incomingValueIndex); + + // Split the edge if necessary & possible. + // Note that we don't want to change anything structurally about 'phi'. + auto newBlock = SplitCriticalEdge(incomingBlock, phi->getParent(), + CriticalEdgeSplittingOptions() + .setKeepOneInputPHIs()); + + // Start inserting before the terminator in the new block. + // If a critical edge was unsplittable, this will insert the code + // unconditionally in the origin block, which is unfortunate but + // acceptable because sign operations cannot fail. + auto blockToInsertInto = newBlock ? newBlock : incomingBlock; + IRBuilderTy builder(blockToInsertInto->getTerminator()); + + // Transform the value. + auto transformedOperand = + emitTransformedConstant(builder, operand, useEntry.second); + + // Replace the incoming value. + phi->setIncomingValue(incomingValueIndex, transformedOperand); + } + + return; + } + + // Otherwise, emit immediately before the user. + IRBuilderTy builder(user); + for (auto &useEntry : usesToTransform.Children) { + auto operandIndex = useEntry.first; + auto operand = cast<Constant>(user->getOperand(operandIndex)); + + auto transformedOperand = + emitTransformedConstant(builder, operand, useEntry.second); + + // Replace the incoming value. + user->setOperand(operandIndex, transformedOperand); + } +} + + +Value *SoftPointerAuth::emitTransformedConstant(IRBuilderTy &builder, + Constant *constant, + const UseSite &usesToTransform) { + // If it's a direct reference to the relocation, we're done. + if (auto global = dyn_cast<GlobalVariable>(constant)) { + assert(isPointerAuthRelocation(global)); + assert(usesToTransform.Children.empty() && + "child uses of direct relocation reference?"); + + // Decompose the relocation. + ConstantStruct *init = cast<ConstantStruct>(global->getInitializer()); + auto pointer = init->getOperand(0); + auto key = init->getOperand(1); + auto primaryDiscriminator = init->getOperand(2); + auto secondaryDiscriminator = init->getOperand(3); + + // Compute the discriminator. + Value *discriminator; + if (primaryDiscriminator->isNullValue()) { + discriminator = secondaryDiscriminator; + } else if (secondaryDiscriminator->isNullValue()) { + discriminator = primaryDiscriminator; + } else { + discriminator = emitBlend(builder, primaryDiscriminator, + secondaryDiscriminator); + } + + // Emit a sign operation. + auto signedValue = emitSign(builder, pointer, key, discriminator); + + // Cast back to the signed pointer type. + return builder.CreateBitCast(signedValue, global->getType()); + } + + // If it's a constant expression, make it an instruction and rebuild + // its operands. + if (auto expr = dyn_cast<ConstantExpr>(constant)) { + assert(!usesToTransform.Children.empty() && + "direct use of constant expression?"); + + auto instruction = expr->getAsInstruction(); + + for (const auto &operandIndexAndUses : usesToTransform.Children) { + auto operandIndex = operandIndexAndUses.first; + + auto newOperand = + emitTransformedConstant(builder, expr->getOperand(operandIndex), + operandIndexAndUses.second); + instruction->setOperand(operandIndex, newOperand); + } + + builder.Insert(instruction); + return instruction; + } + + // Otherwise, it should be a constant aggregate. + // Recursively emit the transformed elements. + auto aggregate = cast<ConstantAggregate>(constant); + assert(!usesToTransform.Children.empty() && + "direct use of whole constant aggregate?"); + + SmallVector<Value*, 16> elts(aggregate->op_begin(), aggregate->op_end()); + + // Transform all of the children we're supposed to transform. + for (const auto &childUseEntry : usesToTransform.Children) { + auto &elt = elts[childUseEntry.first]; + elt = emitTransformedConstant(builder, cast<Constant>(elt), + childUseEntry.second); + } + + // Build up the aggregate value using insertelement / insertvalue + // as appropriate. + auto type = aggregate->getType(); + bool isVector = isa<VectorType>(type); + Value *transformedAggregate = UndefValue::get(type); + for (unsigned i = 0, e = aggregate->getNumOperands(); i != e; ++i) { + if (isVector) + transformedAggregate = + builder.CreateInsertElement(transformedAggregate, elts[i], i); + else + transformedAggregate = + builder.CreateInsertValue(transformedAggregate, elts[i], i); + } + return transformedAggregate; +} + +/*****************************************************************************/ +/*********************** Intrinsics and Operand Bundles **********************/ +/*****************************************************************************/ + +bool SoftPointerAuth::transformCalls() { + bool changed = false; + + for (auto fi = M->begin(), fe = M->end(); fi != fe; ) { + auto fn = &*fi; + ++fi; + + // Soft return authentication is technically possible (even without backend + // support) but not currently necessary. + if (fn->hasFnAttribute("ptrauth-returns")) + report_fatal_error("Soft. lowering of return address auth unsupported"); + + for (auto bi = fn->begin(), be = fn->end(); bi != be; ) { + auto bb = &*bi; + ++bi; + + for (auto ii = bb->begin(), ie = bb->end(); ii != ie; ) { + auto instruction = &*ii; + ++ii; + + if (auto call = dyn_cast<CallInst>(instruction)) { + changed |= transformCall(call); + } else if (auto invoke = dyn_cast<InvokeInst>(instruction)) { + changed |= transformInvoke(invoke); + } + } + } + } + + return changed; +} + +bool SoftPointerAuth::transformCall(CallInst *call) { + // Handle calls with the llvm.ptrauth operand bundle attached. + if (auto bundle = call->getOperandBundle(LLVMContext::OB_ptrauth)) { + return transformPointerAuthCall(call, *bundle); + } + + // Otherwise, look for our intrinsics. + auto callee = call->getCalledFunction(); + if (!callee) return false; + auto intrinsic = CallSite(call).getIntrinsicID(); + if (!intrinsic) return false; + + auto rebuild = [&](function_ref<llvm::Value*(IRBuilderTy&)> fn) { + IRBuilderTy builder(call); + auto result = fn(builder); + call->replaceAllUsesWith(result); + call->eraseFromParent(); + return true; + }; + + switch (intrinsic) { + case Intrinsic::ptrauth_sign: + if (!hasExpectedPrototype(call, VoidPtr, {VoidPtr, Key, Discriminator})) + return false; + return rebuild([&](IRBuilderTy &builder) { + return emitSign(builder, call->getArgOperand(0), + call->getArgOperand(1), call->getArgOperand(2)); + }); + + case Intrinsic::ptrauth_resign: + if (!hasExpectedPrototype(call, VoidPtr, {VoidPtr, Key, Discriminator, + Key, Discriminator})) + return false; + return rebuild([&](IRBuilderTy &builder) { + return emitResign(builder, call->getArgOperand(0), + call->getArgOperand(1), call->getArgOperand(2), + call->getArgOperand(3), call->getArgOperand(4)); + }); + + case Intrinsic::ptrauth_auth: + if (!hasExpectedPrototype(call, VoidPtr, {VoidPtr, Key, Discriminator})) + return false; + return rebuild([&](IRBuilderTy &builder) { + return emitAuth(builder, call->getArgOperand(0), + call->getArgOperand(1), call->getArgOperand(2)); + }); + + case Intrinsic::ptrauth_strip: + if (!hasExpectedPrototype(call, VoidPtr, {VoidPtr, Key})) + return false; + return rebuild([&](IRBuilderTy &builder) { + return emitStrip(builder, call->getArgOperand(0), + call->getArgOperand(1)); + }); + + case Intrinsic::ptrauth_blend: + if (!hasExpectedPrototype(call, Discriminator, + {Discriminator, Discriminator})) + return false; + return rebuild([&](IRBuilderTy &builder) { + return emitBlend(builder, call->getArgOperand(0), + call->getArgOperand(1)); + }); + + case Intrinsic::ptrauth_sign_generic: + if (!hasExpectedPrototype(call, IntPtr, {IntPtr, IntPtr})) + return false; + return rebuild([&](IRBuilderTy &builder) { + return emitSignGeneric(builder, call->getArgOperand(0), + call->getArgOperand(1)); + }); + + default: + break; + } + + return false; +} + +bool SoftPointerAuth::transformInvoke(InvokeInst *call) { + // Handle invokes with the llvm.ptrauth operand bundle attached. + if (auto bundle = call->getOperandBundle(LLVMContext::OB_ptrauth)) { + return transformPointerAuthCall(call, *bundle); + } + + return false; +} + +bool SoftPointerAuth::transformPointerAuthCall(CallSite oldCall, + const OperandBundleUse &bundle) { + if (bundle.Inputs.size() != 2 || + !hasType(bundle.Inputs[0], Key) || + !hasType(bundle.Inputs[1], Discriminator)) + return false; + + IRBuilderTy builder(oldCall.getInstruction()); + + // Authenticate the callee. + Value *oldCallee = oldCall.getCalledValue(); + Value *callee = builder.CreateBitCast(oldCallee, getType(VoidPtr)); + callee = emitAuth(builder, callee, bundle.Inputs[0], bundle.Inputs[1]); + callee = builder.CreateBitCast(callee, oldCallee->getType()); + + // Get the arguments. + SmallVector<Value*, 8> args(oldCall.arg_begin(), oldCall.arg_end()); + + // Get the operand bundles besides llvm.ptrauth (probably none). + SmallVector<OperandBundleDef, 1> opBundles; + for (unsigned i = 0, e = oldCall.getNumOperandBundles(); i != e; ++i) { + auto bundle = oldCall.getOperandBundleAt(i); + if (bundle.getTagID() != LLVMContext::OB_ptrauth) { + opBundles.emplace_back(bundle); + } + } + + // Build the new instruction. + CallSite newCall; + if (oldCall.isInvoke()) { + auto oldInvoke = cast<InvokeInst>(oldCall.getInstruction()); + newCall = builder.CreateInvoke(callee, + oldInvoke->getNormalDest(), + oldInvoke->getUnwindDest(), + args, opBundles); + } else { + newCall = builder.CreateCall(callee, args, opBundles); + } + + // Copy mandatory attributes. + newCall.setCallingConv(oldCall.getCallingConv()); + newCall.setAttributes(oldCall.getAttributes()); + + // TODO: copy metadata? + newCall.getInstruction()->takeName(oldCall.getInstruction()); + + // Destroy the old call. + oldCall.getInstruction()->replaceAllUsesWith(newCall.getInstruction()); + oldCall.getInstruction()->eraseFromParent(); + + return true; +} + +/*****************************************************************************/ +/**************************** Pass Manager Support ***************************/ +/*****************************************************************************/ + +namespace { + +class SoftPointerAuthLegacyPass : public ModulePass { +public: + static char ID; + SoftPointerAuthLegacyPass() : ModulePass(ID) { + initializeSoftPointerAuthLegacyPassPass(*PassRegistry::getPassRegistry()); + } + StringRef getPassName() const override { + return "Soft Pointer Auth Lowering"; + } + bool runOnModule(Module &M) override { return Pass.runOnModule(M); } + +private: + SoftPointerAuth Pass; +}; + +} // end anonymous namespace + +char SoftPointerAuthLegacyPass::ID = 0; +INITIALIZE_PASS(SoftPointerAuthLegacyPass, "soft-ptrauth", + "Lower pointer authentication intrinsics for soft targets", + false, false) + +ModulePass *llvm::createSoftPointerAuthPass() { + return new SoftPointerAuthLegacyPass(); +} diff --git a/llvm/test/Transforms/SoftPointerAuth/intrinsics.ll b/llvm/test/Transforms/SoftPointerAuth/intrinsics.ll new file mode 100644 index 0000000000000..4082a7463ac9f --- /dev/null +++ b/llvm/test/Transforms/SoftPointerAuth/intrinsics.ll @@ -0,0 +1,35 @@ +; RUN: opt < %s -soft-ptrauth -S | FileCheck %s + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.12.0" + +%struct.__block_descriptor = type { i64, i64 } +%struct.__block_literal_generic = type { i8*, i32, i32, i8*, %struct.__block_descriptor* } + +@blockptr = common global void ()* null, align 8 + +define internal void @test1() { +entry: + %0 = load void ()*, void ()** @blockptr, align 8 + %block = bitcast void ()* %0 to %struct.__block_literal_generic* + %fnptr_addr = getelementptr inbounds %struct.__block_literal_generic, %struct.__block_literal_generic* %block, i32 0, i32 3 + %block_opaque = bitcast %struct.__block_literal_generic* %block to i8* + %1 = load i8*, i8** %fnptr_addr, align 8 + %fnptr = bitcast i8* %1 to void (i8*)* + %discriminator = ptrtoint i8** %fnptr_addr to i64 + call void %fnptr(i8* %block_opaque) [ "ptrauth"(i32 1, i64 %discriminator) ] + ret void +} + +; CHECK: define internal void @test1() { +; CHECK: %fnptr_addr = getelementptr inbounds %struct.__block_literal_generic, %struct.__block_literal_generic* %block, i32 0, i32 3 +; CHECK-NEXT: %block_opaque = bitcast %struct.__block_literal_generic* %block to i8* +; CHECK-NEXT: [[T0:%.*]] = load i8*, i8** %fnptr_addr, align 8 +; CHECK-NEXT: %fnptr = bitcast i8* [[T0]] to void (i8*)* +; CHECK-NEXT: %discriminator = ptrtoint i8** %fnptr_addr to i64 +; CHECK-NEXT: [[FNPTR_CAST:%.*]] = bitcast void (i8*)* %fnptr to i8* +; CHECK-NEXT: [[FNPTR_AUTH:%.*]] = call i8* @__ptrauth_auth(i8* [[FNPTR_CAST]], i32 1, i64 %discriminator) [[NOUNWIND:#[0-9]+]] +; CHECK-NEXT: [[FNPTR_AUTH_CAST:%.*]] = bitcast i8* [[FNPTR_AUTH]] to void (i8*)* +; CHECK-NEXT: call void [[FNPTR_AUTH_CAST]](i8* %block_opaque){{$}} + +; CHECK: attributes [[NOUNWIND]] = { nounwind } diff --git a/llvm/test/Transforms/SoftPointerAuth/relocs.ll b/llvm/test/Transforms/SoftPointerAuth/relocs.ll new file mode 100644 index 0000000000000..818af93ebdacf --- /dev/null +++ b/llvm/test/Transforms/SoftPointerAuth/relocs.ll @@ -0,0 +1,23 @@ +; RUN: opt < %s -soft-ptrauth -S | FileCheck %s + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.12.0" + +; CHECK-NOT: @test1_reloc +; CHECK: @test1 = internal global { i8**, i32, i32, i8* } { i8** null, i32 1342177280, i32 0, i8* null }, align 8 + +@test1_reloc = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (i8*)* @test1_function to i8*), i32 1, i64 ptrtoint (i8** getelementptr inbounds ({ i8**, i32, i32, i8* }, { i8**, i32, i32, i8* }* @test1, i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth", align 8 +@test1 = internal constant { i8**, i32, i32, i8* } { i8** null, i32 1342177280, i32 0, i8* bitcast ({ i8*, i32, i64, i64 }* @test1_reloc to i8*) }, align 8 + +define internal void @test1_function(i8*) { +entry: + ret void +} + +; CHECK: define private void @ptrauth_soft_init() { +; CHECK: [[T0:%.*]] = call i8* @__ptrauth_sign(i8* bitcast (void (i8*)* @test1_function to i8*), i32 1, i64 ptrtoint (i8** getelementptr inbounds ({ i8**, i32, i32, i8* }, { i8**, i32, i32, i8* }* @test1, i32 0, i32 3) to i64)) [[NOUNWIND:#[0-9]+]] +; CHECK: [[T1:%.*]] = bitcast i8* [[T0]] to { i8*, i32, i64, i64 }* +; CHECK: [[T2:%.*]] = bitcast { i8*, i32, i64, i64 }* [[T1]] to i8* +; CHECK: store i8* [[T2]], i8** getelementptr inbounds ({ i8**, i32, i32, i8* }, { i8**, i32, i32, i8* }* @test1, i32 0, i32 3) + +; CHECK: attributes [[NOUNWIND]] = { nounwind } From b7c83adb3cf0b6bc9b9f0454f3f72d68e77aea91 Mon Sep 17 00:00:00 2001 From: Adrian Prantl <aprantl@apple.com> Date: Tue, 25 Jun 2019 18:37:15 -0700 Subject: [PATCH 525/582] [DebugInfo] Add 'ptrauth' qualifier support. This patch adds support for the ptrauth qualifier to debug info metadata. The ptrauth qualifier is represented as a DIDerivedType qualified type and uses the SubClassData32 field to store the extra data. --- llvm/include/llvm/BinaryFormat/Dwarf.def | 4 + llvm/include/llvm/IR/DIBuilder.h | 13 ++- llvm/include/llvm/IR/DebugInfoMetadata.h | 121 +++++++++++++++------ llvm/lib/AsmParser/LLParser.cpp | 18 ++- llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 7 +- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 + llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 10 +- llvm/lib/DebugInfo/DWARF/DWARFDie.cpp | 13 +++ llvm/lib/IR/AsmWriter.cpp | 6 + llvm/lib/IR/DIBuilder.cpp | 48 ++++---- llvm/lib/IR/DebugInfo.cpp | 6 +- llvm/lib/IR/DebugInfoMetadata.cpp | 16 +-- llvm/lib/IR/LLVMContextImpl.h | 13 ++- llvm/lib/IR/Verifier.cpp | 1 + llvm/test/Assembler/debug-info.ll | 7 +- llvm/test/DebugInfo/AArch64/ptrauth.ll | 27 +++++ llvm/unittests/IR/MetadataTest.cpp | 85 ++++++++++----- 17 files changed, 290 insertions(+), 107 deletions(-) create mode 100644 llvm/test/DebugInfo/AArch64/ptrauth.ll diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index 34a7410f74744..3ec75e6bc56a2 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -206,6 +206,7 @@ HANDLE_DW_TAG(0x4108, GNU_formal_parameter_pack, 0, GNU, DW_KIND_NONE) HANDLE_DW_TAG(0x4109, GNU_call_site, 0, GNU, DW_KIND_NONE) HANDLE_DW_TAG(0x410a, GNU_call_site_parameter, 0, GNU, DW_KIND_NONE) HANDLE_DW_TAG(0x4200, APPLE_property, 0, APPLE, DW_KIND_NONE) +HANDLE_DW_TAG(0x4300, APPLE_ptrauth_type, 0, LLVM, DW_KIND_NONE) HANDLE_DW_TAG(0xb000, BORLAND_property, 0, BORLAND, DW_KIND_NONE) HANDLE_DW_TAG(0xb001, BORLAND_Delphi_string, 0, BORLAND, DW_KIND_TYPE) HANDLE_DW_TAG(0xb002, BORLAND_Delphi_dynamic_array, 0, BORLAND, DW_KIND_TYPE) @@ -408,6 +409,9 @@ HANDLE_DW_AT(0x3e01, LLVM_config_macros, 0, LLVM) HANDLE_DW_AT(0x3e02, LLVM_isysroot, 0, LLVM) HANDLE_DW_AT(0x3e03, LLVM_tag_offset, 0, LLVM) // Apple extensions. +HANDLE_DW_AT(0x3e04, APPLE_ptrauth_key, 0, LLVM) +HANDLE_DW_AT(0x3e05, APPLE_ptrauth_address_discriminated, 0, LLVM) +HANDLE_DW_AT(0x3e06, APPLE_ptrauth_extra_discriminator, 0, LLVM) HANDLE_DW_AT(0x3fe1, APPLE_optimized, 0, APPLE) HANDLE_DW_AT(0x3fe2, APPLE_flags, 0, APPLE) HANDLE_DW_AT(0x3fe3, APPLE_isa, 0, APPLE) diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h index ad9a35b554144..049ed4b71c9d5 100644 --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -207,11 +207,14 @@ namespace llvm { /// \param AlignInBits Alignment. (optional) /// \param DWARFAddressSpace DWARF address space. (optional) /// \param Name Pointer type name. (optional) - DIDerivedType *createPointerType(DIType *PointeeTy, uint64_t SizeInBits, - uint32_t AlignInBits = 0, - Optional<unsigned> DWARFAddressSpace = - None, - StringRef Name = ""); + DIDerivedType *createPointerType( + DIType *PointeeTy, uint64_t SizeInBits, uint32_t AlignInBits = 0, + Optional<unsigned> DWARFAddressSpace = None, StringRef Name = ""); + + /// Create a __ptrauth qualifier. + DIDerivedType *createPtrAuthQualifiedType(DIType *FromTy, unsigned Key, + bool IsAddressDiscriminated, + unsigned ExtraDiscriminator); /// Create debugging information entry for a pointer to member. /// \param PointeeTy Type pointed to by this pointer. diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h index 28a59576b7c6f..eae300d919b60 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -754,6 +754,35 @@ class DIBasicType : public DIType { /// /// TODO: Split out members (inheritance, fields, methods, etc.). class DIDerivedType : public DIType { +public: + /// Pointer authentication (__ptrauth) metadata. + struct PtrAuthData { + union { + struct { + unsigned Key : 3; + unsigned IsAddressDiscriminated : 1; + unsigned ExtraDiscriminator : 16; + } Data; + unsigned RawData; + } Payload; + + PtrAuthData(unsigned FromRawData) { Payload.RawData = FromRawData; } + PtrAuthData(const PtrAuthData &Copy) { + Payload.RawData = Copy.Payload.RawData; + } + PtrAuthData(unsigned Key, bool IsDiscr, unsigned Discriminator) { + assert(Key < 8); + assert(Discriminator <= 0xffff); + Payload.Data.Key = Key; + Payload.Data.IsAddressDiscriminated = IsDiscr; + Payload.Data.ExtraDiscriminator = Discriminator; + } + bool operator==(struct PtrAuthData Other) const { + return Payload.RawData == Other.Payload.RawData; + } + }; + +private: friend class LLVMContextImpl; friend class MDNode; @@ -764,58 +793,63 @@ class DIDerivedType : public DIType { DIDerivedType(LLVMContext &C, StorageType Storage, unsigned Tag, unsigned Line, uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, Optional<unsigned> DWARFAddressSpace, - DIFlags Flags, ArrayRef<Metadata *> Ops) + Optional<PtrAuthData> PtrAuthData, DIFlags Flags, + ArrayRef<Metadata *> Ops) : DIType(C, DIDerivedTypeKind, Storage, Tag, Line, SizeInBits, AlignInBits, OffsetInBits, Flags, Ops), - DWARFAddressSpace(DWARFAddressSpace) {} + DWARFAddressSpace(DWARFAddressSpace) { + if (PtrAuthData) + SubclassData32 = PtrAuthData->Payload.RawData; + } ~DIDerivedType() = default; static DIDerivedType * getImpl(LLVMContext &Context, unsigned Tag, StringRef Name, DIFile *File, unsigned Line, DIScope *Scope, DIType *BaseType, uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, - Optional<unsigned> DWARFAddressSpace, DIFlags Flags, - Metadata *ExtraData, StorageType Storage, bool ShouldCreate = true) { + Optional<unsigned> DWARFAddressSpace, + Optional<PtrAuthData> PtrAuthData, DIFlags Flags, Metadata *ExtraData, + StorageType Storage, bool ShouldCreate = true) { return getImpl(Context, Tag, getCanonicalMDString(Context, Name), File, Line, Scope, BaseType, SizeInBits, AlignInBits, OffsetInBits, - DWARFAddressSpace, Flags, ExtraData, Storage, ShouldCreate); - } - static DIDerivedType *getImpl(LLVMContext &Context, unsigned Tag, - MDString *Name, Metadata *File, unsigned Line, - Metadata *Scope, Metadata *BaseType, - uint64_t SizeInBits, uint32_t AlignInBits, - uint64_t OffsetInBits, - Optional<unsigned> DWARFAddressSpace, - DIFlags Flags, Metadata *ExtraData, - StorageType Storage, bool ShouldCreate = true); + DWARFAddressSpace, PtrAuthData, Flags, ExtraData, Storage, + ShouldCreate); + } + static DIDerivedType * + getImpl(LLVMContext &Context, unsigned Tag, MDString *Name, Metadata *File, + unsigned Line, Metadata *Scope, Metadata *BaseType, + uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, + Optional<unsigned> DWARFAddressSpace, + Optional<PtrAuthData> PtrAuthData, DIFlags Flags, Metadata *ExtraData, + StorageType Storage, bool ShouldCreate = true); TempDIDerivedType cloneImpl() const { - return getTemporary(getContext(), getTag(), getName(), getFile(), getLine(), - getScope(), getBaseType(), getSizeInBits(), - getAlignInBits(), getOffsetInBits(), - getDWARFAddressSpace(), getFlags(), getExtraData()); + return getTemporary( + getContext(), getTag(), getName(), getFile(), getLine(), getScope(), + getBaseType(), getSizeInBits(), getAlignInBits(), getOffsetInBits(), + getDWARFAddressSpace(), getPtrAuthData(), getFlags(), getExtraData()); } public: - DEFINE_MDNODE_GET(DIDerivedType, - (unsigned Tag, MDString *Name, Metadata *File, - unsigned Line, Metadata *Scope, Metadata *BaseType, - uint64_t SizeInBits, uint32_t AlignInBits, - uint64_t OffsetInBits, - Optional<unsigned> DWARFAddressSpace, DIFlags Flags, - Metadata *ExtraData = nullptr), - (Tag, Name, File, Line, Scope, BaseType, SizeInBits, - AlignInBits, OffsetInBits, DWARFAddressSpace, Flags, - ExtraData)) + DEFINE_MDNODE_GET( + DIDerivedType, + (unsigned Tag, MDString *Name, Metadata *File, unsigned Line, + Metadata *Scope, Metadata *BaseType, uint64_t SizeInBits, + uint32_t AlignInBits, uint64_t OffsetInBits, + Optional<unsigned> DWARFAddressSpace, Optional<PtrAuthData> PtrAuthData, + DIFlags Flags, Metadata *ExtraData = nullptr), + (Tag, Name, File, Line, Scope, BaseType, SizeInBits, AlignInBits, + OffsetInBits, DWARFAddressSpace, PtrAuthData, Flags, ExtraData)) DEFINE_MDNODE_GET(DIDerivedType, (unsigned Tag, StringRef Name, DIFile *File, unsigned Line, DIScope *Scope, DIType *BaseType, uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, - Optional<unsigned> DWARFAddressSpace, DIFlags Flags, + Optional<unsigned> DWARFAddressSpace, + Optional<PtrAuthData> PtrAuthData, DIFlags Flags, Metadata *ExtraData = nullptr), (Tag, Name, File, Line, Scope, BaseType, SizeInBits, - AlignInBits, OffsetInBits, DWARFAddressSpace, Flags, - ExtraData)) + AlignInBits, OffsetInBits, DWARFAddressSpace, PtrAuthData, + Flags, ExtraData)) TempDIDerivedType clone() const { return cloneImpl(); } @@ -827,6 +861,31 @@ class DIDerivedType : public DIType { /// a pointer or reference type respectively. Optional<unsigned> getDWARFAddressSpace() const { return DWARFAddressSpace; } + Optional<PtrAuthData> getPtrAuthData() const { + return getTag() == dwarf::DW_TAG_APPLE_ptrauth_type + ? Optional<PtrAuthData>(PtrAuthData(SubclassData32)) + : None; + } + + /// \returns The PointerAuth key. + Optional<unsigned> getPtrAuthKey() const { + if (auto PtrAuthData = getPtrAuthData()) + return PtrAuthData->Payload.Data.Key; + else return None; + } + /// \returns The PointerAuth address discrimination bit. + Optional<bool> isPtrAuthAddressDiscriminated() const { + if (auto PtrAuthData = getPtrAuthData()) + return PtrAuthData->Payload.Data.IsAddressDiscriminated; + else return None; + } + /// \returns The PointerAuth extra discriminator. + Optional<unsigned> getPtrAuthExtraDiscriminator() const { + if (auto PtrAuthData = getPtrAuthData()) + return PtrAuthData->Payload.Data.ExtraDiscriminator; + else return None; + } + /// Get extra data associated with this derived type. /// /// Class type for pointer-to-members, objective-c property node for ivars, diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 41e1d0bd889a8..575d3766d49dc 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -4505,7 +4505,9 @@ bool LLParser::ParseDIBasicType(MDNode *&Result, bool IsDistinct) { /// ::= !DIDerivedType(tag: DW_TAG_pointer_type, name: "int", file: !0, /// line: 7, scope: !1, baseType: !2, size: 32, /// align: 32, offset: 0, flags: 0, extraData: !3, -/// dwarfAddressSpace: 3) +/// dwarfAddressSpace: 3, ptrAuthKey: 1, +/// ptrAuthIsAddressDiscriminated: true, +/// ptrAuthExtraDiscriminator: 0x1234) bool LLParser::ParseDIDerivedType(MDNode *&Result, bool IsDistinct) { #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \ REQUIRED(tag, DwarfTagField, ); \ @@ -4519,19 +4521,27 @@ bool LLParser::ParseDIDerivedType(MDNode *&Result, bool IsDistinct) { OPTIONAL(offset, MDUnsignedField, (0, UINT64_MAX)); \ OPTIONAL(flags, DIFlagField, ); \ OPTIONAL(extraData, MDField, ); \ - OPTIONAL(dwarfAddressSpace, MDUnsignedField, (UINT32_MAX, UINT32_MAX)); + OPTIONAL(dwarfAddressSpace, MDUnsignedField, (UINT32_MAX, UINT32_MAX)); \ + OPTIONAL(ptrAuthKey, MDUnsignedField, (0, 7)); \ + OPTIONAL(ptrAuthIsAddressDiscriminated, MDBoolField, ); \ + OPTIONAL(ptrAuthExtraDiscriminator, MDUnsignedField, (0, 0xffff)); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS Optional<unsigned> DWARFAddressSpace; if (dwarfAddressSpace.Val != UINT32_MAX) DWARFAddressSpace = dwarfAddressSpace.Val; + Optional<DIDerivedType::PtrAuthData> PtrAuthData; + if (ptrAuthKey.Val) + PtrAuthData = DIDerivedType::PtrAuthData( + (unsigned)ptrAuthKey.Val, ptrAuthIsAddressDiscriminated.Val, + (unsigned)ptrAuthExtraDiscriminator.Val); Result = GET_OR_DISTINCT(DIDerivedType, (Context, tag.Val, name.Val, file.Val, line.Val, scope.Val, baseType.Val, size.Val, align.Val, - offset.Val, DWARFAddressSpace, flags.Val, - extraData.Val)); + offset.Val, DWARFAddressSpace, PtrAuthData, + flags.Val, extraData.Val)); return false; } diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp index 7d6b91843e1b8..c40cb91e4e71c 100644 --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -1322,7 +1322,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata( break; } case bitc::METADATA_DERIVED_TYPE: { - if (Record.size() < 12 || Record.size() > 13) + if (Record.size() < 12 || Record.size() > 14) return error("Invalid record"); // DWARF address space is encoded as N->getDWARFAddressSpace() + 1. 0 means @@ -1330,6 +1330,9 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata( Optional<unsigned> DWARFAddressSpace; if (Record.size() > 12 && Record[12]) DWARFAddressSpace = Record[12] - 1; + Optional<DIDerivedType::PtrAuthData> PtrAuthData; + if (Record.size() > 13 && Record[13]) + PtrAuthData = DIDerivedType::PtrAuthData(Record[13]); IsDistinct = Record[0]; DINode::DIFlags Flags = static_cast<DINode::DIFlags>(Record[10]); @@ -1339,7 +1342,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata( getMDOrNull(Record[3]), Record[4], getDITypeRefOrNull(Record[5]), getDITypeRefOrNull(Record[6]), Record[7], Record[8], - Record[9], DWARFAddressSpace, Flags, + Record[9], DWARFAddressSpace, PtrAuthData, Flags, getDITypeRefOrNull(Record[11]))), NextMetadataNo); NextMetadataNo++; diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index deb4019ea8ba7..987fc8178aa39 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1569,6 +1569,8 @@ void ModuleBitcodeWriter::writeDIDerivedType(const DIDerivedType *N, Record.push_back(*DWARFAddressSpace + 1); else Record.push_back(0); + if (auto PtrAuthData = N->getPtrAuthData()) + Record.push_back(PtrAuthData->Payload.RawData); Stream.EmitRecord(bitc::METADATA_DERIVED_TYPE, Record, Abbrev); Record.clear(); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index be257604cd1a1..5addcec52f193 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -821,7 +821,15 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIDerivedType *DTy) { // or reference types. if (DTy->getDWARFAddressSpace()) addUInt(Buffer, dwarf::DW_AT_address_class, dwarf::DW_FORM_data4, - DTy->getDWARFAddressSpace().getValue()); + *DTy->getDWARFAddressSpace()); + if (auto Key = DTy->getPtrAuthKey()) + addUInt(Buffer, dwarf::DW_AT_APPLE_ptrauth_key, dwarf::DW_FORM_data1, *Key); + if (auto AddrDisc = DTy->isPtrAuthAddressDiscriminated()) + if (AddrDisc) + addFlag(Buffer, dwarf::DW_AT_APPLE_ptrauth_address_discriminated); + if (auto Disc = DTy->getPtrAuthExtraDiscriminator()) + addUInt(Buffer, dwarf::DW_AT_APPLE_ptrauth_extra_discriminator, + dwarf::DW_FORM_data2, *Disc); } void DwarfUnit::constructSubprogramArguments(DIE &Buffer, DITypeRefArray Args) { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index cec194e8b6b3e..611d33514b60a 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -210,6 +210,7 @@ static void dumpTypeName(raw_ostream &OS, const DWARFDie &D) { case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: case DW_TAG_subroutine_type: + case DW_TAG_APPLE_ptrauth_type: break; default: dumpTypeTagName(OS, T); @@ -257,6 +258,18 @@ static void dumpTypeName(raw_ostream &OS, const DWARFDie &D) { case DW_TAG_rvalue_reference_type: OS << "&&"; break; + case DW_TAG_APPLE_ptrauth_type: { + auto getValOrNull = [&](dwarf::Attribute Attr) -> uint64_t { + if (auto Form = D.find(Attr)) + return *Form->getAsUnsignedConstant(); + return 0; + }; + OS << "__ptrauth(" << getValOrNull(DW_AT_APPLE_ptrauth_key) << ", " + << getValOrNull(DW_AT_APPLE_ptrauth_address_discriminated) << ", 0x0" + << utohexstr(getValOrNull(DW_AT_APPLE_ptrauth_extra_discriminator), true) + << ")"; + break; + } default: break; } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index f811c842cf569..c38f2783bb91d 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1860,6 +1860,12 @@ static void writeDIDerivedType(raw_ostream &Out, const DIDerivedType *N, if (const auto &DWARFAddressSpace = N->getDWARFAddressSpace()) Printer.printInt("dwarfAddressSpace", *DWARFAddressSpace, /* ShouldSkipZero */ false); + if (auto Key = N->getPtrAuthKey()) + Printer.printInt("ptrAuthKey", *Key); + if (auto AddrDisc = N->isPtrAuthAddressDiscriminated()) + Printer.printBool("ptrAuthIsAddressDiscriminated", *AddrDisc); + if (auto Disc = N->getPtrAuthExtraDiscriminator()) + Printer.printInt("ptrAuthExtraDiscriminator", *Disc); Out << ")"; } diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index 5d56712274308..6a7c3faa2bdbd 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -266,19 +266,28 @@ DIBasicType *DIBuilder::createBasicType(StringRef Name, uint64_t SizeInBits, DIDerivedType *DIBuilder::createQualifiedType(unsigned Tag, DIType *FromTy) { return DIDerivedType::get(VMContext, Tag, "", nullptr, 0, nullptr, FromTy, 0, - 0, 0, None, DINode::FlagZero); + 0, 0, None, None, DINode::FlagZero); +} + +DIDerivedType * +DIBuilder::createPtrAuthQualifiedType(DIType *FromTy, unsigned Key, + bool IsAddressDiscriminated, + unsigned ExtraDiscriminator) { + return DIDerivedType::get( + VMContext, dwarf::DW_TAG_APPLE_ptrauth_type, "", nullptr, 0, nullptr, + FromTy, 0, 0, 0, None, + Optional<DIDerivedType::PtrAuthData>( + {Key, IsAddressDiscriminated, ExtraDiscriminator}), + DINode::FlagZero); } DIDerivedType *DIBuilder::createPointerType( - DIType *PointeeTy, - uint64_t SizeInBits, - uint32_t AlignInBits, - Optional<unsigned> DWARFAddressSpace, - StringRef Name) { + DIType *PointeeTy, uint64_t SizeInBits, uint32_t AlignInBits, + Optional<unsigned> DWARFAddressSpace, StringRef Name) { // FIXME: Why is there a name here? return DIDerivedType::get(VMContext, dwarf::DW_TAG_pointer_type, Name, nullptr, 0, nullptr, PointeeTy, SizeInBits, - AlignInBits, 0, DWARFAddressSpace, + AlignInBits, 0, DWARFAddressSpace, None, DINode::FlagZero); } @@ -289,7 +298,7 @@ DIDerivedType *DIBuilder::createMemberPointerType(DIType *PointeeTy, DINode::DIFlags Flags) { return DIDerivedType::get(VMContext, dwarf::DW_TAG_ptr_to_member_type, "", nullptr, 0, nullptr, PointeeTy, SizeInBits, - AlignInBits, 0, None, Flags, Base); + AlignInBits, 0, None, None, Flags, Base); } DIDerivedType *DIBuilder::createReferenceType( @@ -299,7 +308,7 @@ DIDerivedType *DIBuilder::createReferenceType( Optional<unsigned> DWARFAddressSpace) { assert(RTy && "Unable to create reference type"); return DIDerivedType::get(VMContext, Tag, "", nullptr, 0, nullptr, RTy, - SizeInBits, AlignInBits, 0, DWARFAddressSpace, + SizeInBits, AlignInBits, 0, DWARFAddressSpace, {}, DINode::FlagZero); } @@ -308,14 +317,14 @@ DIDerivedType *DIBuilder::createTypedef(DIType *Ty, StringRef Name, DIScope *Context) { return DIDerivedType::get(VMContext, dwarf::DW_TAG_typedef, Name, File, LineNo, getNonCompileUnitScope(Context), Ty, 0, 0, - 0, None, DINode::FlagZero); + 0, None, None, DINode::FlagZero); } DIDerivedType *DIBuilder::createFriend(DIType *Ty, DIType *FriendTy) { assert(Ty && "Invalid type!"); assert(FriendTy && "Invalid friend type!"); return DIDerivedType::get(VMContext, dwarf::DW_TAG_friend, "", nullptr, 0, Ty, - FriendTy, 0, 0, 0, None, DINode::FlagZero); + FriendTy, 0, 0, 0, None, None, DINode::FlagZero); } DIDerivedType *DIBuilder::createInheritance(DIType *Ty, DIType *BaseTy, @@ -326,7 +335,7 @@ DIDerivedType *DIBuilder::createInheritance(DIType *Ty, DIType *BaseTy, Metadata *ExtraData = ConstantAsMetadata::get( ConstantInt::get(IntegerType::get(VMContext, 32), VBPtrOffset)); return DIDerivedType::get(VMContext, dwarf::DW_TAG_inheritance, "", nullptr, - 0, Ty, BaseTy, 0, 0, BaseOffset, None, + 0, Ty, BaseTy, 0, 0, BaseOffset, None, None, Flags, ExtraData); } @@ -338,7 +347,8 @@ DIDerivedType *DIBuilder::createMemberType(DIScope *Scope, StringRef Name, DINode::DIFlags Flags, DIType *Ty) { return DIDerivedType::get(VMContext, dwarf::DW_TAG_member, Name, File, LineNumber, getNonCompileUnitScope(Scope), Ty, - SizeInBits, AlignInBits, OffsetInBits, None, Flags); + SizeInBits, AlignInBits, OffsetInBits, None, None, + Flags); } static ConstantAsMetadata *getConstantOrNull(Constant *C) { @@ -353,8 +363,8 @@ DIDerivedType *DIBuilder::createVariantMemberType( Constant *Discriminant, DINode::DIFlags Flags, DIType *Ty) { return DIDerivedType::get(VMContext, dwarf::DW_TAG_member, Name, File, LineNumber, getNonCompileUnitScope(Scope), Ty, - SizeInBits, AlignInBits, OffsetInBits, None, Flags, - getConstantOrNull(Discriminant)); + SizeInBits, AlignInBits, OffsetInBits, None, None, + Flags, getConstantOrNull(Discriminant)); } DIDerivedType *DIBuilder::createBitFieldMemberType( @@ -365,7 +375,7 @@ DIDerivedType *DIBuilder::createBitFieldMemberType( return DIDerivedType::get( VMContext, dwarf::DW_TAG_member, Name, File, LineNumber, getNonCompileUnitScope(Scope), Ty, SizeInBits, /* AlignInBits */ 0, - OffsetInBits, None, Flags, + OffsetInBits, None, None, Flags, ConstantAsMetadata::get(ConstantInt::get(IntegerType::get(VMContext, 64), StorageOffsetInBits))); } @@ -378,7 +388,7 @@ DIBuilder::createStaticMemberType(DIScope *Scope, StringRef Name, DIFile *File, Flags |= DINode::FlagStaticMember; return DIDerivedType::get(VMContext, dwarf::DW_TAG_member, Name, File, LineNumber, getNonCompileUnitScope(Scope), Ty, 0, - AlignInBits, 0, None, Flags, + AlignInBits, 0, None, None, Flags, getConstantOrNull(Val)); } @@ -389,8 +399,8 @@ DIBuilder::createObjCIVar(StringRef Name, DIFile *File, unsigned LineNumber, DIType *Ty, MDNode *PropertyNode) { return DIDerivedType::get(VMContext, dwarf::DW_TAG_member, Name, File, LineNumber, getNonCompileUnitScope(File), Ty, - SizeInBits, AlignInBits, OffsetInBits, None, Flags, - PropertyNode); + SizeInBits, AlignInBits, OffsetInBits, None, None, + Flags, PropertyNode); } DIObjCProperty * diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp index 1bbe6b85d2600..02250e7fd8595 100644 --- a/llvm/lib/IR/DebugInfo.cpp +++ b/llvm/lib/IR/DebugInfo.cpp @@ -1020,9 +1020,9 @@ LLVMMetadataRef LLVMDIBuilderCreatePointerType( LLVMDIBuilderRef Builder, LLVMMetadataRef PointeeTy, uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace, const char *Name, size_t NameLen) { - return wrap(unwrap(Builder)->createPointerType(unwrapDI<DIType>(PointeeTy), - SizeInBits, AlignInBits, - AddressSpace, {Name, NameLen})); + return wrap(unwrap(Builder)->createPointerType( + unwrapDI<DIType>(PointeeTy), SizeInBits, AlignInBits, AddressSpace, + {Name, NameLen})); } LLVMMetadataRef LLVMDIBuilderCreateStructType( diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp index 94ec3abfa7a25..27b34a6a64df5 100644 --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -368,17 +368,19 @@ DIDerivedType *DIDerivedType::getImpl( LLVMContext &Context, unsigned Tag, MDString *Name, Metadata *File, unsigned Line, Metadata *Scope, Metadata *BaseType, uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, - Optional<unsigned> DWARFAddressSpace, DIFlags Flags, Metadata *ExtraData, - StorageType Storage, bool ShouldCreate) { + Optional<unsigned> DWARFAddressSpace, Optional<PtrAuthData> PtrAuthData, + DIFlags Flags, Metadata *ExtraData, StorageType Storage, + bool ShouldCreate) { assert(isCanonical(Name) && "Expected canonical MDString"); DEFINE_GETIMPL_LOOKUP(DIDerivedType, (Tag, Name, File, Line, Scope, BaseType, SizeInBits, - AlignInBits, OffsetInBits, DWARFAddressSpace, Flags, - ExtraData)); + AlignInBits, OffsetInBits, DWARFAddressSpace, + PtrAuthData, Flags, ExtraData)); Metadata *Ops[] = {File, Scope, Name, BaseType, ExtraData}; - DEFINE_GETIMPL_STORE( - DIDerivedType, (Tag, Line, SizeInBits, AlignInBits, OffsetInBits, - DWARFAddressSpace, Flags), Ops); + DEFINE_GETIMPL_STORE(DIDerivedType, + (Tag, Line, SizeInBits, AlignInBits, OffsetInBits, + DWARFAddressSpace, PtrAuthData, Flags), + Ops); } DICompositeType *DICompositeType::getImpl( diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h index 78cf707e0e748..37ad20f28c6e0 100644 --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -413,24 +413,27 @@ template <> struct MDNodeKeyImpl<DIDerivedType> { uint64_t OffsetInBits; uint32_t AlignInBits; Optional<unsigned> DWARFAddressSpace; + Optional<DIDerivedType::PtrAuthData> PtrAuthData; unsigned Flags; Metadata *ExtraData; MDNodeKeyImpl(unsigned Tag, MDString *Name, Metadata *File, unsigned Line, Metadata *Scope, Metadata *BaseType, uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, - Optional<unsigned> DWARFAddressSpace, unsigned Flags, - Metadata *ExtraData) + Optional<unsigned> DWARFAddressSpace, + Optional<DIDerivedType::PtrAuthData> PtrAuthData, + unsigned Flags, Metadata *ExtraData) : Tag(Tag), Name(Name), File(File), Line(Line), Scope(Scope), BaseType(BaseType), SizeInBits(SizeInBits), OffsetInBits(OffsetInBits), AlignInBits(AlignInBits), DWARFAddressSpace(DWARFAddressSpace), - Flags(Flags), ExtraData(ExtraData) {} + PtrAuthData(PtrAuthData), Flags(Flags), ExtraData(ExtraData) {} MDNodeKeyImpl(const DIDerivedType *N) : Tag(N->getTag()), Name(N->getRawName()), File(N->getRawFile()), Line(N->getLine()), Scope(N->getRawScope()), BaseType(N->getRawBaseType()), SizeInBits(N->getSizeInBits()), OffsetInBits(N->getOffsetInBits()), AlignInBits(N->getAlignInBits()), - DWARFAddressSpace(N->getDWARFAddressSpace()), Flags(N->getFlags()), + DWARFAddressSpace(N->getDWARFAddressSpace()), + PtrAuthData(N->getPtrAuthData()), Flags(N->getFlags()), ExtraData(N->getRawExtraData()) {} bool isKeyOf(const DIDerivedType *RHS) const { @@ -441,7 +444,7 @@ template <> struct MDNodeKeyImpl<DIDerivedType> { AlignInBits == RHS->getAlignInBits() && OffsetInBits == RHS->getOffsetInBits() && DWARFAddressSpace == RHS->getDWARFAddressSpace() && - Flags == RHS->getFlags() && + PtrAuthData == RHS->getPtrAuthData() && Flags == RHS->getFlags() && ExtraData == RHS->getRawExtraData(); } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 8fb448bc0921d..9800f84400d5e 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -931,6 +931,7 @@ void Verifier::visitDIDerivedType(const DIDerivedType &N) { N.getTag() == dwarf::DW_TAG_volatile_type || N.getTag() == dwarf::DW_TAG_restrict_type || N.getTag() == dwarf::DW_TAG_atomic_type || + N.getTag() == dwarf::DW_TAG_APPLE_ptrauth_type || N.getTag() == dwarf::DW_TAG_member || N.getTag() == dwarf::DW_TAG_inheritance || N.getTag() == dwarf::DW_TAG_friend, diff --git a/llvm/test/Assembler/debug-info.ll b/llvm/test/Assembler/debug-info.ll index 8c3922ae60921..99ff46c56beef 100644 --- a/llvm/test/Assembler/debug-info.ll +++ b/llvm/test/Assembler/debug-info.ll @@ -1,8 +1,8 @@ ; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s ; RUN: verify-uselistorder %s -; CHECK: !named = !{!0, !0, !1, !2, !3, !4, !5, !6, !7, !8, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37, !38, !39, !40, !41} -!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37, !38, !39, !40, !41, !42, !43, !44} +; CHECK: !named = !{!0, !0, !1, !2, !3, !4, !5, !6, !7, !8, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37, !38, !39, !40, !41, !42} +!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, !24, !25, !26, !27, !28, !29, !30, !31, !32, !33, !34, !35, !36, !37, !38, !39, !40, !41, !42, !43, !44, !45} ; CHECK: !0 = !DISubrange(count: 3) ; CHECK-NEXT: !1 = !DISubrange(count: 3, lowerBound: 4) @@ -104,3 +104,6 @@ ; CHECK-NEXT: !41 = !DILocalVariable(name: "Name", arg: 1, scope: {{.*}}, file: {{.*}}, line: 13, type: {{.*}}, flags: DIFlagArgumentNotModified) !43 = distinct !DISubprogram(name: "fn", scope: !12, file: !12, spFlags: 0) !44 = !DILocalVariable(name: "Name", arg: 1, scope: !43, file: !12, line: 13, type: !7, flags: DIFlagArgumentNotModified) + +; CHECK: !DIDerivedType(tag: DW_TAG_APPLE_ptrauth_type, baseType: !13, ptrAuthKey: 2, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1234) +!45 = !DIDerivedType(tag: DW_TAG_APPLE_ptrauth_type, baseType: !15, ptrAuthKey: 2, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1234) diff --git a/llvm/test/DebugInfo/AArch64/ptrauth.ll b/llvm/test/DebugInfo/AArch64/ptrauth.ll new file mode 100644 index 0000000000000..beee254309eb4 --- /dev/null +++ b/llvm/test/DebugInfo/AArch64/ptrauth.ll @@ -0,0 +1,27 @@ +; RUN: llc %s -filetype=obj -mtriple arm64e-apple-darwin -o - \ +; RUN: | llvm-dwarfdump - | FileCheck %s + +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "*__ptrauth(4, 1, 0x04d2)") +; CHECK: 0x{{0+}}[[TY]]: DW_TAG_APPLE_ptrauth_type +; CHECK-NEXT: DW_AT_type {{.*}}"*" +; CHECK-NEXT: DW_AT_APPLE_ptrauth_key (0x04) +; CHECK-NEXT: DW_AT_APPLE_ptrauth_address_discriminated (true) +; CHECK-NEXT: DW_AT_APPLE_ptrauth_extra_discriminator (0x04d2) + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +@p = common global i8* null, align 8, !dbg !0 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "p", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, emissionKind: FullDebug, globals: !5) +!3 = !DIFile(filename: "/tmp/p.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_APPLE_ptrauth_type, baseType: !9, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1234) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null) diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp index e6c7a50113957..1d912487bd336 100644 --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -103,7 +103,7 @@ class MetadataTest : public testing::Test { DIType *getDerivedType() { return DIDerivedType::getDistinct( Context, dwarf::DW_TAG_pointer_type, "", nullptr, 0, nullptr, - getBasicType("basictype"), 1, 2, 0, None, DINode::FlagZero); + getBasicType("basictype"), 1, 2, 0, None, {}, DINode::FlagZero); } Constant *getConstant() { return ConstantInt::get(Type::getInt32Ty(Context), Counter++); @@ -1292,13 +1292,16 @@ TEST_F(DIDerivedTypeTest, get) { DIType *BaseType = getBasicType("basic"); MDTuple *ExtraData = getTuple(); unsigned DWARFAddressSpace = 8; + DIDerivedType::PtrAuthData PtrAuthData(1, false, 1234); DINode::DIFlags Flags5 = static_cast<DINode::DIFlags>(5); DINode::DIFlags Flags4 = static_cast<DINode::DIFlags>(4); - auto *N = - DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, - 1, Scope, BaseType, 2, 3, 4, DWARFAddressSpace, Flags5, - ExtraData); + auto *N = DIDerivedType::get( + Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, + BaseType, 2, 3, 4, DWARFAddressSpace, None, Flags5, ExtraData); + auto *N1 = DIDerivedType::get(Context, dwarf::DW_TAG_APPLE_ptrauth_type, "", + File, 1, Scope, N, 2, 3, 4, DWARFAddressSpace, + PtrAuthData, Flags5, ExtraData); EXPECT_EQ(dwarf::DW_TAG_pointer_type, N->getTag()); EXPECT_EQ("something", N->getName()); EXPECT_EQ(File, N->getFile()); @@ -1309,53 +1312,71 @@ TEST_F(DIDerivedTypeTest, get) { EXPECT_EQ(3u, N->getAlignInBits()); EXPECT_EQ(4u, N->getOffsetInBits()); EXPECT_EQ(DWARFAddressSpace, N->getDWARFAddressSpace().getValue()); + EXPECT_EQ(None, N->getPtrAuthData()); + EXPECT_EQ(PtrAuthData, N1->getPtrAuthData()); EXPECT_EQ(5u, N->getFlags()); EXPECT_EQ(ExtraData, N->getExtraData()); EXPECT_EQ(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, DWARFAddressSpace, Flags5, ExtraData)); + 4, DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_reference_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, DWARFAddressSpace, Flags5, ExtraData)); + 4, DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "else", - File, 1, Scope, BaseType, 2, 3, - 4, DWARFAddressSpace, Flags5, ExtraData)); + File, 1, Scope, BaseType, 2, 3, 4, + DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", getFile(), 1, Scope, BaseType, 2, - 3, 4, DWARFAddressSpace, Flags5, ExtraData)); + 3, 4, DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 2, Scope, BaseType, 2, 3, - 4, DWARFAddressSpace, Flags5, ExtraData)); + 4, DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, getSubprogram(), - BaseType, 2, 3, 4, DWARFAddressSpace, Flags5, - ExtraData)); - EXPECT_NE(N, DIDerivedType::get( - Context, dwarf::DW_TAG_pointer_type, "something", File, 1, - Scope, getBasicType("basic2"), 2, 3, 4, DWARFAddressSpace, - Flags5, ExtraData)); + BaseType, 2, 3, 4, DWARFAddressSpace, + None, Flags5, ExtraData)); + EXPECT_NE( + N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", + File, 1, Scope, getBasicType("basic2"), 2, 3, 4, + DWARFAddressSpace, None, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 3, 3, - 4, DWARFAddressSpace, Flags5, ExtraData)); + 4, DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 2, - 4, DWARFAddressSpace, Flags5, ExtraData)); + 4, DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 5, DWARFAddressSpace, Flags5, ExtraData)); + 5, DWARFAddressSpace, None, Flags5, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, DWARFAddressSpace + 1, Flags5, ExtraData)); + 4, DWARFAddressSpace + 1, None, Flags5, + ExtraData)); + EXPECT_NE(N1, DIDerivedType::get(Context, dwarf::DW_TAG_APPLE_ptrauth_type, + "", File, 1, Scope, N, 2, 3, 4, + DWARFAddressSpace, None, Flags5, ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, DWARFAddressSpace, Flags4, ExtraData)); + 4, DWARFAddressSpace, None, Flags4, + ExtraData)); EXPECT_NE(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, BaseType, 2, 3, - 4, DWARFAddressSpace, Flags5, getTuple())); + 4, DWARFAddressSpace, None, Flags5, + getTuple())); TempDIDerivedType Temp = N->clone(); EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp))); + TempDIDerivedType Temp1 = N1->clone(); + EXPECT_EQ(N1, MDNode::replaceWithUniqued(std::move(Temp1))); } TEST_F(DIDerivedTypeTest, getWithLargeValues) { @@ -1365,14 +1386,22 @@ TEST_F(DIDerivedTypeTest, getWithLargeValues) { MDTuple *ExtraData = getTuple(); DINode::DIFlags Flags = static_cast<DINode::DIFlags>(5); - auto *N = DIDerivedType::get( - Context, dwarf::DW_TAG_pointer_type, "something", File, 1, Scope, - BaseType, UINT64_MAX, UINT32_MAX - 1, UINT64_MAX - 2, UINT32_MAX - 3, - Flags, ExtraData); + auto *N = DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, "something", + File, 1, Scope, BaseType, UINT64_MAX, + UINT32_MAX - 1, UINT64_MAX - 2, UINT32_MAX - 3, + None, Flags, ExtraData); EXPECT_EQ(UINT64_MAX, N->getSizeInBits()); EXPECT_EQ(UINT32_MAX - 1, N->getAlignInBits()); EXPECT_EQ(UINT64_MAX - 2, N->getOffsetInBits()); - EXPECT_EQ(UINT32_MAX - 3, N->getDWARFAddressSpace().getValue()); + EXPECT_EQ(UINT32_MAX - 3, *N->getDWARFAddressSpace()); + + auto *N1 = DIDerivedType::get( + Context, dwarf::DW_TAG_APPLE_ptrauth_type, "", File, 1, Scope, N, + UINT64_MAX, UINT32_MAX - 1, UINT64_MAX - 2, UINT32_MAX - 3, + DIDerivedType::PtrAuthData(7, true, 0xffff), Flags, ExtraData); + EXPECT_EQ(7U, *N1->getPtrAuthKey()); + EXPECT_EQ(true, *N1->isPtrAuthAddressDiscriminated()); + EXPECT_EQ(0xffffU, *N1->getPtrAuthExtraDiscriminator()); } typedef MetadataTest DICompositeTypeTest; From 46088d16f3a316b06d460ab3a5fa7194cbc6843f Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 17 Sep 2019 23:24:37 -0400 Subject: [PATCH 526/582] Basic ptrauth command-line flags. --- clang/include/clang/Basic/CodeGenOptions.h | 4 + .../clang/Basic/DiagnosticDriverKinds.td | 3 + clang/include/clang/Basic/Features.def | 5 + clang/include/clang/Basic/LangOptions.def | 5 + .../include/clang/Basic/PointerAuthOptions.h | 148 ++++++++++++++++++ clang/include/clang/Basic/TargetInfo.h | 10 ++ clang/include/clang/Driver/Options.td | 20 +++ clang/lib/Basic/TargetInfo.cpp | 1 + clang/lib/Basic/Targets/AArch64.cpp | 3 + clang/lib/Basic/Targets/OSTargets.cpp | 3 + clang/lib/Driver/ToolChains/Clang.cpp | 24 +++ clang/lib/Driver/ToolChains/Darwin.cpp | 31 ++++ clang/lib/Driver/ToolChains/Darwin.h | 4 + clang/lib/Frontend/CompilerInvocation.cpp | 36 ++++- clang/test/Driver/arch-arm64e.c | 57 +++++++ clang/test/Frontend/diagnostics-order.c | 2 +- clang/test/Preprocessor/ptrauth_feature.c | 36 +++++ 17 files changed, 389 insertions(+), 3 deletions(-) create mode 100644 clang/include/clang/Basic/PointerAuthOptions.h create mode 100644 clang/test/Preprocessor/ptrauth_feature.c diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 8881a316d1fb8..f91877c33366e 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_BASIC_CODEGENOPTIONS_H #include "clang/Basic/DebugInfoOptions.h" +#include "clang/Basic/PointerAuthOptions.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/XRayInstr.h" #include "llvm/Support/CodeGen.h" @@ -297,6 +298,9 @@ class CodeGenOptions : public CodeGenOptionsBase { std::vector<std::string> Reciprocals; + /// Configuration for pointer-signing. + PointerAuthOptions PointerAuth; + /// The preferred width for auto-vectorization transforms. This is intended to /// override default transforms based on the width of the architected vector /// registers. diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 5ff03e1335636..10a14a2cd0ac6 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -237,6 +237,9 @@ def err_drv_omp_host_ir_file_not_found : Error< "The provided host compiler IR file '%0' is required to generate code for OpenMP target regions but cannot be found.">; def err_drv_omp_host_target_not_supported : Error< "The target '%0' is not a supported OpenMP host target.">; +def err_drv_ptrauth_not_supported : Error< + "target '%0' does not support native pointer authentication">; + def err_drv_expecting_fopenmp_with_fopenmp_targets : Error< "The option -fopenmp-targets must be used in conjunction with a -fopenmp option compatible with offloading, please use -fopenmp=libomp or -fopenmp=libiomp5.">; def warn_drv_omp_offload_target_duplicate : Warning< diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index aa7ec50f5c912..0f7b9b9290946 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -91,6 +91,11 @@ FEATURE(memory_sanitizer, SanitizerKind::KernelMemory)) FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread)) FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow)) +FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) +FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) +FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos) FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo)) // Objective-C features FEATURE(objc_arr, LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 9d10d2068acbb..666d4fb21480d 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -143,6 +143,11 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly") LANGOPT(Coroutines , 1, 0, "C++20 coroutines") LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments") +LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") +LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication") +LANGOPT(PointerAuthReturns, 1, 0, "return pointer authentication") +LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") +LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h new file mode 100644 index 0000000000000..58650c8b64726 --- /dev/null +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -0,0 +1,148 @@ +//===--- PointerAuthOptions.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines options for configuring pointer-auth technologies +// like ARMv8.3. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H +#define LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Target/TargetOptions.h" +#include <map> +#include <memory> +#include <string> +#include <vector> +#include "llvm/Support/ErrorHandling.h" + +namespace clang { + +class PointerAuthSchema { +public: + enum class Kind { + None, + ARM8_3, + }; + + /// Hardware pointer-signing keys in ARM8.3. + /// + /// These values are the same used in ptrauth.h. + enum class ARM8_3Key { + ASIA = 0, + ASIB = 1, + ASDA = 2, + ASDB = 3 + }; + + /// Forms of extra discrimination. + enum class Discrimination { + /// No additional discrimination. + None, + + /// Include a hash of the entity's type. + Type, + + /// Include a hash of the entity's identity. + Decl, + }; + +private: + enum { + NumKindBits = 2 + }; + union { + /// A common header shared by all pointer authentication kinds. + struct { + unsigned Kind : NumKindBits; + unsigned AddressDiscriminated : 1; + unsigned Discrimination : 2; + } Common; + + struct { + unsigned Kind : NumKindBits; + unsigned AddressDiscriminated : 1; + unsigned Discrimination : 2; + unsigned Key : 2; + } ARM8_3; + }; + +public: + PointerAuthSchema() { + Common.Kind = unsigned(Kind::None); + } + + PointerAuthSchema(ARM8_3Key key, bool isAddressDiscriminated, + Discrimination otherDiscrimination) { + Common.Kind = unsigned(Kind::ARM8_3); + Common.AddressDiscriminated = isAddressDiscriminated; + Common.Discrimination = unsigned(otherDiscrimination); + ARM8_3.Key = unsigned(key); + } + + Kind getKind() const { + return Kind(Common.Kind); + } + + explicit operator bool() const { + return isEnabled(); + } + + bool isEnabled() const { + return getKind() != Kind::None; + } + + bool isAddressDiscriminated() const { + assert(getKind() != Kind::None); + return Common.AddressDiscriminated; + } + + bool hasOtherDiscrimination() const { + return getOtherDiscrimination() != Discrimination::None; + } + + Discrimination getOtherDiscrimination() const { + assert(getKind() != Kind::None); + return Discrimination(Common.Discrimination); + } + + unsigned getKey() const { + switch (getKind()) { + case Kind::None: llvm_unreachable("calling getKey() on disabled schema"); + case Kind::ARM8_3: return unsigned(getARM8_3Key()); + } + llvm_unreachable("bad key kind"); + } + + ARM8_3Key getARM8_3Key() const { + assert(getKind() == Kind::ARM8_3); + return ARM8_3Key(ARM8_3.Key); + } +}; + + +struct PointerAuthOptions { + /// Do member function pointers to virtual functions need to be built + /// as thunks? + bool ThunkCXXVirtualMemberPointers = false; + + /// Should return addresses be authenticated? + bool ReturnAddresses = false; + + /// Do indirect goto label addresses need to be authenticated? + bool IndirectGotos = false; + + /// Do authentication failures cause a trap? + bool AuthTraps = false; +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 9a3bb986930ef..c907dcfb71d79 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -197,6 +197,8 @@ class TargetInfo : public virtual TransferrableTargetInfo, unsigned HasAArch64SVETypes : 1; + unsigned PointerAuthSupported : 1; + // TargetInfo Constructor. Default initializes all fields. TargetInfo(const llvm::Triple &T); @@ -1180,6 +1182,14 @@ class TargetInfo : public virtual TransferrableTargetInfo, return TLSSupported; } + /// \brief Whether the target supports pointer authentication at all. + /// + /// Whether pointer authentication is actually being used is determined + /// by the language option. + bool isPointerAuthSupported() const { + return PointerAuthSupported; + } + /// Return the maximum alignment (in bits) of a TLS variable /// /// Gets the maximum alignment (in bits) of a TLS variable on this target. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index db39f3a1de13a..c310b530ee439 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1950,6 +1950,26 @@ def fstrict_return : Flag<["-"], "fstrict-return">, Group<f_Group>, def fno_strict_return : Flag<["-"], "fno-strict-return">, Group<f_Group>, Flags<[CC1Option]>; +let Group = f_Group in { + let Flags = [CC1Option] in { + def fptrauth_intrinsics : Flag<["-"], "fptrauth-intrinsics">, + HelpText<"Enable pointer-authentication intrinsics">; + def fptrauth_calls : Flag<["-"], "fptrauth-calls">, + HelpText<"Enable signing and authentication of all indirect calls">; + def fptrauth_returns : Flag<["-"], "fptrauth-returns">, + HelpText<"Enable signing and authentication of return addresses">; + def fptrauth_indirect_gotos : Flag<["-"], "fptrauth-indirect-gotos">, + HelpText<"Enable signing and authentication of indirect goto targets">; + def fptrauth_auth_traps : Flag<["-"], "fptrauth-auth-traps">, + HelpText<"Enable traps on authentication failures">; + } + def fno_ptrauth_intrinsics : Flag<["-"], "fno-ptrauth-intrinsics">; + def fno_ptrauth_calls : Flag<["-"], "fno-ptrauth-calls">; + def fno_ptrauth_returns : Flag<["-"], "fno-ptrauth-returns">; + def fno_ptrauth_indirect_gotos : Flag<["-"], "fno-ptrauth-indirect-gotos">; + def fno_ptrauth_auth_traps : Flag<["-"], "fno-ptrauth-auth-traps">; +} + def fallow_editor_placeholders : Flag<["-"], "fallow-editor-placeholders">, Group<f_Group>, Flags<[CC1Option]>, HelpText<"Treat editor placeholders as valid source code">; diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 3a21a19e1f19a..52e8d39460470 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -113,6 +113,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : TargetOpts(), Triple(T) { HasBuiltinMSVaList = false; IsRenderScriptTarget = false; HasAArch64SVETypes = false; + PointerAuthSupported = false; // Default to no types using fpret. RealTypeUsesObjCFPRet = 0; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index bc36e0d46f867..15fd05d0bb29a 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -91,6 +91,9 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple, else if (Triple.getOS() == llvm::Triple::UnknownOS) this->MCountName = Opts.EABIVersion == llvm::EABI::GNU ? "\01_mcount" : "mcount"; + + if (Triple.getArchName() == "arm64e") + PointerAuthSupported = true; } StringRef AArch64TargetInfo::getABI() const { return ABI; } diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp index 72fdb0e7dde8a..8290672863376 100644 --- a/clang/lib/Basic/Targets/OSTargets.cpp +++ b/clang/lib/Basic/Targets/OSTargets.cpp @@ -31,6 +31,9 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, if (Opts.Sanitize.has(SanitizerKind::Address)) Builder.defineMacro("_FORTIFY_SOURCE", "0"); + if (Opts.PointerAuthIntrinsics) + Builder.defineMacro("__PTRAUTH_INTRINSICS__"); + // Darwin defines __weak, __strong, and __unsafe_unretained even in C mode. if (!Opts.ObjC) { // __weak is always defined, for use in blocks and with objc pointers. diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 73c660738aaf4..b463db8b419e3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5062,6 +5062,30 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, !NoCommonDefault)) CmdArgs.push_back("-fno-common"); + if (Args.hasFlag(options::OPT_fptrauth_intrinsics, + options::OPT_fno_ptrauth_intrinsics, false)) + CmdArgs.push_back("-fptrauth-intrinsics"); + + if (Args.hasFlag(options::OPT_fptrauth_calls, + options::OPT_fno_ptrauth_calls, false)) + CmdArgs.push_back("-fptrauth-calls"); + + if (Args.hasFlag(options::OPT_fptrauth_returns, + options::OPT_fno_ptrauth_returns, false)) + CmdArgs.push_back("-fptrauth-returns"); + + if (Args.hasFlag(options::OPT_fptrauth_indirect_gotos, + options::OPT_fno_ptrauth_indirect_gotos, false)) + CmdArgs.push_back("-fptrauth-indirect-gotos"); + + if (Args.hasFlag(options::OPT_fptrauth_auth_traps, + options::OPT_fno_ptrauth_auth_traps, false)) + CmdArgs.push_back("-fptrauth-auth-traps"); + + if (Args.hasFlag(options::OPT_fptrauth_soft, + options::OPT_fno_ptrauth_soft, false)) + CmdArgs.push_back("-fptrauth-soft"); + // -fsigned-bitfields is default, and clang doesn't yet support // -funsigned-bitfields. if (!Args.hasFlag(options::OPT_fsigned_bitfields, diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 339dadded89e1..076b95cc53ad7 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -932,6 +932,37 @@ void DarwinClang::addClangWarningOptions(ArgStringList &CC1Args) const { } } +void DarwinClang::addClangTargetOptions( + const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadKind) const{ + + Darwin::addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadKind); + + // On arm64e, enable pointer authentication (for the return address and + // indirect calls), as well as usage of the intrinsics. + if (getArchName() == "arm64e") { + if (!DriverArgs.hasArg(options::OPT_fptrauth_returns, + options::OPT_fno_ptrauth_returns)) + CC1Args.push_back("-fptrauth-returns"); + + if (!DriverArgs.hasArg(options::OPT_fptrauth_intrinsics, + options::OPT_fno_ptrauth_intrinsics)) + CC1Args.push_back("-fptrauth-intrinsics"); + + if (!DriverArgs.hasArg(options::OPT_fptrauth_calls, + options::OPT_fno_ptrauth_calls)) + CC1Args.push_back("-fptrauth-calls"); + + if (!DriverArgs.hasArg(options::OPT_fptrauth_indirect_gotos, + options::OPT_fno_ptrauth_indirect_gotos)) + CC1Args.push_back("-fptrauth-indirect-gotos"); + + if (!DriverArgs.hasArg(options::OPT_fptrauth_auth_traps, + options::OPT_fno_ptrauth_auth_traps)) + CC1Args.push_back("-fptrauth-auth-traps"); + } +} + /// Take a path that speculatively points into Xcode and return the /// `XCODE/Contents/Developer` path if it is an Xcode path, or an empty path /// otherwise. diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h index 1b1c358c40a4f..74f152c5bd9b0 100644 --- a/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -517,6 +517,10 @@ class LLVM_LIBRARY_VISIBILITY DarwinClang : public Darwin { void addClangWarningOptions(llvm::opt::ArgStringList &CC1Args) const override; + void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadKind) const override; + void AddLinkARCArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const override; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 44d5651b4afff..e523ebea919cd 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -664,8 +664,30 @@ static void setPGOUseInstrumentor(CodeGenOptions &Opts, Opts.setProfileUse(CodeGenOptions::ProfileClangInstr); } +static bool parsePointerAuthOptions(PointerAuthOptions &Opts, + ArgList &Args, + const LangOptions &LangOpts, + const llvm::Triple &Triple, + DiagnosticsEngine &Diags) { + if (!LangOpts.PointerAuthCalls && !LangOpts.PointerAuthReturns && + !LangOpts.PointerAuthIndirectGotos && !LangOpts.PointerAuthAuthTraps) + return true; + + if (Triple.getArch() == llvm::Triple::aarch64) { + Opts.ReturnAddresses = LangOpts.PointerAuthReturns; + Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos; + Opts.AuthTraps = LangOpts.PointerAuthAuthTraps; + return true; + } + + Diags.Report(diag::err_drv_ptrauth_not_supported) + << Triple.str(); + return false; +} + static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, DiagnosticsEngine &Diags, + const LangOptions &LangOpts, const TargetOptions &TargetOpts, const FrontendOptions &FrontendOpts) { bool Success = true; @@ -1366,6 +1388,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true); + Success &= + parsePointerAuthOptions(Opts.PointerAuth, Args, LangOpts, Triple, Diags); Opts.Addrsig = Args.hasArg(OPT_faddrsig); if (Arg *A = Args.getLastArg(OPT_msign_return_address_EQ)) { @@ -3132,6 +3156,12 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, if (InlineArg->getOption().matches(options::OPT_fno_inline)) Opts.NoInlineDefine = true; + Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics); + Opts.PointerAuthCalls = Args.hasArg(OPT_fptrauth_calls); + Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns); + Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); + Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps); + Opts.FastMath = Args.hasArg(OPT_ffast_math) || Args.hasArg(OPT_cl_fast_relaxed_math); Opts.FiniteMathOnly = Args.hasArg(OPT_ffinite_math_only) || @@ -3501,8 +3531,6 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, InputKind DashX = ParseFrontendArgs(Res.getFrontendOpts(), Args, Diags, LangOpts.IsHeaderFile); ParseTargetArgs(Res.getTargetOpts(), Args, Diags); - Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, - Res.getTargetOpts(), Res.getFrontendOpts()); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Res.getFileSystemOpts().WorkingDir); ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags); @@ -3542,6 +3570,10 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, LangOpts.FunctionAlignment = getLastArgIntValue(Args, OPT_function_alignment, 0, Diags); + Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, + LangOpts, Res.getTargetOpts(), + Res.getFrontendOpts()); + if (LangOpts.CUDA) { // During CUDA device-side compilation, the aux triple is the // triple used for host compilation. diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c index bf4b34fba51e5..687877b8775e7 100644 --- a/clang/test/Driver/arch-arm64e.c +++ b/clang/test/Driver/arch-arm64e.c @@ -1,3 +1,60 @@ +// Check that we can manually enable specific ptrauth features. + +// RUN: %clang -arch arm64 -c %s -### 2>&1 | FileCheck %s --check-prefix NONE +// NONE: "-cc1" +// NONE-NOT: "-fptrauth-intrinsics" +// NONE-NOT: "-fptrauth-calls" +// NONE-NOT: "-fptrauth-returns" +// NONE-NOT: "-fptrauth-indirect-gotos" +// NONE-NOT: "-fptrauth-auth-traps" + +// RUN: %clang -arch arm64 -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL +// CALL: "-cc1"{{.*}} {{.*}} "-fptrauth-calls" + +// RUN: %clang -arch arm64 -fptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix INTRIN +// INTRIN: "-cc1"{{.*}} {{.*}} "-fptrauth-intrinsics" + +// RUN: %clang -arch arm64 -fptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix RETURN +// RETURN: "-cc1"{{.*}} {{.*}} "-fptrauth-returns" + +// RUN: %clang -arch arm64 -fptrauth-indirect-gotos -c %s -### 2>&1 | FileCheck %s --check-prefix INDGOTO +// INDGOTO: "-cc1"{{.*}} {{.*}} "-fptrauth-indirect-gotos" + +// RUN: %clang -arch arm64 -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS +// TRAPS: "-cc1"{{.*}} {{.*}} "-fptrauth-auth-traps" + + +// Check the arm64e defaults. + +// RUN: %clang -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -mkernel -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -fapple-kext -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// DEFAULT: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex"{{.*}} + + +// RUN: %clang -arch arm64e -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -mkernel -arch arm64e -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -fapple-kext -arch arm64e -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// DEFAULT-NOCALL-NOT: "-fptrauth-calls" +// DEFAULT-NOCALL: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex" + + +// RUN: %clang -arch arm64e -fno-ptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix NORET + +// NORET-NOT: "-fptrauth-returns" +// NORET: "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex" + +// RUN: %clang -arch arm64e -fno-ptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix NOINTRIN + +// NOINTRIN: "-fptrauth-returns" +// NOINTRIN-NOT: "-fptrauth-intrinsics" +// NOINTRIN: "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex"{{.*}} + + +// RUN: %clang -arch arm64e -fno-ptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix NOTRAP +// NOTRAP: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-target-cpu" "vortex" + + // Check the CPU defaults and overrides. // RUN: %clang -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX diff --git a/clang/test/Frontend/diagnostics-order.c b/clang/test/Frontend/diagnostics-order.c index 37c0cd90d15cc..40ca377b80db5 100644 --- a/clang/test/Frontend/diagnostics-order.c +++ b/clang/test/Frontend/diagnostics-order.c @@ -7,6 +7,6 @@ // // CHECK: error: invalid value '-foo' in '-verify=' // CHECK-NEXT: note: -verify prefixes must start with a letter and contain only alphanumeric characters, hyphens, and underscores -// CHECK-NEXT: warning: optimization level '-O999' is not supported // CHECK-NEXT: error: invalid value 'bogus' in '-std=bogus' // CHECK-NEXT: note: use {{.*}} for {{.*}} standard +// CHECK: warning: optimization level '-O999' is not supported diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c new file mode 100644 index 0000000000000..c9ae6bb3f64ae --- /dev/null +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 %s -E -triple=arm64-- | FileCheck %s --check-prefixes=NOCALLS,NOINTRIN,NORETS,NOQUAL +// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-calls | FileCheck %s --check-prefixes=CALLS,NOINTRIN,NORETS,NOQUAL +// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-returns | FileCheck %s --check-prefixes=NOCALLS,NOINTRIN,RETS,NOQUAL +// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-intrinsics | FileCheck %s --check-prefixes=NOCALLS,INTRIN,NORETS,QUAL + +#if __has_feature(ptrauth_calls) +// CALLS: has_ptrauth_calls +void has_ptrauth_calls() {} +#else +// NOCALLS: no_ptrauth_calls +void no_ptrauth_calls() {} +#endif + +#if __has_feature(ptrauth_intrinsics) +// INTRIN: has_ptrauth_intrinsics +void has_ptrauth_intrinsics() {} +#else +// NOINTRIN: no_ptrauth_intrinsics +void no_ptrauth_intrinsics() {} +#endif + +#if __has_feature(ptrauth_returns) +// RETS: has_ptrauth_returns +void has_ptrauth_returns() {} +#else +// NORETS: no_ptrauth_returns +void no_ptrauth_returns() {} +#endif + +#if __has_feature(ptrauth_qualifier) +// QUAL: has_ptrauth_qualifier +void has_ptrauth_qualifier() {} +#else +// NOQUAL: no_ptrauth_qualifier +void no_ptrauth_qualifier() {} +#endif From 4ec8ebf0bae7f55254b06faa206c941e46be98aa Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 17 Sep 2019 22:27:55 -0400 Subject: [PATCH 527/582] Add an API for computing an ABI-stable hash of a string. Implementation by Akira Hatanaka. --- clang/include/clang/AST/StableHash.h | 35 ++++++ clang/lib/AST/CMakeLists.txt | 1 + clang/lib/AST/StableHash.cpp | 178 +++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 clang/include/clang/AST/StableHash.h create mode 100644 clang/lib/AST/StableHash.cpp diff --git a/clang/include/clang/AST/StableHash.h b/clang/include/clang/AST/StableHash.h new file mode 100644 index 0000000000000..0e78871fafe92 --- /dev/null +++ b/clang/include/clang/AST/StableHash.h @@ -0,0 +1,35 @@ +//===--- StableHash.h - An ABI-stable string hash ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// The interface to an ABI-stable string hash algorithm. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_AST_STABLEHASH_H +#define CLANG_AST_STABLEHASH_H + +#include <cstdint> + +namespace llvm { +class StringRef; +} + +namespace clang { +/// Compute a stable 64-bit hash of the given string. +/// +/// The exact algorithm is the little-endian interpretation of the +/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using +/// a specific key value which can be found in the source. +/// +/// By "stable" we mean that the result of this hash algorithm will +/// the same across different compiler versions and target platforms. +uint64_t getStableStringHash(llvm::StringRef string); + +} // end namespace clang + +#endif diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index 5bae40c86539f..6393d137efdf4 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -91,6 +91,7 @@ add_clang_library(clangAST RecordLayoutBuilder.cpp ScanfFormatString.cpp SelectorLocationsKind.cpp + StableHash.cpp Stmt.cpp StmtCXX.cpp StmtIterator.cpp diff --git a/clang/lib/AST/StableHash.cpp b/clang/lib/AST/StableHash.cpp new file mode 100644 index 0000000000000..f1bdf97ff6b0e --- /dev/null +++ b/clang/lib/AST/StableHash.cpp @@ -0,0 +1,178 @@ +//===--- StableHash.cpp - Context to hold long-lived AST nodes ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements an ABI-stable string hash based on SipHash. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/StableHash.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Debug.h" +#include <cstdint> +#include <cstring> + +using namespace clang; + +#define DEBUG_TYPE "clang-stable-hash" + +#define SIPHASH_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define SIPHASH_U8TO64_LE(p) \ + (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ + ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ + ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ + ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) + +#define SIPHASH_SIPROUND \ + do { \ + v0 += v1; \ + v1 = SIPHASH_ROTL(v1, 13); \ + v1 ^= v0; \ + v0 = SIPHASH_ROTL(v0, 32); \ + v2 += v3; \ + v3 = SIPHASH_ROTL(v3, 16); \ + v3 ^= v2; \ + v0 += v3; \ + v3 = SIPHASH_ROTL(v3, 21); \ + v3 ^= v0; \ + v2 += v1; \ + v1 = SIPHASH_ROTL(v1, 17); \ + v1 ^= v2; \ + v2 = SIPHASH_ROTL(v2, 32); \ + } while (0) + +template <int cROUNDS, int dROUNDS, class ResultTy> +static inline ResultTy siphash(const uint8_t *in, uint64_t inlen, + const uint8_t (&k)[16]) { + static_assert(sizeof(ResultTy) == 8 || sizeof(ResultTy) == 16, + "result type should be uint64_t or uint128_t"); + uint64_t v0 = 0x736f6d6570736575ULL; + uint64_t v1 = 0x646f72616e646f6dULL; + uint64_t v2 = 0x6c7967656e657261ULL; + uint64_t v3 = 0x7465646279746573ULL; + uint64_t b; + uint64_t k0 = SIPHASH_U8TO64_LE(k); + uint64_t k1 = SIPHASH_U8TO64_LE(k + 8); + uint64_t m; + int i; + const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); + const int left = inlen & 7; + b = ((uint64_t)inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + if (sizeof(ResultTy) == 16) { + v1 ^= 0xee; + } + + for (; in != end; in += 8) { + m = SIPHASH_U8TO64_LE(in); + v3 ^= m; + + for (i = 0; i < cROUNDS; ++i) + SIPHASH_SIPROUND; + + v0 ^= m; + } + + switch (left) { + case 7: + b |= ((uint64_t)in[6]) << 48; + LLVM_FALLTHROUGH; + case 6: + b |= ((uint64_t)in[5]) << 40; + LLVM_FALLTHROUGH; + case 5: + b |= ((uint64_t)in[4]) << 32; + LLVM_FALLTHROUGH; + case 4: + b |= ((uint64_t)in[3]) << 24; + LLVM_FALLTHROUGH; + case 3: + b |= ((uint64_t)in[2]) << 16; + LLVM_FALLTHROUGH; + case 2: + b |= ((uint64_t)in[1]) << 8; + LLVM_FALLTHROUGH; + case 1: + b |= ((uint64_t)in[0]); + break; + case 0: + break; + } + + v3 ^= b; + + for (i = 0; i < cROUNDS; ++i) + SIPHASH_SIPROUND; + + v0 ^= b; + + if (sizeof(ResultTy) == 8) { + v2 ^= 0xff; + } else { + v2 ^= 0xee; + } + + for (i = 0; i < dROUNDS; ++i) + SIPHASH_SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; + + // This mess with the result type would be easier with 'if constexpr'. + + uint64_t firstHalf = b; + if (sizeof(ResultTy) == 8) + return firstHalf; + + v1 ^= 0xdd; + + for (i = 0; i < dROUNDS; ++i) + SIPHASH_SIPROUND; + + b = v0 ^ v1 ^ v2 ^ v3; + uint64_t secondHalf = b; + + return firstHalf + | (ResultTy(secondHalf) << (sizeof(ResultTy) == 8 ? 0 : 64)); +} + +/// Compute an ABI-stable hash of the given string. +uint64_t clang::getStableStringHash(llvm::StringRef string) { + static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79, + 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4}; + + // The aliasing is fine here because of omnipotent char. + auto data = reinterpret_cast<const uint8_t*>(string.data()); + return siphash<2, 4, uint64_t>(data, string.size(), K); +} + +uint64_t clang::getPointerAuthStringDiscriminator(const ASTContext &ctxt, + llvm::StringRef string) { + auto rawHash = getStableStringHash(string); + + // Don't do anything target-specific yet. + + // Produce a non-zero 16-bit discriminator. + // We use a 16-bit discriminator because ARM64 can efficiently load + // a 16-bit immediate into the high bits of a register without disturbing + // the remainder of the value, which serves as a nice blend operation. + // 16 bits is also sufficiently compact to not inflate a loader relocation. + // We disallow zero to guarantee a different discriminator from the places + // in the ABI that use a constant zero. + uint64_t discriminator = (rawHash % 0xFFFF) + 1; + LLVM_DEBUG( + llvm::dbgs() << "Ptrauth string disc: " << llvm::utostr(discriminator) + << " (0x" << llvm::utohexstr(discriminator) << ")" + << " of: " << string << "\n"); + return discriminator; +} From a9dd959e60c324a9bbfe96d9d8a42a25ea21220a Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 00:27:52 -0400 Subject: [PATCH 528/582] Add support for the new pointer authentication builtins. Patch by Akira Hatanaka and me. --- clang/include/clang/AST/ASTContext.h | 3 + clang/include/clang/AST/StableHash.h | 11 + clang/include/clang/Basic/Builtins.def | 10 + clang/include/clang/Basic/DiagnosticGroups.td | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 30 ++ clang/include/clang/Basic/TargetInfo.h | 5 + clang/include/clang/Basic/TokenKinds.def | 2 + clang/include/clang/Basic/TypeTraits.h | 2 + clang/include/clang/CodeGen/CodeGenABITypes.h | 18 + .../clang/CodeGen/ConstantInitBuilder.h | 6 + clang/include/clang/Parse/Parser.h | 2 + clang/include/clang/Sema/Sema.h | 3 + clang/lib/AST/ASTContext.cpp | 11 + clang/lib/AST/ExprConstant.cpp | 31 +- clang/lib/AST/ItaniumMangle.cpp | 8 + clang/lib/AST/JSONNodeDumper.cpp | 2 + clang/lib/AST/StmtPrinter.cpp | 3 + clang/lib/AST/TextNodeDumper.cpp | 3 + clang/lib/Basic/TargetInfo.cpp | 4 + clang/lib/Basic/Targets/AArch64.cpp | 6 + clang/lib/Basic/Targets/AArch64.h | 2 + clang/lib/CodeGen/CGBuiltin.cpp | 72 ++++ clang/lib/CodeGen/CGExprConstant.cpp | 97 +++++- clang/lib/CodeGen/CGPointerAuth.cpp | 196 +++++++++++ clang/lib/CodeGen/CMakeLists.txt | 1 + clang/lib/CodeGen/CodeGenModule.cpp | 4 +- clang/lib/CodeGen/CodeGenModule.h | 10 + clang/lib/Headers/CMakeLists.txt | 1 + clang/lib/Headers/ptrauth.h | 302 ++++++++++++++++ clang/lib/Parse/ParseExpr.cpp | 23 ++ clang/lib/Sema/SemaChecking.cpp | 326 ++++++++++++++++++ clang/lib/Sema/SemaExpr.cpp | 15 + clang/test/CodeGen/ptrauth-intrinsics.c | 96 ++++++ clang/test/CodeGenCXX/mangle-fail.cpp | 14 + clang/test/Sema/ptrauth-intrinsics-macro.c | 24 ++ clang/test/Sema/ptrauth.c | 155 +++++++++ clang/test/SemaCXX/ptrauth.cpp | 33 ++ 37 files changed, 1529 insertions(+), 3 deletions(-) create mode 100644 clang/lib/CodeGen/CGPointerAuth.cpp create mode 100644 clang/lib/Headers/ptrauth.h create mode 100644 clang/test/CodeGen/ptrauth-intrinsics.c create mode 100644 clang/test/Sema/ptrauth-intrinsics-macro.c create mode 100644 clang/test/Sema/ptrauth.c create mode 100644 clang/test/SemaCXX/ptrauth.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 5e2f4031d96cc..6f91673a24582 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1147,6 +1147,9 @@ class ASTContext : public RefCountedBase<ASTContext> { /// space. QualType removeAddrSpaceQualType(QualType T) const; + /// Return the "other" type-specific discriminator for the given type. + uint16_t getPointerAuthTypeDiscriminator(QualType T); + /// Apply Objective-C protocol qualifiers to the given type. /// \param allowOnPointerType specifies if we can apply protocol /// qualifiers on ObjCObjectPointerType. It can be set to true when diff --git a/clang/include/clang/AST/StableHash.h b/clang/include/clang/AST/StableHash.h index 0e78871fafe92..5c6f900a37289 100644 --- a/clang/include/clang/AST/StableHash.h +++ b/clang/include/clang/AST/StableHash.h @@ -20,6 +20,8 @@ class StringRef; } namespace clang { +class ASTContext; + /// Compute a stable 64-bit hash of the given string. /// /// The exact algorithm is the little-endian interpretation of the @@ -30,6 +32,15 @@ namespace clang { /// the same across different compiler versions and target platforms. uint64_t getStableStringHash(llvm::StringRef string); +/// Compute a pointer-auth extra discriminator for the given string, +/// suitable for both the blend operation and the __ptrauth qualifier. +/// +/// The result of this hash will be the same across different compiler +/// versions but may vary between targets due to differences in the +/// range of discriminators desired by the target. +uint64_t getPointerAuthStringDiscriminator(const ASTContext &ctx, + llvm::StringRef string); + } // end namespace clang #endif diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 4ed00a13b0043..b4161c2d0c3a6 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1499,6 +1499,16 @@ BUILTIN(__builtin_coro_end, "bv*Ib", "n") BUILTIN(__builtin_coro_suspend, "cIb", "n") BUILTIN(__builtin_coro_param, "bv*v*", "n") +// Pointer authentication builtins. +BUILTIN(__builtin_ptrauth_strip, "v*v*i", "tnc") +BUILTIN(__builtin_ptrauth_blend_discriminator, "zv*i", "tnc") +BUILTIN(__builtin_ptrauth_sign_constant, "v*v*iv*", "tnc") +BUILTIN(__builtin_ptrauth_sign_unauthenticated, "v*v*iv*", "tnc") +BUILTIN(__builtin_ptrauth_sign_generic_data, "zv*v*", "tnc") +BUILTIN(__builtin_ptrauth_auth_and_resign, "v*v*iv*iv*", "tn") +BUILTIN(__builtin_ptrauth_auth, "v*v*iv*", "tn") +BUILTIN(__builtin_ptrauth_string_discriminator, "zcC*", "nc") + // OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions. // We need the generic prototype, since the packet type could be anything. LANGBUILTIN(read_pipe, "i.", "tn", OCLC20_LANG) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index a8d04af1e5249..a7bba469ac817 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -690,6 +690,7 @@ def ZeroLengthArray : DiagGroup<"zero-length-array">; def GNUZeroLineDirective : DiagGroup<"gnu-zero-line-directive">; def GNUZeroVariadicMacroArguments : DiagGroup<"gnu-zero-variadic-macro-arguments">; def Fallback : DiagGroup<"fallback">; +def PtrAuthNullPointers : DiagGroup<"ptrauth-null-pointers">; // This covers both the deprecated case (in C++98) // and the extension case (in C++11 onwards). diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 542aad909d8be..d851e31e7f406 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -736,6 +736,36 @@ def warn_fortify_source_size_mismatch : Warning< "'%0' size argument is too large; destination buffer has size %1," " but size argument is %2">, InGroup<FortifySource>; +def err_ptrauth_disabled_target : + Error<"this target does not support pointer authentication">; +def err_ptrauth_disabled : + Error<"pointer authentication is disabled for the current target">; +def err_ptrauth_invalid_key : + Error<"%0 does not identify a valid pointer authentication key for " + "the current target">; +def err_ptrauth_value_bad_type : + Error<"%select{signed value|extra discriminator|blended pointer|blended " + "integer}0 must have %select{pointer|integer|pointer or integer}1 " + "type; type here is %2">; +def err_ptrauth_bad_constant_pointer : + Error<"argument to ptrauth_sign_constant must refer to a global variable " + "or function">; +def err_ptrauth_bad_constant_discriminator : + Error<"discriminator argument to ptrauth_sign_constant must be a constant " + "integer, the address of the global variable where the result " + "will be stored, or a blend of the two">; +def warn_ptrauth_sign_null_pointer : + Warning<"signing a null pointer will yield a non-null pointer">, + InGroup<PtrAuthNullPointers>; +def warn_ptrauth_auth_null_pointer : + Warning<"authenticating a null pointer will almost certainly trap">, + InGroup<PtrAuthNullPointers>; +def err_ptrauth_string_not_literal : Error< + "argument must be a string literal%select{| of char type}0">; +def err_ptrauth_type_disc_variably_modified : Error< + "cannot pass variably-modified type %0 to " + "'__builtin_ptrauth_type_discriminator'">; + /// main() // static main() is not an error in C, just in C++. def warn_static_main : Warning<"'main' should not be declared static">, diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index c907dcfb71d79..a30c60cc98f99 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1234,6 +1234,11 @@ class TargetInfo : public virtual TransferrableTargetInfo, const LangASMap &getAddressSpaceMap() const { return *AddrSpaceMap; } + /// Determine whether the given pointer-authentication key is valid. + /// + /// The value has been coerced to type 'int'. + virtual bool validatePointerAuthKey(const llvm::APSInt &value) const; + /// Map from the address space field in builtin description strings to the /// language address space. virtual LangAS getOpenCLBuiltinAddressSpace(unsigned AS) const { diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 94fe1ba63a9f6..48d9ca7798eb4 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -529,6 +529,8 @@ KEYWORD(__array_extent , KEYCXX) KEYWORD(__private_extern__ , KEYALL) KEYWORD(__module_private__ , KEYALL) +KEYWORD(__builtin_ptrauth_type_discriminator, KEYALL) + // Extension that will be enabled for Microsoft, Borland and PS4, but can be // disabled via '-fno-declspec'. KEYWORD(__declspec , 0) diff --git a/clang/include/clang/Basic/TypeTraits.h b/clang/include/clang/Basic/TypeTraits.h index 7c1b571f640c2..ff162240f19aa 100644 --- a/clang/include/clang/Basic/TypeTraits.h +++ b/clang/include/clang/Basic/TypeTraits.h @@ -104,6 +104,8 @@ namespace clang { /// __alignof returns the preferred alignment of a type, the alignment /// clang will attempt to give an object of the type if allowed by ABI. UETT_PreferredAlignOf, + /// __builtin_ptrauth_type_discriminator + UETT_PtrAuthTypeDiscriminator, }; } diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index 31f0cea572324..f6394c1d8b190 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -28,6 +28,7 @@ #include "clang/CodeGen/CGFunctionInfo.h" namespace llvm { + class Constant; class DataLayout; class Module; class Function; @@ -84,6 +85,23 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T); unsigned getLLVMFieldNumber(CodeGenModule &CGM, const RecordDecl *RD, const FieldDecl *FD); +/// Compute a stable hash of the given string. +/// +/// The exact algorithm is the little-endian interpretation of the +/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using +/// a specific key value which can be found in the source. +uint64_t computeStableStringHash(StringRef string); + +/// Return a type discriminator for the given function type. +uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, QualType fnType); + +/// Return a signed constant pointer. +llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM, + llvm::Constant *pointer, + unsigned key, + llvm::Constant *storageAddress, + llvm::Constant *otherDiscriminator); + /// Returns the default constructor for a C struct with non-trivially copyable /// fields, generating it if necessary. The returned function uses the `cdecl` /// calling convention, returns void, and takes a single argument that is a diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index fd07e91ba6ae2..61f781476fa4e 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -199,6 +199,12 @@ class ConstantAggregateBuilderBase { add(llvm::ConstantInt::get(intTy, value, isSigned)); } + /// Add a signed pointer using the given pointer authentication schema. + void addSignedPointer(llvm::Constant *pointer, + unsigned key, + bool useAddressDiscrimination, + llvm::Constant *otherDiscriminator); + /// Add a null pointer of a specific type. void addNullPointer(llvm::PointerType *ptrTy) { add(llvm::ConstantPointerNull::get(ptrTy)); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 77e5183517c6f..a9d5cb00bb372 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3104,6 +3104,8 @@ class Parser : public CodeCompletionHandler { ExprResult ParseArrayTypeTrait(); ExprResult ParseExpressionTrait(); + ExprResult ParseBuiltinPtrauthTypeDiscriminator(); + //===--------------------------------------------------------------------===// // Preprocessor code-completion pass-through void CodeCompleteDirective(bool InConditional) override; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 67cc9401dacf6..52fd2fb6c9583 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2139,6 +2139,9 @@ class Sema { SourceLocation AtomicQualLoc = SourceLocation(), SourceLocation UnalignedQualLoc = SourceLocation()); + void diagnosePointerAuthDisabled(SourceLocation loc, SourceRange range); + bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key); + static bool adjustContextForLocalExternDecl(DeclContext *&DC); void DiagnoseFunctionSpecifiers(const DeclSpec &DS); NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 5cccf7859794f..421310ac06393 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -37,6 +37,7 @@ #include "clang/AST/RawCommentList.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StableHash.h" #include "clang/AST/Stmt.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" @@ -2781,6 +2782,16 @@ QualType ASTContext::removeAddrSpaceQualType(QualType T) const { return QualType(TypeNode, Quals.getFastQualifiers()); } +uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { + assert(!T->isDependentType() && + "cannot compute type discriminator of a dependent type"); + SmallString<256> Str; + llvm::raw_svector_ostream Out(Str); + std::unique_ptr<MangleContext> MC(createMangleContext()); + MC->mangleTypeName(T, Out); + return getPointerAuthStringDiscriminator(*this, Str.c_str()); +} + QualType ASTContext::getObjCGCQualType(QualType T, Qualifiers::GC GCAttr) const { QualType CanT = getCanonicalType(T); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7ed0821856708..754277fb1f6de 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -48,6 +48,7 @@ #include "clang/AST/OSLog.h" #include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/StableHash.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" @@ -1853,6 +1854,19 @@ static bool IsStringLiteralCall(const CallExpr *E) { Builtin == Builtin::BI__builtin___NSStringMakeConstantString); } +static bool isGlobalCallLValue(const CallExpr *E) { + if (IsStringLiteralCall(E)) + return true; + + switch (E->getBuiltinCallee()) { + case Builtin::BI__builtin_ptrauth_sign_constant: + return true; + + default: + return false; + } +} + static bool IsGlobalLValue(APValue::LValueBase B) { // C++11 [expr.const]p3 An address constant expression is a prvalue core // constant expression of pointer type that evaluates to... @@ -1894,7 +1908,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { case Expr::ObjCBoxedExprClass: return cast<ObjCBoxedExpr>(E)->isExpressibleAsConstantInitializer(); case Expr::CallExprClass: - return IsStringLiteralCall(cast<CallExpr>(E)); + return isGlobalCallLValue(cast<CallExpr>(E)); // For GCC compatibility, &&label has static storage duration. case Expr::AddrLabelExprClass: return true; @@ -8202,6 +8216,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } case Builtin::BI__builtin_operator_new: return HandleOperatorNewCall(Info, E, Result); + case Builtin::BI__builtin_ptrauth_sign_constant: + return Success(E); case Builtin::BI__builtin_launder: return evaluatePointer(E->getArg(0), Result); case Builtin::BIstrchr: @@ -10615,6 +10631,13 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_expect: return Visit(E->getArg(0)); + case Builtin::BI__builtin_ptrauth_string_discriminator: { + auto literal = cast<StringLiteral>(E->getArg(0)->IgnoreParenImpCasts()); + auto result = getPointerAuthStringDiscriminator(Info.Ctx, + literal->getString()); + return Success(result, E); + } + case Builtin::BI__builtin_ffs: case Builtin::BI__builtin_ffsl: case Builtin::BI__builtin_ffsll: { @@ -11908,6 +11931,12 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( E); } + case UETT_PtrAuthTypeDiscriminator: { + if (E->getArgumentType()->isDependentType()) + return false; + return Success( + Info.Ctx.getPointerAuthTypeDiscriminator(E->getArgumentType()), E); + } case UETT_VecStep: { QualType Ty = E->getTypeOfArgument(); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index c55a901375787..5d6c715853005 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -3995,6 +3995,14 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { case UETT_AlignOf: Out << 'a'; break; + case UETT_PtrAuthTypeDiscriminator: { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, + "cannot yet mangle __builtin_ptrauth_type_discriminator expression"); + Diags.Report(E->getExprLoc(), DiagID); + return; + } case UETT_VecStep: { DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index f60d761c996c5..fdc1b3a553161 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1233,6 +1233,8 @@ void JSONNodeDumper::VisitUnaryExprOrTypeTraitExpr( case UETT_AlignOf: JOS.attribute("name", "alignof"); break; case UETT_VecStep: JOS.attribute("name", "vec_step"); break; case UETT_PreferredAlignOf: JOS.attribute("name", "__alignof"); break; + case UETT_PtrAuthTypeDiscriminator: + JOS.attribute("name", "ptrauth_type_discriminator"); break; case UETT_OpenMPRequiredSimdAlign: JOS.attribute("name", "__builtin_omp_required_simd_align"); break; } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 7759ff6c13898..7b74c05c68dbc 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1278,6 +1278,9 @@ void StmtPrinter::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node){ case UETT_PreferredAlignOf: OS << "__alignof"; break; + case UETT_PtrAuthTypeDiscriminator: + OS << "__builtin_ptrauth_type_discriminator"; + break; case UETT_VecStep: OS << "vec_step"; break; diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 6899b91803a63..062c83268543a 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -813,6 +813,9 @@ void TextNodeDumper::VisitUnaryExprOrTypeTraitExpr( case UETT_AlignOf: OS << " alignof"; break; + case UETT_PtrAuthTypeDiscriminator: + OS << "__builtin_ptrauth_type_discriminator"; + break; case UETT_VecStep: OS << " vec_step"; break; diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 52e8d39460470..63303263ced40 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -755,6 +755,10 @@ bool TargetInfo::validateInputConstraint( return true; } +bool TargetInfo::validatePointerAuthKey(const llvm::APSInt &value) const { + return false; +} + void TargetInfo::CheckFixedPointBits() const { // Check that the number of fractional and integral bits (and maybe sign) can // fit into the bits given for a fixed point type. diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index 15fd05d0bb29a..d59bc955c0e02 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -13,6 +13,7 @@ #include "AArch64.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringExtras.h" @@ -509,6 +510,11 @@ int AArch64TargetInfo::getEHDataRegisterNumber(unsigned RegNo) const { return -1; } +bool AArch64TargetInfo::validatePointerAuthKey( + const llvm::APSInt &value) const { + return 0 <= value && value <= 3; +} + AArch64leTargetInfo::AArch64leTargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) : AArch64TargetInfo(Triple, Opts) {} diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index b6aa07780edda..85b6a7c9622d3 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -97,6 +97,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo { } int getEHDataRegisterNumber(unsigned RegNo) const override; + + bool validatePointerAuthKey(const llvm::APSInt &value) const override; }; class LLVM_LIBRARY_VISIBILITY AArch64leTargetInfo : public AArch64TargetInfo { diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 648837a6d1512..6895b2b9038b3 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3490,6 +3490,78 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__iso_volatile_store64: return RValue::get(EmitISOVolatileStore(*this, E)); + case Builtin::BI__builtin_ptrauth_sign_constant: + return RValue::get(ConstantEmitter(*this).emitAbstract(E, E->getType())); + + case Builtin::BI__builtin_ptrauth_auth: + case Builtin::BI__builtin_ptrauth_auth_and_resign: + case Builtin::BI__builtin_ptrauth_blend_discriminator: + case Builtin::BI__builtin_ptrauth_sign_generic_data: + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + case Builtin::BI__builtin_ptrauth_strip: { + // Emit the arguments. + SmallVector<llvm::Value*, 5> args; + for (auto argExpr : E->arguments()) + args.push_back(EmitScalarExpr(argExpr)); + + // Cast the value to intptr_t, saving its original type. + llvm::Type *origValueType = args[0]->getType(); + if (origValueType->isPointerTy()) + args[0] = Builder.CreatePtrToInt(args[0], IntPtrTy); + + switch (BuiltinID) { + case Builtin::BI__builtin_ptrauth_auth_and_resign: + if (args[4]->getType()->isPointerTy()) + args[4] = Builder.CreatePtrToInt(args[4], IntPtrTy); + LLVM_FALLTHROUGH; + + case Builtin::BI__builtin_ptrauth_auth: + case Builtin::BI__builtin_ptrauth_sign_constant: + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + if (args[2]->getType()->isPointerTy()) + args[2] = Builder.CreatePtrToInt(args[2], IntPtrTy); + break; + + case Builtin::BI__builtin_ptrauth_sign_generic_data: + if (args[1]->getType()->isPointerTy()) + args[1] = Builder.CreatePtrToInt(args[1], IntPtrTy); + break; + + case Builtin::BI__builtin_ptrauth_blend_discriminator: + case Builtin::BI__builtin_ptrauth_strip: + break; + } + + // Call the intrinsic. + auto intrinsicID = [&]() -> unsigned { + switch (BuiltinID) { + case Builtin::BI__builtin_ptrauth_auth: + return llvm::Intrinsic::ptrauth_auth; + case Builtin::BI__builtin_ptrauth_auth_and_resign: + return llvm::Intrinsic::ptrauth_resign; + case Builtin::BI__builtin_ptrauth_blend_discriminator: + return llvm::Intrinsic::ptrauth_blend; + case Builtin::BI__builtin_ptrauth_sign_generic_data: + return llvm::Intrinsic::ptrauth_sign_generic; + case Builtin::BI__builtin_ptrauth_sign_constant: + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + return llvm::Intrinsic::ptrauth_sign; + case Builtin::BI__builtin_ptrauth_strip: + return llvm::Intrinsic::ptrauth_strip; + } + llvm_unreachable("bad ptrauth intrinsic"); + }(); + auto intrinsic = CGM.getIntrinsic(intrinsicID, { IntPtrTy }); + llvm::Value *result = EmitRuntimeCall(intrinsic, args); + + if (BuiltinID != Builtin::BI__builtin_ptrauth_sign_generic_data && + BuiltinID != Builtin::BI__builtin_ptrauth_blend_discriminator && + origValueType->isPointerTy()) { + result = Builder.CreateIntToPtr(result, origValueType); + } + return RValue::get(result); + } + case Builtin::BI__exception_code: case Builtin::BI_exception_code: return RValue::get(EmitSEHExceptionCode()); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 96e8c9c0d0e61..597d1286a0c86 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1428,8 +1428,37 @@ llvm::GlobalValue *ConstantEmitter::getCurrentAddrPrivate() { return global; } +static llvm::Constant *getUnfoldableValue(llvm::Constant *C) { + // Look through any constant expressions that might get folded + while (auto CE = dyn_cast<llvm::ConstantExpr>(C)) { + switch (CE->getOpcode()) { + // Simple type changes. + case llvm::Instruction::BitCast: + case llvm::Instruction::IntToPtr: + case llvm::Instruction::PtrToInt: + break; + + // GEPs, if all the indices are zero. + case llvm::Instruction::GetElementPtr: + for (unsigned i = 1, e = CE->getNumOperands(); i != e; ++i) + if (!CE->getOperand(i)->isNullValue()) + return C; + break; + + default: + return C; + } + C = CE->getOperand(0); + } + return C; +} + void ConstantEmitter::registerCurrentAddrPrivate(llvm::Constant *signal, llvm::GlobalValue *placeholder) { + // Strip anything from the signal value that might get folded into other + // constant expressions in the final initializer. + signal = getUnfoldableValue(signal); + assert(!PlaceholderAddresses.empty()); assert(PlaceholderAddresses.back().first == nullptr); assert(PlaceholderAddresses.back().second == placeholder); @@ -1487,7 +1516,7 @@ namespace { // messing around with llvm::Constant structures, which never itself // does anything that should be visible in compiler output. for (auto &entry : Locations) { - assert(entry.first->getParent() == nullptr && "not a placeholder!"); + assert(entry.first->getName() == "" && "not a placeholder!"); entry.first->replaceAllUsesWith(entry.second); entry.first->eraseFromParent(); } @@ -1772,6 +1801,12 @@ class ConstantLValueEmitter : public ConstStmtVisitor<ConstantLValueEmitter, ConstantLValue VisitMaterializeTemporaryExpr( const MaterializeTemporaryExpr *E); + ConstantLValue emitPointerAuthSignConstant(const CallExpr *E); + llvm::Constant *emitPointerAuthPointer(const Expr *E); + unsigned emitPointerAuthKey(const Expr *E); + std::pair<llvm::Constant*, llvm::Constant*> + emitPointerAuthDiscriminator(const Expr *E); + bool hasNonZeroOffset() const { return !Value.getLValueOffset().isZero(); } @@ -1958,6 +1993,9 @@ ConstantLValueEmitter::VisitAddrLabelExpr(const AddrLabelExpr *E) { ConstantLValue ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) { unsigned builtin = E->getBuiltinCallee(); + if (builtin == Builtin::BI__builtin_ptrauth_sign_constant) + return emitPointerAuthSignConstant(E); + if (builtin != Builtin::BI__builtin___CFStringMakeConstantString && builtin != Builtin::BI__builtin___NSStringMakeConstantString) return nullptr; @@ -1971,6 +2009,63 @@ ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) { } } +ConstantLValue +ConstantLValueEmitter::emitPointerAuthSignConstant(const CallExpr *E) { + auto unsignedPointer = emitPointerAuthPointer(E->getArg(0)); + auto key = emitPointerAuthKey(E->getArg(1)); + llvm::Constant *storageAddress; + llvm::Constant *otherDiscriminator; + std::tie(storageAddress, otherDiscriminator) = + emitPointerAuthDiscriminator(E->getArg(2)); + + auto signedPointer = + CGM.getConstantSignedPointer(unsignedPointer, key, storageAddress, + otherDiscriminator); + return signedPointer; +} + +llvm::Constant *ConstantLValueEmitter::emitPointerAuthPointer(const Expr *E) { + Expr::EvalResult result; + bool succeeded = E->EvaluateAsRValue(result, CGM.getContext()); + assert(succeeded); (void) succeeded; + + // The assertions here are all checked by Sema. + assert(result.Val.isLValue()); + auto base = result.Val.getLValueBase().get<const ValueDecl *>(); + if (auto decl = dyn_cast_or_null<FunctionDecl>(base)) { + assert(result.Val.getLValueOffset().isZero()); + return CGM.getRawFunctionPointer(decl); + } + return ConstantEmitter(CGM, Emitter.CGF) + .emitAbstract(E->getExprLoc(), result.Val, E->getType()); +} + +unsigned ConstantLValueEmitter::emitPointerAuthKey(const Expr *E) { + return E->EvaluateKnownConstInt(CGM.getContext()).getZExtValue(); +} + +std::pair<llvm::Constant*, llvm::Constant*> +ConstantLValueEmitter::emitPointerAuthDiscriminator(const Expr *E) { + E = E->IgnoreParens(); + + if (auto call = dyn_cast<CallExpr>(E)) { + if (call->getBuiltinCallee() == + Builtin::BI__builtin_ptrauth_blend_discriminator) { + auto pointer = ConstantEmitter(CGM).emitAbstract(call->getArg(0), + call->getArg(0)->getType()); + auto extra = ConstantEmitter(CGM).emitAbstract(call->getArg(1), + call->getArg(1)->getType()); + return { pointer, extra }; + } + } + + auto result = ConstantEmitter(CGM).emitAbstract(E, E->getType()); + if (result->getType()->isPointerTy()) + return { result, nullptr }; + else + return { nullptr, result }; +} + ConstantLValue ConstantLValueEmitter::VisitBlockExpr(const BlockExpr *E) { StringRef functionName; diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp new file mode 100644 index 0000000000000..694ee30904f2a --- /dev/null +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -0,0 +1,196 @@ +//===--- CGPointerAuth.cpp - IR generation for pointer authentication -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains common routines relating to the emission of +// pointer authentication operations. +// +//===----------------------------------------------------------------------===// + + +#include "CGCXXABI.h" +#include "CodeGenFunction.h" +#include "CodeGenModule.h" +#include "CGCall.h" +#include "clang/AST/StableHash.h" +#include "clang/CodeGen/ConstantInitBuilder.h" +#include "clang/CodeGen/CodeGenABITypes.h" +#include "clang/Basic/PointerAuthOptions.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/IR/ValueMap.h" +#include "llvm/Analysis/ValueTracking.h" +#include <vector> + +using namespace clang; +using namespace CodeGen; + +uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM, + QualType functionType) { + return CGM.getContext().getPointerAuthTypeDiscriminator(functionType); +} + +/// Compute an ABI-stable hash of the given string. +uint64_t CodeGen::computeStableStringHash(StringRef string) { + return clang::getStableStringHash(string); +} + +/// We use an abstract, side-allocated cache for signed function pointers +/// because (1) most compiler invocations will not need this cache at all, +/// since they don't use signed function pointers, and (2) the +/// representation is pretty complicated (an llvm::ValueMap) and we don't +/// want to have to include that information in CodeGenModule.h. +template <class CacheTy> +static CacheTy &getOrCreateCache(void *&abstractStorage) { + auto cache = static_cast<CacheTy*>(abstractStorage); + if (cache) return *cache; + + abstractStorage = cache = new CacheTy(); + return *cache; +} + +template <class CacheTy> +static void destroyCache(void *&abstractStorage) { + delete static_cast<CacheTy*>(abstractStorage); + abstractStorage = nullptr; +} + +namespace { +struct PointerAuthConstantEntry { + unsigned Key; + llvm::Constant *OtherDiscriminator; + llvm::GlobalVariable *Global; +}; + +using PointerAuthConstantEntries = + std::vector<PointerAuthConstantEntry>; +using ByConstantCacheTy = + llvm::ValueMap<llvm::Constant*, PointerAuthConstantEntries>; +using ByDeclCacheTy = + llvm::DenseMap<const Decl *, llvm::Constant*>; +} + +/// Build a global signed-pointer constant. +static llvm::GlobalVariable * +buildConstantSignedPointer(CodeGenModule &CGM, + llvm::Constant *pointer, + unsigned key, + llvm::Constant *storageAddress, + llvm::Constant *otherDiscriminator) { + ConstantInitBuilder builder(CGM); + auto values = builder.beginStruct(); + values.addBitCast(pointer, CGM.Int8PtrTy); + values.addInt(CGM.Int32Ty, key); + if (storageAddress) { + if (isa<llvm::ConstantInt>(storageAddress)) { + assert(!storageAddress->isNullValue() && + "expecting pointer or special address-discriminator indicator"); + values.add(storageAddress); + } else { + values.add(llvm::ConstantExpr::getPtrToInt(storageAddress, CGM.IntPtrTy)); + } + } else { + values.addInt(CGM.SizeTy, 0); + } + if (otherDiscriminator) { + assert(otherDiscriminator->getType() == CGM.SizeTy); + values.add(otherDiscriminator); + } else { + values.addInt(CGM.SizeTy, 0); + } + + auto *stripped = pointer->stripPointerCasts(); + StringRef name; + if (const auto *origGlobal = dyn_cast<llvm::GlobalValue>(stripped)) + name = origGlobal->getName(); + else if (const auto *ce = dyn_cast<llvm::ConstantExpr>(stripped)) + if (ce->getOpcode() == llvm::Instruction::GetElementPtr) + name = cast<llvm::GEPOperator>(ce)->getPointerOperand()->getName(); + + auto global = values.finishAndCreateGlobal( + name + ".ptrauth", + CGM.getPointerAlign(), + /*constant*/ true, + llvm::GlobalVariable::PrivateLinkage); + global->setSection("llvm.ptrauth"); + + return global; +} + +llvm::Constant * +CodeGenModule::getConstantSignedPointer(llvm::Constant *pointer, + unsigned key, + llvm::Constant *storageAddress, + llvm::Constant *otherDiscriminator) { + // Unique based on the underlying value, not a signing of it. + auto stripped = pointer->stripPointerCasts(); + + PointerAuthConstantEntries *entries = nullptr; + + // We can cache this for discriminators that aren't defined in terms + // of globals. Discriminators defined in terms of globals (1) would + // require additional tracking to be safe and (2) only come up with + // address-specific discrimination, where this entry is almost certainly + // unique to the use-site anyway. + if (!storageAddress && + (!otherDiscriminator || + isa<llvm::ConstantInt>(otherDiscriminator))) { + + // Get or create the cache. + auto &cache = + getOrCreateCache<ByConstantCacheTy>(ConstantSignedPointersByConstant); + + // Check for an existing entry. + entries = &cache[stripped]; + for (auto &entry : *entries) { + if (entry.Key == key && entry.OtherDiscriminator == otherDiscriminator) { + auto global = entry.Global; + return llvm::ConstantExpr::getBitCast(global, pointer->getType()); + } + } + } + + // Build the constant. + auto global = + buildConstantSignedPointer(*this, stripped, key, storageAddress, + otherDiscriminator); + + // Cache if applicable. + if (entries) { + entries->push_back({ key, otherDiscriminator, global }); + } + + // Cast to the original type. + return llvm::ConstantExpr::getBitCast(global, pointer->getType()); +} + +llvm::Constant * +CodeGen::getConstantSignedPointer(CodeGenModule &CGM, + llvm::Constant *pointer, unsigned key, + llvm::Constant *storageAddress, + llvm::Constant *otherDiscriminator) { + return CGM.getConstantSignedPointer(pointer, key, storageAddress, + otherDiscriminator); +} + +void ConstantAggregateBuilderBase::addSignedPointer( + llvm::Constant *pointer, unsigned key, + bool useAddressDiscrimination, llvm::Constant *otherDiscriminator) { + llvm::Constant *storageAddress = nullptr; + if (useAddressDiscrimination) { + storageAddress = getAddrOfCurrentPosition(pointer->getType()); + } + + llvm::Constant *signedPointer = + Builder.CGM.getConstantSignedPointer(pointer, key, storageAddress, + otherDiscriminator); + add(signedPointer); +} + +void CodeGenModule::destroyConstantSignedPointerCaches() { + destroyCache<ByConstantCacheTy>(ConstantSignedPointersByConstant); +} diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 6d1f33b89247d..143aee702501b 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -70,6 +70,7 @@ add_clang_library(clangCodeGen CGOpenCLRuntime.cpp CGOpenMPRuntime.cpp CGOpenMPRuntimeNVPTX.cpp + CGPointerAuth.cpp CGRecordLayoutBuilder.cpp CGStmt.cpp CGStmtOpenMP.cpp diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 480a33f272854..991901dc8d3d5 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -173,7 +173,9 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO, CoverageMapping.reset(new CoverageMappingModuleGen(*this, *CoverageInfo)); } -CodeGenModule::~CodeGenModule() {} +CodeGenModule::~CodeGenModule() { + destroyConstantSignedPointerCaches(); +} void CodeGenModule::createObjCRuntime() { // This is just isGNUFamily(), but we want to force implementors of diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index f5014c05b0672..1274448aeda3c 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -405,6 +405,9 @@ class CodeGenModule : public CodeGenTypeCache { /// Global annotations. std::vector<llvm::Constant*> Annotations; + /// Signed constant pointers. + void *ConstantSignedPointersByConstant = nullptr; + /// Map used to get unique annotation strings. llvm::StringMap<llvm::Constant*> AnnotationStrings; @@ -845,6 +848,11 @@ class CodeGenModule : public CodeGenTypeCache { ForDefinition_t IsForDefinition = NotForDefinition); + llvm::Constant *getConstantSignedPointer(llvm::Constant *pointer, + unsigned key, + llvm::Constant *storageAddress, + llvm::Constant *extraDiscrim); + /// Get the address of the RTTI descriptor for the given type. llvm::Constant *GetAddrOfRTTIDescriptor(QualType Ty, bool ForEH = false); @@ -1514,6 +1522,8 @@ class CodeGenModule : public CodeGenTypeCache { /// function. void SimplifyPersonality(); + void destroyConstantSignedPointerCaches(); + /// Helper function for ConstructAttributeList and AddDefaultFnAttrs. /// Constructs an AttrList for a function with the given properties. void ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone, diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index 8ff648fdb4e0b..136d40bd5ea2a 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -83,6 +83,7 @@ set(files pconfigintrin.h popcntintrin.h prfchwintrin.h + ptrauth.h ptwriteintrin.h rdseedintrin.h rtmintrin.h diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h new file mode 100644 index 0000000000000..2fab3125edf13 --- /dev/null +++ b/clang/lib/Headers/ptrauth.h @@ -0,0 +1,302 @@ +/*===---- ptrauth.h - Pointer authentication -------------------------------=== + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __PTRAUTH_H +#define __PTRAUTH_H + +#include <stdint.h> + +typedef enum { + ptrauth_key_asia = 0, + ptrauth_key_asib = 1, + ptrauth_key_asda = 2, + ptrauth_key_asdb = 3, + + /* A process-independent key which can be used to sign code pointers. + Signing and authenticating with this key is a no-op in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_independent_code = ptrauth_key_asia, + + /* A process-specific key which can be used to sign code pointers. + Signing and authenticating with this key is enforced even in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_dependent_code = ptrauth_key_asib, + + /* A process-independent key which can be used to sign data pointers. + Signing and authenticating with this key is a no-op in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_independent_data = ptrauth_key_asda, + + /* A process-specific key which can be used to sign data pointers. + Signing and authenticating with this key is a no-op in processes + which disable ABI pointer authentication. */ + ptrauth_key_process_dependent_data = ptrauth_key_asdb, + + /* The key used to sign C function pointers. + The extra data is always 0. */ + ptrauth_key_function_pointer = ptrauth_key_process_independent_code, + + /* The key used to sign return addresses on the stack. + The extra data is based on the storage address of the return address. + On ARM64, that is always the storage address of the return address plus 8 + (or, in other words, the value of the stack pointer on function entry) */ + ptrauth_key_return_address = ptrauth_key_process_dependent_code, + + /* The key used to sign frame pointers on the stack. + The extra data is based on the storage address of the frame pointer. + On ARM64, that is always the storage address of the frame pointer plus 16 + (or, in other words, the value of the stack pointer on function entry) */ + ptrauth_key_frame_pointer = ptrauth_key_process_dependent_data, + + /* The key used to sign block function pointers, including: + invocation functions, + block object copy functions, + block object destroy functions, + __block variable copy functions, and + __block variable destroy functions. + The extra data is always the address at which the function pointer + is stored. + + Note that block object pointers themselves (i.e. the direct + representations of values of block-pointer type) are not signed. */ + ptrauth_key_block_function = ptrauth_key_asia, + + /* The key used to sign C++ v-table pointers. + The extra data is always 0. */ + ptrauth_key_cxx_vtable_pointer = ptrauth_key_asda, + + /* Other pointers signed under the ABI use private ABI rules. */ + +} ptrauth_key; + +/* An integer type of the appropriate size for a discriminator argument. */ +typedef uintptr_t ptrauth_extra_data_t; + +/* An integer type of the appropriate size for a generic signature. */ +typedef uintptr_t ptrauth_generic_signature_t; + +/* A signed pointer value embeds the original pointer together with + a signature that attests to the validity of that pointer. Because + this signature must use only "spare" bits of the pointer, a + signature's validity is probabilistic in practice: it is unlikely + but still plausible that an invalidly-derived signature will + somehow equal the correct signature and therefore successfully + authenticate. Nonetheless, this scheme provides a strong degree + of protection against certain kinds of attacks. */ + +/* Authenticating a pointer that was not signed with the given key + and extra-data value will (likely) fail by trapping. */ + +/* The null function pointer is always the all-zero bit pattern. + Signing an all-zero bit pattern will embed a (likely) non-zero + signature in the result, and so the result will not seem to be + a null function pointer. Authenticating this value will yield + a null function pointer back. However, authenticating an + all-zero bit pattern will probably fail, because the + authentication will expect a (likely) non-zero signature to + embedded in the value. + + Because of this, if a pointer may validly be null, you should + check for null before attempting to authenticate it with one + of these intrinsics. This is not necessary when using the + __ptrauth qualifier; the compiler will perform this check + automatically. */ + +#ifdef __PTRAUTH_INTRINSICS__ + +/* Strip the signature from a value without authenticating it. + + If the value is a function pointer, the result will not be a + legal function pointer because of the missing signature, and + attempting to call it will result in an authentication failure. + + The value must be an expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The result will have the same type as the original value. */ +#define ptrauth_strip(__value, __key) \ + __builtin_ptrauth_strip(__value, __key) + +/* Blend a constant discriminator into the given pointer-like value + to form a new discriminator. Not all bits of the inputs are + guaranteed to contribute to the result. + + On arm64e, the integer must fall within the range of a uint16_t; + other bits may be ignored. + + For the purposes of ptrauth_sign_constant, the result of calling + this function is considered a constant expression if the arguments + are constant. Some restrictions may be imposed on the pointer. + + The first argument must be an expression of pointer type. + The second argument must be an expression of integer type. + The result will have type uintptr_t. */ +#define ptrauth_blend_discriminator(__pointer, __integer) \ + __builtin_ptrauth_blend_discriminator(__pointer, __integer) + +/* Add a signature to the given pointer value using a specific key, + using the given extra data as a salt to the signing process. + + The value must be a constant expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be a constant expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. + + This is a constant expression if the extra data is an integer or + null pointer constant. */ +#define ptrauth_sign_constant(__value, __key, __data) \ + __builtin_ptrauth_sign_constant(__value, __key, __data) + +/* Add a signature to the given pointer value using a specific key, + using the given extra data as a salt to the signing process. + + This operation does not authenticate the original value and is + therefore potentially insecure if an attacker could possibly + control that value. + + The value must be an expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. */ +#define ptrauth_sign_unauthenticated(__value, __key, __data) \ + __builtin_ptrauth_sign_unauthenticated(__value, __key, __data) + +/* Authenticate a pointer using one scheme and resign it using another. + + If the result is subsequently authenticated using the new scheme, that + authentication is guaranteed to fail if and only if the initial + authentication failed. + + The value must be an expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. + + This operation is guaranteed to not leave the intermediate value + available for attack before it is re-signed. + + Do not pass a null pointer to this function. A null pointer + will not successfully authenticate. */ +#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, __new_data) \ + __builtin_ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, __new_data) + +/* Authenticate a pointer using one scheme and resign it as a C + function pointer. + + If the result is subsequently authenticated using the new scheme, that + authentication is guaranteed to fail if and only if the initial + authentication failed. + + The value must be an expression of function pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. + + This operation is guaranteed to not leave the intermediate value + available for attack before it is re-signed. Additionally, if this + expression is used syntactically as the function expression in a + call, only a single authentication will be performed. */ +#define ptrauth_auth_function(__value, __old_key, __old_data) \ + ptrauth_auth_and_resign(__value, __old_key, __old_data, ptrauth_key_function_pointer, 0) + +/* Authenticate a data pointer. + + The value must be an expression of non-function pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The result will have the same type as the original value. + + If the authentication fails, dereferencing the resulting pointer + will fail. */ +#define ptrauth_auth_data(__value, __old_key, __old_data) \ + __builtin_ptrauth_auth(__value, __old_key, __old_data) + +/* Compute a constant discriminator from the given string. + + The result can be used as the second argument to + ptrauth_blend_discriminator or the third argument to the + __ptrauth qualifier. It has type size_t. + + The argument must be a string literal. + A call to this function is an integer constant expression. */ +#define ptrauth_string_discriminator(__string) \ + __builtin_ptrauth_string_discriminator(__string) + +/* Compute a constant discriminator from the given type. + + The result can be used as the second argument to + ptrauth_blend_discriminator or the third argument to the + __ptrauth qualifier. It has type size_t. + + If the type is a C++ member function pointer type, the result is + the discriminator used to signed member function pointers of that + type. This property is currently not true of other types. + + The argument must be a type. + A call to this function is an integer constant expression. */ +#define ptrauth_type_discriminator(__type) \ + __builtin_ptrauth_type_discriminator(__type) + + +/* Compute a signature for the given pair of pointer-sized values. + The order of the arguments is significant. + + Like a pointer signature, the resulting signature depends on + private key data and therefore should not be reliably reproducible + by attackers. That means that this can be used to validate the + integrity of arbitrary data by storing a signature for that data + alongside it, then checking that the signature is still valid later. + Data which exceeds two pointers in size can be signed by either + computing a tree of generic signatures or just signing an ordinary + cryptographic hash of the data. + + The result has type ptrauth_generic_signature_t. However, it may + not have as many bits of entropy as that type's width would suggest; + some implementations are known to compute a compressed signature as + if the arguments were a pointer and a discriminator. + + The arguments must be either pointers or integers; if integers, they + will be coerce to uintptr_t. */ +#define ptrauth_sign_generic_data(__value, __data) \ + __builtin_ptrauth_sign_generic_data(__value, __data) + +#else + +#define ptrauth_strip(__value, __key) __value +#define ptrauth_blend_discriminator(__pointer, __integer) ((uintptr_t)0) +#define ptrauth_sign_constant(__value, __key, __data) __value +#define ptrauth_sign_unauthenticated(__value, __key, __data) __value +#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, __new_data) __value +#define ptrauth_auth_function(__value, __old_key, __old_data) __value +#define ptrauth_auth_data(__value, __old_key, __old_data) __value +#define ptrauth_string_discriminator(__string) ((uintptr_t)0) +#define ptrauth_type_discriminator(__type) ((uintptr_t)0) +#define ptrauth_sign_generic_data(__value, __data) ((ptrauth_generic_signature_t)0) + +#endif /* __PTRAUTH_INTRINSICS__ */ + +#endif /* __PTRAUTH_H */ diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index b74a95a3cd4b6..4c59aa746f6dc 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -580,6 +580,26 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { }; } +ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() { + SourceLocation Loc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume()) + return ExprError(); + + TypeResult Ty = ParseTypeName(); + if (Ty.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + SourceLocation EndLoc = Tok.getLocation(); + T.consumeClose(); + return Actions.ActOnUnaryExprOrTypeTraitExpr( + Loc, UETT_PtrAuthTypeDiscriminator, + /*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc)); +} + /// Parse a cast-expression, or, if \pisUnaryExpression is true, parse /// a unary-expression. /// @@ -1439,6 +1459,9 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw___array_extent: return ParseArrayTypeTrait(); + case tok::kw___builtin_ptrauth_type_discriminator: + return ParseBuiltinPtrauthTypeDiscriminator(); + case tok::kw___is_lvalue_expr: case tok::kw___is_rvalue_expr: return ParseExpressionTrait(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8322a9bf14775..6d376ef4d37e6 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -125,6 +125,20 @@ static bool checkArgCount(Sema &S, CallExpr *call, unsigned desiredArgCount) { << call->getArg(1)->getSourceRange(); } +static bool convertArgumentToType(Sema &S, Expr *&Value, QualType Ty) { + if (Value->isTypeDependent()) + return false; + + InitializedEntity Entity = + InitializedEntity::InitializeParameter(S.Context, Ty, false); + ExprResult Result = + S.PerformCopyInitialization(Entity, SourceLocation(), Value); + if (Result.isInvalid()) + return true; + Value = Result.get(); + return false; +} + /// Check that the first argument to __builtin_annotation is an integer /// and the second argument is a non-wide string literal. static bool SemaBuiltinAnnotation(Sema &S, CallExpr *TheCall) { @@ -994,6 +1008,299 @@ static bool SemaOpenCLBuiltinToAddr(Sema &S, unsigned BuiltinID, return false; } +namespace { + enum PointerAuthOpKind { + PAO_Strip, PAO_Sign, PAO_Auth, PAO_SignGeneric, PAO_Discriminator, + PAO_BlendPointer, PAO_BlendInteger + }; +} + +static bool checkPointerAuthEnabled(Sema &S, Expr *E) { + if (S.getLangOpts().PointerAuthIntrinsics) + return false; + + S.diagnosePointerAuthDisabled(E->getExprLoc(), E->getSourceRange()); + return true; +} + +void Sema::diagnosePointerAuthDisabled(SourceLocation loc, SourceRange range) { + if (!getLangOpts().SoftPointerAuth && + !Context.getTargetInfo().isPointerAuthSupported()) { + Diag(loc, diag::err_ptrauth_disabled_target) << range; + } else { + Diag(loc, diag::err_ptrauth_disabled) << range; + } +} + +static bool checkPointerAuthKey(Sema &S, Expr *&Arg) { + // Convert it to type 'int'. + if (convertArgumentToType(S, Arg, S.Context.IntTy)) + return true; + + // Value-dependent expressions are okay; wait for template instantiation. + if (Arg->isValueDependent()) + return false; + + unsigned KeyValue; + return S.checkConstantPointerAuthKey(Arg, KeyValue); +} + +bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) { + // Attempt to constant-evaluate the expression. + llvm::APSInt KeyValue; + if (!Arg->isIntegerConstantExpr(KeyValue, Context)) { + Diag(Arg->getExprLoc(), diag::err_expr_not_ice) << 0 + << Arg->getSourceRange(); + return true; + } + + // Ask the target to validate the key parameter. + if (!Context.getTargetInfo().validatePointerAuthKey(KeyValue)) { + llvm::SmallString<32> Value; { + llvm::raw_svector_ostream Str(Value); + Str << KeyValue; + } + + Diag(Arg->getExprLoc(), diag::err_ptrauth_invalid_key) + << Value << Arg->getSourceRange(); + return true; + } + + Result = KeyValue.getZExtValue(); + return false; +} + +static std::pair<const ValueDecl *, CharUnits> +findConstantBaseAndOffset(Sema &S, Expr *E) { + // Must evaluate as a pointer. + Expr::EvalResult result; + if (!E->EvaluateAsRValue(result, S.Context) || + !result.Val.isLValue()) + return std::make_pair(nullptr, CharUnits()); + + // Base must be a declaration and can't be weakly imported. + auto baseDecl = + result.Val.getLValueBase().dyn_cast<const ValueDecl *>(); + if (!baseDecl || baseDecl->hasAttr<WeakRefAttr>()) + return std::make_pair(nullptr, CharUnits()); + + return std::make_pair(baseDecl, result.Val.getLValueOffset()); +} + +static bool checkPointerAuthValue(Sema &S, Expr *&Arg, + PointerAuthOpKind OpKind, + bool RequireConstant = false) { + if (Arg->hasPlaceholderType()) { + ExprResult R = S.CheckPlaceholderExpr(Arg); + if (R.isInvalid()) return true; + Arg = R.get(); + } + + auto allowsPointer = [](PointerAuthOpKind OpKind) { + return OpKind != PAO_BlendInteger; + }; + auto allowsInteger = [](PointerAuthOpKind OpKind) { + return OpKind == PAO_Discriminator || + OpKind == PAO_BlendInteger || + OpKind == PAO_SignGeneric; + }; + + // Require the value to have the right range of type. + QualType ExpectedTy; + if (allowsPointer(OpKind) && Arg->getType()->isPointerType()) { + ExpectedTy = Arg->getType().getUnqualifiedType(); + } else if (allowsPointer(OpKind) && Arg->getType()->isNullPtrType()) { + ExpectedTy = S.Context.VoidPtrTy; + } else if (allowsInteger(OpKind) && + Arg->getType()->isIntegralOrUnscopedEnumerationType()) { + ExpectedTy = S.Context.getUIntPtrType(); + + // Diagnose the failures. + } else { + S.Diag(Arg->getExprLoc(), diag::err_ptrauth_value_bad_type) + << unsigned(OpKind == PAO_Discriminator ? 1 : + OpKind == PAO_BlendPointer ? 2 : + OpKind == PAO_BlendInteger ? 3 : 0) + << unsigned(allowsInteger(OpKind) ? + (allowsPointer(OpKind) ? 2 : 1) : 0) + << Arg->getType() + << Arg->getSourceRange(); + return true; + } + + // Convert to that type. This should just be an lvalue-to-rvalue + // conversion. + if (convertArgumentToType(S, Arg, ExpectedTy)) + return true; + + if (!RequireConstant) { + // Warn about null pointers for non-generic sign and auth operations. + if ((OpKind == PAO_Sign || OpKind == PAO_Auth) && + Arg->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNull)) { + S.Diag(Arg->getExprLoc(), + OpKind == PAO_Sign ? diag::warn_ptrauth_sign_null_pointer + : diag::warn_ptrauth_auth_null_pointer) + << Arg->getSourceRange(); + } + + return false; + } + + // Perform special checking on the arguments to ptrauth_sign_constant. + + // The main argument. + if (OpKind == PAO_Sign) { + // Require the value we're signing to have a special form. + auto result = findConstantBaseAndOffset(S, Arg); + bool invalid; + + // Must be rooted in a declaration reference. + if (!result.first) { + invalid = true; + + // If it's a function declaration, we can't have an offset. + } else if (isa<FunctionDecl>(result.first)) { + invalid = !result.second.isZero(); + + // Otherwise we're fine. + } else { + invalid = false; + } + + if (invalid) { + S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_pointer); + } + return invalid; + } + + // The discriminator argument. + assert(OpKind == PAO_Discriminator); + + // Must be a pointer or integer or blend thereof. + Expr *pointer = nullptr; + Expr *integer = nullptr; + if (auto call = dyn_cast<CallExpr>(Arg->IgnoreParens())) { + if (call->getBuiltinCallee() == + Builtin::BI__builtin_ptrauth_blend_discriminator) { + pointer = call->getArg(0); + integer = call->getArg(1); + } + } + if (!pointer && !integer) { + if (Arg->getType()->isPointerType()) + pointer = Arg; + else + integer = Arg; + } + + // Check the pointer. + bool invalid = false; + if (pointer) { + assert(pointer->getType()->isPointerType()); + + // TODO: if we're initializing a global, check that the address is + // somehow related to what we're initializing. This probably will + // never really be feasible and we'll have to catch it at link-time. + auto result = findConstantBaseAndOffset(S, pointer); + if (!result.first || !isa<VarDecl>(result.first)) { + invalid = true; + } + } + + // Check the integer. + if (integer) { + assert(integer->getType()->isIntegerType()); + if (!integer->isEvaluatable(S.Context)) + invalid = true; + } + + if (invalid) { + S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_discriminator); + } + return invalid; +} + +static ExprResult SemaPointerAuthStrip(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 2)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Strip) | + checkPointerAuthKey(S, Call->getArgs()[1])) + return ExprError(); + + Call->setType(Call->getArgs()[0]->getType()); + return Call; +} + +static ExprResult SemaPointerAuthBlendDiscriminator(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 2)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_BlendPointer) | + checkPointerAuthValue(S, Call->getArgs()[1], PAO_BlendInteger)) + return ExprError(); + + Call->setType(S.Context.getUIntPtrType()); + return Call; +} + +static ExprResult SemaPointerAuthSignGenericData(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 2)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_SignGeneric) | + checkPointerAuthValue(S, Call->getArgs()[1], PAO_Discriminator)) + return ExprError(); + + Call->setType(S.Context.getUIntPtrType()); + return Call; +} + +static ExprResult SemaPointerAuthSignOrAuth(Sema &S, CallExpr *Call, + PointerAuthOpKind OpKind, + bool RequireConstant) { + if (checkArgCount(S, Call, 3)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], OpKind, RequireConstant) | + checkPointerAuthKey(S, Call->getArgs()[1]) | + checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator, + RequireConstant)) + return ExprError(); + + Call->setType(Call->getArgs()[0]->getType()); + return Call; +} + +static ExprResult SemaPointerAuthAuthAndResign(Sema &S, CallExpr *Call) { + if (checkArgCount(S, Call, 5)) return ExprError(); + if (checkPointerAuthEnabled(S, Call)) return ExprError(); + if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) | + checkPointerAuthKey(S, Call->getArgs()[1]) | + checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) | + checkPointerAuthKey(S, Call->getArgs()[3]) | + checkPointerAuthValue(S, Call->getArgs()[4], PAO_Discriminator)) + return ExprError(); + + Call->setType(Call->getArgs()[0]->getType()); + return Call; +} + +static ExprResult SemaPointerAuthStringDiscriminator(Sema &S, CallExpr *call) { + if (checkPointerAuthEnabled(S, call)) return ExprError(); + + // We've already performed normal call type-checking. + Expr *arg = call->getArgs()[0]->IgnoreParenImpCasts(); + + // Operand must be an ordinary or UTF-8 string literal. + auto literal = dyn_cast<StringLiteral>(arg); + if (!literal || literal->getCharByteWidth() != 1) { + S.Diag(arg->getExprLoc(), diag::err_ptrauth_string_not_literal) + << (literal ? 1 : 0) + << arg->getSourceRange(); + return ExprError(); + } + + return call; +} + + static ExprResult SemaBuiltinLaunder(Sema &S, CallExpr *TheCall) { if (checkArgCount(S, TheCall, 1)) return ExprError(); @@ -1457,6 +1764,25 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, TheCall->setType(Context.VoidPtrTy); break; + case Builtin::BI__builtin_ptrauth_strip: + return SemaPointerAuthStrip(*this, TheCall); + case Builtin::BI__builtin_ptrauth_blend_discriminator: + return SemaPointerAuthBlendDiscriminator(*this, TheCall); + case Builtin::BI__builtin_ptrauth_sign_constant: + return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Sign, + /*constant*/ true); + case Builtin::BI__builtin_ptrauth_sign_unauthenticated: + return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Sign, + /*constant*/ false); + case Builtin::BI__builtin_ptrauth_auth: + return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Auth, + /*constant*/ false); + case Builtin::BI__builtin_ptrauth_sign_generic_data: + return SemaPointerAuthSignGenericData(*this, TheCall); + case Builtin::BI__builtin_ptrauth_auth_and_resign: + return SemaPointerAuthAuthAndResign(*this, TheCall); + case Builtin::BI__builtin_ptrauth_string_discriminator: + return SemaPointerAuthStringDiscriminator(*this, TheCall); // OpenCL v2.0, s6.13.16 - Pipe functions case Builtin::BIread_pipe: case Builtin::BIwrite_pipe: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2b5d2bbf76f8a..77f3c45f9fa67 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3748,6 +3748,17 @@ static bool CheckVecStepTraitOperandType(Sema &S, QualType T, return false; } +static bool CheckPtrAuthTypeDiscriminatorOperandType(Sema &S, QualType T, + SourceLocation Loc, + SourceRange ArgRange) { + if (T->isVariablyModifiedType()) { + S.Diag(Loc, diag::err_ptrauth_type_disc_variably_modified) << T << ArgRange; + return true; + } + + return false; +} + static bool CheckExtensionTraitOperandType(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange, @@ -3947,6 +3958,10 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType, if (ExprKind == UETT_VecStep) return CheckVecStepTraitOperandType(*this, ExprType, OpLoc, ExprRange); + if (ExprKind == UETT_PtrAuthTypeDiscriminator) + return CheckPtrAuthTypeDiscriminatorOperandType( + *this, ExprType, OpLoc, ExprRange); + // Whitelist some types as extensions if (!CheckExtensionTraitOperandType(*this, ExprType, OpLoc, ExprRange, ExprKind)) diff --git a/clang/test/CodeGen/ptrauth-intrinsics.c b/clang/test/CodeGen/ptrauth-intrinsics.c new file mode 100644 index 0000000000000..cf3e70359e781 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-intrinsics.c @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +void (*fnptr)(void); +long int_discriminator; +void *ptr_discriminator; +long signature; + +// CHECK-LABEL: define void @test_auth() +void test_auth() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 0, i64 [[DISC]]) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_auth(fnptr, 0, ptr_discriminator); +} + +// CHECK-LABEL: define void @test_auth_peephole() +void test_auth_peephole() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: call void [[PTR]]() [ "ptrauth"(i32 0, i64 [[DISC]]) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth(fnptr, 0, ptr_discriminator)(); +} + +// CHECK-LABEL: define void @test_strip() +void test_strip() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.strip.i64(i64 [[T0]], i32 0) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_strip(fnptr, 0); +} + +// CHECK-LABEL: define void @test_sign_unauthenticated() +void test_sign_unauthenticated() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 [[T0]], i32 0, i64 [[DISC]]) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_sign_unauthenticated(fnptr, 0, ptr_discriminator); +} + +// CHECK-LABEL: define void @test_auth_and_resign() +void test_auth_and_resign() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 0, i64 [[DISC]], i32 3, i64 15) + // CHECK-NEXT: [[RESULT:%.*]] = inttoptr i64 [[T1]] to void ()* + // CHECK-NEXT: store void ()* [[RESULT]], void ()** @fnptr, + fnptr = __builtin_ptrauth_auth_and_resign(fnptr, 0, ptr_discriminator, 3, 15); +} + +// CHECK-LABEL: define void @test_blend_discriminator() +void test_blend_discriminator() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC:%.*]] = load i64, i64* @int_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[RESULT:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 [[DISC]]) + // CHECK-NEXT: store i64 [[RESULT]], i64* @int_discriminator, + int_discriminator = __builtin_ptrauth_blend_discriminator(fnptr, int_discriminator); +} + +// CHECK-LABEL: define void @test_sign_generic_data() +void test_sign_generic_data() { + // CHECK: [[PTR:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load i8*, i8** @ptr_discriminator, + // CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()* [[PTR]] to i64 + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8* [[DISC0]] to i64 + // CHECK-NEXT: [[RESULT:%.*]] = call i64 @llvm.ptrauth.sign.generic.i64(i64 [[T0]], i64 [[DISC]]) + // CHECK-NEXT: store i64 [[RESULT]], i64* @signature, + signature = __builtin_ptrauth_sign_generic_data(fnptr, ptr_discriminator); +} + +// CHECK-LABEL: define void @test_string_discriminator() +void test_string_discriminator() { + // CHECK: [[X:%.*]] = alloca i32 + + // Check a couple of random discriminators used by Swift. + + // CHECK: store i32 58298, i32* [[X]], + int x = __builtin_ptrauth_string_discriminator("InitializeWithCopy"); + + // CHECK: store i32 9112, i32* [[X]], + x = __builtin_ptrauth_string_discriminator("DestroyArray"); +} diff --git a/clang/test/CodeGenCXX/mangle-fail.cpp b/clang/test/CodeGenCXX/mangle-fail.cpp index b588d57749fa3..7d842c53896db 100644 --- a/clang/test/CodeGenCXX/mangle-fail.cpp +++ b/clang/test/CodeGenCXX/mangle-fail.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -emit-llvm-only -x c++ -std=c++11 -triple %itanium_abi_triple -verify %s -DN=1 // RUN: %clang_cc1 -emit-llvm-only -x c++ -std=c++11 -triple %itanium_abi_triple -verify %s -DN=2 +// RUN: %clang_cc1 -emit-llvm-only -x c++ -std=c++11 -triple %itanium_abi_triple -verify %s -DN=3 struct A { int a; }; @@ -13,6 +14,19 @@ template void test<int>(int (&)[sizeof(int)]); template<class T> void test(int (&)[sizeof((A){}, T())]) {} // expected-error {{cannot yet mangle}} template void test<int>(int (&)[sizeof(A)]); +#elif N == 3 +// __builtin_ptrauth_type_discriminator +template <class T, unsigned disc> +struct S1 {}; + +template<class T> +void func(S1<T, __builtin_ptrauth_type_discriminator(T)> s1) { // expected-error {{cannot yet mangle __builtin_ptrauth_type_discriminator expression}} +} + +void testfunc1() { + func(S1<int, __builtin_ptrauth_type_discriminator(int)>()); +} + // FIXME: There are several more cases we can't yet mangle. #else diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c new file mode 100644 index 0000000000000..0f082441c412d --- /dev/null +++ b/clang/test/Sema/ptrauth-intrinsics-macro.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify %s + +// expected-no-diagnostics + +#include <ptrauth.h> + +#define VALID_CODE_KEY 0 +#define VALID_DATA_KEY 2 + +extern int dv; + +void test(int *dp, int (*fp)(int), int value) { + dp = ptrauth_strip(dp, VALID_DATA_KEY); + uintptr_t t0 = ptrauth_blend_discriminator(dp, value); + t0 = ptrauth_type_discriminator(int (*)(int)); + dp = ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0); + dp = ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0); + dp = ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp); + fp = ptrauth_auth_function(fp, VALID_CODE_KEY, 0); + dp = ptrauth_auth_data(dp, VALID_DATA_KEY, 0); + int t1 = ptrauth_string_discriminator("string"); + int t2 = ptrauth_sign_generic_data(dp, 0); +} diff --git a/clang/test/Sema/ptrauth.c b/clang/test/Sema/ptrauth.c new file mode 100644 index 0000000000000..e5d67992a7f16 --- /dev/null +++ b/clang/test/Sema/ptrauth.c @@ -0,0 +1,155 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s + +#if __has_feature(ptrauth_intrinsics) +#warning Pointer authentication enabled! +// expected-warning@-1 {{Pointer authentication enabled!}} +#endif + +#if __aarch64__ +#define VALID_CODE_KEY 0 +#define VALID_DATA_KEY 2 +#define INVALID_KEY 200 +#else +#error Provide these constants if you port this test +#endif + +#define NULL ((void*) 0) +struct A { int x; } mismatched_type; + +extern int dv; +extern int fv(int); + +void test_strip(int *dp, int (*fp)(int)) { + __builtin_ptrauth_strip(dp); // expected-error {{too few arguments}} + __builtin_ptrauth_strip(dp, VALID_DATA_KEY, dp); // expected-error {{too many arguments}} + (void) __builtin_ptrauth_strip(NULL, VALID_DATA_KEY); // no warning + + __builtin_ptrauth_strip(mismatched_type, VALID_DATA_KEY); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_strip(dp, mismatched_type); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + + int *dr = __builtin_ptrauth_strip(dp, VALID_DATA_KEY); + dr = __builtin_ptrauth_strip(dp, INVALID_KEY); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_strip(fp, VALID_CODE_KEY); + fr = __builtin_ptrauth_strip(fp, INVALID_KEY); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_strip(dp, VALID_DATA_KEY); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_blend_discriminator(int *dp, int (*fp)(int), int value) { + __builtin_ptrauth_blend_discriminator(dp); // expected-error {{too few arguments}} + __builtin_ptrauth_blend_discriminator(dp, dp, dp); // expected-error {{too many arguments}} + (void) __builtin_ptrauth_blend_discriminator(dp, value); // no warning + + __builtin_ptrauth_blend_discriminator(mismatched_type, value); // expected-error {{blended pointer must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_blend_discriminator(dp, mismatched_type); // expected-error {{blended integer must have integer type; type here is 'struct A'}} + + float *mismatch = __builtin_ptrauth_blend_discriminator(dp, value); // expected-warning {{incompatible integer to pointer conversion initializing 'float *' with an expression of type}} +} + +void test_sign_constant(int *dp, int (*fp)(int)) { + __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY); // expected-error {{too few arguments}} + __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, &dv, &dv); // expected-error {{too many arguments}} + + __builtin_ptrauth_sign_constant(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_sign_constant(&dv, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_sign_constant(NULL, VALID_DATA_KEY, &dv); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}} + + int *dr = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0); + dr = __builtin_ptrauth_sign_constant(&dv, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + dr = __builtin_ptrauth_sign_constant(dp, VALID_DATA_KEY, 0); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}} + dr = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, dp); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}} + + int (*fr)(int) = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, 0); + fr = __builtin_ptrauth_sign_constant(&fv, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + fr = __builtin_ptrauth_sign_constant(fp, VALID_DATA_KEY, 0); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}} + fr = __builtin_ptrauth_sign_constant(&fv, VALID_DATA_KEY, dp); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}} + + fr = __builtin_ptrauth_sign_constant(&fv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&fr, 0)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}} + fr = __builtin_ptrauth_sign_constant(&fv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&dv, *dp)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}} + fr = __builtin_ptrauth_sign_constant(&fv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&dv, 0)); + + float *mismatch = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_sign_unauthenticated(int *dp, int (*fp)(int)) { + __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY); // expected-error {{too few arguments}} + __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, dp, dp); // expected-error {{too many arguments}} + + __builtin_ptrauth_sign_unauthenticated(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_sign_unauthenticated(dp, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_sign_unauthenticated(NULL, VALID_DATA_KEY, 0); // expected-warning {{signing a null pointer will yield a non-null pointer}} + + int *dr = __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0); + dr = __builtin_ptrauth_sign_unauthenticated(dp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_sign_unauthenticated(fp, VALID_CODE_KEY, 0); + fr = __builtin_ptrauth_sign_unauthenticated(fp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_sign_unauthenticated(dp, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_auth(int *dp, int (*fp)(int)) { + __builtin_ptrauth_auth(dp, VALID_DATA_KEY); // expected-error {{too few arguments}} + __builtin_ptrauth_auth(dp, VALID_DATA_KEY, dp, dp); // expected-error {{too many arguments}} + + __builtin_ptrauth_auth(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_auth(dp, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_auth(dp, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_auth(NULL, VALID_DATA_KEY, 0); // expected-warning {{authenticating a null pointer will almost certainly trap}} + + int *dr = __builtin_ptrauth_auth(dp, VALID_DATA_KEY, 0); + dr = __builtin_ptrauth_auth(dp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_auth(fp, VALID_CODE_KEY, 0); + fr = __builtin_ptrauth_auth(fp, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_auth(dp, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_auth_and_resign(int *dp, int (*fp)(int)) { + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY); // expected-error {{too few arguments}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, dp, VALID_DATA_KEY, dp, 0); // expected-error {{too many arguments}} + + __builtin_ptrauth_auth_and_resign(mismatched_type, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-error {{signed value must have pointer type; type here is 'struct A'}} + __builtin_ptrauth_auth_and_resign(dp, mismatched_type, 0, VALID_DATA_KEY, dp); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, mismatched_type, VALID_DATA_KEY, dp); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, mismatched_type, dp); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}} + __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_auth_and_resign(NULL, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-warning {{authenticating a null pointer will almost certainly trap}} + + int *dr = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); + dr = __builtin_ptrauth_auth_and_resign(dp, INVALID_KEY, 0, VALID_DATA_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + dr = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, INVALID_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + int (*fr)(int) = __builtin_ptrauth_auth_and_resign(fp, VALID_CODE_KEY, 0, VALID_CODE_KEY, dp); + fr = __builtin_ptrauth_auth_and_resign(fp, INVALID_KEY, 0, VALID_CODE_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + fr = __builtin_ptrauth_auth_and_resign(fp, VALID_CODE_KEY, 0, INVALID_KEY, dp); // expected-error {{does not identify a valid pointer authentication key for the current target}} + + float *mismatch = __builtin_ptrauth_auth_and_resign(dp, VALID_DATA_KEY, 0, VALID_DATA_KEY, dp); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}} +} + +void test_sign_generic_data(int *dp) { + __builtin_ptrauth_sign_generic_data(dp); // expected-error {{too few arguments}} + __builtin_ptrauth_sign_generic_data(dp, 0, 0); // expected-error {{too many arguments}} + + __builtin_ptrauth_sign_generic_data(mismatched_type, 0); // expected-error {{signed value must have pointer or integer type; type here is 'struct A'}} + __builtin_ptrauth_sign_generic_data(dp, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}} + + (void) __builtin_ptrauth_sign_generic_data(NULL, 0); // no warning + + unsigned long dr = __builtin_ptrauth_sign_generic_data(dp, 0); + dr = __builtin_ptrauth_sign_generic_data(dp, &dv); + dr = __builtin_ptrauth_sign_generic_data(12314, 0); + dr = __builtin_ptrauth_sign_generic_data(12314, &dv); + + int *mismatch = __builtin_ptrauth_sign_generic_data(dp, 0); // expected-warning {{incompatible integer to pointer conversion initializing 'int *' with an expression of type}} +} diff --git a/clang/test/SemaCXX/ptrauth.cpp b/clang/test/SemaCXX/ptrauth.cpp new file mode 100644 index 0000000000000..691edc9a2e576 --- /dev/null +++ b/clang/test/SemaCXX/ptrauth.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -fsyntax-only -verify -fptrauth-intrinsics %s + +struct S { + virtual int foo(); +}; + +template <class T> +constexpr unsigned dependentOperandDisc() { + return __builtin_ptrauth_type_discriminator(T); +} + +void test_builtin_ptrauth_type_discriminator(unsigned s) { + typedef int (S::*MemFnTy)(); + MemFnTy memFnPtr; + int (S::*memFnPtr2)(); + + constexpr unsigned d = __builtin_ptrauth_type_discriminator(MemFnTy); + static_assert(d == 60844); + static_assert(__builtin_ptrauth_type_discriminator(int (S::*)()) == d); + static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr)) == d); + static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr2)) == d); + static_assert(__builtin_ptrauth_type_discriminator(decltype(&S::foo)) == d); + static_assert(dependentOperandDisc<decltype(&S::foo)>() == d); + static_assert(__builtin_ptrauth_type_discriminator(void (S::*)(int)) == 39121); + static_assert(__builtin_ptrauth_type_discriminator(void (S::*)(float)) == 52453); + static_assert(__builtin_ptrauth_type_discriminator(int *) == 42396); + + int t; + int vmarray[s]; + __builtin_ptrauth_type_discriminator(t); // expected-error {{unknown type name 't'}} + __builtin_ptrauth_type_discriminator(&t); // expected-error {{expected a type}} + __builtin_ptrauth_type_discriminator(decltype(vmarray)); // expected-error {{cannot pass variably-modified type 'decltype(vmarray)'}} +} From 2c01c3374bbd33334ee7df41a69cd8e0cf02a2f4 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 01:11:19 -0400 Subject: [PATCH 529/582] IRGen preliminaries for pointer authentication, hopefully NFC. --- clang/include/clang/CodeGen/CodeGenABITypes.h | 4 + .../clang/CodeGen/ConstantInitBuilder.h | 10 +- clang/lib/CodeGen/CGBuiltin.cpp | 2 +- clang/lib/CodeGen/CGCall.cpp | 3 + clang/lib/CodeGen/CGCall.h | 62 +++- clang/lib/CodeGen/CGExpr.cpp | 17 +- clang/lib/CodeGen/CGObjC.cpp | 6 +- clang/lib/CodeGen/CGPointerAuth.cpp | 276 ++++++++++++++++++ clang/lib/CodeGen/CodeGenFunction.cpp | 90 ++++++ clang/lib/CodeGen/CodeGenFunction.h | 25 ++ clang/lib/CodeGen/CodeGenModule.h | 37 +++ 11 files changed, 513 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index f6394c1d8b190..a8457ac349cd6 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -43,6 +43,7 @@ class CXXMethodDecl; class CodeGenOptions; class CoverageSourceInfo; class DiagnosticsEngine; +class GlobalDecl; class HeaderSearchOptions; class ObjCMethodDecl; class PreprocessorOptions; @@ -92,6 +93,9 @@ unsigned getLLVMFieldNumber(CodeGenModule &CGM, /// a specific key value which can be found in the source. uint64_t computeStableStringHash(StringRef string); +/// Return a declaration discriminator for the given global decl. +uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); + /// Return a type discriminator for the given function type. uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, QualType fnType); diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 61f781476fa4e..87cc00957196b 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -25,8 +25,11 @@ #include <vector> namespace clang { -namespace CodeGen { +class GlobalDecl; +class PointerAuthSchema; +class QualType; +namespace CodeGen { class CodeGenModule; /// A convenience builder class for complex constant initializers, @@ -199,6 +202,11 @@ class ConstantAggregateBuilderBase { add(llvm::ConstantInt::get(intTy, value, isSigned)); } + /// Add a signed pointer using the given pointer authentication schema. + void addSignedPointer(llvm::Constant *pointer, + const PointerAuthSchema &schema, GlobalDecl calleeDecl, + QualType calleeType); + /// Add a signed pointer using the given pointer authentication schema. void addSignedPointer(llvm::Constant *pointer, unsigned key, diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 6895b2b9038b3..970de010bf78a 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4189,7 +4189,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, // using exactly the normal call path. if (getContext().BuiltinInfo.isPredefinedLibFunction(BuiltinID)) return emitLibraryCall(*this, FD, E, - cast<llvm::Constant>(EmitScalarExpr(E->getCallee()))); + CGM.getRawFunctionPointer(FD)); // Check that a call to a target specific builtin has the correct target // features. diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 62e8fa0370131..8bab5cd2c6aa6 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4370,6 +4370,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, SmallVector<llvm::OperandBundleDef, 1> BundleList = getBundlesForFunclet(CalleePtr); + // Add the pointer-authentication bundle. + EmitPointerAuthOperandBundle(ConcreteCallee.getPointerAuthInfo(), BundleList); + // Emit the actual call/invoke instruction. llvm::CallBase *CI; if (!InvokeDest) { diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index cc11ded704abb..b27f41dc4065c 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -62,6 +62,42 @@ class CGCalleeInfo { const GlobalDecl getCalleeDecl() const { return CalleeDecl; } }; + /// Information necessary for pointer authentication. + class CGPointerAuthInfo { + unsigned Signed : 1; + unsigned Key : 31; + llvm::Value *Discriminator; + public: + CGPointerAuthInfo() { + Signed = false; + } + CGPointerAuthInfo(unsigned key, llvm::Value *discriminator) + : Discriminator(discriminator) { + assert(!discriminator || + discriminator->getType()->isIntegerTy() || + discriminator->getType()->isPointerTy()); + Signed = true; + Key = key; + } + + explicit operator bool() const { + return isSigned(); + } + + bool isSigned() const { + return Signed; + } + + unsigned getKey() const { + assert(isSigned()); + return Key; + } + llvm::Value *getDiscriminator() const { + assert(isSigned()); + return Discriminator; + } + }; + /// All available information about a concrete callee. class CGCallee { enum class SpecialKind : uintptr_t { @@ -73,6 +109,10 @@ class CGCalleeInfo { Last = Virtual }; + struct OrdinaryInfoStorage { + CGCalleeInfo AbstractInfo; + CGPointerAuthInfo PointerAuthInfo; + }; struct BuiltinInfoStorage { const FunctionDecl *Decl; unsigned ID; @@ -89,7 +129,7 @@ class CGCalleeInfo { SpecialKind KindOrFunctionPointer; union { - CGCalleeInfo AbstractInfo; + OrdinaryInfoStorage OrdinaryInfo; BuiltinInfoStorage BuiltinInfo; PseudoDestructorInfoStorage PseudoDestructorInfo; VirtualInfoStorage VirtualInfo; @@ -108,9 +148,11 @@ class CGCalleeInfo { /// Construct a callee. Call this constructor directly when this /// isn't a direct call. - CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr) + CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr, + const CGPointerAuthInfo &pointerAuthInfo = /*FIXME*/ CGPointerAuthInfo()) : KindOrFunctionPointer(SpecialKind(uintptr_t(functionPtr))) { - AbstractInfo = abstractInfo; + OrdinaryInfo.AbstractInfo = abstractInfo; + OrdinaryInfo.PointerAuthInfo = pointerAuthInfo; assert(functionPtr && "configuring callee without function pointer"); assert(functionPtr->getType()->isPointerTy()); assert(functionPtr->getType()->getPointerElementType()->isFunctionTy()); @@ -132,13 +174,13 @@ class CGCalleeInfo { static CGCallee forDirect(llvm::Constant *functionPtr, const CGCalleeInfo &abstractInfo = CGCalleeInfo()) { - return CGCallee(abstractInfo, functionPtr); + return CGCallee(abstractInfo, functionPtr, CGPointerAuthInfo()); } static CGCallee forDirect(llvm::FunctionCallee functionPtr, const CGCalleeInfo &abstractInfo = CGCalleeInfo()) { - return CGCallee(abstractInfo, functionPtr.getCallee()); + return CGCallee(abstractInfo, functionPtr.getCallee(), CGPointerAuthInfo()); } static CGCallee forVirtual(const CallExpr *CE, GlobalDecl MD, Address Addr, @@ -178,7 +220,11 @@ class CGCalleeInfo { if (isVirtual()) return VirtualInfo.MD; assert(isOrdinary()); - return AbstractInfo; + return OrdinaryInfo.AbstractInfo; + } + const CGPointerAuthInfo &getPointerAuthInfo() const { + assert(isOrdinary()); + return OrdinaryInfo.PointerAuthInfo; } llvm::Value *getFunctionPointer() const { assert(isOrdinary()); @@ -188,6 +234,10 @@ class CGCalleeInfo { assert(isOrdinary()); KindOrFunctionPointer = SpecialKind(uintptr_t(functionPtr)); } + void setPointerAuthInfo(CGPointerAuthInfo pointerAuth) { + assert(isOrdinary()); + OrdinaryInfo.PointerAuthInfo = pointerAuth; + } bool isVirtual() const { return KindOrFunctionPointer == SpecialKind::Virtual; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index dcd365c8eaf0f..49c24fbba104b 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -2393,14 +2393,14 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF, return LV; } -static llvm::Constant *EmitFunctionDeclPointer(CodeGenModule &CGM, - const FunctionDecl *FD) { +llvm::Constant *CodeGenModule::getRawFunctionPointer(const FunctionDecl *FD, + llvm::Type *Ty) { if (FD->hasAttr<WeakRefAttr>()) { - ConstantAddress aliasee = CGM.GetWeakRefReference(FD); + ConstantAddress aliasee = GetWeakRefReference(FD); return aliasee.getPointer(); } - llvm::Constant *V = CGM.GetAddrOfFunction(FD); + llvm::Constant *V = GetAddrOfFunction(FD, Ty); if (!FD->hasPrototype()) { if (const FunctionProtoType *Proto = FD->getType()->getAs<FunctionProtoType>()) { @@ -2408,10 +2408,9 @@ static llvm::Constant *EmitFunctionDeclPointer(CodeGenModule &CGM, // isn't the same as the type of a use. Correct for this with a // bitcast. QualType NoProtoType = - CGM.getContext().getFunctionNoProtoType(Proto->getReturnType()); - NoProtoType = CGM.getContext().getPointerType(NoProtoType); - V = llvm::ConstantExpr::getBitCast(V, - CGM.getTypes().ConvertType(NoProtoType)); + getContext().getFunctionNoProtoType(Proto->getReturnType()); + NoProtoType = getContext().getPointerType(NoProtoType); + V = llvm::ConstantExpr::getBitCast(V,getTypes().ConvertType(NoProtoType)); } } return V; @@ -2419,7 +2418,7 @@ static llvm::Constant *EmitFunctionDeclPointer(CodeGenModule &CGM, static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF, const Expr *E, const FunctionDecl *FD) { - llvm::Value *V = EmitFunctionDeclPointer(CGF.CGM, FD); + llvm::Constant *V = CGF.CGM.getFunctionPointer(FD); CharUnits Alignment = CGF.getContext().getDeclAlign(FD); return CGF.MakeAddrLValue(V, E->getType(), Alignment, AlignmentSource::Decl); diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index 1fa72678081af..ab90ac2bd56ec 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -3559,7 +3559,8 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( EmitStmt(TheCall); FinishFunction(); - HelperFn = llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy); + HelperFn = CGM.getFunctionPointer(Fn, FD->getType()); + HelperFn = llvm::ConstantExpr::getBitCast(HelperFn, VoidPtrTy); CGM.setAtomicSetterHelperFnMap(Ty, HelperFn); return HelperFn; } @@ -3664,7 +3665,8 @@ CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction( AggValueSlot::DoesNotOverlap)); FinishFunction(); - HelperFn = llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy); + HelperFn = CGM.getFunctionPointer(Fn, FD->getType()); + HelperFn = llvm::ConstantExpr::getBitCast(HelperFn, VoidPtrTy); CGM.setAtomicGetterHelperFnMap(Ty, HelperFn); return HelperFn; } diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 694ee30904f2a..69b1a1580b453 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -29,6 +29,31 @@ using namespace clang; using namespace CodeGen; +/// Given a pointer-authentication schema, return a concrete "other" +/// discriminator for it. +llvm::Constant * +CodeGenModule::getPointerAuthOtherDiscriminator(const PointerAuthSchema &schema, + GlobalDecl decl, + QualType type) { + switch (schema.getOtherDiscrimination()) { + case PointerAuthSchema::Discrimination::None: + return nullptr; + + case PointerAuthSchema::Discrimination::Type: + assert(!type.isNull() && + "type not provided for type-discriminated schema"); + return llvm::ConstantInt::get( + IntPtrTy, getContext().getPointerAuthTypeDiscriminator(type)); + + case PointerAuthSchema::Discrimination::Decl: + assert(decl.getDecl() && + "declaration not provided for decl-discriminated schema"); + return llvm::ConstantInt::get(IntPtrTy, + getPointerAuthDeclDiscriminator(decl)); + } + llvm_unreachable("bad discrimination kind"); +} + uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM, QualType functionType) { return CGM.getContext().getPointerAuthTypeDiscriminator(functionType); @@ -39,6 +64,186 @@ uint64_t CodeGen::computeStableStringHash(StringRef string) { return clang::getStableStringHash(string); } +uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM, + GlobalDecl declaration) { + return CGM.getPointerAuthDeclDiscriminator(declaration); +} + +/// Return the "other" decl-specific discriminator for the given decl. +uint16_t +CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl declaration) { + uint16_t &entityHash = PtrAuthDiscriminatorHashes[declaration]; + + if (entityHash == 0) { + StringRef name = getMangledName(declaration); + entityHash = getPointerAuthStringDiscriminator(getContext(), name); + } + + return entityHash; +} + +/// Return the abstract pointer authentication schema for a +/// function pointer of the given type. +CGPointerAuthInfo +CodeGenModule::getFunctionPointerAuthInfo(QualType functionType) { + // Check for a generic pointer authentication schema. + auto &schema = getCodeGenOpts().PointerAuth.FunctionPointers; + if (!schema) return CGPointerAuthInfo(); + + assert(!schema.isAddressDiscriminated() && + "function pointers cannot use address-specific discrimination"); + + auto discriminator = + getPointerAuthOtherDiscriminator(schema, GlobalDecl(), functionType); + return CGPointerAuthInfo(schema.getKey(), discriminator); +} + +/// Return the natural pointer authentication for values of the given +/// pointer type. +static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM, + QualType type) { + assert(type->isPointerType()); + + // Function pointers use the function-pointer schema by default. + if (auto ptrTy = type->getAs<PointerType>()) { + auto functionType = ptrTy->getPointeeType(); + if (functionType->isFunctionType()) { + return CGM.getFunctionPointerAuthInfo(functionType); + } + } + + // Normal data pointers never use direct pointer authentication by default. + return CGPointerAuthInfo(); +} + +llvm::Value *CodeGenFunction::EmitPointerAuthBlendDiscriminator( + llvm::Value *storageAddress, llvm::Value *discriminator) { + storageAddress = Builder.CreatePtrToInt(storageAddress, IntPtrTy); + auto intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_blend, + { CGM.IntPtrTy }); + return Builder.CreateCall(intrinsic, {storageAddress, discriminator}); +} + +/// Emit the concrete pointer authentication informaton for the +/// given authentication schema. +CGPointerAuthInfo +CodeGenFunction::EmitPointerAuthInfo(const PointerAuthSchema &schema, + llvm::Value *storageAddress, + GlobalDecl schemaDecl, + QualType schemaType) { + if (!schema) return CGPointerAuthInfo(); + + llvm::Value *discriminator = + CGM.getPointerAuthOtherDiscriminator(schema, schemaDecl, schemaType); + + if (schema.isAddressDiscriminated()) { + assert(storageAddress && + "address not provided for address-discriminated schema"); + + if (discriminator) + discriminator = + EmitPointerAuthBlendDiscriminator(storageAddress, discriminator); + else + discriminator = Builder.CreatePtrToInt(storageAddress, IntPtrTy); + } + + return CGPointerAuthInfo(schema.getKey(), discriminator); +} + +CGPointerAuthInfo +CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier qualifier, + Address storageAddress) { + assert(qualifier && + "don't call this if you don't know that the qualifier is present"); + + llvm::Value *discriminator = nullptr; + if (unsigned extra = qualifier.getExtraDiscriminator()) { + discriminator = llvm::ConstantInt::get(IntPtrTy, extra); + } + + if (qualifier.isAddressDiscriminated()) { + assert(storageAddress.isValid() && + "address discrimination without address"); + auto storagePtr = storageAddress.getPointer(); + if (discriminator) { + discriminator = + EmitPointerAuthBlendDiscriminator(storagePtr, discriminator); + } else { + discriminator = Builder.CreatePtrToInt(storagePtr, IntPtrTy); + } + } + + return CGPointerAuthInfo(qualifier.getKey(), discriminator); +} + +static bool isZeroConstant(llvm::Value *value) { + if (auto ci = dyn_cast<llvm::ConstantInt>(value)) + return ci->isZero(); + return false; +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthResign(llvm::Value *value, QualType type, + const CGPointerAuthInfo &curAuthInfo, + const CGPointerAuthInfo &newAuthInfo, + bool isKnownNonNull) { + // Fast path: if neither schema wants a signature, we're done. + if (!curAuthInfo && !newAuthInfo) + return value; + + // If the value is obviously null, we're done. + auto null = + CGM.getNullPointer(cast<llvm::PointerType>(value->getType()), type); + if (value == null) { + return value; + } + + // If both schemas sign the same way, we're done. + if (curAuthInfo && newAuthInfo && + curAuthInfo.getKey() == newAuthInfo.getKey()) { + auto curD = curAuthInfo.getDiscriminator(); + auto newD = newAuthInfo.getDiscriminator(); + if (curD == newD || + (curD == nullptr && isZeroConstant(newD)) || + (newD == nullptr && isZeroConstant(curD))) + return value; + } + + llvm::BasicBlock *initBB = Builder.GetInsertBlock(); + llvm::BasicBlock *resignBB = nullptr, *contBB = nullptr; + + // Null pointers have to be mapped to null, and the ptrauth_resign + // intrinsic doesn't do that. + if (!isKnownNonNull && !llvm::isKnownNonZero(value, CGM.getDataLayout())) { + contBB = createBasicBlock("resign.cont"); + resignBB = createBasicBlock("resign.nonnull"); + + auto isNonNull = Builder.CreateICmpNE(value, null); + Builder.CreateCondBr(isNonNull, resignBB, contBB); + EmitBlock(resignBB); + } + + // Perform the auth/sign/resign operation. + if (!newAuthInfo) { + value = EmitPointerAuthAuth(curAuthInfo, value); + } else if (!curAuthInfo) { + value = EmitPointerAuthSign(newAuthInfo, value); + } else { + value = EmitPointerAuthResignCall(value, curAuthInfo, newAuthInfo); + } + + // Clean up with a phi if we branched before. + if (contBB) { + EmitBlock(contBB); + auto phi = Builder.CreatePHI(value->getType(), 2); + phi->addIncoming(null, initBB); + phi->addIncoming(value, resignBB); + value = phi; + } + + return value; +} + /// We use an abstract, side-allocated cache for signed function pointers /// because (1) most compiler invocations will not need this cache at all, /// since they don't use signed function pointers, and (2) the @@ -168,6 +373,21 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *pointer, return llvm::ConstantExpr::getBitCast(global, pointer->getType()); } +/// Sign a constant pointer using the given scheme, producing a constant +/// with the same IR type. +llvm::Constant * +CodeGenModule::getConstantSignedPointer(llvm::Constant *pointer, + const PointerAuthSchema &schema, + llvm::Constant *storageAddress, + GlobalDecl schemaDecl, + QualType schemaType) { + llvm::Constant *otherDiscriminator = + getPointerAuthOtherDiscriminator(schema, schemaDecl, schemaType); + + return getConstantSignedPointer(pointer, schema.getKey(), + storageAddress, otherDiscriminator); +} + llvm::Constant * CodeGen::getConstantSignedPointer(CodeGenModule &CGM, llvm::Constant *pointer, unsigned key, @@ -177,6 +397,24 @@ CodeGen::getConstantSignedPointer(CodeGenModule &CGM, otherDiscriminator); } +/// Sign the given pointer and add it to the constant initializer +/// currently being built. +void ConstantAggregateBuilderBase::addSignedPointer( + llvm::Constant *pointer, const PointerAuthSchema &schema, + GlobalDecl calleeDecl, QualType calleeType) { + if (!schema) return add(pointer); + + llvm::Constant *storageAddress = nullptr; + if (schema.isAddressDiscriminated()) { + storageAddress = getAddrOfCurrentPosition(pointer->getType()); + } + + llvm::Constant *signedPointer = + Builder.CGM.getConstantSignedPointer(pointer, schema, storageAddress, + calleeDecl, calleeType); + add(signedPointer); +} + void ConstantAggregateBuilderBase::addSignedPointer( llvm::Constant *pointer, unsigned key, bool useAddressDiscrimination, llvm::Constant *otherDiscriminator) { @@ -193,4 +431,42 @@ void ConstantAggregateBuilderBase::addSignedPointer( void CodeGenModule::destroyConstantSignedPointerCaches() { destroyCache<ByConstantCacheTy>(ConstantSignedPointersByConstant); + destroyCache<ByDeclCacheTy>(ConstantSignedPointersByDecl); +} + +llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *pointer, + QualType functionType, + const FunctionDecl *FD) { + if (auto pointerAuth = getFunctionPointerAuthInfo(functionType)) { + // Check a cache that, for now, just has entries for functions signed + // with the standard function-pointer scheme. + // Cache function pointers based on their decl. Anything without a decl is + // going to be a one-off that doesn't need to be cached anyway. + llvm::Constant **entry = nullptr; + if (FD) { + auto &cache = + getOrCreateCache<ByDeclCacheTy>(ConstantSignedPointersByDecl); + entry = &cache[FD->getCanonicalDecl()]; + if (*entry) + return llvm::ConstantExpr::getBitCast(*entry, pointer->getType()); + } + + // If the cache misses, build a new constant. It's not a *problem* to + // have more than one of these for a particular function, but it's nice + // to avoid it. + pointer = getConstantSignedPointer( + pointer, pointerAuth.getKey(), nullptr, + cast_or_null<llvm::Constant>(pointerAuth.getDiscriminator())); + + // Store the result back into the cache, if any. + if (entry) + *entry = pointer; + } + + return pointer; +} + +llvm::Constant *CodeGenModule::getFunctionPointer(const FunctionDecl *FD, + llvm::Type *Ty) { + return getFunctionPointer(getRawFunctionPointer(FD, Ty), FD->getType(), FD); } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 3f9a52ab7638a..67ffbd8fad4c1 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2390,3 +2390,93 @@ llvm::DebugLoc CodeGenFunction::SourceLocToDebugLoc(SourceLocation Location) { return llvm::DebugLoc(); } + +void CodeGenFunction::EmitPointerAuthOperandBundle( + const CGPointerAuthInfo &pointerAuth, + SmallVectorImpl<llvm::OperandBundleDef> &bundles) { + if (!pointerAuth.isSigned()) return; + + auto key = Builder.getInt32(pointerAuth.getKey()); + + llvm::Value *discriminator = pointerAuth.getDiscriminator(); + if (!discriminator) { + discriminator = Builder.getSize(0); + } + + llvm::Value *args[] = { key, discriminator }; + bundles.emplace_back("ptrauth", args); +} + +static llvm::Value *EmitPointerAuthCommon(CodeGenFunction &CGF, + const CGPointerAuthInfo &pointerAuth, + llvm::Value *pointer, + unsigned intrinsicID) { + if (!pointerAuth) return pointer; + + auto key = CGF.Builder.getInt32(pointerAuth.getKey()); + + llvm::Value *discriminator = pointerAuth.getDiscriminator(); + if (!discriminator) { + discriminator = CGF.Builder.getSize(0); + } + + // Convert the pointer to intptr_t before signing it. + auto origType = pointer->getType(); + pointer = CGF.Builder.CreatePtrToInt(pointer, CGF.IntPtrTy); + + // call i64 @llvm.ptrauth.sign.i64(i64 %pointer, i32 %key, i64 %discriminator) + auto intrinsic = + CGF.CGM.getIntrinsic(intrinsicID, { CGF.IntPtrTy }); + pointer = CGF.EmitRuntimeCall(intrinsic, { pointer, key, discriminator }); + + // Convert back to the original type. + pointer = CGF.Builder.CreateIntToPtr(pointer, origType); + return pointer; +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthSign(const CGPointerAuthInfo &pointerAuth, + llvm::Value *pointer) { + return EmitPointerAuthCommon(*this, pointerAuth, pointer, + llvm::Intrinsic::ptrauth_sign); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthAuth(const CGPointerAuthInfo &pointerAuth, + llvm::Value *pointer) { + return EmitPointerAuthCommon(*this, pointerAuth, pointer, + llvm::Intrinsic::ptrauth_auth); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthResignCall(llvm::Value *value, + const CGPointerAuthInfo &curAuth, + const CGPointerAuthInfo &newAuth) { + assert(curAuth && newAuth); + + // Convert the pointer to intptr_t before signing it. + auto origType = value->getType(); + value = Builder.CreatePtrToInt(value, IntPtrTy); + + auto curKey = Builder.getInt32(curAuth.getKey()); + auto newKey = Builder.getInt32(newAuth.getKey()); + + llvm::Value *curDiscriminator = curAuth.getDiscriminator(); + if (!curDiscriminator) curDiscriminator = Builder.getSize(0); + + llvm::Value *newDiscriminator = newAuth.getDiscriminator(); + if (!newDiscriminator) newDiscriminator = Builder.getSize(0); + + // call i64 @llvm.ptrauth.resign.i64(i64 %pointer, + // i32 %curKey, i64 %curDiscriminator, + // i32 %newKey, i64 %newDiscriminator) + auto intrinsic = + CGM.getIntrinsic(llvm::Intrinsic::ptrauth_resign, { IntPtrTy }); + value = EmitRuntimeCall(intrinsic, + { value, curKey, curDiscriminator, + newKey, newDiscriminator }); + + // Convert back to the original type. + value = Builder.CreateIntToPtr(value, origType); + return value; +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 2c20ba4e6b65b..bab5b04f4287d 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3648,6 +3648,31 @@ class CodeGenFunction : public CodeGenTypeCache { CXXDtorType Type, const CXXRecordDecl *RD); + /// Create the discriminator from the storage address and the entity hash. + llvm::Value *EmitPointerAuthBlendDiscriminator(llvm::Value *storageAddress, + llvm::Value *discriminator); + + CGPointerAuthInfo EmitPointerAuthInfo(const PointerAuthSchema &schema, + llvm::Value *storageAddress, + GlobalDecl calleeDecl, + QualType calleeType); + llvm::Value *EmitPointerAuthSign(const CGPointerAuthInfo &info, + llvm::Value *pointer); + llvm::Value *EmitPointerAuthAuth(const CGPointerAuthInfo &info, + llvm::Value *pointer); + llvm::Value *EmitPointerAuthResign(llvm::Value *pointer, + QualType pointerType, + const CGPointerAuthInfo &curAuthInfo, + const CGPointerAuthInfo &newAuthInfo, + bool isKnownNonNull); + llvm::Value *EmitPointerAuthResignCall(llvm::Value *pointer, + const CGPointerAuthInfo &curInfo, + const CGPointerAuthInfo &newInfo); + void EmitPointerAuthOperandBundle(const CGPointerAuthInfo &info, + SmallVectorImpl<llvm::OperandBundleDef> &bundles); + + CGPointerAuthInfo EmitPointerAuthInfo(PointerAuthQualifier qualifier, + Address storageAddress); // Return the copy constructor name with the prefix "__copy_constructor_" // removed. static std::string getNonTrivialCopyConstructorStr(QualType QT, diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 1274448aeda3c..1b084fc41c147 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -66,6 +66,7 @@ class Stmt; class InitListExpr; class StringLiteral; class NamedDecl; +class PointerAuthSchema; class ValueDecl; class VarDecl; class LangOptions; @@ -406,6 +407,7 @@ class CodeGenModule : public CodeGenTypeCache { std::vector<llvm::Constant*> Annotations; /// Signed constant pointers. + void *ConstantSignedPointersByDecl = nullptr; void *ConstantSignedPointersByConstant = nullptr; /// Map used to get unique annotation strings. @@ -550,6 +552,8 @@ class CodeGenModule : public CodeGenTypeCache { MetadataTypeMap VirtualMetadataIdMap; MetadataTypeMap GeneralizedMetadataIdMap; + llvm::DenseMap<GlobalDecl, uint16_t> PtrAuthDiscriminatorHashes; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, @@ -848,11 +852,44 @@ class CodeGenModule : public CodeGenTypeCache { ForDefinition_t IsForDefinition = NotForDefinition); + /// Return a function pointer for a reference to the given function. + /// This correctly handles weak references, but does not apply a + /// pointer signature. + llvm::Constant *getRawFunctionPointer(const FunctionDecl *FD, + llvm::Type *Ty = nullptr); + + /// Return the ABI-correct function pointer value for a reference + /// to the given function. This will apply a pointer signature if + /// necessary, caching the result for the given function. + llvm::Constant *getFunctionPointer(const FunctionDecl *FD, + llvm::Type *Ty = nullptr); + + /// Return the ABI-correct function pointer value for a reference + /// to the given function. This will apply a pointer signature if + /// necessary, but will only cache the result if \p FD is passed. + llvm::Constant *getFunctionPointer(llvm::Constant *pointer, + QualType functionType, + const FunctionDecl *FD = nullptr); + + CGPointerAuthInfo getFunctionPointerAuthInfo(QualType functionType); + + CGPointerAuthInfo getMemberFunctionPointerAuthInfo(QualType functionType); + + llvm::Constant *getConstantSignedPointer(llvm::Constant *pointer, + const PointerAuthSchema &schema, + llvm::Constant *storageAddress, + GlobalDecl schemaDecl, + QualType schemaType); llvm::Constant *getConstantSignedPointer(llvm::Constant *pointer, unsigned key, llvm::Constant *storageAddress, llvm::Constant *extraDiscrim); + llvm::Constant * + getPointerAuthOtherDiscriminator(const PointerAuthSchema &schema, + GlobalDecl schemaDecl, QualType schemaType); + uint16_t getPointerAuthDeclDiscriminator(GlobalDecl GD); + /// Get the address of the RTTI descriptor for the given type. llvm::Constant *GetAddrOfRTTIDescriptor(QualType Ty, bool ForEH = false); From 181e26190566fc629c5289b3d3345132d93da18b Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 01:24:04 -0400 Subject: [PATCH 530/582] Implement pointer authentication for function pointers. --- .../include/clang/Basic/PointerAuthOptions.h | 3 + clang/lib/CodeGen/CGExpr.cpp | 96 +++++++++++++++++- clang/lib/CodeGen/CGExprConstant.cpp | 5 +- clang/lib/Frontend/CompilerInvocation.cpp | 9 ++ clang/test/CodeGen/ptrauth-weak_import.c | 10 ++ clang/test/CodeGen/ptrauth.c | 97 +++++++++++++++++++ .../ptrauth-property-object-reference.mm | 59 +++++++++++ 7 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-weak_import.c create mode 100644 clang/test/CodeGen/ptrauth.c create mode 100644 clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 58650c8b64726..860d7e9c8a91f 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -141,6 +141,9 @@ struct PointerAuthOptions { /// Do authentication failures cause a trap? bool AuthTraps = false; + + /// The ABI for C function pointers. + PointerAuthSchema FunctionPointers; }; } // end namespace clang diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 49c24fbba104b..2a3cfffcd63d5 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4547,10 +4547,70 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, const FunctionDecl *FD) { return CGCallee::forBuiltin(builtinID, FD); } - llvm::Constant *calleePtr = EmitFunctionDeclPointer(CGF.CGM, FD); + llvm::Constant *calleePtr = CGF.CGM.getRawFunctionPointer(FD); return CGCallee::forDirect(calleePtr, GlobalDecl(FD)); } +static unsigned getPointerAuthKeyValue(const ASTContext &Context, + const Expr *key) { + Expr::EvalResult result; + bool success = key->EvaluateAsInt(result, Context); + assert(success && "pointer auth key wasn't a constant?"); (void) success; + return result.Val.getInt().getZExtValue(); +} + +static bool isFunctionPointerAuth(CodeGenModule &CGM, const Expr *key, + const Expr *discriminator) { + // Verify that the ABI uses function-pointer signing at all. + auto &authSchema = CGM.getCodeGenOpts().PointerAuth.FunctionPointers; + if (!authSchema.isEnabled()) + return false; + + // Verify that the key matches the ABI's key. + if (authSchema.getKey() != getPointerAuthKeyValue(CGM.getContext(), key)) + return false; + + // If the ABI uses weird discrimination for function pointers, just give up. + assert(!authSchema.isAddressDiscriminated()); + if (authSchema.getOtherDiscrimination() + != PointerAuthSchema::Discrimination::None) { + return false; + } + + if (discriminator->getType()->isPointerType()) { + return discriminator->isNullPointerConstant(CGM.getContext(), + Expr::NPC_NeverValueDependent); + } else { + assert(discriminator->getType()->isIntegerType()); + Expr::EvalResult result; + return (discriminator->EvaluateAsInt(result, CGM.getContext()) && + result.Val.getInt() == 0); + } +} + +/// Given an expression for a function pointer that's been signed with +/// a variant scheme, and given a constant expression for the key value +/// and an expression for the discriminator, produce a callee for the +/// function pointer using that scheme. +static CGCallee EmitSignedFunctionPointerCallee(CodeGenFunction &CGF, + const Expr *functionPointerExpr, + const Expr *keyExpr, + const Expr *discriminatorExpr) { + llvm::Value *calleePtr = CGF.EmitScalarExpr(functionPointerExpr); + auto key = getPointerAuthKeyValue(CGF.getContext(), keyExpr); + auto discriminator = CGF.EmitScalarExpr(discriminatorExpr); + + if (discriminator->getType()->isPointerTy()) + discriminator = CGF.Builder.CreatePtrToInt(discriminator, CGF.IntPtrTy); + + auto functionType = + functionPointerExpr->getType()->castAs<PointerType>()->getPointeeType(); + CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>()); + CGPointerAuthInfo pointerAuth(key, discriminator); + CGCallee callee(calleeInfo, calleePtr, pointerAuth); + return callee; +} + CGCallee CodeGenFunction::EmitCallee(const Expr *E) { E = E->IgnoreParens(); @@ -4579,6 +4639,36 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { // Treat pseudo-destructor calls differently. } else if (auto PDE = dyn_cast<CXXPseudoDestructorExpr>(E)) { return CGCallee::forPseudoDestructor(PDE); + + // Peephole specific builtin calls. + } else if (auto CE = dyn_cast<CallExpr>(E)) { + if (unsigned builtin = CE->getBuiltinCallee()) { + // If the callee is a __builtin_ptrauth_sign_unauthenticated to the + // ABI function-pointer signing schema, perform an unauthenticated call. + if (builtin == Builtin::BI__builtin_ptrauth_sign_unauthenticated && + isFunctionPointerAuth(CGM, CE->getArg(1), CE->getArg(2))) { + CGCallee callee = EmitCallee(CE->getArg(0)); + if (callee.isOrdinary()) + callee.setPointerAuthInfo(CGPointerAuthInfo()); + return callee; + } + + // If the callee is a __builtin_ptrauth_auth_and_resign to the + // ABI function-pointer signing schema, avoid the intermediate resign. + if (builtin == Builtin::BI__builtin_ptrauth_auth_and_resign && + isFunctionPointerAuth(CGM, CE->getArg(3), CE->getArg(4))) { + return EmitSignedFunctionPointerCallee(*this, CE->getArg(0), + CE->getArg(1), CE->getArg(2)); + + // If the callee is a __builtin_ptrauth_auth when ABI function pointer + // signing is disabled, we need to promise to use the unattackable + // OperandBundle code pattern. + } else if (builtin == Builtin::BI__builtin_ptrauth_auth && + !CGM.getCodeGenOpts().PointerAuth.FunctionPointers.isEnabled()) { + return EmitSignedFunctionPointerCallee(*this, CE->getArg(0), + CE->getArg(1), CE->getArg(2)); + } + } } // Otherwise, we have an indirect reference. @@ -4592,14 +4682,14 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { calleePtr = EmitLValue(E).getPointer(); } assert(functionType->isFunctionType()); - GlobalDecl GD; if (const auto *VD = dyn_cast_or_null<VarDecl>(E->getReferencedDeclOfCallee())) GD = GlobalDecl(VD); CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), GD); - CGCallee callee(calleeInfo, calleePtr); + CGPointerAuthInfo pointerAuth = CGM.getFunctionPointerAuthInfo(functionType); + CGCallee callee(calleeInfo, calleePtr, pointerAuth); return callee; } diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 597d1286a0c86..819e697e52647 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1902,8 +1902,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { if (D->hasAttr<WeakRefAttr>()) return CGM.GetWeakRefReference(D).getPointer(); - if (auto FD = dyn_cast<FunctionDecl>(D)) - return CGM.GetAddrOfFunction(FD); + if (auto FD = dyn_cast<FunctionDecl>(D)) { + return CGM.getFunctionPointer(FD); + } if (auto VD = dyn_cast<VarDecl>(D)) { // We can never refer to a variable with local storage. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index e523ebea919cd..d711df3d96a97 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -674,6 +674,15 @@ static bool parsePointerAuthOptions(PointerAuthOptions &Opts, return true; if (Triple.getArch() == llvm::Triple::aarch64) { + if (LangOpts.PointerAuthCalls) { + using Key = PointerAuthSchema::ARM8_3Key; + using Discrimination = PointerAuthSchema::Discrimination; + // If you change anything here, be sure to update <ptrauth.h>. + Opts.FunctionPointers = + PointerAuthSchema(Key::ASIA, false, Discrimination::None); + Opts.ThunkCXXVirtualMemberPointers = false; + } + Opts.ReturnAddresses = LangOpts.PointerAuthReturns; Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos; Opts.AuthTraps = LangOpts.PointerAuthAuthTraps; diff --git a/clang/test/CodeGen/ptrauth-weak_import.c b/clang/test/CodeGen/ptrauth-weak_import.c new file mode 100644 index 0000000000000..71717b78c4cb8 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-weak_import.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +extern void foo() __attribute__((weak_import)); + +// CHECK-LABEL: define void @bar() +// CHECK: br i1 icmp ne (void (...)* bitcast ({ i8*, i32, i64, i64 }* @foo.ptrauth to void (...)*), void (...)* null), label +void bar() { + if (foo) + foo(); +} diff --git a/clang/test/CodeGen/ptrauth.c b/clang/test/CodeGen/ptrauth.c new file mode 100644 index 0000000000000..097e876647fd9 --- /dev/null +++ b/clang/test/CodeGen/ptrauth.c @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +#define FNPTRKEY 0 + +void (*fnptr)(void); +long discriminator; + +extern void external_function(void); +// CHECK: [[EXTERNAL_FUNCTION:@.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @fptr1 = global void ()* bitcast ({ i8*, i32, i64, i64 }* [[EXTERNAL_FUNCTION]] to void ()*) +void (*fptr1)(void) = external_function; +// CHECK: @fptr2 = global void ()* bitcast ({ i8*, i32, i64, i64 }* [[EXTERNAL_FUNCTION]] to void ()*) +void (*fptr2)(void) = &external_function; + +// CHECK: [[SIGNED:@.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 2, i64 0, i64 26 }, section "llvm.ptrauth", align 8 +// CHECK: @fptr3 = global void ()* bitcast ({ i8*, i32, i64, i64 }* [[SIGNED]] to void ()*) +void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); + +// CHECK: @fptr4 = global void ()* bitcast ({ i8*, i32, i64, i64 }* [[SIGNED:@.*]] to void ()*) +// CHECK: [[SIGNED]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 2, i64 ptrtoint (void ()** @fptr4 to i64), i64 26 }, section "llvm.ptrauth", align 8 +void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); + +// CHECK-LABEL: define void @test_call() +void test_call() { + // CHECK: [[T0:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 0) ] + fnptr(); +} + +// CHECK-LABEL: define void @test_direct_call() +void test_direct_call() { + // CHECK: call void @test_call(){{$}} + test_call(); +} + +void abort(); +// CHECK-LABEL: define void @test_direct_builtin_call() +void test_direct_builtin_call() { + // CHECK: call void @abort() {{#[0-9]+$}} + abort(); +} + +// CHECK-LABEL: define void @test_sign_unauthenticated_peephole() +void test_sign_unauthenticated_peephole() { + // CHECK: [[T0:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: call void [[T0]](){{$}} + // CHECK-NEXT: ret void + __builtin_ptrauth_sign_unauthenticated(fnptr, FNPTRKEY, 0)(); +} + +// This peephole doesn't kick in because it's incorrect when ABI pointer +// authentication is enabled. +// CHECK-LABEL: define void @test_auth_peephole() +void test_auth_peephole() { + // CHECK: [[T0:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, i64* @discriminator, + // CHECK-NEXT: [[T2:%.*]] = ptrtoint void ()* [[T0]] to i64 + // CHECK-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T2]], i32 0, i64 [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = inttoptr i64 [[T3]] to void ()* + // CHECK-NEXT: call void [[T4]]() [ "ptrauth"(i32 0, i64 0) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth(fnptr, 0, discriminator)(); +} + +// CHECK-LABEL: define void @test_auth_and_resign_peephole() +void test_auth_and_resign_peephole() { + // CHECK: [[T0:%.*]] = load void ()*, void ()** @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, i64* @discriminator, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 2, i64 [[T1]]) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth_and_resign(fnptr, 2, discriminator, FNPTRKEY, 0)(); +} + +// CHECK-LABEL: define void ()* @test_function_pointer() +// CHECK: [[EXTERNAL_FUNCTION]] +void (*test_function_pointer())(void) { + return external_function; +} + +// rdar://34562484 - Handle IR types changing in the caching mechanism. +struct InitiallyIncomplete; +extern struct InitiallyIncomplete returns_initially_incomplete(void); +// CHECK-LABEL: define void @use_while_incomplete() +void use_while_incomplete() { + // CHECK: [[VAR:%.*]] = alloca {}*, + // CHECK-NEXT: store {}* bitcast ({ i8*, i32, i64, i64 }* @returns_initially_incomplete.ptrauth to {}*), {}** [[VAR]], + // CHECK-NEXT: ret void + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} +struct InitiallyIncomplete { int x; }; +// CHECK-LABEL: define void @use_while_complete() +void use_while_complete() { + // CHECK: [[VAR:%.*]] = alloca i64 ()*, + // CHECK-NEXT: store i64 ()* bitcast ({ i8*, i32, i64, i64 }* @returns_initially_incomplete.ptrauth to i64 ()*), i64 ()** [[VAR]], + // CHECK-NEXT: ret void + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} diff --git a/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm new file mode 100644 index 0000000000000..55707202d01c7 --- /dev/null +++ b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 %s -triple arm64-apple-ios11.0 -fobjc-runtime=ios-11.0 -fptrauth-calls -emit-llvm -o - | FileCheck %s + +extern int DEFAULT(); + +struct TCPPObject +{ + TCPPObject(); + ~TCPPObject(); + TCPPObject(const TCPPObject& inObj, int i = DEFAULT()); + TCPPObject& operator=(const TCPPObject& inObj); + int filler[64]; +}; + + +@interface MyDocument +{ +@private + TCPPObject _cppObject; + TCPPObject _cppObject1; +} +@property (assign, readwrite, atomic) const TCPPObject MyProperty; +@property (assign, readwrite, atomic) const TCPPObject MyProperty1; +@end + +@implementation MyDocument + @synthesize MyProperty = _cppObject; + @synthesize MyProperty1 = _cppObject1; +@end + +// CHECK-LABEL: @__copy_helper_atomic_property_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%struct.TCPPObject*, %struct.TCPPObject*)* @__copy_helper_atomic_property_ to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK-LABEL: @__assign_helper_atomic_property_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%struct.TCPPObject*, %struct.TCPPObject*)* @__assign_helper_atomic_property_ to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK-LABEL: define internal void @__copy_helper_atomic_property_(%struct.TCPPObject* %0, %struct.TCPPObject* %1) # +// CHECK: [[TWO:%.*]] = load %struct.TCPPObject*, %struct.TCPPObject** [[ADDR:%.*]], align 8 +// CHECK: [[THREE:%.*]] = load %struct.TCPPObject*, %struct.TCPPObject** [[ADDR1:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call i32 @_Z7DEFAULTv() +// CHECK: call %struct.TCPPObject* @_ZN10TCPPObjectC1ERKS_i(%struct.TCPPObject* [[TWO]], %struct.TCPPObject* dereferenceable({{[0-9]+}}) [[THREE]], i32 [[CALL]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument MyProperty]"( +// CHECK: [[ONE:%.*]] = bitcast i8* [[ADDPTR:%.*]] to %struct.TCPPObject* +// CHECK: [[TWO:%.*]] = bitcast %struct.TCPPObject* [[ONE]] to i8* +// CHECK: [[THREE:%.*]] = bitcast %struct.TCPPObject* [[AGGRESULT:%.*]] to i8* +// CHECK: call void @objc_copyCppObjectAtomic(i8* [[THREE]], i8* [[TWO]], i8* bitcast ({ i8*, i32, i64, i64 }* @__copy_helper_atomic_property_.ptrauth to i8*)) +// CHECK: ret void + +// CHECK-LABEL: define internal void @__assign_helper_atomic_property_(%struct.TCPPObject* %0, %struct.TCPPObject* %1) # +// CHECK: [[THREE:%.*]] = load %struct.TCPPObject*, %struct.TCPPObject** [[ADDR1:%.*]], align 8 +// CHECK: [[TWO:%.*]] = load %struct.TCPPObject*, %struct.TCPPObject** [[ADDR:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call dereferenceable({{[0-9]+}}) %struct.TCPPObject* @_ZN10TCPPObjectaSERKS_(%struct.TCPPObject* [[TWO]], %struct.TCPPObject* dereferenceable({{[0-9]+}}) [[THREE]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument setMyProperty:]"( +// CHECK: [[ONE:%.*]] = bitcast i8* [[ADDRPTR:%.*]] to %struct.TCPPObject* +// CHECK: [[TWO:%.*]] = bitcast %struct.TCPPObject* [[ONE]] to i8* +// CHECK: [[THREE:%.*]] = bitcast %struct.TCPPObject* [[MYPROPERTY:%.*]] to i8* +// CHECK: call void @objc_copyCppObjectAtomic(i8* [[TWO]], i8* [[THREE]], i8* bitcast ({ i8*, i32, i64, i64 }* @__assign_helper_atomic_property_.ptrauth to i8*)) +// CHECK: ret void From fad0aa401b88b979d2242ecf4e4209add0e4f8e5 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 01:35:40 -0400 Subject: [PATCH 531/582] Pointer authentication for blocks. Patch by Akira Hatanaka and me. --- .../include/clang/Basic/PointerAuthOptions.h | 9 ++ clang/lib/CodeGen/CGBlocks.cpp | 85 ++++++++++++++----- clang/lib/CodeGen/CGBlocks.h | 2 +- clang/lib/CodeGen/CGObjCMac.cpp | 2 +- clang/lib/Frontend/CompilerInvocation.cpp | 6 ++ clang/test/CodeGen/ptrauth-blocks.c | 39 +++++++++ clang/test/CodeGenObjC/ptrauth-blocks.m | 64 ++++++++++++++ 7 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-blocks.c create mode 100644 clang/test/CodeGenObjC/ptrauth-blocks.m diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 860d7e9c8a91f..65d59a03074df 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -144,6 +144,15 @@ struct PointerAuthOptions { /// The ABI for C function pointers. PointerAuthSchema FunctionPointers; + + /// The ABI for block invocation function pointers. + PointerAuthSchema BlockInvocationFunctionPointers; + + /// The ABI for block object copy/destroy function pointers. + PointerAuthSchema BlockHelperFunctionPointers; + + /// The ABI for __block variable copy/destroy function pointers. + PointerAuthSchema BlockByrefHelperFunctionPointers; }; } // end namespace clang diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index f90d9439af257..7e957f035ab65 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -123,7 +123,7 @@ static std::string getBlockDescriptorName(const CGBlockInfo &BlockInfo, std::string Name = "__block_descriptor_"; Name += llvm::to_string(BlockInfo.BlockSize.getQuantity()) + "_"; - if (BlockInfo.needsCopyDisposeHelpers()) { + if (BlockInfo.needsCopyDisposeHelpers(CGM.getContext())) { if (CGM.getLangOpts().Exceptions) Name += "e"; if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions) @@ -222,14 +222,17 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, // Optional copy/dispose helpers. bool hasInternalHelper = false; - if (blockInfo.needsCopyDisposeHelpers()) { + if (blockInfo.needsCopyDisposeHelpers(CGM.getContext())) { + auto &schema = + CGM.getCodeGenOpts().PointerAuth.BlockHelperFunctionPointers; + // copy_func_helper_decl llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo); - elements.add(copyHelper); + elements.addSignedPointer(copyHelper, schema, GlobalDecl(), QualType()); // destroy_func_decl llvm::Constant *disposeHelper = buildDisposeHelper(CGM, blockInfo); - elements.add(disposeHelper); + elements.addSignedPointer(disposeHelper, schema, GlobalDecl(), QualType()); if (cast<llvm::Function>(copyHelper->getOperand(0))->hasInternalLinkage() || cast<llvm::Function>(disposeHelper->getOperand(0)) @@ -966,7 +969,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { flags = BLOCK_HAS_SIGNATURE; if (blockInfo.HasCapturedVariableLayout) flags |= BLOCK_HAS_EXTENDED_LAYOUT; - if (blockInfo.needsCopyDisposeHelpers()) + if (blockInfo.needsCopyDisposeHelpers(CGM.getContext())) flags |= BLOCK_HAS_COPY_DISPOSE; if (blockInfo.HasCXXObject) flags |= BLOCK_HAS_CXX_OBJ; @@ -1009,11 +1012,26 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { llvm::ConstantInt::get(IntTy, blockInfo.BlockAlign.getQuantity()), getIntSize(), "block.align"); } - addHeaderField(blockFn, GenVoidPtrSize, "block.invoke"); - if (!IsOpenCL) + + if (!IsOpenCL) { + llvm::Value *blockFnPtr = llvm::ConstantExpr::getBitCast(InvokeFn, VoidPtrTy); + auto blockFnPtrAddr = projectField(index, "block.invoke"); + if (auto &schema = + CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers) { + QualType type = blockInfo.getBlockExpr()->getType() + ->castAs<BlockPointerType>()->getPointeeType(); + auto authInfo = EmitPointerAuthInfo(schema, blockFnPtrAddr.getPointer(), + GlobalDecl(), type); + blockFnPtr = EmitPointerAuthSign(authInfo, blockFnPtr); + } + Builder.CreateStore(blockFnPtr, blockFnPtrAddr); + offset += getPointerSize(); + index++; + addHeaderField(descriptor, getPointerSize(), "block.descriptor"); - else if (auto *Helper = - CGM.getTargetCodeGenInfo().getTargetOpenCLBlockHelper()) { + } else if (auto *Helper = + CGM.getTargetCodeGenInfo().getTargetOpenCLBlockHelper()) { + addHeaderField(blockFn, GenVoidPtrSize, "block.invoke"); for (auto I : Helper->getCustomFieldValues(*this, blockInfo)) { addHeaderField( I.first, @@ -1021,7 +1039,8 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { CGM.getDataLayout().getTypeAllocSize(I.first->getType())), I.second); } - } + } else + addHeaderField(blockFn, GenVoidPtrSize, "block.invoke"); } // Finally, capture all the values into the block. @@ -1261,6 +1280,8 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, ASTContext &Ctx = getContext(); CallArgList Args; + llvm::Value *FuncPtr = nullptr; + if (getLangOpts().OpenCL) { // For OpenCL, BlockPtr is already casted to generic block literal. @@ -1278,7 +1299,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, if (!isa<ParmVarDecl>(E->getCalleeDecl())) Func = CGM.getOpenCLRuntime().getInvokeFunction(E->getCallee()); else { - llvm::Value *FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 2); + FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 2); Func = Builder.CreateAlignedLoad(FuncPtr, getPointerAlign()); } } else { @@ -1286,7 +1307,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, BlockPtr = Builder.CreatePointerCast( BlockPtr, llvm::PointerType::get(GenBlockTy, 0), "block.literal"); // Get pointer to the block invoke function - llvm::Value *FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 3); + FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 3); // First argument is a block literal casted to a void pointer BlockPtr = Builder.CreatePointerCast(BlockPtr, VoidPtrTy); @@ -1309,7 +1330,14 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, Func = Builder.CreatePointerCast(Func, BlockFTyPtr); // Prepare the callee. - CGCallee Callee(CGCalleeInfo(), Func); + CGPointerAuthInfo PointerAuth; + if (auto &AuthSchema = + CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers) { + assert(FuncPtr != nullptr && "Missing function pointer for AuthInfo"); + PointerAuth = EmitPointerAuthInfo(AuthSchema, FuncPtr, + GlobalDecl(), FnType); + } + CGCallee Callee(CGCalleeInfo(), Func, PointerAuth); // And call the block. return EmitCall(FnInfo, Callee, ReturnValue, Args); @@ -1411,14 +1439,25 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, // Reserved fields.addInt(CGM.IntTy, 0); + + // Function + if (auto &schema = + CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers) { + QualType fnType = blockInfo.getBlockExpr() + ->getType() + ->castAs<BlockPointerType>() + ->getPointeeType(); + fields.addSignedPointer(blockFn, schema, GlobalDecl(), fnType); + } else { + fields.add(blockFn); + } } else { fields.addInt(CGM.IntTy, blockInfo.BlockSize.getQuantity()); fields.addInt(CGM.IntTy, blockInfo.BlockAlign.getQuantity()); + // Function + fields.add(blockFn); } - // Function - fields.add(blockFn); - if (!IsOpenCL) { // Descriptor fields.add(buildBlockDescriptor(CGM, blockInfo)); @@ -2826,8 +2865,16 @@ void CodeGenFunction::emitByrefStructureInit(const AutoVarEmission &emission) { unsigned nextHeaderIndex = 0; CharUnits nextHeaderOffset; auto storeHeaderField = [&](llvm::Value *value, CharUnits fieldSize, - const Twine &name) { + const Twine &name, bool isFunction = false) { auto fieldAddr = Builder.CreateStructGEP(addr, nextHeaderIndex, name); + if (isFunction) { + if (auto &schema = CGM.getCodeGenOpts().PointerAuth + .BlockByrefHelperFunctionPointers) { + auto pointerAuth = EmitPointerAuthInfo(schema, fieldAddr.getPointer(), + GlobalDecl(), QualType()); + value = EmitPointerAuthSign(pointerAuth, value); + } + } Builder.CreateStore(value, fieldAddr); nextHeaderIndex++; @@ -2910,9 +2957,9 @@ void CodeGenFunction::emitByrefStructureInit(const AutoVarEmission &emission) { if (helpers) { storeHeaderField(helpers->CopyHelper, getPointerSize(), - "byref.copyHelper"); + "byref.copyHelper", /*function*/ true); storeHeaderField(helpers->DisposeHelper, getPointerSize(), - "byref.disposeHelper"); + "byref.disposeHelper", /*function*/ true); } if (ByRefHasLifetime && HasByrefExtendedLayout) { diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h index c4bfde6661542..ade5126027a48 100644 --- a/clang/lib/CodeGen/CGBlocks.h +++ b/clang/lib/CodeGen/CGBlocks.h @@ -287,7 +287,7 @@ class CGBlockInfo { CGBlockInfo(const BlockDecl *blockDecl, StringRef Name); // Indicates whether the block needs a custom copy or dispose function. - bool needsCopyDisposeHelpers() const { + bool needsCopyDisposeHelpers(const ASTContext &Ctx) const { return NeedsCopyDispose && !Block->doesNotEscape(); } }; diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 381a22be75b34..46882bbb339e0 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2961,7 +2961,7 @@ std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM, const CGBlockInfo &blockInfo) { fillRunSkipBlockVars(CGM, blockInfo); return getBlockLayoutInfoString(RunSkipBlockVars, - blockInfo.needsCopyDisposeHelpers()); + blockInfo.needsCopyDisposeHelpers(CGM.getContext())); } llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index d711df3d96a97..79fda47c96b39 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -680,6 +680,12 @@ static bool parsePointerAuthOptions(PointerAuthOptions &Opts, // If you change anything here, be sure to update <ptrauth.h>. Opts.FunctionPointers = PointerAuthSchema(Key::ASIA, false, Discrimination::None); + Opts.BlockInvocationFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); + Opts.BlockHelperFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); + Opts.BlockByrefHelperFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); Opts.ThunkCXXVirtualMemberPointers = false; } diff --git a/clang/test/CodeGen/ptrauth-blocks.c b/clang/test/CodeGen/ptrauth-blocks.c new file mode 100644 index 0000000000000..fc01aa3dbcc53 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-blocks.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s -o - | FileCheck %s + +void (^blockptr)(void); + +// CHECK: [[INVOCATION_1:@.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (i8*)* {{@.*}} to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i8**, i32, i32, i8*, %struct.__block_descriptor* }, { i8**, i32, i32, i8*, %struct.__block_descriptor* }* [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { i8**, i32, i32, i8*, %struct.__block_descriptor* } { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast ({ i8*, i32, i64, i64 }* [[INVOCATION_1]] to i8*), +void (^globalblock)(void) = ^{}; + +// CHECK-LABEL: define void @test_block_call() +void test_block_call() { + // CHECK: [[T0:%.*]] = load void ()*, void ()** @blockptr, + // CHECK-NEXT: [[BLOCK:%.*]] = bitcast void ()* [[T0]] to [[BLOCK_T:%.*]]*{{$}} + // CHECK-NEXT: [[FNADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[BLOCK_OPAQUE:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to i8* + // CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[FNADDR]], + // CHECK-NEXT: [[FNPTR:%.*]] = bitcast i8* [[T0]] to void (i8*)* + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8** [[FNADDR]] to i64 + // CHECK-NEXT: call void [[FNPTR]](i8* [[BLOCK_OPAQUE]]) [ "ptrauth"(i32 0, i64 [[DISC]]) ] + blockptr(); +} + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_literal( +void test_block_literal(int i) { + // CHECK: [[I:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align + // CHECK: [[FNPTRADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint i8** [[FNPTRADDR]] to i64 + // CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i32 (i8*)* {{@.*}} to i64), i32 0, i64 [[DISCRIMINATOR]]) + // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGNED]] to i8* + // CHECK-NEXT: store i8* [[T0]], i8** [[FNPTRADDR]] + use_block(^{return i;}); +} + +struct A { + int value; +}; +struct A *createA(void); diff --git a/clang/test/CodeGenObjC/ptrauth-blocks.m b/clang/test/CodeGenObjC/ptrauth-blocks.m new file mode 100644 index 0000000000000..2e3614b7820c0 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-blocks.m @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s + +void (^blockptr)(void); + +// CHECK: [[INVOCATION_1:@.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (i8*)* {{@.*}} to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i8**, i32, i32, i8*, %struct.__block_descriptor* }, { i8**, i32, i32, i8*, %struct.__block_descriptor* }* [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { i8**, i32, i32, i8*, %struct.__block_descriptor* } { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast ({ i8*, i32, i64, i64 }* [[INVOCATION_1]] to i8*), +void (^globalblock)(void) = ^{}; + +// CHECK: [[COPYDISPOSE_COPY:@.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (i8*, i8*)* {{@.*}} to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i64, i64, i8*, i8*, i8*, i64 }, { i64, i64, i8*, i8*, i8*, i64 }* [[COPYDISPOSE_DESCRIPTOR:@.*]], i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[COPYDISPOSE_DISPOSE:@.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (i8*)* {{@.*}} to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i64, i64, i8*, i8*, i8*, i64 }, { i64, i64, i8*, i8*, i8*, i64 }* [[COPYDISPOSE_DESCRIPTOR]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[COPYDISPOSE_DESCRIPTOR:@.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast ({ i8*, i32, i64, i64 }* [[COPYDISPOSE_COPY]] to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* [[COPYDISPOSE_DISPOSE]] to i8*), + +@interface A +- (int) count; +@end + +// CHECK-LABEL: define void @test_block_call() +void test_block_call() { + // CHECK: [[T0:%.*]] = load void ()*, void ()** @blockptr, + // CHECK-NEXT: [[BLOCK:%.*]] = bitcast void ()* [[T0]] to [[BLOCK_T:%.*]]*{{$}} + // CHECK-NEXT: [[FNADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[BLOCK_OPAQUE:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to i8* + // CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[FNADDR]], + // CHECK-NEXT: [[FNPTR:%.*]] = bitcast i8* [[T0]] to void (i8*)* + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint i8** [[FNADDR]] to i64 + // CHECK-NEXT: call void [[FNPTR]](i8* [[BLOCK_OPAQUE]]) [ "ptrauth"(i32 0, i64 [[DISC]]) ] + blockptr(); +} + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_literal( +void test_block_literal(int i) { + // CHECK: [[I:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align + // CHECK: [[FNPTRADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint i8** [[FNPTRADDR]] to i64 + // CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i32 (i8*)* {{@.*}} to i64), i32 0, i64 [[DISCRIMINATOR]]) + // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGNED]] to i8* + // CHECK-NEXT: store i8* [[T0]], i8** [[FNPTRADDR]] + use_block(^{return i;}); +} + +// CHECK-LABEL: define void @test_copy_destroy +void test_copy_destroy(A *a) { + // CHECK: [[COPYDISPOSE_DESCRIPTOR]] + use_block(^{return [a count];}); +} + +// CHECK-LABEL: define void @test_byref_copy_destroy +void test_byref_copy_destroy(A *a) { + // CHECK: [[COPY_FIELD:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], {{%.*}}* [[BYREF:%.*]], i32 0, i32 4 + // CHECK-NEXT: [[T0:%.*]] = ptrtoint i8** [[COPY_FIELD]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (void (i8*, i8*)* {{@.*}} to i64), i32 0, i64 [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = inttoptr i64 [[T1]] to i8* + // CHECK-NEXT: store i8* [[T2]], i8** [[COPY_FIELD]], align 8 + // CHECK: [[DISPOSE_FIELD:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[BYREF]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = ptrtoint i8** [[DISPOSE_FIELD]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (void (i8*)* {{@.*}} to i64), i32 0, i64 [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = inttoptr i64 [[T1]] to i8* + // CHECK-NEXT: store i8* [[T2]], i8** [[DISPOSE_FIELD]], align 8 + __block A *aweak = a; + use_block(^{return [aweak count];}); +} From ea400995f8dec032681c901a446c7f2e7f31ff31 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 01:39:04 -0400 Subject: [PATCH 532/582] Pointer authentication support for ObjC method lists. --- .../include/clang/Basic/PointerAuthOptions.h | 3 ++ clang/lib/CodeGen/CGObjCMac.cpp | 12 ++++++-- clang/lib/Frontend/CompilerInvocation.cpp | 2 ++ clang/test/CodeGenObjC/ptrauth-method-list.m | 30 +++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 clang/test/CodeGenObjC/ptrauth-method-list.m diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 65d59a03074df..fd5f8a8c630ba 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -153,6 +153,9 @@ struct PointerAuthOptions { /// The ABI for __block variable copy/destroy function pointers. PointerAuthSchema BlockByrefHelperFunctionPointers; + + /// The ABI for Objective-C method lists. + PointerAuthSchema ObjCMethodListFunctionPointers; }; } // end namespace clang diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 46882bbb339e0..b05fb989168ea 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -6639,7 +6639,14 @@ void CGObjCNonFragileABIMac::emitMethodConstant(ConstantArrayBuilder &builder, } else { llvm::Function *fn = GetMethodDefinition(MD); assert(fn && "no definition for method?"); - method.addBitCast(fn, ObjCTypes.Int8PtrTy); + + if (const auto &schema = + CGM.getCodeGenOpts().PointerAuth.ObjCMethodListFunctionPointers) { + auto *bitcast = llvm::ConstantExpr::getBitCast(fn, ObjCTypes.Int8PtrTy); + method.addSignedPointer(bitcast, schema, GlobalDecl(), QualType()); + } else { + method.addBitCast(fn, ObjCTypes.Int8PtrTy); + } } method.finishAndAddTo(builder); @@ -7212,7 +7219,8 @@ CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF, llvm::Value *calleePtr = CGF.Builder.CreateLoad(calleeAddr, "msgSend_fn"); calleePtr = CGF.Builder.CreateBitCast(calleePtr, MSI.MessengerType); - CGCallee callee(CGCalleeInfo(), calleePtr); + CGPointerAuthInfo pointerAuth; // This code path is unsupported. + CGCallee callee(CGCalleeInfo(), calleePtr, pointerAuth); RValue result = CGF.EmitCall(MSI.CallInfo, callee, returnSlot, args); return nullReturn.complete(CGF, returnSlot, result, resultType, formalArgs, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 79fda47c96b39..2c65a2b2f9d56 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -686,6 +686,8 @@ static bool parsePointerAuthOptions(PointerAuthOptions &Opts, PointerAuthSchema(Key::ASIA, true, Discrimination::None); Opts.BlockByrefHelperFunctionPointers = PointerAuthSchema(Key::ASIA, true, Discrimination::None); + Opts.ObjCMethodListFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); Opts.ThunkCXXVirtualMemberPointers = false; } diff --git a/clang/test/CodeGenObjC/ptrauth-method-list.m b/clang/test/CodeGenObjC/ptrauth-method-list.m new file mode 100644 index 0000000000000..8f8f4be626edf --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-method-list.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm -o - %s | FileCheck %s + +// CHECK: @"\01+[C pm1].ptrauth" = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (i8*, i8*)* @"\01+[C pm1]" to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, { i32, i32, [2 x %struct._objc_method] }* @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: @"\01+[C m1].ptrauth" = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (i8*, i8*)* @"\01+[C m1]" to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, { i32, i32, [2 x %struct._objc_method] }* @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_METH_VAR_NAME{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* bitcast ({ i8*, i32, i64, i64 }* @"\01+[C pm1].ptrauth" to i8*) }, %struct._objc_method { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @OBJC_METH_VAR_NAME{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* bitcast ({ i8*, i32, i64, i64 }* @"\01+[C m1].ptrauth" to i8*) }] }, section "__DATA, __objc_const" +// CHECK: "\01-[C pm0].ptrauth" = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%0*, i8*)* @"\01-[C pm0]" to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, { i32, i32, [2 x %struct._objc_method] }* @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: "\01-[C m0].ptrauth" = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%0*, i8*)* @"\01-[C m0]" to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, { i32, i32, [2 x %struct._objc_method] }* @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: @"_OBJC_$_INSTANCE_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @OBJC_METH_VAR_NAME{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* bitcast ({ i8*, i32, i64, i64 }* @"\01-[C pm0].ptrauth" to i8*) }, %struct._objc_method { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @OBJC_METH_VAR_NAME{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @OBJC_METH_VAR_TYPE_, i32 0, i32 0), i8* bitcast ({ i8*, i32, i64, i64 }* @"\01-[C m0].ptrauth" to i8*) }] }, section "__DATA, __objc_const" + +@protocol P +- (void) pm0; ++ (void) pm1; +@end + +@interface C<P> +- (void) m0; ++ (void) m1; +@end + +@implementation C +- (void) pm0 {} ++ (void) pm1 {} +- (void) m0 {} ++ (void) m1 {} +@end + +void test_method_list(C *c) { + [c m0]; + [C m1]; +} From c077762f44ccedab6c9be64737f71150d590e7d4 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Tue, 17 Sep 2019 22:33:21 -0400 Subject: [PATCH 533/582] Record the original declaration for which a v-table slot was created. NFC. Patch by Akira Hatanaka. --- clang/include/clang/AST/GlobalDecl.h | 4 ++ clang/include/clang/AST/VTableBuilder.h | 29 ++++++++ clang/include/clang/Basic/ABI.h | 5 +- clang/lib/AST/VTableBuilder.cpp | 94 ++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/AST/GlobalDecl.h b/clang/include/clang/AST/GlobalDecl.h index 145e961a23a38..5ceb5e24f47a6 100644 --- a/clang/include/clang/AST/GlobalDecl.h +++ b/clang/include/clang/AST/GlobalDecl.h @@ -106,6 +106,10 @@ class GlobalDecl { LHS.MultiVersionIndex == RHS.MultiVersionIndex; } + bool operator!=(const GlobalDecl &Other) const { + return !(*this == Other); + } + void *getAsOpaquePtr() const { return Value.getOpaqueValue(); } static GlobalDecl getFromOpaquePtr(void *P) { diff --git a/clang/include/clang/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h index 43c84292c0915..3a98c7ca08798 100644 --- a/clang/include/clang/AST/VTableBuilder.h +++ b/clang/include/clang/AST/VTableBuilder.h @@ -345,6 +345,10 @@ class VTableContextBase { }; class ItaniumVTableContext : public VTableContextBase { +public: + typedef llvm::DenseMap<const CXXMethodDecl *, const CXXMethodDecl *> + OriginalMethodMapTy; + private: /// Contains the index (relative to the vtable address point) @@ -368,6 +372,10 @@ class ItaniumVTableContext : public VTableContextBase { VirtualBaseClassOffsetOffsetsMapTy; VirtualBaseClassOffsetOffsetsMapTy VirtualBaseClassOffsetOffsets; + /// Map from a virtual method to the nearest method in the primary base class + /// chain that it overrides. + OriginalMethodMapTy OriginalMethodMap; + void computeVTableRelatedInformation(const CXXRecordDecl *RD) override; public: @@ -399,6 +407,27 @@ class ItaniumVTableContext : public VTableContextBase { CharUnits getVirtualBaseOffsetOffset(const CXXRecordDecl *RD, const CXXRecordDecl *VBase); + /// Return the method that added the v-table slot that will be used to call + /// the given method. + /// + /// In the Itanium ABI, where overrides always cause methods to be added to + /// the primary v-table if they're not already there, this will be the first + /// declaration in the primary base class chain for which the return type + /// adjustment is trivial. + GlobalDecl findOriginalMethod(GlobalDecl GD); + + const CXXMethodDecl *findOriginalMethodInMap(const CXXMethodDecl *MD) const; + + void setOriginalMethod(const CXXMethodDecl *Key, const CXXMethodDecl *Val) { + OriginalMethodMap[Key] = Val; + } + + /// This method is reserved for the implementation and shouldn't be used + /// directly. + const OriginalMethodMapTy &getOriginalMethodMap() { + return OriginalMethodMap; + } + static bool classof(const VTableContextBase *VT) { return !VT->isMicrosoft(); } diff --git a/clang/include/clang/Basic/ABI.h b/clang/include/clang/Basic/ABI.h index 2401ffa20494e..b367bae66de90 100644 --- a/clang/include/clang/Basic/ABI.h +++ b/clang/include/clang/Basic/ABI.h @@ -184,7 +184,10 @@ struct ThunkInfo { /// Holds a pointer to the overridden method this thunk is for, /// if needed by the ABI to distinguish different thunks with equal - /// adjustments. Otherwise, null. + /// adjustments. + /// In the Itanium ABI, this field can hold the method that created the + /// vtable entry for this thunk. + /// Otherwise, null. /// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using /// an ABI-specific comparator. const CXXMethodDecl *Method; diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp index 5688042dadd91..d19838936abc7 100644 --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -1133,11 +1133,38 @@ void ItaniumVTableBuilder::ComputeThisAdjustments() { continue; // Add it. - VTableThunks[VTableIndex].This = ThisAdjustment; + auto SetThisAdjustmentThunk = [&](uint64_t Idx) { + // If a this pointer adjustment is required, record the method that + // created the vtable entry. MD is not necessarily the method that + // created the entry since derived classes overwrite base class + // information in MethodInfoMap, hence findOriginalMethodInMap is called + // here. + // + // For example, in the following class hierarchy, if MD = D1::m and + // Overrider = D2:m, the original method that created the entry is B0:m, + // which is what findOriginalMethodInMap(MD) returns: + // + // struct B0 { int a; virtual void m(); }; + // struct D0 : B0 { int a; void m() override; }; + // struct D1 : B0 { int a; void m() override; }; + // struct D2 : D0, D1 { int a; void m() override; }; + // + // We need to record the method because we cannot + // call findOriginalMethod to find the method that created the entry if + // the method in the entry requires adjustment. + // + // Do not set ThunkInfo::Method if Idx is already in VTableThunks. This + // can happen when covariant return adjustment is required too. + if (!VTableThunks.count(Idx)) + VTableThunks[Idx].Method = VTables.findOriginalMethodInMap(MD); + VTableThunks[Idx].This = ThisAdjustment; + }; + + SetThisAdjustmentThunk(VTableIndex); if (isa<CXXDestructorDecl>(MD)) { // Add an adjustment for the deleting destructor as well. - VTableThunks[VTableIndex + 1].This = ThisAdjustment; + SetThisAdjustmentThunk(VTableIndex + 1); } } @@ -1496,6 +1523,8 @@ void ItaniumVTableBuilder::AddMethods( FindNearestOverriddenMethod(MD, PrimaryBases)) { if (ComputeReturnAdjustmentBaseOffset(Context, MD, OverriddenMD).isEmpty()) { + VTables.setOriginalMethod(MD, OverriddenMD); + // Replace the method info of the overridden method with our own // method. assert(MethodInfoMap.count(OverriddenMD) && @@ -1594,6 +1623,13 @@ void ItaniumVTableBuilder::AddMethods( ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); + // If a return adjustment is required, record the method that created the + // vtable entry. We need to record the method because we cannot call + // findOriginalMethod to find the method that created the entry if the + // method in the entry requires adjustment. + if (!ReturnAdjustment.isEmpty()) + VTableThunks[Components.size()].Method = MD; + AddMethod(Overrider.Method, ReturnAdjustment); } } @@ -1868,11 +1904,32 @@ void ItaniumVTableBuilder::LayoutVTablesForVirtualBases( } } +static void printThunkMethod(const ThunkInfo &Info, raw_ostream &Out) { + if (Info.Method) { + std::string Str = + PredefinedExpr::ComputeName(PredefinedExpr::PrettyFunctionNoVirtual, + Info.Method); + Out << " method: " << Str; + } +} + /// dumpLayout - Dump the vtable layout. void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { // FIXME: write more tests that actually use the dumpLayout output to prevent // ItaniumVTableBuilder regressions. + Out << "Original map\n"; + + for (const auto &P : VTables.getOriginalMethodMap()) { + std::string Str0 = + PredefinedExpr::ComputeName(PredefinedExpr::PrettyFunctionNoVirtual, + P.first); + std::string Str1 = + PredefinedExpr::ComputeName(PredefinedExpr::PrettyFunctionNoVirtual, + P.second); + Out << " " << Str0 << " -> " << Str1 << "\n"; + } + if (isBuildingConstructorVTable()) { Out << "Construction vtable for ('"; MostDerivedClass->printQualifiedName(Out); @@ -1957,6 +2014,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { } Out << ']'; + printThunkMethod(Thunk, Out); } // If this function pointer has a 'this' pointer adjustment, dump it. @@ -1970,6 +2028,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { } Out << ']'; + printThunkMethod(Thunk, Out); } } @@ -2006,6 +2065,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { Out << ']'; } + printThunkMethod(Thunk, Out); } break; @@ -2106,7 +2166,6 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { ThunkInfoVectorTy ThunksVector = Thunks[MD]; llvm::sort(ThunksVector, [](const ThunkInfo &LHS, const ThunkInfo &RHS) { - assert(LHS.Method == nullptr && RHS.Method == nullptr); return std::tie(LHS.This, LHS.Return) < std::tie(RHS.This, RHS.Return); }); @@ -2263,6 +2322,35 @@ ItaniumVTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD, return I->second; } +GlobalDecl ItaniumVTableContext::findOriginalMethod(GlobalDecl GD) { + const auto *MD = cast<CXXMethodDecl>(GD.getDecl()); + computeVTableRelatedInformation(MD->getParent()); + const auto *OriginalMD = findOriginalMethodInMap(MD); + + if (const auto *DD = dyn_cast<CXXDestructorDecl>(OriginalMD)) + return GlobalDecl(DD, GD.getDtorType()); + return OriginalMD; +} + +const CXXMethodDecl * +ItaniumVTableContext::findOriginalMethodInMap(const CXXMethodDecl *MD) const { + // Traverse the chain of virtual methods until we find the method that added + // the v-table slot. + while (true) { + auto I = OriginalMethodMap.find(MD); + + // MD doesn't exist in OriginalMethodMap, so it must be the method we are + // looking for. + if (I == OriginalMethodMap.end()) + break; + + // Set MD to the overridden method. + MD = I->second; + } + + return MD; +} + static std::unique_ptr<VTableLayout> CreateVTableLayout(const ItaniumVTableBuilder &Builder) { SmallVector<VTableLayout::VTableThunkTy, 1> From a0183393e32b3cbbd7c4e2c03305a5bf500b7b53 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 01:56:45 -0400 Subject: [PATCH 534/582] Implement pointer authentication for C++ virtual functions, v-tables, and VTTs. This schema should probably be improved to use address diversity (and maybe declaration diversity?) for v-table pointers in both objects and VTTs, but we probably need an opt-out attribute to support (broken) code that relies on memcpy'ing C++ objects with v-tables. Patch primarily by Akira Hatanaka. --- .../include/clang/Basic/PointerAuthOptions.h | 13 + clang/lib/CodeGen/CGCXX.cpp | 11 +- clang/lib/CodeGen/CGClass.cpp | 14 + clang/lib/CodeGen/CGVTT.cpp | 5 + clang/lib/CodeGen/CGVTables.cpp | 13 + clang/lib/CodeGen/ItaniumCXXABI.cpp | 45 +- clang/lib/Frontend/CompilerInvocation.cpp | 7 + .../ptrauth-apple-kext-indirect-call-2.cpp | 105 +++ .../ptrauth-apple-kext-indirect-call.cpp | 42 ++ ...-apple-kext-indirect-virtual-dtor-call.cpp | 50 ++ clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp | 11 + clang/test/CodeGenCXX/ptrauth-thunks.cpp | 27 + .../CodeGenCXX/ptrauth-virtual-function.cpp | 597 ++++++++++++++++++ 13 files changed, 933 insertions(+), 7 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp create mode 100644 clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp create mode 100644 clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp create mode 100644 clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp create mode 100644 clang/test/CodeGenCXX/ptrauth-thunks.cpp create mode 100644 clang/test/CodeGenCXX/ptrauth-virtual-function.cpp diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index fd5f8a8c630ba..5aa18e07a5771 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -156,6 +156,19 @@ struct PointerAuthOptions { /// The ABI for Objective-C method lists. PointerAuthSchema ObjCMethodListFunctionPointers; + + /// The ABI for C++ virtual table pointers (the pointer to the table + /// itself) as installed in an actual class instance. + PointerAuthSchema CXXVTablePointers; + + /// The ABI for C++ virtual table pointers as installed in a VTT. + PointerAuthSchema CXXVTTVTablePointers; + + /// The ABI for most C++ virtual function pointers, i.e. v-table entries. + PointerAuthSchema CXXVirtualFunctionPointers; + + /// The ABI for variadic C++ virtual function pointers. + PointerAuthSchema CXXVirtualVariadicFunctionPointers; }; } // end namespace clang diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp index 7e5fe0fd6b1d5..e9d673181a893 100644 --- a/clang/lib/CodeGen/CGCXX.cpp +++ b/clang/lib/CodeGen/CGCXX.cpp @@ -264,7 +264,16 @@ static CGCallee BuildAppleKextVirtualCall(CodeGenFunction &CGF, CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfnkxt"); llvm::Value *VFunc = CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.PointerAlignInBytes); - CGCallee Callee(GD, VFunc); + + CGPointerAuthInfo PointerAuth; + if (auto &Schema = + CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) { + auto OrigMD = + CGM.getItaniumVTableContext().findOriginalMethod(GD.getCanonicalDecl()); + PointerAuth = CGF.EmitPointerAuthInfo(Schema, VFuncPtr, OrigMD, QualType()); + } + + CGCallee Callee(GD, VFunc, PointerAuth); return Callee; } diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 04ef912b18bd4..b27d6c834f41a 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2494,6 +2494,13 @@ void CodeGenFunction::InitializeVTablePointer(const VPtr &Vptr) { VTableField = Builder.CreateBitCast(VTableField, VTablePtrTy->getPointerTo()); VTableAddressPoint = Builder.CreateBitCast(VTableAddressPoint, VTablePtrTy); + if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) { + CGPointerAuthInfo PointerAuth = EmitPointerAuthInfo(Schema, nullptr, + GlobalDecl(), + QualType()); + VTableAddressPoint = EmitPointerAuthSign(PointerAuth, VTableAddressPoint); + } + llvm::StoreInst *Store = Builder.CreateStore(VTableAddressPoint, VTableField); TBAAAccessInfo TBAAInfo = CGM.getTBAAVTablePtrAccessInfo(VTablePtrTy); CGM.DecorateInstructionWithTBAA(Store, TBAAInfo); @@ -2593,6 +2600,13 @@ llvm::Value *CodeGenFunction::GetVTablePtr(Address This, TBAAAccessInfo TBAAInfo = CGM.getTBAAVTablePtrAccessInfo(VTableTy); CGM.DecorateInstructionWithTBAA(VTable, TBAAInfo); + if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) { + CGPointerAuthInfo PointerAuth = EmitPointerAuthInfo(Schema, nullptr, + GlobalDecl(), + QualType()); + VTable = cast<llvm::Instruction>(EmitPointerAuthAuth(PointerAuth, VTable)); + } + if (CGM.getCodeGenOpts().OptimizationLevel > 0 && CGM.getCodeGenOpts().StrictVTablePointers) CGM.DecorateInstructionWithInvariantGroup(VTable, RD); diff --git a/clang/lib/CodeGen/CGVTT.cpp b/clang/lib/CodeGen/CGVTT.cpp index e79f3f3dd8bce..5bd50fb805870 100644 --- a/clang/lib/CodeGen/CGVTT.cpp +++ b/clang/lib/CodeGen/CGVTT.cpp @@ -85,6 +85,11 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT, Init = llvm::ConstantExpr::getBitCast(Init, Int8PtrTy); + if (auto &schema = + CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers) + Init = CGM.getConstantSignedPointer(Init, schema, nullptr, GlobalDecl(), + QualType()); + VTTComponents.push_back(Init); } diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index f9f25e7e57adc..6b1ce852de207 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -708,14 +708,27 @@ void CodeGenVTables::addVTableComponent( nextVTableThunkIndex++; fnPtr = maybeEmitThunk(GD, thunkInfo, /*ForVTable=*/true); + if (CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) { + assert(thunkInfo.Method && "Method not set"); + GD = GD.getWithDecl(thunkInfo.Method); + } // Otherwise we can use the method definition directly. } else { llvm::Type *fnTy = CGM.getTypes().GetFunctionTypeForVTable(GD); fnPtr = CGM.GetAddrOfFunction(GD, fnTy, /*ForVTable=*/true); + if (CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) + GD = getItaniumVTableContext().findOriginalMethod(GD); } fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy); + + if (auto &schema = + CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers) { + builder.addSignedPointer(fnPtr, schema, GD, QualType()); + return; + } + builder.add(fnPtr); return; } diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 8f9b16470b642..878da4d2f8bd0 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1729,12 +1729,27 @@ llvm::Value *ItaniumCXXABI::getVTableAddressPointInStructorWithVTT( VTT = CGF.Builder.CreateConstInBoundsGEP1_64(VTT, VirtualPointerIndex); // And load the address point from the VTT. - return CGF.Builder.CreateAlignedLoad(VTT, CGF.getPointerAlign()); + llvm::Value *AP = CGF.Builder.CreateAlignedLoad(VTT, CGF.getPointerAlign()); + + if (auto &Schema = CGF.CGM.getCodeGenOpts().PointerAuth.CXXVTTVTablePointers) { + CGPointerAuthInfo PointerAuth = CGF.EmitPointerAuthInfo(Schema, VTT, + GlobalDecl(), + QualType()); + AP = CGF.EmitPointerAuthAuth(PointerAuth, AP); + } + + return AP; } llvm::Constant *ItaniumCXXABI::getVTableAddressPointForConstExpr( BaseSubobject Base, const CXXRecordDecl *VTableClass) { - return getVTableAddressPoint(Base, VTableClass); + llvm::Constant *AP = getVTableAddressPoint(Base, VTableClass); + + if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) + AP = CGM.getConstantSignedPointer(AP, Schema, nullptr, GlobalDecl(), + QualType()); + + return AP; } llvm::GlobalVariable *ItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *RD, @@ -1781,15 +1796,16 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, llvm::Value *VTable = CGF.GetVTablePtr(This, Ty, MethodDecl->getParent()); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); - llvm::Value *VFunc; - if (CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) { + llvm::Value *VFunc, *VFuncPtr = nullptr; + auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers; + if (!Schema && CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) { VFunc = CGF.EmitVTableTypeCheckedLoad( MethodDecl->getParent(), VTable, VTableIndex * CGM.getContext().getTargetInfo().getPointerWidth(0) / 8); } else { CGF.EmitTypeMetadataCodeForVCall(MethodDecl->getParent(), VTable, Loc); - llvm::Value *VFuncPtr = + VFuncPtr = CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); auto *VFuncLoad = CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); @@ -1809,7 +1825,13 @@ CGCallee ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, VFunc = VFuncLoad; } - CGCallee Callee(GD, VFunc); + CGPointerAuthInfo PointerAuth; + if (Schema) { + assert(VFuncPtr && "virtual function pointer not set"); + GD = CGM.getItaniumVTableContext().findOriginalMethod(GD.getCanonicalDecl()); + PointerAuth = CGF.EmitPointerAuthInfo(Schema, VFuncPtr, GD, QualType()); + } + CGCallee Callee(GD, VFunc, PointerAuth); return Callee; } @@ -1928,6 +1950,13 @@ static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, Address VTablePtrPtr = CGF.Builder.CreateElementBitCast(V, CGF.Int8PtrTy); llvm::Value *VTablePtr = CGF.Builder.CreateLoad(VTablePtrPtr); + if (auto &Schema = CGF.CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) { + CGPointerAuthInfo PointerAuth = CGF.EmitPointerAuthInfo(Schema, nullptr, + GlobalDecl(), + QualType()); + VTablePtr = CGF.EmitPointerAuthAuth(PointerAuth, VTablePtr); + } + llvm::Value *OffsetPtr = CGF.Builder.CreateConstInBoundsGEP1_64(VTablePtr, VirtualAdjustment); @@ -3268,6 +3297,10 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty) { llvm::ConstantExpr::getInBoundsGetElementPtr(CGM.Int8PtrTy, VTable, Two); VTable = llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy); + if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) + VTable = CGM.getConstantSignedPointer(VTable, Schema, nullptr, GlobalDecl(), + QualType()); + Fields.push_back(VTable); } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 2c65a2b2f9d56..ef2b75c97ea43 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -688,6 +688,13 @@ static bool parsePointerAuthOptions(PointerAuthOptions &Opts, PointerAuthSchema(Key::ASIA, true, Discrimination::None); Opts.ObjCMethodListFunctionPointers = PointerAuthSchema(Key::ASIA, true, Discrimination::None); + Opts.CXXVTablePointers = + PointerAuthSchema(Key::ASDA, false, Discrimination::None); + Opts.CXXVTTVTablePointers = + PointerAuthSchema(Key::ASDA, false, Discrimination::None); + Opts.CXXVirtualFunctionPointers = + Opts.CXXVirtualVariadicFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::Decl); Opts.ThunkCXXVirtualMemberPointers = false; } diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp new file mode 100644 index 0000000000000..17ea16accb612 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -fno-rtti -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV1A = unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* null, i8* bitcast ({ i8*, i32, i64, i64 }* @_ZNK1A3abcEv.ptrauth to i8*), i8* null] } +// CHECK: @_ZTV4Base = unnamed_addr constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* null, i8* bitcast ({ i8*, i32, i64, i64 }* @_ZNK4Base3abcEv.ptrauth to i8*), i8* null] } +// CHECK: @_ZTV8Derived2 = unnamed_addr constant { [5 x i8*] } { [5 x i8*] [i8* null, i8* null, i8* null, i8* bitcast ({ i8*, i32, i64, i64 }* @_ZNK8Derived23efgEv.ptrauth to i8*), i8* null] } +// CHECK: @_ZTV2D2 = unnamed_addr constant { [5 x i8*] } { [5 x i8*] [i8* null, i8* null, i8* null, i8* bitcast ({ i8*, i32, i64, i64 }* @_ZNK2D23abcEv.ptrauth to i8*), i8* null] } + +struct A { + virtual const char* abc(void) const; +}; + +const char* A::abc(void) const {return "A"; }; + +struct B : virtual A { + virtual void VF(); +}; + +void B::VF() {} + +void FUNC(B* p) { +// CHECK: [[T1:%.*]] = load i8* (%struct.A*)*, i8* (%struct.A*)** getelementptr inbounds (i8* (%struct.A*)*, i8* (%struct.A*)** bitcast ({ [4 x i8*] }* @_ZTV1A to i8* (%struct.A*)**), i64 2) +// CHECK-NEXT: [[BT1:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i8* (%struct.A*)** getelementptr inbounds (i8* (%struct.A*)*, i8* (%struct.A*)** bitcast ({ [4 x i8*] }* @_ZTV1A to i8* (%struct.A*)**), i64 2) to i64), i64 12401) +// CHECK-NEXT: [[T2:%.*]] = call i8* [[T1]](%struct.A* {{.*}}) [ "ptrauth"(i32 0, i64 [[BT1]]) ] + const char* c = p->A::abc(); +} + + +// Test2 +struct Base { virtual char* abc(void) const; }; + +char* Base::abc() const { return 0; } + +struct Derived : public Base { +}; + +void FUNC1(Derived* p) { +// CHECK: [[U1:%.*]] = load i8* (%struct.Base*)*, i8* (%struct.Base*)** getelementptr inbounds (i8* (%struct.Base*)*, i8* (%struct.Base*)** bitcast ({ [4 x i8*] }* @_ZTV4Base to i8* (%struct.Base*)**), i64 2) +// CHECK-NEXT: [[BU1:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i8* (%struct.Base*)** getelementptr inbounds (i8* (%struct.Base*)*, i8* (%struct.Base*)** bitcast ({ [4 x i8*] }* @_ZTV4Base to i8* (%struct.Base*)**), i64 2) to i64), i64 64320) +// CHECK-NEXT: [[U2:%.*]] = call i8* [[U1]](%struct.Base* {{.*}}) [ "ptrauth"(i32 0, i64 [[BU1]]) ] + char* c = p->Base::abc(); +} + + +// Test3 +struct Base2 { }; + +struct Derived2 : virtual Base2 { + virtual char* efg(void) const; +}; + +char* Derived2::efg(void) const { return 0; } + +void FUNC2(Derived2* p) { +// CHECK: [[V1:%.*]] = load i8* (%struct.Derived2*)*, i8* (%struct.Derived2*)** getelementptr inbounds (i8* (%struct.Derived2*)*, i8* (%struct.Derived2*)** bitcast ({ [5 x i8*] }* @_ZTV8Derived2 to i8* (%struct.Derived2*)**), i64 3) +// CHECK-NEXT: [[BV1:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i8* (%struct.Derived2*)** getelementptr inbounds (i8* (%struct.Derived2*)*, i8* (%struct.Derived2*)** bitcast ({ [5 x i8*] }* @_ZTV8Derived2 to i8* (%struct.Derived2*)**), i64 3) to i64), i64 36603) +// CHECK-NEXT: [[V2:%.*]] = call i8* [[V1]](%struct.Derived2* {{.*}}) [ "ptrauth"(i32 0, i64 [[BV1]]) ] + char* c = p->Derived2::efg(); +} + +// Test4 +struct Base3 { }; + +struct D1 : virtual Base3 { +}; + +struct D2 : virtual Base3 { + virtual char *abc(void) const; +}; + +struct Sub : D1, D2 { +}; + +char* D2::abc(void) const { return 0; } + +void FUNC3(Sub* p) { +// CHECK: [[W1:%.*]] = load i8* (%struct.D2*)*, i8* (%struct.D2*)** getelementptr inbounds (i8* (%struct.D2*)*, i8* (%struct.D2*)** bitcast ({ [5 x i8*] }* @_ZTV2D2 to i8* (%struct.D2*)**), i64 3) +// CHECK-NEXT: [[BW1:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i8* (%struct.D2*)** getelementptr inbounds (i8* (%struct.D2*)*, i8* (%struct.D2*)** bitcast ({ [5 x i8*] }* @_ZTV2D2 to i8* (%struct.D2*)**), i64 3) to i64), i64 20222) +// CHECK-NEXT: [[W2:%.*]] = call i8* [[W1]](%struct.D2* {{.*}}) [ "ptrauth"(i32 0, i64 [[BW1]]) ] + char* c = p->D2::abc(); +} + + +// Test4 +struct Base4 { virtual void abc(); }; + +void Base4::abc() {} + +struct Derived4 : public Base4 { + void abc() override; +}; + +void Derived4::abc() {} + +void FUNC4(Derived4* p) { +// CHECK: %[[VTABLE:[a-z]+]] = load void (%struct.Derived4*)**, void (%struct.Derived4*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%struct.Derived4*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%struct.Derived4*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%struct.Derived4*)*, void (%struct.Derived4*)** %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load void (%struct.Derived4*)*, void (%struct.Derived4*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%struct.Derived4*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 426) +// CHECK: call void %[[T5]](%struct.Derived4* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + p->abc(); +} diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp new file mode 100644 index 0000000000000..008ba6e3d244c --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [5 x i8*] } { [5 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI5TemplIiE to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5TemplIiE1fEv.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5TemplIiE1gEv.ptrauth to i8*), i8* null] } + +struct Base { + virtual void abc(void) const; +}; + +void Base::abc(void) const {} + +void FUNC(Base* p) { + p->Base::abc(); +} + +// CHECK: getelementptr inbounds (void (%struct.Base*)*, void (%struct.Base*)** bitcast ({ [4 x i8*] }* @_ZTV4Base to void (%struct.Base*)**), i64 2) +// CHECK-NOT: call void @_ZNK4Base3abcEv + +template<class T> +struct Templ { + virtual void f() {} + virtual void g() {} +}; +template<class T> +struct SubTempl : public Templ<T> { + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl<int>* t) { + // Qualified calls go through the (qualified) vtable in apple-kext mode. + // Since t's this pointer points to SubTempl's vtable, the call needs + // to load Templ<int>'s vtable. Hence, Templ<int>::g needs to be + // instantiated in this TU, for it's referenced by the vtable. + // (This happens only in apple-kext mode; elsewhere virtual calls can always + // use the vtable pointer off this instead of having to load the vtable + // symbol.) + t->Templ::f(); +} + +// CHECK: getelementptr inbounds (void (%struct.Templ*)*, void (%struct.Templ*)** bitcast ({ [5 x i8*] }* @_ZTV5TemplIiE to void (%struct.Templ*)**), i64 2) +// CHECK: define internal void @_ZN5TemplIiE1fEv(%struct.Templ* %this) +// CHECK: define internal void @_ZN5TemplIiE1gEv(%struct.Templ* %this) diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp new file mode 100644 index 0000000000000..3e081e4e5a6eb --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++98 -fptrauth-calls -fapple-kext -fno-rtti -disable-O0-optnone -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [7 x i8*] } { [7 x i8*] [i8* null, i8* null, i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5TemplIiED1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5TemplIiED0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5TemplIiE1fEv.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5TemplIiE1gEv.ptrauth to i8*), i8* null] } + +struct B1 { + virtual ~B1(); +}; + +B1::~B1() {} + +void DELETE(B1 *pb1) { + pb1->B1::~B1(); +} +// CHECK-LABEL: define void @_ZN2B1D0Ev +// CHECK: [[T1:%.*]] = load %struct.B1* (%struct.B1*)*, %struct.B1* (%struct.B1*)** getelementptr inbounds (%struct.B1* (%struct.B1*)*, %struct.B1* (%struct.B1*)** bitcast ({ [5 x i8*] }* @_ZTV2B1 to %struct.B1* (%struct.B1*)**), i64 2) +// CHECK-NEXT: [[B1:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (%struct.B1* (%struct.B1*)** getelementptr inbounds (%struct.B1* (%struct.B1*)*, %struct.B1* (%struct.B1*)** bitcast ({ [5 x i8*] }* @_ZTV2B1 to %struct.B1* (%struct.B1*)**), i64 2) to i64), i64 14635) +// CHECK-NEXT: call %struct.B1* [[T1]](%struct.B1* [[T2:%.*]]) [ "ptrauth"(i32 0, i64 [[B1]]) ] +// CHECK-LABEL: define void @_Z6DELETEP2B1 +// CHECK: [[T3:%.*]] = load %struct.B1* (%struct.B1*)*, %struct.B1* (%struct.B1*)** getelementptr inbounds (%struct.B1* (%struct.B1*)*, %struct.B1* (%struct.B1*)** bitcast ({ [5 x i8*] }* @_ZTV2B1 to %struct.B1* (%struct.B1*)**), i64 2) +// CHECK-NEXT: [[B3:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (%struct.B1* (%struct.B1*)** getelementptr inbounds (%struct.B1* (%struct.B1*)*, %struct.B1* (%struct.B1*)** bitcast ({ [5 x i8*] }* @_ZTV2B1 to %struct.B1* (%struct.B1*)**), i64 2) to i64), i64 14635) +// CHECK-NEXT: call %struct.B1* [[T3]](%struct.B1* [[T4:%.*]]) [ "ptrauth"(i32 0, i64 [[B3]]) + +template<class T> +struct Templ { + virtual ~Templ(); // Out-of-line so that the destructor doesn't cause a vtable + virtual void f() {} + virtual void g() {} +}; +template<class T> +struct SubTempl : public Templ<T> { + virtual ~SubTempl() {} // override + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl<int>* t) { + // Qualified calls go through the (qualified) vtable in apple-kext mode. + // Since t's this pointer points to SubTempl's vtable, the call needs + // to load Templ<int>'s vtable. Hence, Templ<int>::g needs to be + // instantiated in this TU, for it's referenced by the vtable. + // (This happens only in apple-kext mode; elsewhere virtual calls can always + // use the vtable pointer off this instead of having to load the vtable + // symbol.) + t->Templ::~Templ(); +} + +// CHECK: getelementptr inbounds (%struct.Templ* (%struct.Templ*)*, %struct.Templ* (%struct.Templ*)** bitcast ({ [7 x i8*] }* @_ZTV5TemplIiE to %struct.Templ* (%struct.Templ*)**), i64 2) +// CHECK: declare void @_ZN5TemplIiED0Ev(%struct.Templ*) +// CHECK: define internal void @_ZN5TemplIiE1fEv(%struct.Templ* %this) +// CHECK: define internal void @_ZN5TemplIiE1gEv(%struct.Templ* %this) diff --git a/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp new file mode 100644 index 0000000000000..03fd677a9641c --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 %s -I%S -triple=arm64-apple-ios -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck %s +#include <typeinfo> + +struct A { int a; }; + +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global i8* +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv117__class_type_infoE, i64 2) to i8*), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTS1A = linkonce_odr hidden constant [3 x i8] c"1A\00" +// CHECK: @_ZTI1A = linkonce_odr hidden constant { i8*, i8* } { i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTVN10__cxxabiv117__class_type_infoE.ptrauth to i8*), i8* inttoptr (i64 add (i64 ptrtoint ([3 x i8]* @_ZTS1A to i64), i64 -9223372036854775808) to i8*) } + +auto ATI = typeid(A); diff --git a/clang/test/CodeGenCXX/ptrauth-thunks.cpp b/clang/test/CodeGenCXX/ptrauth-thunks.cpp new file mode 100644 index 0000000000000..8bc0ba8fb6065 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-thunks.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - -O1 | FileCheck %s + +namespace Test1 { + struct B1 { + virtual void* foo1() { + return 0; + } + }; + struct Pad1 { + virtual ~Pad1() {} + }; + struct Proxy1 : Pad1, B1 { + virtual ~Proxy1() {} + }; + struct D : virtual Proxy1 { + virtual ~D() {} + virtual void* foo1(); + }; + void* D::foo1() { + return (void*)this; + } +} + +// CHECK-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test11DD0Ev(%"struct.Test1::D"* %this) +// CHECK: %[[BitcastThis:.*]] = bitcast %"struct.Test1::D"* %this to i64* +// CHECK: %[[SignedVTable:.*]] = load i64, i64* %[[BitcastThis]], align 8 +// CHECK: %[[VTable:.*]] = tail call i64 @llvm.ptrauth.auth.i64(i64 %[[SignedVTable]], i32 2, i64 0) \ No newline at end of file diff --git a/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp new file mode 100644 index 0000000000000..22b96377d8aa3 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp @@ -0,0 +1,597 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck %s + +// Check virtual function pointers in vtables are signed and their relocation +// structures are emitted. + +// CHECK: @_ZTV2B1 = unnamed_addr constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI2B1 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B12m0Ev.ptrauth to i8*)] }, align 8 +// CHECK: @_ZTV2B1.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (i8** getelementptr inbounds ({ [3 x i8*] }, { [3 x i8*] }* @_ZTV2B1, i32 0, inrange i32 0, i32 2) to i8*), i32 2, i64 0, i64 0 }, section "llvm.ptrauth" +// CHECK: @g_B1 = global { i8** } { i8** getelementptr inbounds ({ i8*, i32, i64, i64 }, { i8*, i32, i64, i64 }* @_ZTV2B1.ptrauth, i32 0, i32 0) } + +// CHECK: @_ZTV2B0 = unnamed_addr constant { [7 x i8*] } { [7 x i8*] [{{.*}} i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B0D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B0D0Ev.ptrauth to i8*)] } +// CHECK: @_ZN2B02m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*] }, { [7 x i8*] }* @_ZTV2B0, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S1* (%class.B0*)* @_ZN2B02m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*] }, { [7 x i8*] }* @_ZTV2B0, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*] }, { [7 x i8*] }* @_ZTV2B0, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2B0D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.B0* (%class.B0*)* @_ZN2B0D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*] }, { [7 x i8*] }* @_ZTV2B0, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2B0D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B0D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*] }, { [7 x i8*] }* @_ZTV2B0, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D0 = unnamed_addr constant { [9 x i8*] } { [9 x i8*] [{{.*}} i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D02m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTch0_h4_N2D02m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth.1 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D0D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D0D0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D02m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D02m3Ev.ptrauth to i8*)] } +// CHECK: @_ZN2D02m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D0*)* @_ZN2D02m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTch0_h4_N2D02m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D0*)* @_ZTch0_h4_N2D02m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.1 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2D0D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.D0* (%class.D0*)* @_ZN2D0D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2D0D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D0*)* @_ZN2D0D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D02m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D0*)* @_ZN2D02m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, i32 0, i32 7) to i64), i64 35045 }, section "llvm.ptrauth" +// CHECK: @_ZN2D02m3Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D0*)* @_ZN2D02m3Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, i32 0, i32 8) to i64), i64 10565 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D1 = unnamed_addr constant { [8 x i8*] } { [8 x i8*] [{{.*}} i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D12m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTch0_h4_N2D12m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth.2 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D1D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D1D0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D12m1Ev.ptrauth to i8*)] } +// CHECK: @_ZN2D12m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D1*)* @_ZN2D12m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [8 x i8*] }, { [8 x i8*] }* @_ZTV2D1, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTch0_h4_N2D12m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D1*)* @_ZTch0_h4_N2D12m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [8 x i8*] }, { [8 x i8*] }* @_ZTV2D1, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.2 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [8 x i8*] }, { [8 x i8*] }* @_ZTV2D1, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2D1D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.D1* (%class.D1*)* @_ZN2D1D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [8 x i8*] }, { [8 x i8*] }* @_ZTV2D1, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2D1D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D1*)* @_ZN2D1D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [8 x i8*] }, { [8 x i8*] }* @_ZTV2D1, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D12m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D1*)* @_ZN2D12m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [8 x i8*] }, { [8 x i8*] }* @_ZTV2D1, i32 0, i32 0, i32 7) to i64), i64 52864 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D2 = unnamed_addr constant { [9 x i8*], [8 x i8*] } { [9 x i8*] [{{.*}} i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D22m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTch0_h4_N2D22m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth.3 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D2D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D2D0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D22m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D22m3Ev.ptrauth to i8*)], [8 x i8*] [{{.*}} i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D22m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTchn16_h4_N2D22m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth.4 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D2D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D2D0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D22m1Ev.ptrauth to i8*)] } +// CHECK: @_ZN2D22m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D2*)* @_ZN2D22m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTch0_h4_N2D22m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D2*)* @_ZTch0_h4_N2D22m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.3 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2D2D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.D2* (%class.D2*)* @_ZN2D2D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2D2D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D2*)* @_ZN2D2D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D22m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D2*)* @_ZN2D22m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 0, i32 7) to i64), i64 35045 }, section "llvm.ptrauth" +// CHECK: @_ZN2D22m3Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D2*)* @_ZN2D22m3Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 0, i32 8) to i64), i64 10565 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D22m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D2*)* @_ZThn16_N2D22m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 1, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTchn16_h4_N2D22m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D2*)* @_ZTchn16_h4_N2D22m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 1, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.4 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 1, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D2D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.D2* (%class.D2*)* @_ZThn16_N2D2D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 1, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D2D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D2*)* @_ZThn16_N2D2D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 1, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D22m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D2*)* @_ZThn16_N2D22m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, i32 1, i32 7) to i64), i64 52864 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D3 = unnamed_addr constant { [7 x i8*], [7 x i8*], [11 x i8*] } { [7 x i8*] [i8* inttoptr (i64 32 to i8*), i8* null, i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }* @_ZTI2D3 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D32m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D32m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D3D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2D3D0Ev.ptrauth to i8*)], [7 x i8*] [i8* inttoptr (i64 16 to i8*), i8* inttoptr (i64 -16 to i8*), i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }* @_ZTI2D3 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D32m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D32m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D3D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZThn16_N2D3D0Ev.ptrauth to i8*)], [11 x i8*] [i8* inttoptr (i64 -32 to i8*), i8* null, i8* inttoptr (i64 -32 to i8*), i8* inttoptr (i64 -32 to i8*), i8* inttoptr (i64 -32 to i8*), i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }* @_ZTI2D3 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n24_N2D32m0Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTcv0_n32_h4_N2D32m1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth.11 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n48_N2D3D1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n48_N2D3D0Ev.ptrauth to i8*)] } + +// CHECK: @_ZTC2D30_2V0 = unnamed_addr constant { [7 x i8*], [11 x i8*] } { [7 x i8*] [i8* inttoptr (i64 32 to i8*), i8* null, i8* bitcast (i8** @_ZTI2V0 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V02m0Ev.ptrauth.12 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V02m1Ev.ptrauth.13 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V0D1Ev.ptrauth.14 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V0D0Ev.ptrauth.15 to i8*)], [11 x i8*] [i8* inttoptr (i64 -32 to i8*), i8* null, i8* inttoptr (i64 -32 to i8*), i8* inttoptr (i64 -32 to i8*), i8* inttoptr (i64 -32 to i8*), i8* bitcast (i8** @_ZTI2V0 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n24_N2V02m0Ev.ptrauth.16 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTcv0_n32_h4_N2V02m1Ev.ptrauth.17 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth.18 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n48_N2V0D1Ev.ptrauth.19 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n48_N2V0D0Ev.ptrauth.20 to i8*)] } + +// CHECK: @_ZN2V02m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZN2V02m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 3) to i64), i64 44578 }, section "llvm.ptrauth" +// CHECK: @_ZN2V02m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V0*)* @_ZN2V02m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 4) to i64), i64 30766 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V0* (%class.V0*)* @_ZN2V0D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 5) to i64), i64 57279 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZN2V0D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 6) to i64), i64 62452 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V02m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZTv0_n24_N2V02m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V02m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V0*)* @_ZTcv0_n32_h4_N2V02m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.5 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V0* (%class.V0*)* @_ZTv0_n48_N2V0D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZTv0_n48_N2V0D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" + +// CHECK: @_ZTC2D316_2V1 = unnamed_addr constant { [7 x i8*], [11 x i8*] } { [7 x i8*] [i8* inttoptr (i64 16 to i8*), i8* null, i8* bitcast (i8** @_ZTI2V1 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V12m0Ev.ptrauth.21 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V12m1Ev.ptrauth.22 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V1D1Ev.ptrauth.23 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2V1D0Ev.ptrauth.24 to i8*)], [11 x i8*] [i8* inttoptr (i64 -16 to i8*), i8* null, i8* inttoptr (i64 -16 to i8*), i8* inttoptr (i64 -16 to i8*), i8* inttoptr (i64 -16 to i8*), i8* bitcast (i8** @_ZTI2V1 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n24_N2V12m0Ev.ptrauth.25 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTcv0_n32_h4_N2V12m1Ev.ptrauth.26 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN2B02m2Ev.ptrauth.27 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n48_N2V1D1Ev.ptrauth.28 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZTv0_n48_N2V1D0Ev.ptrauth.29 to i8*)] } +// CHECK: @_ZN2V12m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZN2V12m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 3) to i64), i64 49430 }, section "llvm.ptrauth" +// CHECK: @_ZN2V12m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V1*)* @_ZN2V12m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 4) to i64), i64 57119 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V1* (%class.V1*)* @_ZN2V1D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 5) to i64), i64 60799 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZN2V1D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 6) to i64), i64 52565 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V12m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZTv0_n24_N2V12m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V12m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V1*)* @_ZTcv0_n32_h4_N2V12m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.6 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V1* (%class.V1*)* @_ZTv0_n48_N2V1D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZTv0_n48_N2V1D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D32m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D3*)* @_ZN2D32m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 0, i32 3) to i64), i64 44578 }, section "llvm.ptrauth" +// CHECK: @_ZN2D32m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D3*)* @_ZN2D32m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 0, i32 4) to i64), i64 30766 }, section "llvm.ptrauth" +// CHECK: @_ZN2D3D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.D3* (%class.D3*)* @_ZN2D3D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 0, i32 5) to i64), i64 57279 }, section "llvm.ptrauth" +// CHECK: @_ZN2D3D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D3*)* @_ZN2D3D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 0, i32 6) to i64), i64 62452 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D32m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D3*)* @_ZThn16_N2D32m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 1, i32 3) to i64), i64 49430 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D32m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D3*)* @_ZThn16_N2D32m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 1, i32 4) to i64), i64 57119 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D3D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.D3* (%class.D3*)* @_ZThn16_N2D3D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 1, i32 5) to i64), i64 60799 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D3D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D3*)* @_ZThn16_N2D3D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 1, i32 6) to i64), i64 52565 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2D32m0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D3*)* @_ZTv0_n24_N2D32m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 2, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2D32m1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.D3*)* @_ZTcv0_n32_h4_N2D32m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 2, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.11 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 2, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2D3D1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.D3* (%class.D3*)* @_ZTv0_n48_N2D3D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 2, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2D3D0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.D3*)* @_ZTv0_n48_N2D3D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [7 x i8*], [11 x i8*] }, { [7 x i8*], [7 x i8*], [11 x i8*] }* @_ZTV2D3, i32 0, i32 2, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2V02m0Ev.ptrauth.12 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZN2V02m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 3) to i64), i64 44578 }, section "llvm.ptrauth" +// CHECK: @_ZN2V02m1Ev.ptrauth.13 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V0*)* @_ZN2V02m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 4) to i64), i64 30766 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D1Ev.ptrauth.14 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V0* (%class.V0*)* @_ZN2V0D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 5) to i64), i64 57279 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D0Ev.ptrauth.15 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZN2V0D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 0, i32 6) to i64), i64 62452 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V02m0Ev.ptrauth.16 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZTv0_n24_N2V02m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V02m1Ev.ptrauth.17 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V0*)* @_ZTcv0_n32_h4_N2V02m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.18 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D1Ev.ptrauth.19 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V0* (%class.V0*)* @_ZTv0_n48_N2V0D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D0Ev.ptrauth.20 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V0*)* @_ZTv0_n48_N2V0D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D30_2V0, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2V12m0Ev.ptrauth.21 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZN2V12m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 3) to i64), i64 49430 }, section "llvm.ptrauth" +// CHECK: @_ZN2V12m1Ev.ptrauth.22 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V1*)* @_ZN2V12m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 4) to i64), i64 57119 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D1Ev.ptrauth.23 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V1* (%class.V1*)* @_ZN2V1D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 5) to i64), i64 60799 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D0Ev.ptrauth.24 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZN2V1D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 0, i32 6) to i64), i64 52565 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V12m0Ev.ptrauth.25 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZTv0_n24_N2V12m0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V12m1Ev.ptrauth.26 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%struct.S2* (%class.V1*)* @_ZTcv0_n32_h4_N2V12m1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.27 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.B0*)* @_ZN2B02m2Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D1Ev.ptrauth.28 = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.V1* (%class.V1*)* @_ZTv0_n48_N2V1D1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D0Ev.ptrauth.29 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%class.V1*)* @_ZTv0_n48_N2V1D0Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*], [11 x i8*] }, { [7 x i8*], [11 x i8*] }* @_ZTC2D316_2V1, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" + + +struct S0 { + int f; +}; + +struct S1 { + int f; +}; + +struct S2 : S0, S1 { + int f; +}; + +class B0 { +public: + virtual void m0(); + virtual S1 *m1(); + virtual void m2(); + virtual ~B0(); + int f; +}; + +class B1 { +public: + virtual void m0(); +}; + +class D0 : public B0 { +public: + void m0() override; + S2 *m1() override; + virtual void m3(); + int f; +}; + +class D1 : public B0 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +class D2 : public D0, public D1 { +public: + void m0() override; + S2 *m1() override; + void m3() override; + int f; +}; + +class V0 : public virtual B0 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +class V1 : public virtual B0 { +public: + void m0() override; + S2 *m1() override; + ~V1(); + int f; +}; + +class D3 : public V0, public V1 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +B1 g_B1; + +void B0::m0() {} + +void B1::m0() {} + +void D0::m0() {} + +void D1::m0() {} + +void D2::m0() {} + +void D3::m0() {} + +V1::~V1() { + m1(); +} + +// Check sign/authentication of vtable pointers and authentication of virtual +// functions. + +// CHECK-LABEL: define %class.V1* @_ZN2V1D2Ev( +// CHECK: %[[T0:[0-9]+]] = load i8*, i8** %{{.*}} +// CHECK: %[[T1:[0-9]+]] = ptrtoint i8* %[[T0]] to i64 +// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T1]], i32 2, i64 0) +// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to i8* +// CHECK: %[[SLOT:[0-9]+]] = bitcast %class.V1* %{{.*}} to i32 (...)*** +// CHECK: %[[T5:[0-9]+]] = bitcast i8* %[[T3]] to i32 (...)** +// CHECK: %[[T6:[0-9]+]] = ptrtoint i32 (...)** %[[T5]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 %[[T6]], i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T7]] to i32 (...)** +// CHECK: store i32 (...)** %[[SIGNED_VTADDR]], i32 (...)*** %[[SLOT]] + +// CHECK-LABEL: define void @_Z8testB0m0P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.B0*)**, void (%class.B0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.B0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.B0*)*, void (%class.B0*)** %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load void (%class.B0*)*, void (%class.B0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](%class.B0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m0(B0 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testB0m1P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load %struct.S1* (%class.B0*)**, %struct.S1* (%class.B0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint %struct.S1* (%class.B0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to %struct.S1* (%class.B0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds %struct.S1* (%class.B0*)*, %struct.S1* (%class.B0*)** %[[T4]], i64 1 +// CHECK: %[[T5:[0-9]+]] = load %struct.S1* (%class.B0*)*, %struct.S1* (%class.B0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint %struct.S1* (%class.B0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 15165) +// CHECK: call %struct.S1* %[[T5]](%class.B0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m1(B0 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testB0m2P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.B0*)**, void (%class.B0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.B0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.B0*)*, void (%class.B0*)** %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load void (%class.B0*)*, void (%class.B0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](%class.B0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m2(B0 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z8testD0m0P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.D0*)**, void (%class.D0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.D0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.D0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.D0*)*, void (%class.D0*)** %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load void (%class.D0*)*, void (%class.D0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.D0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](%class.D0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m0(D0 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD0m1P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load %struct.S2* (%class.D0*)**, %struct.S2* (%class.D0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint %struct.S2* (%class.D0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to %struct.S2* (%class.D0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds %struct.S2* (%class.D0*)*, %struct.S2* (%class.D0*)** %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load %struct.S2* (%class.D0*)*, %struct.S2* (%class.D0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint %struct.S2* (%class.D0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 35045) +// CHECK: call %struct.S2* %[[T5]](%class.D0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m1(D0 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testD0m2P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.B0*)**, void (%class.B0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.B0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.B0*)*, void (%class.B0*)** %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load void (%class.B0*)*, void (%class.B0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](%class.B0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m2(D0 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z8testD0m3P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.D0*)**, void (%class.D0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.D0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.D0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.D0*)*, void (%class.D0*)** %[[T4]], i64 6 +// CHECK: %[[T5:[0-9]+]] = load void (%class.D0*)*, void (%class.D0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.D0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 10565) +// CHECK: call void %[[T5]](%class.D0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m3(D0 *a) { + a->m3(); +} + + +// CHECK-LABEL: define void @_Z8testD1m0P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.D1*)**, void (%class.D1*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.D1*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.D1*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.D1*)*, void (%class.D1*)** %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load void (%class.D1*)*, void (%class.D1*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.D1*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](%class.D1* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m0(D1 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD1m1P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load %struct.S2* (%class.D1*)**, %struct.S2* (%class.D1*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint %struct.S2* (%class.D1*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to %struct.S2* (%class.D1*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds %struct.S2* (%class.D1*)*, %struct.S2* (%class.D1*)** %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load %struct.S2* (%class.D1*)*, %struct.S2* (%class.D1*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint %struct.S2* (%class.D1*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 52864) +// CHECK: call %struct.S2* %[[T5]](%class.D1* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m1(D1 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testD1m2P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.B0*)**, void (%class.B0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.B0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.B0*)*, void (%class.B0*)** %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load void (%class.B0*)*, void (%class.B0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](%class.B0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m2(D1 *a) { + a->m2(); +} + + +// CHECK-LABEL: define void @_Z8testD2m0P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.D2*)**, void (%class.D2*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.D2*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.D2*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.D2*)*, void (%class.D2*)** %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load void (%class.D2*)*, void (%class.D2*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.D2*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](%class.D2* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m0(D2 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD2m1P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load %struct.S2* (%class.D2*)**, %struct.S2* (%class.D2*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint %struct.S2* (%class.D2*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to %struct.S2* (%class.D2*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds %struct.S2* (%class.D2*)*, %struct.S2* (%class.D2*)** %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load %struct.S2* (%class.D2*)*, %struct.S2* (%class.D2*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint %struct.S2* (%class.D2*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 35045) +// CHECK: call %struct.S2* %[[T5]](%class.D2* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m1(D2 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z10testD2m2D0P2D2( +// CHECK: call void @_ZN2B02m2Ev(%class.B0* %{{.*}}){{$}} + +void testD2m2D0(D2 *a) { + a->D0::m2(); +} + +// CHECK-LABEL: define void @_Z10testD2m2D1P2D2( +// CHECK: call void @_ZN2B02m2Ev(%class.B0* %{{.*}}){{$}} + +void testD2m2D1(D2 *a) { + a->D1::m2(); +} + +// CHECK-LABEL: define void @_Z8testD2m3P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.D2*)**, void (%class.D2*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.D2*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.D2*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.D2*)*, void (%class.D2*)** %[[T4]], i64 6 +// CHECK: %[[T5:[0-9]+]] = load void (%class.D2*)*, void (%class.D2*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.D2*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 10565) +// CHECK: call void %[[T5]](%class.D2* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m3(D2 *a) { + a->m3(); +} + +// CHECK-LABEL: define void @_Z8testD3m0P2D3( +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.D3*)**, void (%class.D3*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.D3*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.D3*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.D3*)*, void (%class.D3*)** %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load void (%class.D3*)*, void (%class.D3*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.D3*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 44578) +// CHECK: call void %[[T5]](%class.D3* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3m0(D3 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD3m1P2D3( +// CHECK: %[[VTABLE:[a-z]+]] = load %struct.S2* (%class.D3*)**, %struct.S2* (%class.D3*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint %struct.S2* (%class.D3*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to %struct.S2* (%class.D3*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds %struct.S2* (%class.D3*)*, %struct.S2* (%class.D3*)** %[[T4]], i64 1 +// CHECK: %[[T5:[0-9]+]] = load %struct.S2* (%class.D3*)*, %struct.S2* (%class.D3*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint %struct.S2* (%class.D3*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 30766) +// CHECK: call %struct.S2* %[[T5]](%class.D3* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3m1(D3 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testD3m2P2D3( +// CHECK: %[[VTABLE:[a-z0-9]+]] = load void (%class.B0*)**, void (%class.B0*)*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.B0*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.B0*)*, void (%class.B0*)** %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load void (%class.B0*)*, void (%class.B0*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.B0*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](%class.B0* %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3m2(D3 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z17testD3Destructor0P2D3( +// CHECK: %[[T1:[0-9]+]] = bitcast %class.D3* %{{.*}} to void (%class.D3*)*** +// CHECK: %[[VTABLE:[a-z]+]] = load void (%class.D3*)**, void (%class.D3*)*** %[[T1]] +// CHECK: %[[T2:[0-9]+]] = ptrtoint void (%class.D3*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to void (%class.D3*)** +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds void (%class.D3*)*, void (%class.D3*)** %[[T4]], i64 3 +// CHECK: %[[T5:[0-9]+]] = load void (%class.D3*)*, void (%class.D3*)** %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint void (%class.D3*)** %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 62452) +// CHECK: call void %[[T5]](%class.D3* %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3Destructor0(D3 *a) { + delete a; +} + +// CHECK-LABEL: define void @_Z17testD3Destructor1P2D3( +// CHECK: %[[T1:[0-9]+]] = bitcast %class.D3* %{{.*}} to i64** +// CHECK: %[[VTABLE0:[a-z0-9]+]] = load i64*, i64** %[[T1]] +// CHECK: %[[T2:[0-9]+]] = ptrtoint i64* %[[VTABLE0]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to i64* +// CHECK: %[[COMPLETE_OFFSET_PTR:.*]] = getelementptr inbounds i64, i64* %[[T4]], i64 -2 +// CHECK: %[[T5:[0-9]+]] = load i64, i64* %[[COMPLETE_OFFSET_PTR]] +// CHECK: %[[T6:[0-9]+]] = bitcast %class.D3* %{{.*}} to i8* +// CHECK: %[[T7:[0-9]+]] = getelementptr inbounds i8, i8* %[[T6]], i64 %[[T5]] +// CHECK: %[[T8:[0-9]+]] = bitcast %class.D3* %{{.*}} to %class.D3* (%class.D3*)*** +// CHECK: %[[VTABLE1:[a-z0-9]+]] = load %class.D3* (%class.D3*)**, %class.D3* (%class.D3*)*** %[[T8]] +// CHECK: %[[T9:[0-9]+]] = ptrtoint %class.D3* (%class.D3*)** %[[VTABLE1]] to i64 +// CHECK: %[[T10:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T9]], i32 2, i64 0) +// CHECK: %[[T11:[0-9]+]] = inttoptr i64 %[[T10]] to %class.D3* (%class.D3*)** +// CHECK: %[[VFN:[a-z0-9]+]] = getelementptr inbounds %class.D3* (%class.D3*)*, %class.D3* (%class.D3*)** %[[T11]], i64 2 +// CHECK: %[[T12:[0-9]+]] = load %class.D3* (%class.D3*)*, %class.D3* (%class.D3*)** %[[VFN]] +// CHECK: %[[T13:[0-9]+]] = ptrtoint %class.D3* (%class.D3*)** %[[VFN]] to i64 +// CHECK: %[[T14:[0-9]+]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T13]], i64 57279) +// CHECK: %call = call %class.D3* %[[T12]](%class.D3* %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T14]]) ] +// CHECK: call void @_ZdlPv(i8* %[[T7]]) + +void testD3Destructor1(D3 *a) { + ::delete a; +} + +// CHECK-LABEL: define void @_Z17testD3Destructor2P2D3( +// CHECK: %[[T1:.*]] = bitcast %class.D3* %{{.*}} to %class.D3* (%class.D3*)*** +// CHECK: %[[VTABLE:.*]] = load %class.D3* (%class.D3*)**, %class.D3* (%class.D3*)*** %[[T1]] +// CHECK: %[[T2:.*]] = ptrtoint %class.D3* (%class.D3*)** %[[VTABLE]] to i64 +// CHECK: %[[T3:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:.*]] = inttoptr i64 %[[T3]] to %class.D3* (%class.D3*)** +// CHECK: %[[VFN:.*]] = getelementptr inbounds %class.D3* (%class.D3*)*, %class.D3* (%class.D3*)** %[[T4]], i64 2 +// CHECK: %[[T5:.*]] = load %class.D3* (%class.D3*)*, %class.D3* (%class.D3*)** %[[VFN]] +// CHECK: %[[T6:.*]] = ptrtoint %class.D3* (%class.D3*)** %[[VFN]] to i64 +// CHECK: %[[T7:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[T6]], i64 57279) +// CHECK: %call = call %class.D3* %[[T5]](%class.D3* %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3Destructor2(D3 *a) { + a->~D3(); +} + +void materializeConstructors() { + B0 B0; + B1 B1; + D0 D0; + D1 D1; + D2 D2; + D3 D3; + V0 V0; + V1 V1; +} + +// CHECK-LABEL: define linkonce_odr %class.B0* @_ZN2B0C2Ev( +// CHECK: %[[THIS:[0-9]+]] = bitcast %class.B0* %{{.*}} to i32 (...)*** +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i8** getelementptr inbounds ({ [7 x i8*] }, { [7 x i8*] }* @_ZTV2B0, i32 0, inrange i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to i32 (...)** +// CHECK: store i32 (...)** %[[SIGNED_VTADDR]], i32 (...)*** %[[THIS]] + +// CHECK-LABEL: define linkonce_odr %class.D0* @_ZN2D0C2Ev( +// CHECK: %[[THIS:[0-9]+]] = bitcast %class.D0* %{{.*}} to i32 (...)*** +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*] }, { [9 x i8*] }* @_ZTV2D0, i32 0, inrange i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to i32 (...)** +// CHECK: store i32 (...)** %[[SIGNED_VTADDR]], i32 (...)*** %[[THIS]] + +// CHECK-LABEL: define linkonce_odr %class.D1* @_ZN2D1C2Ev( +// CHECK: %[[THIS:[0-9]+]] = bitcast %class.D1* %{{.*}} to i32 (...)*** +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i8** getelementptr inbounds ({ [8 x i8*] }, { [8 x i8*] }* @_ZTV2D1, i32 0, inrange i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to i32 (...)** +// CHECK: store i32 (...)** %[[SIGNED_VTADDR]], i32 (...)*** %[[THIS]] + +// CHECK-LABEL: define linkonce_odr %class.D2* @_ZN2D2C2Ev( +// CHECK: %[[SLOT0:[0-9+]]] = bitcast %class.D2* %[[THIS:[a-z0-9]+]] to i32 (...)*** +// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, inrange i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[T1:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR0]] to i32 (...)** +// CHECK: store i32 (...)** %[[T1]], i32 (...)*** %[[SLOT0]] +// CHECK: %[[T2:[0-9]+]] = bitcast %class.D2* %[[THIS]] to i8* +// CHECK: %[[T3:[a-z0-9.]+]] = getelementptr inbounds i8, i8* %[[T2]], i64 16 +// CHECK: %[[SLOT1:[0-9]+]] = bitcast i8* %[[T3]] to i32 (...)*** +// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i8** getelementptr inbounds ({ [9 x i8*], [8 x i8*] }, { [9 x i8*], [8 x i8*] }* @_ZTV2D2, i32 0, inrange i32 1, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[T5:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR1]] to i32 (...)** +// CHECK: store i32 (...)** %[[T5]], i32 (...)*** %[[SLOT1]] + +// CHECK-LABEL: define linkonce_odr %class.V0* @_ZN2V0C2Ev( +// CHECK: %[[VTT:[a-z0-9]+]] = load i8**, i8*** %{{.*}} +// CHECK: %[[T0:[0-9]+]] = load i8*, i8** %[[VTT]] +// CHECK: %[[T1:[0-9]+]] = ptrtoint i8* %[[T0]] to i64 +// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T1]], i32 2, i64 0) +// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to i8* +// CHECK: %[[SLOT0:[0-9]+]] = bitcast %class.V0* %[[THIS:[a-z0-9]+]] to i32 (...)*** +// CHECK: %[[T5:[0-9]+]] = bitcast i8* %[[T3]] to i32 (...)** +// CHECK: %[[VTADDR0:[0-9]+]] = ptrtoint i32 (...)** %[[T5]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 %[[VTADDR0]], i32 2, i64 0) +// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = inttoptr i64 %[[T7]] to i32 (...)** +// CHECK: store i32 (...)** %[[SIGN_VTADDR0]], i32 (...)*** %[[SLOT0]] +// CHECK: %[[T9:[0-9]+]] = getelementptr inbounds i8*, i8** %[[VTT]], i64 1 +// CHECK: %[[T10:[0-9]+]] = load i8*, i8** %[[T9]] +// CHECK: %[[T11:[0-9]+]] = ptrtoint i8* %[[T10]] to i64 +// CHECK: %[[T12:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T11]], i32 2, i64 0) +// CHECK: %[[T13:[0-9]+]] = inttoptr i64 %[[T12]] to i8* +// CHECK: %[[T14:[0-9]+]] = bitcast %class.V0* %[[THIS]] to i8** +// CHECK: %[[VTABLE:[a-z]+]] = load i8*, i8** %[[T14]] +// CHECK: %[[T15:[0-9]+]] = ptrtoint i8* %[[VTABLE]] to i64 +// CHECK: %[[T16:[0-9]+]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[T15]], i32 2, i64 0) +// CHECK: %[[T17:[0-9]+]] = inttoptr i64 %[[T16]] to i8* +// CHECK: %[[VBASE_OFFSET_PTR:[a-z.]+]] = getelementptr i8, i8* %[[T17]], i64 -24 +// CHECK: %[[T18:[0-9]+]] = bitcast i8* %[[VBASE_OFFSET_PTR]] to i64* +// CHECK: %[[VBASE_OFFSET:[a-z.]+]] = load i64, i64* %[[T18]] +// CHECK: %[[T19:[0-9]+]] = bitcast %class.V0* %[[THIS]] to i8* +// CHECK: %[[T20:[a-z.]+]] = getelementptr inbounds i8, i8* %[[T19]], i64 %[[VBASE_OFFSET]] +// CHECK: %[[SLOT1:[0-9]+]] = bitcast i8* %[[T20]] to i32 (...)*** +// CHECK: %[[T21:[0-9]+]] = bitcast i8* %[[T13]] to i32 (...)** +// CHECK: %[[VTADDR1:[0-9]+]] = ptrtoint i32 (...)** %[[T21]] to i64 +// CHECK: %[[T23:[0-9]+]] = call i64 @llvm.ptrauth.sign.i64(i64 %[[VTADDR1]], i32 2, i64 0) +// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = inttoptr i64 %[[T23]] to i32 (...)** +// CHECK: store i32 (...)** %[[SIGN_VTADDR1]], i32 (...)*** %[[SLOT1]] From 8c976181e39305a61af5d369475933bf6aaf4f2a Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:04:46 -0400 Subject: [PATCH 535/582] Pointer authentication support for C++ member function pointers. Patch by Akira Hatanaka. --- .../include/clang/Basic/PointerAuthOptions.h | 3 + clang/lib/CodeGen/CGCall.cpp | 17 +- clang/lib/CodeGen/CGPointerAuth.cpp | 51 +++ clang/lib/CodeGen/CodeGenFunction.h | 3 +- clang/lib/CodeGen/CodeGenModule.h | 8 + clang/lib/CodeGen/ItaniumCXXABI.cpp | 222 +++++++++- clang/lib/Frontend/CompilerInvocation.cpp | 2 + .../ptrauth-member-function-pointer.cpp | 388 ++++++++++++++++++ 8 files changed, 680 insertions(+), 14 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 5aa18e07a5771..e04b8dbd034ca 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -169,6 +169,9 @@ struct PointerAuthOptions { /// The ABI for variadic C++ virtual function pointers. PointerAuthSchema CXXVirtualVariadicFunctionPointers; + + /// The ABI for C++ member function pointers. + PointerAuthSchema CXXMemberFunctionPointers; }; } // end namespace clang diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 8bab5cd2c6aa6..f1d2c95f54853 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3797,7 +3797,8 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, ReturnValueSlot ReturnValue, const CallArgList &CallArgs, llvm::CallBase **callOrInvoke, - SourceLocation Loc) { + SourceLocation Loc, + bool IsVirtualFunctionPointerThunk) { // FIXME: We no longer need the types from CallArgs; lift up and simplify. assert(Callee.isOrdinary() || Callee.isVirtual()); @@ -3872,7 +3873,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, Address SRetAlloca = Address::invalid(); llvm::Value *UnusedReturnSizePtr = nullptr; if (RetAI.isIndirect() || RetAI.isInAlloca() || RetAI.isCoerceAndExpand()) { - if (!ReturnValue.isNull()) { + if (IsVirtualFunctionPointerThunk && RetAI.isIndirect()) { + SRetPtr = Address(CurFn->arg_begin() + IRFunctionArgs.getSRetArgNo(), + CharUnits::fromQuantity(1)); + } else if (!ReturnValue.isNull()) { SRetPtr = ReturnValue.getValue(); } else { SRetPtr = CreateMemTemp(RetTy, "tmp", &SRetAlloca); @@ -4483,7 +4487,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, CallArgs.freeArgumentMemory(*this); // Extract the return value. - RValue Ret = [&] { + RValue Ret; + + // If the current function is a virtual function pointer thunk, avoid copying + // the return value of the musttail call to a temporary. + if (IsVirtualFunctionPointerThunk) + Ret = RValue::get(CI); + else + Ret = [&] { switch (RetAI.getKind()) { case ABIArgInfo::CoerceAndExpand: { auto coercionType = RetAI.getCoerceAndExpandType(); diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 69b1a1580b453..ed9e275daac78 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -98,6 +98,22 @@ CodeGenModule::getFunctionPointerAuthInfo(QualType functionType) { return CGPointerAuthInfo(schema.getKey(), discriminator); } +CGPointerAuthInfo +CodeGenModule::getMemberFunctionPointerAuthInfo(QualType functionType) { + assert(functionType->getAs<MemberPointerType>() && + "MemberPointerType expected"); + auto &schema = getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers; + if (!schema) + return CGPointerAuthInfo(); + + assert(!schema.isAddressDiscriminated() && + "function pointers cannot use address-specific discrimination"); + + auto discriminator = + getPointerAuthOtherDiscriminator(schema, GlobalDecl(), functionType); + return CGPointerAuthInfo(schema.getKey(), discriminator); +} + /// Return the natural pointer authentication for values of the given /// pointer type. static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM, @@ -432,6 +448,7 @@ void ConstantAggregateBuilderBase::addSignedPointer( void CodeGenModule::destroyConstantSignedPointerCaches() { destroyCache<ByConstantCacheTy>(ConstantSignedPointersByConstant); destroyCache<ByDeclCacheTy>(ConstantSignedPointersByDecl); + destroyCache<ByDeclCacheTy>(SignedThunkPointers); } llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *pointer, @@ -470,3 +487,37 @@ llvm::Constant *CodeGenModule::getFunctionPointer(const FunctionDecl *FD, llvm::Type *Ty) { return getFunctionPointer(getRawFunctionPointer(FD, Ty), FD->getType(), FD); } + +llvm::Constant * +CodeGenModule::getMemberFunctionPointer(llvm::Constant *pointer, + QualType functionType, + const FunctionDecl *FD) { + if (auto pointerAuth = getMemberFunctionPointerAuthInfo(functionType)) { + llvm::Constant **entry = nullptr; + if (FD) { + auto &cache = + getOrCreateCache<ByDeclCacheTy>(SignedThunkPointers); + entry = &cache[FD->getCanonicalDecl()]; + if (*entry) + return llvm::ConstantExpr::getBitCast(*entry, pointer->getType()); + } + + pointer = getConstantSignedPointer( + pointer, pointerAuth.getKey(), nullptr, + cast_or_null<llvm::Constant>(pointerAuth.getDiscriminator())); + + if (entry) + *entry = pointer; + } + + return pointer; +} + +llvm::Constant * +CodeGenModule::getMemberFunctionPointer(const FunctionDecl *FD, llvm::Type *Ty) { + QualType functionType = FD->getType(); + functionType = getContext().getMemberPointerType( + functionType, cast<CXXMethodDecl>(FD)->getParent()->getTypeForDecl()); + return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), functionType, + FD); +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index bab5b04f4287d..78bed1b2abed2 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3598,7 +3598,8 @@ class CodeGenFunction : public CodeGenTypeCache { /// LLVM arguments and the types they were derived from. RValue EmitCall(const CGFunctionInfo &CallInfo, const CGCallee &Callee, ReturnValueSlot ReturnValue, const CallArgList &Args, - llvm::CallBase **callOrInvoke, SourceLocation Loc); + llvm::CallBase **callOrInvoke, SourceLocation Loc, + bool IsVirtualFunctionPointerThunk = false); RValue EmitCall(const CGFunctionInfo &CallInfo, const CGCallee &Callee, ReturnValueSlot ReturnValue, const CallArgList &Args, llvm::CallBase **callOrInvoke = nullptr) { diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 1b084fc41c147..d6a22458ccf66 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -408,6 +408,7 @@ class CodeGenModule : public CodeGenTypeCache { /// Signed constant pointers. void *ConstantSignedPointersByDecl = nullptr; + void *SignedThunkPointers = nullptr; void *ConstantSignedPointersByConstant = nullptr; /// Map used to get unique annotation strings. @@ -871,6 +872,13 @@ class CodeGenModule : public CodeGenTypeCache { QualType functionType, const FunctionDecl *FD = nullptr); + llvm::Constant *getMemberFunctionPointer(const FunctionDecl *FD, + llvm::Type *Ty = nullptr); + + llvm::Constant *getMemberFunctionPointer(llvm::Constant *pointer, + QualType functionType, + const FunctionDecl *FD = nullptr); + CGPointerAuthInfo getFunctionPointerAuthInfo(QualType functionType); CGPointerAuthInfo getMemberFunctionPointerAuthInfo(QualType functionType); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 878da4d2f8bd0..1be29a40f420b 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -29,6 +29,7 @@ #include "clang/AST/Type.h" #include "clang/AST/StmtCXX.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" @@ -368,6 +369,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { bool NeedsVTTParameter(GlobalDecl GD) override; + llvm::Constant * + getOrCreateVirtualFunctionPointerThunk(const CXXMethodDecl *MD); + /**************************** RTTI Uniqueness ******************************/ protected: @@ -406,6 +410,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { const CXXRecordDecl *RD) override; private: + llvm::Constant * + getSignedVirtualMemberFunctionPointer(const CXXMethodDecl *MD); + bool hasAnyUnusedVirtualInlineFunction(const CXXRecordDecl *RD) const { const auto &VtableLayout = CGM.getItaniumVTableContext().getVTableLayout(RD); @@ -777,7 +784,23 @@ CGCallee ItaniumCXXABI::EmitLoadOfMemberFunctionPointer( CalleePtr->addIncoming(VirtualFn, FnVirtual); CalleePtr->addIncoming(NonVirtualFn, FnNonVirtual); - CGCallee Callee(FPT, CalleePtr); + CGPointerAuthInfo PointerAuth; + + if (const auto &Schema = + CGM.getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers) { + llvm::PHINode *DiscriminatorPHI = Builder.CreatePHI(CGF.IntPtrTy, 2); + DiscriminatorPHI->addIncoming(llvm::ConstantInt::get(CGF.IntPtrTy, 0), + FnVirtual); + const auto &AuthInfo = + CGM.getMemberFunctionPointerAuthInfo(QualType(MPT, 0)); + assert(Schema.getKey() == AuthInfo.getKey() && + "Keys for virtual and non-virtual member functions must match"); + auto *NonVirtualDiscriminator = AuthInfo.getDiscriminator(); + DiscriminatorPHI->addIncoming(NonVirtualDiscriminator, FnNonVirtual); + PointerAuth = CGPointerAuthInfo(Schema.getKey(), DiscriminatorPHI); + } + + CGCallee Callee(FPT, CalleePtr, PointerAuth); return Callee; } @@ -804,6 +827,26 @@ llvm::Value *ItaniumCXXABI::EmitMemberDataPointerAddress( return Builder.CreateBitCast(Addr, PType); } +// See if it's possible to return a constant signed pointer. +static llvm::Constant *pointerAuthResignConstant( + llvm::Value *Ptr, const CGPointerAuthInfo &CurAuthInfo, + const CGPointerAuthInfo &NewAuthInfo, CodeGenModule &CGM) { + Optional<llvm::GlobalPtrAuthInfo> Info = + llvm::GlobalPtrAuthInfo::analyze(Ptr); + + if (!Info || !isa<llvm::Constant>(NewAuthInfo.getDiscriminator())) + return nullptr; + + assert(Info->getKey()->getZExtValue() == CurAuthInfo.getKey() && + Info->getAddrDiscriminator()->isZeroValue() && + Info->getDiscriminator() == CurAuthInfo.getDiscriminator() && + "unexpected key or discriminators"); + + return CGM.getConstantSignedPointer( + Info->getPointer(), NewAuthInfo.getKey(), nullptr, + cast<llvm::Constant>(NewAuthInfo.getDiscriminator())); +} + /// Perform a bitcast, derived-to-base, or base-to-derived member pointer /// conversion. /// @@ -831,21 +874,62 @@ llvm::Value * ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, const CastExpr *E, llvm::Value *src) { + // Use constant emission if we can. + if (isa<llvm::Constant>(src)) + return EmitMemberPointerConversion(E, cast<llvm::Constant>(src)); + assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || E->getCastKind() == CK_BaseToDerivedMemberPointer || E->getCastKind() == CK_ReinterpretMemberPointer); + CGBuilderTy &Builder = CGF.Builder; + QualType dstType = E->getType(); + + if (dstType->isMemberFunctionPointerType()) + if (const auto &newAuthInfo = + CGM.getMemberFunctionPointerAuthInfo(dstType)) { + QualType srcType = E->getSubExpr()->getType(); + assert(srcType->isMemberFunctionPointerType()); + const auto &curAuthInfo = CGM.getMemberFunctionPointerAuthInfo(srcType); + llvm::Value *memFnPtr = Builder.CreateExtractValue(src, 0, "memptr.ptr"); + llvm::Type *origTy = memFnPtr->getType(); + + llvm::BasicBlock *startBB = Builder.GetInsertBlock(); + llvm::BasicBlock *resignBB = CGF.createBasicBlock("resign"); + llvm::BasicBlock *mergeBB = CGF.createBasicBlock("merge"); + + // Check whether we have a virtual offset or a pointer to a function. + assert(UseARMMethodPtrABI && "ARM ABI expected"); + llvm::Value *adj = Builder.CreateExtractValue(src, 1, "memptr.adj"); + llvm::Constant *ptrdiff_1 = llvm::ConstantInt::get(CGM.PtrDiffTy, 1); + llvm::Value *andVal = Builder.CreateAnd(adj, ptrdiff_1); + llvm::Value *isVirtualOffset = + Builder.CreateIsNotNull(andVal, "is.virtual.offset"); + Builder.CreateCondBr(isVirtualOffset, mergeBB, resignBB); + + CGF.EmitBlock(resignBB); + llvm::Type *ptrTy = llvm::PointerType::getUnqual(CGM.Int8Ty); + memFnPtr = Builder.CreateIntToPtr(memFnPtr, ptrTy); + memFnPtr = CGF.EmitPointerAuthResign(memFnPtr, srcType, curAuthInfo, + newAuthInfo, + isa<llvm::Constant>(src)); + memFnPtr = Builder.CreatePtrToInt(memFnPtr, origTy); + llvm::Value *resignedVal = Builder.CreateInsertValue(src, memFnPtr, 0); + resignBB = Builder.GetInsertBlock(); + + CGF.EmitBlock(mergeBB); + llvm::PHINode *newSrc = Builder.CreatePHI(src->getType(), 2); + newSrc->addIncoming(src, startBB); + newSrc->addIncoming(resignedVal, resignBB); + src = newSrc; + } + // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; - // Use constant emission if we can. - if (isa<llvm::Constant>(src)) - return EmitMemberPointerConversion(E, cast<llvm::Constant>(src)); - llvm::Constant *adj = getMemberPointerAdjustment(E); if (!adj) return src; - CGBuilderTy &Builder = CGF.Builder; bool isDerivedToBase = (E->getCastKind() == CK_DerivedToBaseMemberPointer); const MemberPointerType *destTy = @@ -890,6 +974,22 @@ ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E, E->getCastKind() == CK_BaseToDerivedMemberPointer || E->getCastKind() == CK_ReinterpretMemberPointer); + QualType dstType = E->getType(); + + if (dstType->isMemberFunctionPointerType()) + if (const auto &newAuthInfo = + CGM.getMemberFunctionPointerAuthInfo(dstType)) { + assert(UseARMMethodPtrABI && "ARM ABI expected"); + QualType srcType = E->getSubExpr()->getType(); + const auto &curAuthInfo = CGM.getMemberFunctionPointerAuthInfo(srcType); + llvm::Constant *memFnPtr = llvm::ConstantExpr::getExtractValue(src, 0); + llvm::Constant *constPtr = + pointerAuthResignConstant(cast<llvm::User>(memFnPtr)->getOperand(0), + curAuthInfo, newAuthInfo, CGM); + constPtr = llvm::ConstantExpr::getPtrToInt(constPtr, memFnPtr->getType()); + src = llvm::ConstantExpr::getInsertValue(src, constPtr, 0); + } + // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; @@ -980,9 +1080,33 @@ llvm::Constant *ItaniumCXXABI::BuildMemberPointer(const CXXMethodDecl *MD, // least significant bit of adj then makes exactly the same // discrimination as the least significant bit of ptr does for // Itanium. - MemPtr[0] = llvm::ConstantInt::get(CGM.PtrDiffTy, VTableOffset); - MemPtr[1] = llvm::ConstantInt::get(CGM.PtrDiffTy, - 2 * ThisAdjustment.getQuantity() + 1); + + // We cannot use the Itanium ABI's representation for virtual member + // function pointers under pointer authentication because it would + // require us to store both the virtual offset and the constant + // discriminator in the pointer, which would be immediately vulnerable + // to attack. Instead we introduce a thunk that does the virtual dispatch + // and store it as if it were a non-virtual member function. This means + // that virtual function pointers may not compare equal anymore, but + // fortunately they aren't required to by the standard, and we do make + // a best-effort attempt to re-use the thunk. + // + // To support interoperation with code in which pointer authentication + // is disabled, derefencing a member function pointer must still handle + // the virtual case, but it can use a discriminator which should never + // be valid. + const auto &Schema = + CGM.getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers; + if (Schema) + MemPtr[0] = llvm::ConstantExpr::getPtrToInt( + getSignedVirtualMemberFunctionPointer(MD), CGM.PtrDiffTy); + else + MemPtr[0] = llvm::ConstantInt::get(CGM.PtrDiffTy, VTableOffset); + // Don't set the LSB of adj to 1 if pointer authentication for member + // function pointers is enabled. + MemPtr[1] = + llvm::ConstantInt::get(CGM.PtrDiffTy, + 2 * ThisAdjustment.getQuantity() + !Schema); } else { // Itanium C++ ABI 2.3: // For a virtual function, [the pointer field] is 1 plus the @@ -1004,7 +1128,7 @@ llvm::Constant *ItaniumCXXABI::BuildMemberPointer(const CXXMethodDecl *MD, // function type is incomplete. Ty = CGM.PtrDiffTy; } - llvm::Constant *addr = CGM.GetAddrOfFunction(MD, Ty); + llvm::Constant *addr = CGM.getMemberFunctionPointer(MD, Ty); MemPtr[0] = llvm::ConstantExpr::getPtrToInt(addr, CGM.PtrDiffTy); MemPtr[1] = llvm::ConstantInt::get(CGM.PtrDiffTy, @@ -2781,6 +2905,72 @@ bool ItaniumCXXABI::NeedsVTTParameter(GlobalDecl GD) { return false; } +llvm::Constant * +ItaniumCXXABI::getOrCreateVirtualFunctionPointerThunk(const CXXMethodDecl *MD) { + SmallString<256> MethodName; + llvm::raw_svector_ostream Out(MethodName); + getMangleContext().mangleCXXName(MD, Out); + MethodName += "_vfpthunk_"; + StringRef ThunkName = MethodName.str(); + llvm::Function *ThunkFn; + if ((ThunkFn = cast_or_null<llvm::Function>( + CGM.getModule().getNamedValue(ThunkName)))) + return ThunkFn; + + const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeCXXMethodDeclaration(MD); + llvm::FunctionType *ThunkTy = CGM.getTypes().GetFunctionType(FnInfo); + llvm::GlobalValue::LinkageTypes Linkage = + MD->isExternallyVisible() ? llvm::GlobalValue::LinkOnceODRLinkage + : llvm::GlobalValue::InternalLinkage; + ThunkFn = + llvm::Function::Create(ThunkTy, Linkage, ThunkName, &CGM.getModule()); + ThunkFn->setVisibility(llvm::GlobalValue::HiddenVisibility); + assert(ThunkFn->getName() == ThunkName && "name was uniqued!"); + + CGM.SetLLVMFunctionAttributes(MD, FnInfo, ThunkFn); + CGM.SetLLVMFunctionAttributesForDefinition(MD, ThunkFn); + + // Start codegen. + CodeGenFunction CGF(CGM); + CGF.CurGD = GlobalDecl(MD); + CGF.CurFuncIsThunk = true; + + // Build FunctionArgs. + FunctionArgList FunctionArgs; + CGF.BuildFunctionArgList(CGF.CurGD, FunctionArgs); + + CGF.StartFunction(GlobalDecl(), FnInfo.getReturnType(), ThunkFn, FnInfo, + FunctionArgs, MD->getLocation(), SourceLocation()); + llvm::Value *ThisVal = loadIncomingCXXThis(CGF); + setCXXABIThisValue(CGF, ThisVal); + + CallArgList CallArgs; + for (const VarDecl *VD : FunctionArgs) + CGF.EmitDelegateCallArg(CallArgs, VD, SourceLocation()); + + const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>(); + RequiredArgs Required = RequiredArgs::forPrototypePlus(FPT, /*this*/ 1); + const CGFunctionInfo &CallInfo = + CGM.getTypes().arrangeCXXMethodCall(CallArgs, FPT, Required, 0); + CGCallee Callee = CGCallee::forVirtual(nullptr, GlobalDecl(MD), + getThisAddress(CGF), ThunkTy); + llvm::CallBase *CallOrInvoke; + CGF.EmitCall(CallInfo, Callee, ReturnValueSlot(), CallArgs, &CallOrInvoke, + SourceLocation(), true); + auto *Call = cast<llvm::CallInst>(CallOrInvoke); + Call->setTailCallKind(llvm::CallInst::TCK_MustTail); + if (Call->getType()->isVoidTy()) + CGF.Builder.CreateRetVoid(); + else + CGF.Builder.CreateRet(Call); + + // Finish the function to maintain CodeGenFunction invariants. + // FIXME: Don't emit unreachable code. + CGF.EmitBlock(CGF.createBasicBlock()); + CGF.FinishFunction(); + return ThunkFn; +} + namespace { class ItaniumRTTIBuilder { CodeGenModule &CGM; // Per-module state. @@ -4405,6 +4595,18 @@ ItaniumCXXABI::LoadVTablePtr(CodeGenFunction &CGF, Address This, return {CGF.GetVTablePtr(This, CGM.Int8PtrTy, RD), RD}; } +llvm::Constant * +ItaniumCXXABI::getSignedVirtualMemberFunctionPointer(const CXXMethodDecl *MD) { + const CXXMethodDecl *origMD = + cast<CXXMethodDecl>(CGM.getItaniumVTableContext() + .findOriginalMethod(MD->getCanonicalDecl()) + .getDecl()); + llvm::Constant *thunk = getOrCreateVirtualFunctionPointerThunk(origMD); + QualType funcType = CGM.getContext().getMemberPointerType( + MD->getType(), MD->getParent()->getTypeForDecl()); + return CGM.getMemberFunctionPointer(thunk, funcType, MD); +} + void WebAssemblyCXXABI::emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) { if (CGF.getTarget().hasFeature("exception-handling")) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index ef2b75c97ea43..b29aa3611ff96 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -695,6 +695,8 @@ static bool parsePointerAuthOptions(PointerAuthOptions &Opts, Opts.CXXVirtualFunctionPointers = Opts.CXXVirtualVariadicFunctionPointers = PointerAuthSchema(Key::ASIA, true, Discrimination::Decl); + Opts.CXXMemberFunctionPointers = + PointerAuthSchema(Key::ASIA, false, Discrimination::Type); Opts.ThunkCXXVirtualMemberPointers = false; } diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp new file mode 100644 index 0000000000000..9e2c10d8a3337 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp @@ -0,0 +1,388 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK %s + +// CHECK: %[[STRUCT_BASE0:.*]] = type { i32 (...)** } +// CHECK: %[[STRUCT_DERIVED0:.*]] = type { %[[STRUCT_BASE0]] } +// CHECK: %[[STRUCT_A0:.*]] = type { [4 x i32] } +// CHECK: %[[STRUCT_A1:.*]] = type { [8 x i32] } +// CHECK: %[[STRUCT_TRIVIALS:.*]] = type { [4 x i32] } +// CHECK: %[[STRUCT_BASE1:.*]] = type { i32 (...)** } +// CHECK: %[[STRUCT_DERIVED1:.*]] = type { %[[STRUCT_BASE0]], %[[STRUCT_BASE1]] } + +// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base011nonvirtual0Ev to i8*), i32 0, i64 0, i64 [[TYPEDISC0:22163]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base08virtual1Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base08virtual3Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*, i32, ...)* @_ZN5Base016virtual_variadicEiz_vfpthunk_ to i8*), i32 0, i64 0, i64 34368 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth.1 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base011nonvirtual0Ev to i8*), i32 0, i64 0, i64 [[TYPEDISC1:35591]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base08virtual1Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base08virtual3Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_DERIVED0]]*)* @_ZN8Derived011nonvirtual5Ev to i8*), i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_DERIVED0]]*)* @_ZN8Derived08virtual6Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast ([2 x i64] (%[[STRUCT_DERIVED0]]*)* @_ZN8Derived010return_aggEv_vfpthunk_ to i8*), i32 0, i64 0, i64 64418 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived04sretEv_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_A1]]*, %[[STRUCT_DERIVED0]]*)* @_ZN8Derived04sretEv_vfpthunk_ to i8*), i32 0, i64 0, i64 28187 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_DERIVED0]]*, [2 x i64])* @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_ to i8*), i32 0, i64 0, i64 8992 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE1]]*)* @_ZN5Base18virtual7Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 [[TYPEDISC2:61596]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_DERIVED1]]*)* @_ZN8Derived18virtual7Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base08virtual1Ev_vfpthunk_ to i8*), i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 + +// CHECK: @gmethod0 = global { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, align 8 +// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth.6 = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%struct.Derived0*)* @_ZN8Derived011nonvirtual5Ev to i8*), i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @gmethod1 = global { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN8Derived011nonvirtual5Ev.ptrauth.6 to i64), i64 0 }, align 8 +// CHECK: @gmethod2 = global { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, align 8 + +// CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x i8*] } { [5 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI5Base0 to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual1Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual3Ev.ptrauth to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN5Base016virtual_variadicEiz.ptrauth to i8*)] }, align 8 +// CHECK: @_ZN5Base08virtual1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base08virtual1Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [5 x i8*] }, { [5 x i8*] }* @_ZTV5Base0, i32 0, i32 0, i32 2) to i64), i64 55600 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*)* @_ZN5Base08virtual3Ev to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [5 x i8*] }, { [5 x i8*] }* @_ZTV5Base0, i32 0, i32 0, i32 3) to i64), i64 53007 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base016virtual_variadicEiz.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void (%[[STRUCT_BASE0]]*, i32, ...)* @_ZN5Base016virtual_variadicEiz to i8*), i32 0, i64 ptrtoint (i8** getelementptr inbounds ({ [5 x i8*] }, { [5 x i8*] }* @_ZTV5Base0, i32 0, i32 0, i32 4) to i64), i64 7464 }, section "llvm.ptrauth", align 8 + +struct Base0 { + void nonvirtual0(); + virtual void virtual1(); + virtual void virtual3(); + virtual void virtual_variadic(int, ...); +}; + +struct A0 { + int d[4]; +}; + +struct A1 { + int d[8]; +}; + +struct __attribute__((trivial_abi)) TrivialS { + TrivialS(const TrivialS &); + ~TrivialS(); + int p[4]; +}; + +struct Derived0 : Base0 { + void virtual1() override; + void nonvirtual5(); + virtual void virtual6(); + virtual A0 return_agg(); + virtual A1 sret(); + virtual void trivial_abi(TrivialS); +}; + +struct Base1 { + virtual void virtual7(); +}; + +struct Derived1 : Base0, Base1 { + void virtual1() override; + void virtual7() override; +}; + +typedef void (Base0::*MethodTy0)(); +typedef void (Base0::*VariadicMethodTy0)(int, ...); +typedef void (Derived0::*MethodTy1)(); + +// CHECK: define void @_ZN5Base08virtual1Ev( + +// CHECK: define void @_Z5test0v() +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[VARMETHOD1:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD2:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD3:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD4:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD5:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD6:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD7:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base011nonvirtual0Ev.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD0]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[VARMETHOD1]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, { i64, i64 }* %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 to i64), i64 0 }, { i64, i64 }* %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 to i64), i64 0 }, { i64, i64 }* %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN8Derived011nonvirtual5Ev.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD2]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD3]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN8Derived04sretEv_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD4]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD5]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD6]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD7]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 to i64), i64 0 }, { i64, i64 }* %[[METHOD7]], align 8 +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base08virtual1Ev_vfpthunk_(%[[STRUCT_BASE0]]* %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca %[[STRUCT_BASE0]]*, align 8 +// CHECK: store %[[STRUCT_BASE0]]* %[[THIS]], %[[STRUCT_BASE0]]** %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load %[[STRUCT_BASE0]]*, %[[STRUCT_BASE0]]** %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V0:.*]] = load %[[STRUCT_BASE0]]*, %[[STRUCT_BASE0]]** %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V1:.*]] = bitcast %[[STRUCT_BASE0]]* %[[THIS1]] to void (%[[STRUCT_BASE0]]*)*** +// CHECK-NEXT: %[[VTABLE:.*]] = load void (%[[STRUCT_BASE0]]*)**, void (%[[STRUCT_BASE0]]*)*** %[[V1]], align 8 +// CHECK-NEXT: %[[V2:.*]] = ptrtoint void (%[[STRUCT_BASE0]]*)** %[[VTABLE]] to i64 +// CHECK-NEXT: %[[V3:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[V2]], i32 2, i64 0) +// CHECK-NEXT: %[[V4:.*]] = inttoptr i64 %[[V3]] to void (%[[STRUCT_BASE0]]*)** +// CHECK-NEXT: %[[VFN:.*]] = getelementptr inbounds void (%[[STRUCT_BASE0]]*)*, void (%[[STRUCT_BASE0]]*)** %[[V4]], i64 0 +// CHECK-NEXT: %[[V5:.*]] = load void (%[[STRUCT_BASE0]]*)*, void (%[[STRUCT_BASE0]]*)** %[[VFN]], align 8 +// CHECK-NEXT: %[[V6:.*]] = ptrtoint void (%[[STRUCT_BASE0]]*)** %[[VFN]] to i64 +// CHECK-NEXT: %[[V7:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V6]], i64 55600) +// CHECK-NEXT: musttail call void %[[V5]](%[[STRUCT_BASE0]]* %[[V0]]) [ "ptrauth"(i32 0, i64 %[[V7]]) ] +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base08virtual3Ev_vfpthunk_(%[[STRUCT_BASE0]]* %{{.*}}) +// CHECK: %[[VTABLE:.*]] = load void (%[[STRUCT_BASE0]]*)**, void (%[[STRUCT_BASE0]]*)*** %{{.*}}, align 8 +// CHECK: %[[V2:.*]] = ptrtoint void (%[[STRUCT_BASE0]]*)** %[[VTABLE]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[V2]], i32 2, i64 0) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to void (%[[STRUCT_BASE0]]*)** +// CHECK: getelementptr inbounds void (%[[STRUCT_BASE0]]*)*, void (%[[STRUCT_BASE0]]*)** %[[V4]], i64 1 +// CHECK: call i64 @llvm.ptrauth.blend.i64(i64 %{{.*}}, i64 53007) + +// CHECK: define linkonce_odr hidden void @_ZN5Base016virtual_variadicEiz_vfpthunk_(%[[STRUCT_BASE0]]* %[[THIS:.*]], i32 %0, ...) +// CHECK: %[[THIS_ADDR:.*]] = alloca %[[STRUCT_BASE0]]*, align 8 +// CHECK-NEXT: %[[_ADDR:.*]] = alloca i32, align 4 +// CHECK-NEXT: store %[[STRUCT_BASE0]]* %[[THIS]], %[[STRUCT_BASE0]]** %[[THIS_ADDR]], align 8 +// CHECK: store i32 %0, i32* %[[_ADDR]], align 4 +// CHECK: %[[THIS1:.*]] = load %[[STRUCT_BASE0]]*, %[[STRUCT_BASE0]]** %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V1:.*]] = load %[[STRUCT_BASE0]]*, %[[STRUCT_BASE0]]** %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V2:.*]] = load i32, i32* %[[_ADDR]], align 4 +// CHECK-NEXT: %[[V3:.*]] = bitcast %[[STRUCT_BASE0]]* %[[THIS1]] to void (%[[STRUCT_BASE0]]*, i32, ...)*** +// CHECK-NEXT: %[[VTABLE:.*]] = load void (%[[STRUCT_BASE0]]*, i32, ...)**, void (%[[STRUCT_BASE0]]*, i32, ...)*** %[[V3]], align 8 +// CHECK-NEXT: %[[V4:.*]] = ptrtoint void (%[[STRUCT_BASE0]]*, i32, ...)** %[[VTABLE]] to i64 +// CHECK-NEXT: %[[V5:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[V4]], i32 2, i64 0) +// CHECK-NEXT: %[[V6:.*]] = inttoptr i64 %[[V5]] to void (%[[STRUCT_BASE0]]*, i32, ...)** +// CHECK-NEXT: %[[VFN:.*]] = getelementptr inbounds void (%[[STRUCT_BASE0]]*, i32, ...)*, void (%[[STRUCT_BASE0]]*, i32, ...)** %[[V6]], i64 2 +// CHECK-NEXT: %[[V7:.*]] = load void (%[[STRUCT_BASE0]]*, i32, ...)*, void (%[[STRUCT_BASE0]]*, i32, ...)** %[[VFN]], align 8 +// CHECK-NEXT: %[[V8:.*]] = ptrtoint void (%[[STRUCT_BASE0]]*, i32, ...)** %[[VFN]] to i64 +// CHECK-NEXT: %[[V9:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V8]], i64 7464) +// CHECK-NEXT: musttail call void (%[[STRUCT_BASE0]]*, i32, ...) %[[V7]](%[[STRUCT_BASE0]]* %[[V1]], i32 %[[V2]], ...) [ "ptrauth"(i32 0, i64 %[[V9]]) ] +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN8Derived08virtual6Ev_vfpthunk_(%[[STRUCT_DERIVED0]]* %{{.*}}) +// CHECK: %[[VTABLE:.*]] = load void (%[[STRUCT_DERIVED0]]*)**, void (%[[STRUCT_DERIVED0]]*)*** %[[V1]], align 8 +// CHECK: %[[V2:.*]] = ptrtoint void (%[[STRUCT_DERIVED0]]*)** %[[VTABLE]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[V2]], i32 2, i64 0) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to void (%[[STRUCT_DERIVED0]]*)** +// CHECK: getelementptr inbounds void (%[[STRUCT_DERIVED0]]*)*, void (%[[STRUCT_DERIVED0]]*)** %[[V4]], i64 3 +// CHECK: call i64 @llvm.ptrauth.blend.i64(i64 %{{.*}}, i64 55535) + +// Check that the return value of the musttail call isn't copied to a temporary. + +// CHECK: define linkonce_odr hidden [2 x i64] @_ZN8Derived010return_aggEv_vfpthunk_(%[[STRUCT_DERIVED0]]* %{{.*}}) +// CHECK: %[[CALL:.*]] = musttail call [2 x i64] %{{.*}}(%[[STRUCT_DERIVED0]]* %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] +// CHECK-NEXT: ret [2 x i64] %[[CALL]] + +// Check that the sret pointer passed to the caller is forwarded to the musttail +// call. + +// CHECK: define linkonce_odr hidden void @_ZN8Derived04sretEv_vfpthunk_(%[[STRUCT_A1]]* noalias sret %[[AGG_RESULT:.*]], %[[STRUCT_DERIVED0]]* %{{.*}}) +// CHECK: musttail call void %{{.*}}(%[[STRUCT_A1]]* sret %[[AGG_RESULT]], %[[STRUCT_DERIVED0]]* %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] +// CHECK-NEXT: ret void + +// Check that the thunk function doesn't destruct the trivial_abi argument. + +// CHECK: define linkonce_odr hidden void @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_(%[[STRUCT_DERIVED0]]* %{{.*}}, [2 x i64] %{{.*}}) +// NODEBUG-NOT: call +// CHECK: call i64 @llvm.ptrauth.auth.i64( +// NODEBUG-NOT: call +// CHECK: call i64 @llvm.ptrauth.blend.i64( +// NODEBUG-NOT: call +// CHECK: musttail call void +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base18virtual7Ev_vfpthunk_(%[[STRUCT_BASE1]]* %{{.*}}) +// CHECK: %[[VTABLE:.*]] = load void (%[[STRUCT_BASE1]]*)**, void (%[[STRUCT_BASE1]]*)*** %{{.*}}, align 8 +// CHECK: %[[V2:.*]] = ptrtoint void (%[[STRUCT_BASE1]]*)** %[[VTABLE]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[V2]], i32 2, i64 0) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to void (%[[STRUCT_BASE1]]*)** +// CHECK: getelementptr inbounds void (%[[STRUCT_BASE1]]*)*, void (%[[STRUCT_BASE1]]*)** %[[V4]], i64 0 + +// CHECK: define linkonce_odr hidden void @_ZN8Derived18virtual7Ev_vfpthunk_(%[[STRUCT_DERIVED1]]* %{{.*}}) +// CHECK: %[[VTABLE:.*]] = load void (%[[STRUCT_DERIVED1]]*)**, void (%[[STRUCT_DERIVED1]]*)*** %[[V1]], align 8 +// CHECK: %[[V2:.*]] = ptrtoint void (%[[STRUCT_DERIVED1]]*)** %[[VTABLE]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[V2]], i32 2, i64 0) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to void (%[[STRUCT_DERIVED1]]*)** +// CHECK: getelementptr inbounds void (%[[STRUCT_DERIVED1]]*)*, void (%[[STRUCT_DERIVED1]]*)** %[[V4]], i64 3 + +void Base0::virtual1() {} + +void test0() { + MethodTy0 method0; + method0 = &Base0::nonvirtual0; + method0 = &Base0::virtual1; + method0 = &Base0::virtual3; + + VariadicMethodTy0 varmethod1; + varmethod1 = &Base0::virtual_variadic; + + MethodTy1 method2; + method2 = &Derived0::nonvirtual0; + method2 = &Derived0::virtual1; + method2 = &Derived0::virtual3; + method2 = &Derived0::nonvirtual5; + method2 = &Derived0::virtual6; + + A0 (Derived0::*method3)(); + method3 = &Derived0::return_agg; + + A1 (Derived0::*method4)(); + method4 = &Derived0::sret; + + void (Derived0::*method5)(TrivialS); + method5 = &Derived0::trivial_abi; + + void (Base1::*method6)(); + method6 = &Base1::virtual7; + + void (Derived1::*method7)(); + method7 = &Derived1::virtual7; + method7 = &Derived1::virtual1; +} + +// CHECK: define void @_Z5test1P5Base0MS_FvvE(%[[STRUCT_BASE0]]* %[[A0:.*]], [2 x i64] %[[A1_COERCE:.*]]) +// CHECK: %[[A1:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[A0_ADDR:.*]] = alloca %[[STRUCT_BASE0]]*, align 8 +// CHECK: %[[A1_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[V0:.*]] = bitcast { i64, i64 }* %[[A1]] to [2 x i64]* +// CHECK: store [2 x i64] %[[A1_COERCE]], [2 x i64]* %[[V0]], align 8 +// CHECK: %[[A11:.*]] = load { i64, i64 }, { i64, i64 }* %[[A1]], align 8 +// CHECK: store %[[STRUCT_BASE0]]* %[[A0]], %[[STRUCT_BASE0]]** %[[A0_ADDR]], align 8 +// CHECK: store { i64, i64 } %[[A11]], { i64, i64 }* %[[A1_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load %[[STRUCT_BASE0]]*, %[[STRUCT_BASE0]]** %[[A0_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load { i64, i64 }, { i64, i64 }* %[[A1_ADDR]], align 8 +// CHECK: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V2]], 1 +// CHECK: %[[MEMPTR_ADJ_SHIFTED:.*]] = ashr i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_BASE0]]* %[[V1]] to i8* +// CHECK: %[[V4:.*]] = getelementptr inbounds i8, i8* %[[V3]], i64 %[[MEMPTR_ADJ_SHIFTED]] +// CHECK: %[[THIS_ADJUSTED:.*]] = bitcast i8* %[[V4]] to %[[STRUCT_BASE0]]* +// CHECK: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V2]], 0 +// CHECK: %[[V5:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[MEMPTR_ISVIRTUAL:.*]] = icmp ne i64 %[[V5]], 0 +// CHECK: br i1 %[[MEMPTR_ISVIRTUAL]] + +// CHECK: %[[V6:.*]] = bitcast %[[STRUCT_BASE0]]* %[[THIS_ADJUSTED]] to i8** +// CHECK: %[[VTABLE:.*]] = load i8*, i8** %[[V6]], align 8 +// CHECK: %[[V7:.*]] = ptrtoint i8* %[[VTABLE]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[V7]], i32 2, i64 0) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to i8* +// CHECK: %[[V10:.*]] = trunc i64 %[[MEMPTR_PTR]] to i32 +// CHECK: %[[V11:.*]] = zext i32 %[[V10]] to i64 +// CHECK: %[[V12:.*]] = getelementptr i8, i8* %[[V9]], i64 %[[V11]] +// CHECK: %[[V13:.*]] = bitcast i8* %[[V12]] to void (%[[STRUCT_BASE0]]*)** +// CHECK: %[[MEMPTR_VIRTUALFN:.*]] = load void (%[[STRUCT_BASE0]]*)*, void (%[[STRUCT_BASE0]]*)** %[[V13]], align 8 +// CHECK: br + +// CHECK: %[[MEMPTR_NONVIRTUALFN:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to void (%[[STRUCT_BASE0]]*)* +// CHECK: br + +// CHECK: %[[V14:.*]] = phi void (%[[STRUCT_BASE0]]*)* [ %[[MEMPTR_VIRTUALFN]], {{.*}} ], [ %[[MEMPTR_NONVIRTUALFN]], {{.*}} ] +// CHECK: %[[V15:.*]] = phi i64 [ 0, {{.*}} ], [ [[TYPEDISC0]], {{.*}} ] +// CHECK: call void %[[V14]](%[[STRUCT_BASE0]]* %[[THIS_ADJUSTED]]) [ "ptrauth"(i32 0, i64 %[[V15]]) ] +// CHECK: ret void + +void test1(Base0 *a0, MethodTy0 a1) { + (a0->*a1)(); +} + +// CHECK: define void @_Z15testConversion0M5Base0FvvEM8Derived0FvvE([2 x i64] %[[METHOD0_COERCE:.*]], [2 x i64] %[[METHOD1_COERCE:.*]]) +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD1:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD0_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD1_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[V0:.*]] = bitcast { i64, i64 }* %[[METHOD0]] to [2 x i64]* +// CHECK: store [2 x i64] %[[METHOD0_COERCE]], [2 x i64]* %[[V0]], align 8 +// CHECK: %[[METHOD01:.*]] = load { i64, i64 }, { i64, i64 }* %[[METHOD0]], align 8 +// CHECK: %[[V1:.*]] = bitcast { i64, i64 }* %[[METHOD1]] to [2 x i64]* +// CHECK: store [2 x i64] %[[METHOD1_COERCE]], [2 x i64]* %[[V1]], align 8 +// CHECK: %[[METHOD12:.*]] = load { i64, i64 }, { i64, i64 }* %[[METHOD1]], align 8 +// CHECK: store { i64, i64 } %[[METHOD01]], { i64, i64 }* %[[METHOD0_ADDR]], align 8 +// CHECK: store { i64, i64 } %[[METHOD12]], { i64, i64 }* %[[METHOD1_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load { i64, i64 }, { i64, i64 }* %[[METHOD0_ADDR]], align 8 +// CHECK: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V2]], 0 +// CHECK: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V2]], 1 +// CHECK: %[[V3:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[IS_VIRTUAL_OFFSET:.*]] = icmp ne i64 %[[V3]], 0 +// CHECK: br i1 %[[IS_VIRTUAL_OFFSET]] + +// CHECK: %[[V4:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to i8* +// CHECK: %[[V5:.*]] = icmp ne i8* %[[V4]], null +// CHECK: br i1 %[[V5]] + +// CHECK: %[[V6:.*]] = ptrtoint i8* %[[V4]] to i64 +// CHECK: %[[V7:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V6]], i32 0, i64 [[TYPEDISC0]], i32 0, i64 [[TYPEDISC1]]) +// CHECK: %[[V8:.*]] = inttoptr i64 %[[V7]] to i8* +// CHECK: br + +// CHECK: %[[V9:.*]] = phi i8* [ null, {{.*}} ], [ %[[V8]], {{.*}} ] +// CHECK: %[[V1:.*]] = ptrtoint i8* %[[V9]] to i64 +// CHECK: %[[V11:.*]] = insertvalue { i64, i64 } %[[V2]], i64 %[[V10]], 0 +// CHECK: br + +// CHECK: %[[V12:.*]] = phi { i64, i64 } [ %[[V2]], {{.*}} ], [ %[[V11]], {{.*}} ] +// CHECK: store { i64, i64 } %[[V12]], { i64, i64 }* %[[METHOD1_ADDR]], align 8 +// CHECK: ret void + +void testConversion0(MethodTy0 method0, MethodTy1 method1) { + method1 = method0; +} + +// CHECK: define void @_Z15testConversion1M5Base0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign.i64(i64 %{{.*}}, i32 0, i64 [[TYPEDISC0]], i32 0, i64 [[TYPEDISC1]]) + +void testConversion1(MethodTy0 method0) { + MethodTy1 method1 = reinterpret_cast<MethodTy1>(method0); +} + +// CHECK: define void @_Z15testConversion2M8Derived0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign.i64(i64 %{{.*}}, i32 0, i64 [[TYPEDISC1]], i32 0, i64 [[TYPEDISC0]]) + +void testConversion2(MethodTy1 method1) { + MethodTy0 method0 = static_cast<MethodTy0>(method1); +} + +// CHECK: define void @_Z15testConversion3M8Derived0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign.i64(i64 %{{.*}}, i32 0, i64 [[TYPEDISC1]], i32 0, i64 [[TYPEDISC0]]) + +void testConversion3(MethodTy1 method1) { + MethodTy0 method0 = reinterpret_cast<MethodTy0>(method1); +} + +// No need to call @llvm.ptrauth.resign.i64 if the source member function +// pointer is a constant. + +// CHECK: define void @_Z15testConversion4v( +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint ({ i8*, i32, i64, i64 }* @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, { i64, i64 }* %[[METHOD0]], align 8 +// CHECK: ret void + +void testConversion4() { + MethodTy0 method0 = reinterpret_cast<MethodTy0>(&Derived0::virtual1); +} + +// This code used to crash. +namespace testNonVirtualThunk { + struct R {}; + + struct B0 { + virtual void bar(); + }; + + struct B1 { + virtual R foo(); + }; + + struct D : B0, B1 { + virtual R foo(); + }; + + D d; +} + +// CHECK: define void @_Z39test_builtin_ptrauth_type_discriminatorv() +// CHECK: store i32 [[TYPEDISC0]], i32* % +// CHECK: store i32 [[TYPEDISC1]], i32* % +// CHECK: store i32 [[TYPEDISC2]], i32* % + +void test_builtin_ptrauth_type_discriminator() { + unsigned d; + d = __builtin_ptrauth_type_discriminator(decltype(&Base0::virtual1)); + d = __builtin_ptrauth_type_discriminator(decltype(&Derived0::virtual6)); + d = __builtin_ptrauth_type_discriminator(decltype(&Base1::virtual7)); +} + +MethodTy1 gmethod0 = reinterpret_cast<MethodTy1>(&Base0::nonvirtual0); +MethodTy0 gmethod1 = reinterpret_cast<MethodTy0>(&Derived0::nonvirtual5); +MethodTy0 gmethod2 = reinterpret_cast<MethodTy0>(&Derived0::virtual1); From 25e71b4def630303b2d71bc0c8519194c087a773 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:18:28 -0400 Subject: [PATCH 536/582] Add IR attributes to functions compiled under pointer authentication. Patch by Ahmed Bougacha. --- clang/lib/CodeGen/CGCall.cpp | 8 ++++++ .../CodeGen/ptrauth-function-attributes.c | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 clang/test/CodeGen/ptrauth-function-attributes.c diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index f1d2c95f54853..4417033b20b44 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1780,6 +1780,14 @@ void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone, FuncAttrs.addAttribute("stackrealign"); if (CodeGenOpts.Backchain) FuncAttrs.addAttribute("backchain"); + if (CodeGenOpts.PointerAuth.ReturnAddresses) + FuncAttrs.addAttribute("ptrauth-returns"); + if (CodeGenOpts.PointerAuth.FunctionPointers) + FuncAttrs.addAttribute("ptrauth-calls"); + if (CodeGenOpts.PointerAuth.IndirectGotos) + FuncAttrs.addAttribute("ptrauth-indirect-gotos"); + if (CodeGenOpts.PointerAuth.AuthTraps) + FuncAttrs.addAttribute("ptrauth-auth-traps"); if (CodeGenOpts.SpeculativeLoadHardening) FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening); diff --git a/clang/test/CodeGen/ptrauth-function-attributes.c b/clang/test/CodeGen/ptrauth-function-attributes.c new file mode 100644 index 0000000000000..03f996d89d848 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-attributes.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-returns -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,RETS +// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-returns -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,RETS +// RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS +// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS +// RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-indirect-gotos -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS +// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-indirect-gotos -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS +// RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,TRAPS +// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,TRAPS +// RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF + +// ALL-LABEL: define void @test() #0 +void test() { +} + +// RETS: attributes #0 = {{{.*}} "ptrauth-returns" {{.*}}} +// CALLS: attributes #0 = {{{.*}} "ptrauth-calls" {{.*}}} +// GOTOS: attributes #0 = {{{.*}} "ptrauth-indirect-gotos" {{.*}}} +// TRAPS: attributes #0 = {{{.*}} "ptrauth-auth-traps" {{.*}}} +// OFF-NOT: attributes {{.*}} "ptrauth- From 6ac1ad095fb3ebb80008d6d0f22b7a2349ac12e3 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:21:37 -0400 Subject: [PATCH 537/582] Sign function pointers passed to atexit and __cxa_atexit. Patch by Akira Hatanaka. --- clang/lib/CodeGen/CGDeclCXX.cpp | 9 +++++-- clang/lib/CodeGen/CodeGenFunction.h | 2 +- clang/lib/CodeGen/ItaniumCXXABI.cpp | 11 ++++++-- .../CodeGenCXX/ptrauth-static-destructors.cpp | 26 +++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-static-destructors.cpp diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 5b172a3480be1..bfd68a60280e6 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -225,7 +225,7 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D, /// Create a stub function, suitable for being passed to atexit, /// which passes the given address to the given destructor function. -llvm::Function *CodeGenFunction::createAtExitStub(const VarDecl &VD, +llvm::Constant *CodeGenFunction::createAtExitStub(const VarDecl &VD, llvm::FunctionCallee dtor, llvm::Constant *addr) { // Get the destructor function type, void(*)(void). @@ -254,7 +254,12 @@ llvm::Function *CodeGenFunction::createAtExitStub(const VarDecl &VD, CGF.FinishFunction(); - return fn; + // Get a proper function pointer. + FunctionProtoType::ExtProtoInfo EPI(getContext().getDefaultCallingConvention( + /*IsVariadic=*/false, /*IsCXXMethod=*/false)); + QualType fnType = getContext().getFunctionType(getContext().VoidTy, + {getContext().VoidPtrTy}, EPI); + return CGM.getFunctionPointer(fn, fnType); } /// Register a global destructor using the C atexit runtime function. diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 78bed1b2abed2..cba6cd6be85d5 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3971,7 +3971,7 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr, bool PerformInit); - llvm::Function *createAtExitStub(const VarDecl &VD, llvm::FunctionCallee Dtor, + llvm::Constant *createAtExitStub(const VarDecl &VD, llvm::FunctionCallee Dtor, llvm::Constant *Addr); /// Call atexit() with a function that passes the given argument to diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 1be29a40f420b..6adc6a8c63963 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -2544,6 +2544,14 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit.getCallee())) fn->setDoesNotThrow(); + auto &Context = CGF.CGM.getContext(); + FunctionProtoType::ExtProtoInfo EPI(Context.getDefaultCallingConvention( + /*IsVariadic=*/false, /*IsCXXMethod=*/false)); + QualType fnType = + Context.getFunctionType(Context.VoidTy, {Context.VoidPtrTy}, EPI); + llvm::Constant *dtorCallee = cast<llvm::Constant>(dtor.getCallee()); + dtorCallee = CGF.CGM.getFunctionPointer(dtorCallee, fnType); + if (!addr) // addr is null when we are trying to register a dtor annotated with // __attribute__((destructor)) in a constructor function. Using null here is @@ -2551,8 +2559,7 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, // function. addr = llvm::Constant::getNullValue(CGF.Int8PtrTy); - llvm::Value *args[] = {llvm::ConstantExpr::getBitCast( - cast<llvm::Constant>(dtor.getCallee()), dtorTy), + llvm::Value *args[] = {llvm::ConstantExpr::getBitCast(dtorCallee, dtorTy), llvm::ConstantExpr::getBitCast(addr, AddrInt8PtrTy), handle}; CGF.EmitNounwindRuntimeCall(atexit, args); diff --git a/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp b/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp new file mode 100644 index 0000000000000..6c8d0c560681a --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: | FileCheck %s --check-prefix=CXAATEXIT + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: -fno-use-cxa-atexit \ +// RUN: | FileCheck %s --check-prefix=ATEXIT + +class Foo { + public: + ~Foo() { + } +}; + +Foo global; + +// CXAATEXIT: @_ZN3FooD1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.Foo* (%class.Foo*)* @_ZN3FooD1Ev to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CXAATEXIT: define internal void @__cxx_global_var_init() +// CXAATEXIT: call i32 @__cxa_atexit(void (i8*)* bitcast ({ i8*, i32, i64, i64 }* @_ZN3FooD1Ev.ptrauth to void (i8*)*), i8* getelementptr inbounds (%class.Foo, %class.Foo* @global, i32 0, i32 0), i8* @__dso_handle) + + +// ATEXIT: @__dtor_global.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @__dtor_global to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// ATEXIT: define internal void @__cxx_global_var_init() +// ATEXIT: %{{.*}} = call i32 @atexit(void ()* bitcast ({ i8*, i32, i64, i64 }* @__dtor_global.ptrauth to void ()*)) + +// ATEXIT: define internal void @__dtor_global() {{.*}} section "__TEXT,__StaticInit,regular,pure_instructions" { +// ATEXIT: %{{.*}} = call %class.Foo* @_ZN3FooD1Ev(%class.Foo* @global) From 21d1be074aed134ddb3fdca514cdc6c6ab6961b3 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:24:15 -0400 Subject: [PATCH 538/582] Sign the destructor pointer passed to __cxa_throw. Patch by Akira Hatanaka. --- clang/lib/CodeGen/ItaniumCXXABI.cpp | 1 + clang/test/CodeGenCXX/ptrauth-throw.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 clang/test/CodeGenCXX/ptrauth-throw.cpp diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 6adc6a8c63963..e82d718881a37 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1391,6 +1391,7 @@ void ItaniumCXXABI::emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) { if (!Record->hasTrivialDestructor()) { CXXDestructorDecl *DtorD = Record->getDestructor(); Dtor = CGM.getAddrOfCXXStructor(GlobalDecl(DtorD, Dtor_Complete)); + Dtor = CGM.getFunctionPointer(Dtor, DtorD->getType()); Dtor = llvm::ConstantExpr::getBitCast(Dtor, CGM.Int8PtrTy); } } diff --git a/clang/test/CodeGenCXX/ptrauth-throw.cpp b/clang/test/CodeGenCXX/ptrauth-throw.cpp new file mode 100644 index 0000000000000..8aebe97034273 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-throw.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s + +class Foo { + public: + ~Foo() { + } +}; + +void f() { + throw Foo(); +} + +// CHECK: @_ZN3FooD1Ev.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (%class.Foo* (%class.Foo*)* @_ZN3FooD1Ev to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK: define void @_Z1fv() +// CHECK: call void @__cxa_throw(i8* %{{.*}}, i8* bitcast ({ i8*, i8* }* @_ZTI3Foo to i8*), i8* bitcast ({ i8*, i32, i64, i64 }* @_ZN3FooD1Ev.ptrauth to i8*)) From a8e307fc0a534328df8e76ef23d87436032358c5 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:25:39 -0400 Subject: [PATCH 539/582] Sign the v-table pointer in ObjC exception RTTI. --- clang/lib/CodeGen/CGObjCMac.cpp | 12 +++++++++--- clang/test/CodeGenObjC/ptrauth-attr-exception.m | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 clang/test/CodeGenObjC/ptrauth-attr-exception.m diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index b05fb989168ea..338ac145642c6 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -7732,11 +7732,17 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, } llvm::Value *VTableIdx = llvm::ConstantInt::get(CGM.Int32Ty, 2); + llvm::Constant *VTablePtr = llvm::ConstantExpr::getInBoundsGetElementPtr( + VTableGV->getValueType(), VTableGV, VTableIdx); + ConstantInitBuilder builder(CGM); auto values = builder.beginStruct(ObjCTypes.EHTypeTy); - values.add( - llvm::ConstantExpr::getInBoundsGetElementPtr(VTableGV->getValueType(), - VTableGV, VTableIdx)); + + if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) { + values.addSignedPointer(VTablePtr, Schema, GlobalDecl(), QualType()); + } else { + values.add(VTablePtr); + } values.add(GetClassName(ClassName)); values.add(GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition)); diff --git a/clang/test/CodeGenObjC/ptrauth-attr-exception.m b/clang/test/CodeGenObjC/ptrauth-attr-exception.m new file mode 100644 index 0000000000000..c90f747e4741d --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-attr-exception.m @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s + +__attribute__((objc_root_class)) +@interface Root { + Class isa; +} +@end + +__attribute__((objc_exception)) +@interface A : Root +@end + +@implementation A +@end + +// CHECK: @objc_ehtype_vtable.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @objc_ehtype_vtable, i32 2) to i8*), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @"OBJC_EHTYPE_$_A" = global {{%.*}} { i8** getelementptr inbounds ({ i8*, i32, i64, i64 }, { i8*, i32, i64, i64 }* @objc_ehtype_vtable.ptrauth, i32 0, i32 0) From e004081624dd3c76239f4b05f1d2b410f5d0cf82 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:28:07 -0400 Subject: [PATCH 540/582] Add prototype support for software pointer authentication. --- clang/include/clang/Basic/LangOptions.def | 1 + .../include/clang/Basic/PointerAuthOptions.h | 33 ++++++++++++++++ clang/include/clang/Driver/Options.td | 3 ++ clang/lib/CodeGen/BackendUtil.cpp | 12 ++++++ clang/lib/Frontend/CompilerInvocation.cpp | 39 +++++++++++++++++++ clang/test/Driver/arch-arm64e.c | 4 ++ 6 files changed, 92 insertions(+) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 666d4fb21480d..8b6e57df8c82f 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -148,6 +148,7 @@ LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication") LANGOPT(PointerAuthReturns, 1, 0, "return pointer authentication") LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps") +LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index e04b8dbd034ca..d712c17ee2bea 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -29,9 +29,21 @@ class PointerAuthSchema { public: enum class Kind { None, + Soft, ARM8_3, }; + /// Software pointer-signing "keys". + enum class SoftKey { + FunctionPointers = 0, + BlockInvocationFunctionPointers = 1, + BlockHelperFunctionPointers = 2, + ObjCMethodListFunctionPointers = 3, + CXXVTablePointers = 4, + CXXVirtualFunctionPointers = 5, + CXXMemberFunctionPointers = 6, + }; + /// Hardware pointer-signing keys in ARM8.3. /// /// These values are the same used in ptrauth.h. @@ -66,6 +78,13 @@ class PointerAuthSchema { unsigned Discrimination : 2; } Common; + struct { + unsigned Kind : NumKindBits; + unsigned AddressDiscriminated : 1; + unsigned Discrimination : 2; + unsigned Key : 3; + } Soft; + struct { unsigned Kind : NumKindBits; unsigned AddressDiscriminated : 1; @@ -79,6 +98,14 @@ class PointerAuthSchema { Common.Kind = unsigned(Kind::None); } + PointerAuthSchema(SoftKey key, bool isAddressDiscriminated, + Discrimination otherDiscrimination) { + Common.Kind = unsigned(Kind::Soft); + Common.AddressDiscriminated = isAddressDiscriminated; + Common.Discrimination = unsigned(otherDiscrimination); + Soft.Key = unsigned(key); + } + PointerAuthSchema(ARM8_3Key key, bool isAddressDiscriminated, Discrimination otherDiscrimination) { Common.Kind = unsigned(Kind::ARM8_3); @@ -116,11 +143,17 @@ class PointerAuthSchema { unsigned getKey() const { switch (getKind()) { case Kind::None: llvm_unreachable("calling getKey() on disabled schema"); + case Kind::Soft: return unsigned(getSoftKey()); case Kind::ARM8_3: return unsigned(getARM8_3Key()); } llvm_unreachable("bad key kind"); } + SoftKey getSoftKey() const { + assert(getKind() == Kind::Soft); + return SoftKey(Soft.Key); + } + ARM8_3Key getARM8_3Key() const { assert(getKind() == Kind::ARM8_3); return ARM8_3Key(ARM8_3.Key); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index c310b530ee439..08db77e4c8095 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1962,12 +1962,15 @@ let Group = f_Group in { HelpText<"Enable signing and authentication of indirect goto targets">; def fptrauth_auth_traps : Flag<["-"], "fptrauth-auth-traps">, HelpText<"Enable traps on authentication failures">; + def fptrauth_soft : Flag<["-"], "fptrauth-soft">, + HelpText<"Enable software lowering of pointer authentication">; } def fno_ptrauth_intrinsics : Flag<["-"], "fno-ptrauth-intrinsics">; def fno_ptrauth_calls : Flag<["-"], "fno-ptrauth-calls">; def fno_ptrauth_returns : Flag<["-"], "fno-ptrauth-returns">; def fno_ptrauth_indirect_gotos : Flag<["-"], "fno-ptrauth-indirect-gotos">; def fno_ptrauth_auth_traps : Flag<["-"], "fno-ptrauth-auth-traps">; + def fno_ptrauth_soft : Flag<["-"], "fno-ptrauth-soft">; } def fallow_editor_placeholders : Flag<["-"], "fallow-editor-placeholders">, diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 8a4d3bd28824b..4b8bddc5b096c 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -336,6 +336,11 @@ static void addDataFlowSanitizerPass(const PassManagerBuilder &Builder, PM.add(createDataFlowSanitizerPass(LangOpts.SanitizerBlacklistFiles)); } +static void addSoftPointerAuthPass(const PassManagerBuilder &Builder, + legacy::PassManagerBase &PM) { + PM.add(createSoftPointerAuthPass()); +} + static TargetLibraryInfoImpl *createTLII(llvm::Triple &TargetTriple, const CodeGenOptions &CodeGenOpts) { TargetLibraryInfoImpl *TLII = new TargetLibraryInfoImpl(TargetTriple); @@ -685,6 +690,13 @@ void EmitAssemblyHelper::CreatePasses(legacy::PassManager &MPM, addDataFlowSanitizerPass); } + if (LangOpts.SoftPointerAuth) { + PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast, + addSoftPointerAuthPass); + PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0, + addSoftPointerAuthPass); + } + // Set up the per-function pass manager. FPM.add(new TargetLibraryInfoWrapperPass(*TLII)); if (CodeGenOpts.VerifyModule) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index b29aa3611ff96..e096545e76e78 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -673,6 +673,44 @@ static bool parsePointerAuthOptions(PointerAuthOptions &Opts, !LangOpts.PointerAuthIndirectGotos && !LangOpts.PointerAuthAuthTraps) return true; + if (LangOpts.SoftPointerAuth) { + if (LangOpts.PointerAuthCalls) { + using Key = PointerAuthSchema::SoftKey; + using Discrimination = PointerAuthSchema::Discrimination; + Opts.FunctionPointers = + PointerAuthSchema(Key::FunctionPointers, false, Discrimination::None); + Opts.BlockInvocationFunctionPointers = + PointerAuthSchema(Key::BlockInvocationFunctionPointers, true, + Discrimination::None); + Opts.BlockHelperFunctionPointers = + PointerAuthSchema(Key::BlockHelperFunctionPointers, true, + Discrimination::None); + Opts.BlockByrefHelperFunctionPointers = + PointerAuthSchema(Key::BlockHelperFunctionPointers, true, + Discrimination::None); + Opts.ObjCMethodListFunctionPointers = + PointerAuthSchema(Key::ObjCMethodListFunctionPointers, true, + Discrimination::None); + Opts.CXXVTablePointers = + Opts.CXXVTTVTablePointers = + PointerAuthSchema(Key::CXXVTablePointers, false, + Discrimination::None); + Opts.CXXVirtualFunctionPointers = + Opts.CXXVirtualVariadicFunctionPointers = + PointerAuthSchema(Key::CXXVirtualFunctionPointers, true, + Discrimination::Decl); + Opts.CXXMemberFunctionPointers = + PointerAuthSchema(Key::CXXMemberFunctionPointers, false, + Discrimination::Type); + Opts.ThunkCXXVirtualMemberPointers = false; + } + + Opts.ReturnAddresses = LangOpts.PointerAuthReturns; + Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos; + Opts.AuthTraps = LangOpts.PointerAuthAuthTraps; + return true; + } + if (Triple.getArch() == llvm::Triple::aarch64) { if (LangOpts.PointerAuthCalls) { using Key = PointerAuthSchema::ARM8_3Key; @@ -3187,6 +3225,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns); Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps); + Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); Opts.FastMath = Args.hasArg(OPT_ffast_math) || Args.hasArg(OPT_cl_fast_relaxed_math); diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c index 687877b8775e7..40b471c8f130d 100644 --- a/clang/test/Driver/arch-arm64e.c +++ b/clang/test/Driver/arch-arm64e.c @@ -7,6 +7,7 @@ // NONE-NOT: "-fptrauth-returns" // NONE-NOT: "-fptrauth-indirect-gotos" // NONE-NOT: "-fptrauth-auth-traps" +// NONE-NOT: "-fptrauth-soft" // RUN: %clang -arch arm64 -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL // CALL: "-cc1"{{.*}} {{.*}} "-fptrauth-calls" @@ -23,6 +24,9 @@ // RUN: %clang -arch arm64 -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS // TRAPS: "-cc1"{{.*}} {{.*}} "-fptrauth-auth-traps" +// RUN: %clang -arch arm64 -fptrauth-soft -c %s -### 2>&1 | FileCheck %s --check-prefix SOFT +// SOFT: "-cc1"{{.*}} {{.*}} "-fptrauth-soft" + // Check the arm64e defaults. From f58c74f684cd0cb8eed398ffc46eac15cfb461c7 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:37:28 -0400 Subject: [PATCH 541/582] Implement the __ptrauth type qualifier. Patch by Akira Hatanaka and me. --- clang/include/clang/AST/ASTContext.h | 10 + .../include/clang/AST/NonTrivialTypeVisitor.h | 2 + clang/include/clang/AST/Type.h | 167 +++- clang/include/clang/Basic/Attr.td | 8 + clang/include/clang/Basic/AttrDocs.td | 159 ++++ .../clang/Basic/DiagnosticSemaKinds.td | 37 +- clang/include/clang/Basic/TokenKinds.def | 2 +- clang/include/clang/Parse/Parser.h | 2 + clang/lib/AST/ASTContext.cpp | 4 + clang/lib/AST/DeclCXX.cpp | 25 + clang/lib/AST/ItaniumMangle.cpp | 49 +- clang/lib/AST/Type.cpp | 2 + clang/lib/AST/TypePrinter.cpp | 37 + clang/lib/CodeGen/CGBlocks.cpp | 61 ++ clang/lib/CodeGen/CGCall.cpp | 3 +- clang/lib/CodeGen/CGClass.cpp | 3 + clang/lib/CodeGen/CGDebugInfo.cpp | 20 +- clang/lib/CodeGen/CGDecl.cpp | 8 +- clang/lib/CodeGen/CGExpr.cpp | 51 +- clang/lib/CodeGen/CGExprConstant.cpp | 59 +- clang/lib/CodeGen/CGExprScalar.cpp | 66 ++ clang/lib/CodeGen/CGNonTrivialStruct.cpp | 15 + clang/lib/CodeGen/CGPointerAuth.cpp | 121 +++ clang/lib/CodeGen/CodeGenFunction.h | 20 + clang/lib/Format/FormatToken.h | 1 + clang/lib/Format/UnwrappedLineParser.cpp | 2 +- clang/lib/Headers/ptrauth.h | 54 ++ clang/lib/Parse/ParseDecl.cpp | 46 ++ clang/lib/Sema/SemaCast.cpp | 14 + clang/lib/Sema/SemaChecking.cpp | 3 + clang/lib/Sema/SemaDecl.cpp | 18 +- clang/lib/Sema/SemaDeclCXX.cpp | 40 +- clang/lib/Sema/SemaExpr.cpp | 16 +- clang/lib/Sema/SemaType.cpp | 103 +++ clang/test/AST/ast-dump-ptrauth-json.cpp | 5 + clang/test/CodeGen/ptrauth-debuginfo.c | 26 + clang/test/CodeGen/ptrauth-in-c-struct.c | 129 +++ .../CodeGen/ptrauth-qualifier-loadstore.c | 744 ++++++++++++++++++ clang/test/CodeGen/ptrauth-qualifier.c | 87 ++ .../CodeGenCXX/ptrauth-qualifier-struct.cpp | 168 ++++ .../CodeGenObjCXX/ptrauth-struct-cxx-abi.mm | 38 + clang/test/Sema/ptrauth-intrinsics-macro.c | 16 + clang/test/Sema/ptrauth-qualifier.c | 127 +++ clang/test/SemaCXX/ptrauth-qualifier.cpp | 120 +++ ...warn-nontrivial-struct-memaccess-ptrauth.m | 41 + 45 files changed, 2680 insertions(+), 49 deletions(-) create mode 100644 clang/test/AST/ast-dump-ptrauth-json.cpp create mode 100644 clang/test/CodeGen/ptrauth-debuginfo.c create mode 100644 clang/test/CodeGen/ptrauth-in-c-struct.c create mode 100644 clang/test/CodeGen/ptrauth-qualifier-loadstore.c create mode 100644 clang/test/CodeGen/ptrauth-qualifier.c create mode 100644 clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp create mode 100644 clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm create mode 100644 clang/test/Sema/ptrauth-qualifier.c create mode 100644 clang/test/SemaCXX/ptrauth-qualifier.cpp create mode 100644 clang/test/SemaObjC/warn-nontrivial-struct-memaccess-ptrauth.m diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 6f91673a24582..7f69439ff2484 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1986,6 +1986,16 @@ class ASTContext : public RefCountedBase<ASTContext> { return getQualifiedType(type.getUnqualifiedType(), Qs); } + /// \brief Return a type with the given __ptrauth qualifier. + QualType getPointerAuthType(QualType type, PointerAuthQualifier pointerAuth) { + assert(!type.getPointerAuth()); + assert(pointerAuth); + + Qualifiers qs; + qs.setPointerAuth(pointerAuth); + return getQualifiedType(type, qs); + } + unsigned char getFixedPointScale(QualType Ty) const; unsigned char getFixedPointIBits(QualType Ty) const; FixedPointSemantics getFixedPointSemantics(QualType Ty) const; diff --git a/clang/include/clang/AST/NonTrivialTypeVisitor.h b/clang/include/clang/AST/NonTrivialTypeVisitor.h index aafcedb9d10b8..ce389178b7b22 100644 --- a/clang/include/clang/AST/NonTrivialTypeVisitor.h +++ b/clang/include/clang/AST/NonTrivialTypeVisitor.h @@ -93,6 +93,8 @@ struct CopiedTypeVisitor { return asDerived().visitARCStrong(FT, std::forward<Ts>(Args)...); case QualType::PCK_ARCWeak: return asDerived().visitARCWeak(FT, std::forward<Ts>(Args)...); + case QualType::PCK_PtrAuth: + return asDerived().visitPtrAuth(FT, std::forward<Ts>(Args)...); case QualType::PCK_Struct: return asDerived().visitStruct(FT, std::forward<Ts>(Args)...); case QualType::PCK_Trivial: diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index ecbbd73e19fb4..93a0746cd117e 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -128,6 +128,99 @@ using CanQualType = CanQual<Type>; #define TYPE(Class, Base) class Class##Type; #include "clang/AST/TypeNodes.inc" +/// Pointer-authentication qualifiers. +class PointerAuthQualifier { + enum { + EnabledShift = 0, + EnabledBits = 1, + EnabledMask = 1 << EnabledShift, + AddressDiscriminatedShift = EnabledShift + EnabledBits, + AddressDiscriminatedBits = 1, + AddressDiscriminatedMask = 1 << AddressDiscriminatedBits, + KeyShift = AddressDiscriminatedShift + AddressDiscriminatedBits, + KeyBits = 14, + KeyMask = ((1 << KeyBits) - 1) << KeyShift, + DiscriminatorShift = KeyShift + KeyBits, + DiscriminatorBits = 16 + }; + + // bits: |0 |1 |2..15|16 ... 31| + // |Enabled|Address|Key |Discriminator| + uint32_t Data; + +public: + enum { + /// The maximum supported pointer-authentication key. + MaxKey = (1u << KeyBits) - 1, + + /// The maximum supported pointer-authentication discriminator. + MaxDiscriminator = (1u << DiscriminatorBits) - 1 + }; + +public: + PointerAuthQualifier() : Data(0) {} + PointerAuthQualifier(unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) + : Data(EnabledMask + | (isAddressDiscriminated ? AddressDiscriminatedMask : 0) + | (key << KeyShift) + | (extraDiscriminator << DiscriminatorShift)) { + assert(key <= MaxKey); + assert(extraDiscriminator <= MaxDiscriminator); + } + + bool isPresent() const { + return Data != 0; + } + + explicit operator bool() const { + return isPresent(); + } + + unsigned getKey() const { + assert(isPresent()); + return (Data & KeyMask) >> KeyShift; + } + + bool isAddressDiscriminated() const { + assert(isPresent()); + return (Data & AddressDiscriminatedMask) >> AddressDiscriminatedShift; + } + + unsigned getExtraDiscriminator() const { + assert(isPresent()); + return (Data >> DiscriminatorShift); + } + + friend bool operator==(PointerAuthQualifier lhs, PointerAuthQualifier rhs) { + return lhs.Data == rhs.Data; + } + friend bool operator!=(PointerAuthQualifier lhs, PointerAuthQualifier rhs) { + return lhs.Data != rhs.Data; + } + + uint32_t getAsOpaqueValue() const { + return Data; + } + + // Deserialize pointer-auth qualifiers from an opaque representation. + static PointerAuthQualifier fromOpaqueValue(uint32_t opaque) { + PointerAuthQualifier result; + result.Data = opaque; + return result; + } + + std::string getAsString() const; + std::string getAsString(const PrintingPolicy &Policy) const; + + bool isEmptyWhenPrinted(const PrintingPolicy &Policy) const; + void print(raw_ostream &OS, const PrintingPolicy &Policy) const; + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Data); + } +}; + /// The collection of all-type qualifiers we support. /// Clang supports five independent qualifiers: /// * C99: const, volatile, and restrict @@ -183,6 +276,8 @@ class Qualifiers { FastMask = (1 << FastWidth) - 1 }; + Qualifiers() : Mask(0), PtrAuth() {} + /// Returns the common set of qualifiers while removing them from /// the given sets. static Qualifiers removeCommonQualifiers(Qualifiers &L, Qualifiers &R) { @@ -218,6 +313,13 @@ class Qualifiers { L.removeAddressSpace(); R.removeAddressSpace(); } + + if (L.PtrAuth == R.PtrAuth) { + Q.PtrAuth = L.PtrAuth; + L.PtrAuth = PointerAuthQualifier(); + R.PtrAuth = PointerAuthQualifier(); + } + return Q; } @@ -240,15 +342,16 @@ class Qualifiers { } // Deserialize qualifiers from an opaque representation. - static Qualifiers fromOpaqueValue(unsigned opaque) { + static Qualifiers fromOpaqueValue(uint64_t opaque) { Qualifiers Qs; - Qs.Mask = opaque; + Qs.Mask = uint32_t(opaque); + Qs.PtrAuth = PointerAuthQualifier::fromOpaqueValue(uint32_t(opaque >> 32)); return Qs; } // Serialize these qualifiers into an opaque representation. - unsigned getAsOpaqueValue() const { - return Mask; + uint64_t getAsOpaqueValue() const { + return uint64_t(Mask) | (uint64_t(PtrAuth.getAsOpaqueValue()) << 32); } bool hasConst() const { return Mask & Const; } @@ -381,6 +484,16 @@ class Qualifiers { setAddressSpace(space); } + PointerAuthQualifier getPointerAuth() const { + return PtrAuth; + } + void setPointerAuth(PointerAuthQualifier q) { + PtrAuth = q; + } + void removePtrAuth() { + PtrAuth = PointerAuthQualifier(); + } + // Fast qualifiers are those that can be allocated directly // on a QualType object. bool hasFastQualifiers() const { return getFastQualifiers(); } @@ -403,7 +516,9 @@ class Qualifiers { /// Return true if the set contains any qualifiers which require an ExtQuals /// node to be allocated. - bool hasNonFastQualifiers() const { return Mask & ~FastMask; } + bool hasNonFastQualifiers() const { + return (Mask & ~FastMask) || PtrAuth; + } Qualifiers getNonFastQualifiers() const { Qualifiers Quals = *this; Quals.setFastQualifiers(0); @@ -411,8 +526,8 @@ class Qualifiers { } /// Return true if the set contains any qualifiers. - bool hasQualifiers() const { return Mask; } - bool empty() const { return !Mask; } + bool hasQualifiers() const { return Mask || PtrAuth; } + bool empty() const { return !hasQualifiers(); } /// Add the qualifiers from the given set to this set. void addQualifiers(Qualifiers Q) { @@ -429,6 +544,9 @@ class Qualifiers { if (Q.hasObjCLifetime()) addObjCLifetime(Q.getObjCLifetime()); } + + if (Q.PtrAuth) + PtrAuth = Q.PtrAuth; } /// Remove the qualifiers from the given set from this set. @@ -446,6 +564,9 @@ class Qualifiers { if (getAddressSpace() == Q.getAddressSpace()) removeAddressSpace(); } + + if (PtrAuth == Q.PtrAuth) + PtrAuth = PointerAuthQualifier(); } /// Add the qualifiers from the given set to this set, given that @@ -457,7 +578,10 @@ class Qualifiers { !hasObjCGCAttr() || !qs.hasObjCGCAttr()); assert(getObjCLifetime() == qs.getObjCLifetime() || !hasObjCLifetime() || !qs.hasObjCLifetime()); + assert(!PtrAuth || !qs.PtrAuth || PtrAuth == qs.PtrAuth); Mask |= qs.Mask; + if (qs.PtrAuth) + PtrAuth = qs.PtrAuth; } /// Returns true if address space A is equal to or a superset of B. @@ -490,6 +614,8 @@ class Qualifiers { // be changed. (getObjCGCAttr() == other.getObjCGCAttr() || !hasObjCGCAttr() || !other.hasObjCGCAttr()) && + // Pointer-auth qualifiers must match exactly. + PtrAuth == other.PtrAuth && // ObjC lifetime qualifiers must match exactly. getObjCLifetime() == other.getObjCLifetime() && // CVR qualifiers may subset. @@ -522,8 +648,12 @@ class Qualifiers { /// another set of qualifiers, not considering qualifier compatibility. bool isStrictSupersetOf(Qualifiers Other) const; - bool operator==(Qualifiers Other) const { return Mask == Other.Mask; } - bool operator!=(Qualifiers Other) const { return Mask != Other.Mask; } + bool operator==(Qualifiers Other) const { + return Mask == Other.Mask && PtrAuth == Other.PtrAuth; + } + bool operator!=(Qualifiers Other) const { + return Mask != Other.Mask || PtrAuth != Other.PtrAuth; + } explicit operator bool() const { return hasQualifiers(); } @@ -559,6 +689,7 @@ class Qualifiers { void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Mask); + PtrAuth.Profile(ID); } private: @@ -566,6 +697,8 @@ class Qualifiers { // |C R V|U|GCAttr|Lifetime|AddressSpace| uint32_t Mask = 0; + PointerAuthQualifier PtrAuth; + static const uint32_t UMask = 0x8; static const uint32_t UShift = 3; static const uint32_t GCAttrMask = 0x30; @@ -1078,6 +1211,14 @@ class QualType { // true when Type is objc's weak and weak is enabled but ARC isn't. bool isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const; + PointerAuthQualifier getPointerAuth() const; + + bool hasAddressDiscriminatedPointerAuth() const { + if (auto ptrauth = getPointerAuth()) + return ptrauth.isAddressDiscriminated(); + return false; + } + enum PrimitiveDefaultInitializeKind { /// The type does not fall into any of the following categories. Note that /// this case is zero-valued so that values of this enum can be used as a @@ -1123,6 +1264,9 @@ class QualType { /// with the ARC __weak qualifier. PCK_ARCWeak, + /// The type is an address-discriminated signed pointer type. + PCK_PtrAuth, + /// The type is a struct containing a field whose type is neither /// PCK_Trivial nor PCK_VolatileTrivial. /// Note that a C++ struct type does not necessarily match this; C++ copying @@ -6270,6 +6414,11 @@ inline Qualifiers::GC QualType::getObjCGCAttr() const { return getQualifiers().getObjCGCAttr(); } +/// Return the pointer-auth qualifier of this type. +inline PointerAuthQualifier QualType::getPointerAuth() const { + return getQualifiers().getPointerAuth(); +} + inline bool QualType::hasNonTrivialToPrimitiveDefaultInitializeCUnion() const { if (auto *RD = getTypePtr()->getBaseElementTypeUnsafe()->getAsRecordDecl()) return hasNonTrivialToPrimitiveDefaultInitializeCUnion(RD); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 653e069c28097..d860885df840a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2406,6 +2406,14 @@ def ObjCRequiresPropertyDefs : InheritableAttr { let Documentation = [Undocumented]; } +def PointerAuth : TypeAttr { + let Spellings = [Keyword<"__ptrauth">]; + let Args = [IntArgument<"Key">, + BoolArgument<"AddressDiscriminated", 1>, + IntArgument<"ExtraDiscriminator", 1>]; + let Documentation = [PtrAuthDocs]; +} + def Unused : InheritableAttr { let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">, C2x<"", "maybe_unused">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2ba1d908d809c..9e6d96a6bb74b 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1350,6 +1350,165 @@ Also see the documentation for `@available }]; } +def PtrAuthDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``__ptrauth`` qualifier allows the programmer to directly control +how pointers are signed when they are stored in a particular variable. +This can be used to strengthen the default protections of pointer +authentication and make it more difficult for an attacker to escalate +an ability to alter memory into full control of a process. + +.. code-block:: c + + #include <ptrauth.h> + + typedef void (*my_callback)(const void*); + my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback; + +The first argument to ``__ptrauth`` is the name of the signing key. +Valid key names for the target are defined in ``<ptrauth.h>``. + +On ARM64, there are four keys: + +- ``ptrauth_key_process_independent_data`` +- ``ptrauth_key_process_dependent_data`` +- ``ptrauth_key_process_independent_code`` +- ``ptrauth_key_process_dependent_code`` + +In general, prefer using a code key for function pointers and a data key +for object pointers. The ARM64 architecture allows loads and calls to +execute more efficiently when the pointer is signed with an appropriate +key. Using code keys only for function pointers also substantially lessens +the risk of creating a so-called "signing oracle" for function pointers; +see the general pointer authentication language documentation. + +Using a process-dependent key provides stronger protection against +cross-process attacks. However, it also inhibits certain memory +optimizations when a shared library is loaded into multiple processes. +Using a process-independent key also allows signed pointers to be passed +in shared memory. Note that even the process-independent keys may change +after a reboot, so signed values should never be serialized. + +The second argument to ``__ptrauth`` is a flag (0 or 1) specifying whether +the object should use address discrimination. If only one argument is +given, the flag defaults to 0. Address discrimination provides strong +protection against attacks which copy signed pointers around in memory. +An attacker cannot usefully copy an arbitrary signed pointer over an +address-discriminated object. Nor can a value taken from an +address-discriminated object be usefully copied over some other signed +pointer. However, it is more expensive to copy values from one +address-discriminated object to another, even if the other arguments to +``__ptrauth`` are the same, and it is not valid to copy them with +``memcpy``. It is also not valid to map memory containing an +address-discriminated object into different places in the address +space, e.g. with ``mmap``. + +The third argument to ``__ptrauth`` is a small non-negative integer +which allows additional discrimination between objects. Using a +unique extra discriminator provides strong protection against attacks +which work by substituting one signed value for another. For example, +an attacker cannot usefully overwrite an object with a pointer from an +object using a different extra discriminator; this protection is similar +to the protection offered by address discrimination. A unique extra +discriminator also protects against "slide" attacks where an attacker +alters a pointer instead of altering the memory that the pointer points to. +The extra discriminator must be a constant expression. On ARM64, +its value must be between 0 and 65535. If the argument is not provided, +the default value is 0. It is generally preferable not to use the value 0, +especially with the process-independent keys, as this combination is used +in various places in the standard language ABI. + +The type qualified by ``__ptrauth`` must be a pointer type. Currently +only C pointer types are allowed and not block pointers, Objective-C +object pointers, or C++ references. ``__ptrauth`` is parsed and interpreted +using the same language rules as qualifiers like ``const`` and ``volatile``. +For example: + +.. code-block:: c + + __ptrauth(...) int *ex0; /* invalid: qualifies 'int', which is not a pointer type */ + int * __ptrauth(...) ex1; /* valid: ex1 has qualified type */ + int * __ptrauth(...) *ex2; /* valid: ex2 is a pointer to a qualified object */ + + typedef int *intp; + __ptrauth(...) intp ex3; /* valid: ex3 has qualified type */ + intp __ptrauth(...) ex4; /* valid: means the exact same thing as ex3 */ + +Assigning a non-null pointer to a ``__ptrauth``-qualified l-value, or +initializing a ``__ptrauth``-qualified object with a non-null pointer, +causes the pointer to be signed according to the described schema before +being stored into memory. If such an initialization is a constant +initialization, then the signing is also done as part of constant +initialization: that is, it is done when the program is loaded, before +any dynamic initialization occurs. Loading a non-null pointer from a +``__ptrauth``-qualified l-value causes the pointer to be authenticated +according to the describe schema before being produced as the result +of the expression. A null pointer keeps its standard representation when +stored in a ``__ptrauth``-qualified object; on a typical target where this +is an all-zero pattern, this means that operations like ``bzero`` and +``calloc`` do still correctly initialize objects with null. + +If a ``__ptrauth``-qualified l-value of function pointer type is +used as the function operand of a call expression, the function pointer +will be authenticated "atomically" with the call, such that an attacker +will not be able to corrupt the destination of the call even in the +presence of undefined behavior. (That is, the compiler must not +leave an un-signed pointer that it will later unconditionally trust +in a place where it could be feasibly overwritten by an attacker, +such as the stack or a callee-save register during an intervening call. +The compiler is not required to protect against improbable attacks +such as corruption of the register file, as might occur with a +corrupted kernel. It also need not guard against jumps to an arbitrary +place in the instruction stream, since such jumps would require an +attacker to already fully control the PC.) + +If the ABI specifies that a pointer is always signed --- that is, +if the pointer is a function pointer and the target uses ABI function +pointer authentication --- then signing and authenticating it as part +of a load/store actually means resigning it to/from the standard ABI +signature schema. Similarly, if both operands of a simple assignment +operator are ``__ptrauth``-qualified, the pointer copied by the +assignment is resigned from the right-hand operand's schema to the +left-hand operand's schema. These resigning operations are also done +"atomically" in the same sense as above. + +As a final guarantee, if the right-hand operand of an assignment or +the expression used to initialize a ``__ptrauth``-qualified object is +a direct reference to an object or function (e.g. ``&my_var``), the +signing of that pointer is atomic with the evaluaton of the reference +in this same sense. + +Otherwise, there are no guarantees of atomicity, and it is the +programmer's responsibility to avoid allowing a store into a +``__ptrauth``-qualified object to create a potential "signing oracle" +which an attacker could use to sign an arbitrary pointer of their choice. +Such oracles are particularly problematic when the signing uses a code +key because the oracle could potentially be used to allow an attacker +to construct a validly-signed function pointer, v-table entry, or +return address that points to an arbitrary instruction, allowing them +to completely take over the PC. Programmers attempting to use +``__ptrauth`` to protect a data pointer, or to protect function pointers +on targets that do not use ABI function pointer authentication, should +aim to maintain a "chain of authentication" from initialization all +the way to the point at which the pointer is used. If this is infeasible, +they should consider using ``ptrauth_sign_generic_data`` instead. + +Types that are written in r-value positions, such as return types, +parameter types, and cast types, may not be ``__ptrauth``-qualified +at the outermost level. This may be supported in the future. + +In C++, the arguments to ``__ptrauth`` may not be instantiation-dependent. +This may be supported in the future. + +This feature may be tested for with ``__has_feature(ptrauth_qualifier)``. +It is enabled whenever the ``ptrauth`` intrinsics are enabled. + +``<ptrauth.h>`` provides predefined qualifiers for various language +features that implicitly use pointer authentication. + }]; +} + def ExternalSourceSymbolDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d851e31e7f406..5dbc9c0316bc0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -766,6 +766,26 @@ def err_ptrauth_type_disc_variably_modified : Error< "cannot pass variably-modified type %0 to " "'__builtin_ptrauth_type_discriminator'">; +// __ptrauth qualifier +def err_ptrauth_qualifier_return : Error< + "return types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_param : Error< + "parameter types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_cast : Error< + "cast types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_nonpointer : Error< + "__ptrauth qualifier may only be applied to pointer types; type here is %0">; +def err_ptrauth_qualifier_redundant : Error< + "type %0 is already __ptrauth-qualified">; +def err_ptrauth_qualifier_bad_arg_count : Error< + "__ptrauth qualifier must take between 1 and 3 arguments">; +def err_ptrauth_qualifier_arg_not_ice : Error< + "argument to __ptrauth must be an integer constant expression">; +def err_ptrauth_qualifier_address_discrimination_invalid : Error< + "address discrimination flag for __ptrauth must be 0 or 1; value is %0">; +def err_ptrauth_qualifier_extra_discriminator_invalid : Error< + "extra discriminator for __ptrauth must between 0 and %1; value is %0">; + /// main() // static main() is not an error in C, just in C++. def warn_static_main : Warning<"'main' should not be declared static">, @@ -4970,7 +4990,7 @@ def note_deleted_special_member_class_subobject : Note< "%select{default|corresponding|default|default|default}4 constructor}0|" "destructor}5" "%select{||s||}4" - "|is an ObjC pointer}6">; + "|is an ObjC pointer|has an address-discriminated ptrauth qualifier}6">; def note_deleted_default_ctor_uninit_field : Note< "%select{default constructor of|constructor inherited by}0 " "%1 is implicitly deleted because field %2 of " @@ -7386,6 +7406,19 @@ def err_typecheck_incompatible_ownership : Error< "sending to parameter of different type}0,1" "|%diff{casting $ to type $|casting between types}0,1}2" " changes retain/release properties of pointer">; +def err_typecheck_incompatible_ptrauth : Error< + "%select{%diff{assigning $ to $|assigning to different types}1,0" + "|%diff{passing $ to parameter of type $|" + "passing to parameter of different type}0,1" + "|%diff{returning $ from a function with result type $|" + "returning from function with different return type}0,1" + "|%diff{converting $ to type $|converting between types}0,1" + "|%diff{initializing $ with an expression of type $|" + "initializing with expression of different type}0,1" + "|%diff{sending $ to parameter of type $|" + "sending to parameter of different type}0,1" + "|%diff{casting $ to type $|casting between types}0,1}2" + " changes pointer-authentication of pointee type">; def err_typecheck_comparison_of_distinct_blocks : Error< "comparison of distinct block types%diff{ ($ and $)|}0,1">; @@ -7690,6 +7723,8 @@ def ext_typecheck_cond_pointer_integer_mismatch : ExtWarn< "pointer/integer type mismatch in conditional expression" "%diff{ ($ and $)|}0,1">, InGroup<DiagGroup<"conditional-type-mismatch">>; +def err_typecheck_cond_incompatible_ptrauth : Error< + "__ptrauth qualification mismatch%diff{ ($ and $)|}0,1">; def err_typecheck_choose_expr_requires_constant : Error< "'__builtin_choose_expr' requires a constant expression">; def warn_unused_expr : Warning<"expression result unused">, diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 48d9ca7798eb4..71bf6c54df5a2 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -314,7 +314,7 @@ KEYWORD(_Thread_local , KEYALL) KEYWORD(__func__ , KEYALL) KEYWORD(__objc_yes , KEYALL) KEYWORD(__objc_no , KEYALL) - +KEYWORD(__ptrauth , KEYALL) // C++ 2.11p1: Keywords. KEYWORD(asm , KEYCXX|KEYGNU) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index a9d5cb00bb372..15d800a293ea1 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2632,6 +2632,8 @@ class Parser : public CodeCompletionHandler { void ParseAlignmentSpecifier(ParsedAttributes &Attrs, SourceLocation *endLoc = nullptr); + void ParsePtrauthQualifier(ParsedAttributes &Attrs); + VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const; VirtSpecifiers::Specifier isCXX11VirtSpecifier() const { return isCXX11VirtSpecifier(Tok); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 421310ac06393..b03bfe41da2c9 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6204,6 +6204,9 @@ bool ASTContext::BlockRequiresCopying(QualType Ty, return true; } + if (Ty.hasAddressDiscriminatedPointerAuth()) + return true; + // The block needs copy/destroy helpers if Ty is non-trivial to destructively // move or destroy. if (Ty.isNonTrivialToPrimitiveDestructiveMove() || Ty.isDestructedType()) @@ -8880,6 +8883,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() || LQuals.getAddressSpace() != RQuals.getAddressSpace() || LQuals.getObjCLifetime() != RQuals.getObjCLifetime() || + LQuals.getPointerAuth() != RQuals.getPointerAuth() || LQuals.hasUnaligned() != RQuals.hasUnaligned()) return {}; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 12ec44fa02791..5810f9f5a6bd6 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1031,6 +1031,31 @@ void CXXRecordDecl::addedMember(Decl *D) { } else if (!T.isCXX98PODType(Context)) data().PlainOldData = false; + // If a class has an address-discriminated signed pointer member, it is a + // non-POD type and its copy constructor, move constructor, copy assignment + // operator, move assignment operator are non-trivial. + if (PointerAuthQualifier Q = T.getPointerAuth()) { + if (Q.isAddressDiscriminated()) { + struct DefinitionData &Data = data(); + Data.PlainOldData = false; + Data.HasTrivialSpecialMembers &= + ~(SMF_CopyConstructor | SMF_MoveConstructor | + SMF_CopyAssignment | SMF_MoveAssignment); + setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); + + // Copy/move constructors/assignment operators of a union are deleted by + // default if it has an address-discriminated ptrauth field. + if (isUnion()) { + data().DefaultedCopyConstructorIsDeleted = true; + data().DefaultedMoveConstructorIsDeleted = true; + data().DefaultedMoveAssignmentIsDeleted = true; + data().NeedOverloadResolutionForCopyConstructor = true; + data().NeedOverloadResolutionForMoveConstructor = true; + data().NeedOverloadResolutionForMoveAssignment = true; + } + } + } + if (T->isReferenceType()) { if (!Field->hasInClassInitializer()) data().HasUninitializedReferenceMember = true; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 5d6c715853005..b2dbfcd968edf 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2319,38 +2319,41 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals, const DependentAddressSp if (Quals.getObjCLifetime() == Qualifiers::OCL_Weak) mangleVendorQualifier("__weak"); + // The __unsafe_unretained qualifier is *not* mangled, so that + // __unsafe_unretained types in ARC produce the same manglings as the + // equivalent (but, naturally, unqualified) types in non-ARC, providing + // better ABI compatibility. + // + // It's safe to do this because unqualified 'id' won't show up + // in any type signatures that need to be mangled. + // __unaligned (from -fms-extensions) if (Quals.hasUnaligned()) mangleVendorQualifier("__unaligned"); - // Remaining ARC ownership qualifiers. - switch (Quals.getObjCLifetime()) { - case Qualifiers::OCL_None: - break; - - case Qualifiers::OCL_Weak: - // Do nothing as we already handled this case above. - break; - - case Qualifiers::OCL_Strong: + // The __strong ARC qualifier. + if (Quals.getObjCLifetime() == Qualifiers::OCL_Strong) mangleVendorQualifier("__strong"); - break; - case Qualifiers::OCL_Autoreleasing: - mangleVendorQualifier("__autoreleasing"); - break; + // __ptrauth. Note that this is parameterized. + if (auto ptrauth = Quals.getPointerAuth()) { + mangleVendorQualifier("__ptrauth"); - case Qualifiers::OCL_ExplicitNone: - // The __unsafe_unretained qualifier is *not* mangled, so that - // __unsafe_unretained types in ARC produce the same manglings as the - // equivalent (but, naturally, unqualified) types in non-ARC, providing - // better ABI compatibility. - // - // It's safe to do this because unqualified 'id' won't show up - // in any type signatures that need to be mangled. - break; + // For now, since we only allow non-dependent arguments, we can just + // inline the mangling of those arguments as literals. We treat the + // key and extra-discriminator arguments as 'unsigned int' and the + // address-discriminated argument as 'bool'. + Out << "I" + "Lj" << ptrauth.getKey() << "E" + "Lb" << unsigned(ptrauth.isAddressDiscriminated()) << "E" + "Lj" << ptrauth.getExtraDiscriminator() << "E" + "E"; } + // The __autoreleasing ARC qualifier. + if (Quals.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) + mangleVendorQualifier("__autoreleasing"); + // <CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const if (Quals.hasRestrict()) Out << 'r'; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index d45f1c73fb4a5..29cf68ae06df0 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2365,6 +2365,8 @@ QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const { case Qualifiers::OCL_Weak: return PCK_ARCWeak; default: + if (hasAddressDiscriminatedPointerAuth()) + return PCK_PtrAuth; return Qs.hasVolatile() ? PCK_VolatileTrivial : PCK_Trivial; } } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 386daf23260ce..fe9fa48c64a09 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1524,6 +1524,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::Ptr64: case attr::SPtr: case attr::UPtr: + case attr::PointerAuth: case attr::AddressSpace: llvm_unreachable("This attribute should have been handled already"); @@ -1751,6 +1752,30 @@ void clang::printTemplateArgumentList(raw_ostream &OS, printTo(OS, Args, Policy, false); } +std::string PointerAuthQualifier::getAsString() const { + LangOptions LO; + return getAsString(PrintingPolicy(LO)); +} + +std::string PointerAuthQualifier::getAsString(const PrintingPolicy &P) const { + SmallString<64> Buf; + llvm::raw_svector_ostream StrOS(Buf); + print(StrOS, P); + return StrOS.str(); +} + +bool PointerAuthQualifier::isEmptyWhenPrinted(const PrintingPolicy &P) const { + return !isPresent(); +} + +void PointerAuthQualifier::print(raw_ostream &OS, + const PrintingPolicy &P) const { + if (!isPresent()) return; + OS << "__ptrauth(" << getKey() << "," + << unsigned(isAddressDiscriminated()) << "," + << getExtraDiscriminator() << ")"; +} + std::string Qualifiers::getAsString() const { LangOptions LO; return getAsString(PrintingPolicy(LO)); @@ -1780,6 +1805,10 @@ bool Qualifiers::isEmptyWhenPrinted(const PrintingPolicy &Policy) const { if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)) return false; + if (auto pointerAuth = getPointerAuth()) + if (!pointerAuth.isEmptyWhenPrinted(Policy)) + return false; + return true; } @@ -1867,6 +1896,14 @@ void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy, } } + if (auto pointerAuth = getPointerAuth()) { + if (addSpace) + OS << ' '; + addSpace = true; + + pointerAuth.print(OS, Policy); + } + if (appendSpaceIfNonEmpty && addSpace) OS << ' '; } diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index 7e957f035ab65..708ff5341f227 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -69,6 +69,7 @@ namespace { /// entity that's captured by a block. enum class BlockCaptureEntityKind { CXXRecord, // Copy or destroy + AddressDiscriminatedPointerAuth, ARCWeak, ARCStrong, NonTrivialCStruct, @@ -626,6 +627,10 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF, lifetime = Qualifiers::OCL_Strong; } + // So do types with address-discriminated pointer authentication. + } else if (variable->getType().hasAddressDiscriminatedPointerAuth()) { + info.NeedsCopyDispose = true; + // So do types that require non-trivial copy construction. } else if (CI.hasCopyExpr()) { info.NeedsCopyDispose = true; @@ -1741,6 +1746,10 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags); } + if (T.hasAddressDiscriminatedPointerAuth()) + return std::make_pair( + BlockCaptureEntityKind::AddressDiscriminatedPointerAuth, Flags); + Flags = BLOCK_FIELD_IS_OBJECT; bool isBlockPointer = T->isBlockPointerType(); if (isBlockPointer) @@ -1761,6 +1770,10 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, return std::make_pair(!isBlockPointer ? BlockCaptureEntityKind::ARCStrong : BlockCaptureEntityKind::BlockObject, Flags); + case QualType::PCK_PtrAuth: + return std::make_pair( + BlockCaptureEntityKind::AddressDiscriminatedPointerAuth, + BlockFieldFlags()); case QualType::PCK_Trivial: case QualType::PCK_VolatileTrivial: { if (!T->isObjCRetainableType()) @@ -1886,6 +1899,13 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E, case BlockCaptureEntityKind::ARCStrong: Str += "s"; break; + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: { + auto PtrAuth = CaptureTy.getPointerAuth(); + assert(PtrAuth && PtrAuth.isAddressDiscriminated()); + Str += "p" + llvm::to_string(PtrAuth.getKey()) + "d" + + llvm::to_string(PtrAuth.getExtraDiscriminator()); + break; + } case BlockCaptureEntityKind::BlockObject: { const VarDecl *Var = CI.getVariable(); unsigned F = Flags.getBitMask(); @@ -2001,6 +2021,7 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, } break; } + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: case BlockCaptureEntityKind::None: break; } @@ -2105,6 +2126,14 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { case BlockCaptureEntityKind::ARCWeak: EmitARCCopyWeak(dstField, srcField); break; + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: { + auto type = CI.getVariable()->getType(); + auto ptrauth = type.getPointerAuth(); + assert(ptrauth && ptrauth.isAddressDiscriminated()); + EmitPointerAuthCopy(ptrauth, type, dstField, srcField); + // We don't need to push cleanups for ptrauth types. + continue; + } case BlockCaptureEntityKind::NonTrivialCStruct: { // If this is a C struct that requires non-trivial copy construction, // emit a call to its copy constructor. @@ -2448,6 +2477,33 @@ class CXXByrefHelpers final : public BlockByrefHelpers { } }; +/// Emits the copy/dispose helpers for a __block variable with +/// address-discriminated pointer authentication. +class AddressDiscriminatedByrefHelpers final : public BlockByrefHelpers { + QualType VarType; + +public: + AddressDiscriminatedByrefHelpers(CharUnits alignment, QualType type) + : BlockByrefHelpers(alignment), VarType(type) { + assert(type.hasAddressDiscriminatedPointerAuth()); + } + + void emitCopy(CodeGenFunction &CGF, Address destField, + Address srcField) override { + CGF.EmitPointerAuthCopy(VarType.getPointerAuth(), VarType, + destField, srcField); + } + + bool needsDispose() const override { return false; } + void emitDispose(CodeGenFunction &CGF, Address field) override { + llvm_unreachable("should never be called"); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const override { + id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr()); + } +}; + /// Emits the copy/dispose helpers for a __block variable that is a non-trivial /// C struct. class NonTrivialCStructByrefHelpers final : public BlockByrefHelpers { @@ -2667,6 +2723,11 @@ CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType, CGM, byrefInfo, CXXByrefHelpers(valueAlignment, type, copyExpr)); } + if (type.hasAddressDiscriminatedPointerAuth()) { + return ::buildByrefHelpers( + CGM, byrefInfo, AddressDiscriminatedByrefHelpers(valueAlignment, type)); + } + // If type is a non-trivial C struct type that is non-trivial to // destructly move or destroy, build the copy and dispose helpers. if (type.isNonTrivialToPrimitiveDestructiveMove() == QualType::PCK_Struct || diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4417033b20b44..b1049b6cdc88c 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3637,7 +3637,8 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, } if (HasAggregateEvalKind && isa<ImplicitCastExpr>(E) && - cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue) { + cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue && + !type.isNonTrivialToPrimitiveCopy()) { LValue L = EmitLValue(cast<CastExpr>(E)->getSubExpr()); assert(L.isSimple()); args.addUncopiedAggregate(L, type); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index b27d6c834f41a..6c241f7a754f7 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -910,6 +910,9 @@ namespace { Qualifiers Qual = F->getType().getQualifiers(); if (Qual.hasVolatile() || Qual.hasObjCLifetime()) return false; + if (PointerAuthQualifier Q = F->getType().getPointerAuth()) + if (Q.isAddressDiscriminated()) + return false; return true; } diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 7c63743f3b43d..0c62053781f3a 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -849,6 +849,15 @@ llvm::DIType *CGDebugInfo::CreateQualifiedType(QualType Ty, } else if (Qc.hasRestrict()) { Tag = llvm::dwarf::DW_TAG_restrict_type; Qc.removeRestrict(); + } else if (Qc.getPointerAuth().isPresent()) { + unsigned Key = Qc.getPointerAuth().getKey(); + bool IsDiscr = Qc.getPointerAuth().isAddressDiscriminated(); + unsigned ExtraDiscr = Qc.getPointerAuth().getExtraDiscriminator(); + Qc.removePtrAuth(); + assert(Qc.empty() && "Unknown type qualifier for debug info"); + auto *FromTy = getOrCreateType(QualType(T, 0), Unit); + return DBuilder.createPtrAuthQualifiedType(FromTy, Key, IsDiscr, + ExtraDiscr); } else { assert(Qc.empty() && "Unknown type qualifier for debug info"); return getOrCreateType(QualType(T, 0), Unit); @@ -874,8 +883,8 @@ llvm::DIType *CGDebugInfo::CreateType(const ObjCObjectPointerType *Ty, Ty->getPointeeType(), Unit); } -llvm::DIType *CGDebugInfo::CreateType(const PointerType *Ty, - llvm::DIFile *Unit) { +llvm::DIType * +CGDebugInfo::CreateType(const PointerType *Ty, llvm::DIFile *Unit) { return CreatePointerLikeType(llvm::dwarf::DW_TAG_pointer_type, Ty, Ty->getPointeeType(), Unit); } @@ -997,10 +1006,9 @@ CGDebugInfo::getOrCreateRecordFwdDecl(const RecordType *Ty, return RetTy; } -llvm::DIType *CGDebugInfo::CreatePointerLikeType(llvm::dwarf::Tag Tag, - const Type *Ty, - QualType PointeeTy, - llvm::DIFile *Unit) { +llvm::DIType *CGDebugInfo::CreatePointerLikeType( + llvm::dwarf::Tag Tag, const Type *Ty, QualType PointeeTy, + llvm::DIFile *Unit) { // Bit size, align and offset of the type. // Size is always the size of a pointer. We can't use getTypeSize here // because that does not return the correct value for references. diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 563841c068f60..bf66b85a6e72d 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -741,7 +741,13 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D, LValue lvalue, bool capturedByInit) { Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime(); if (!lifetime) { - llvm::Value *value = EmitScalarExpr(init); + llvm::Value *value; + if (auto ptrauth = lvalue.getQuals().getPointerAuth()) { + value = EmitPointerAuthQualify(ptrauth, init, lvalue.getAddress()); + lvalue.getQuals().removePtrAuth(); + } else { + value = EmitScalarExpr(init); + } if (capturedByInit) drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D)); EmitNullabilityCheck(lvalue, value, init->getExprLoc()); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 2a3cfffcd63d5..b8481848474a5 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1772,6 +1772,15 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *value, LValue lvalue, /// method emits the address of the lvalue, then loads the result as an rvalue, /// returning the rvalue. RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) { + // Load from __ptrauth. + if (auto ptrauth = LV.getQuals().getPointerAuth()) { + LV.getQuals().removePtrAuth(); + auto value = EmitLoadOfLValue(LV, Loc).getScalarVal(); + return RValue::get(EmitPointerAuthUnqualify(ptrauth, value, + LV.getType(), LV.getAddress(), + /*known nonnull*/ false)); + } + if (LV.isObjCWeak()) { // load of a __weak object. Address AddrWeakObj = LV.getAddress(); @@ -1950,6 +1959,13 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, return EmitStoreThroughBitfieldLValue(Src, Dst); } + // Handle __ptrauth qualification by re-signing the value. + if (auto pointerAuth = Dst.getQuals().getPointerAuth()) { + Src = RValue::get(EmitPointerAuthQualify(pointerAuth, Src.getScalarVal(), + Dst.getType(), Dst.getAddress(), + /*known nonnull*/ false)); + } + // There's special magic for assigning into an ARC-qualified l-value. if (Qualifiers::ObjCLifetime Lifetime = Dst.getQuals().getObjCLifetime()) { switch (Lifetime) { @@ -3871,7 +3887,8 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { bool IsBaseCXXThis = IsWrappedCXXThis(BaseExpr); if (IsBaseCXXThis) SkippedChecks.set(SanitizerKind::Alignment, true); - if (IsBaseCXXThis || isa<DeclRefExpr>(BaseExpr)) + if (IsBaseCXXThis || isa<DeclRefExpr>(BaseExpr) || + isa<llvm::ConstantPointerNull>(Addr.getPointer())) SkippedChecks.set(SanitizerKind::Null, true); EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy, /*Alignment=*/CharUnits::Zero(), SkippedChecks); @@ -4621,6 +4638,27 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { return EmitCallee(ICE->getSubExpr()); } + // Try to remember the original __ptrauth qualifier for loads of + // function pointers. + if (ICE->getCastKind() == CK_LValueToRValue) { + auto subExpr = ICE->getSubExpr(); + if (auto ptrType = subExpr->getType()->getAs<PointerType>()) { + auto result = EmitOrigPointerRValue(E); + + QualType functionType = ptrType->getPointeeType(); + assert(functionType->isFunctionType()); + + GlobalDecl GD; + if (const auto *VD = + dyn_cast_or_null<VarDecl>(E->getReferencedDeclOfCallee())) { + GD = GlobalDecl(VD); + } + CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), GD); + CGCallee callee(calleeInfo, result.first, result.second); + return callee; + } + } + // Resolve direct calls. } else if (auto DRE = dyn_cast<DeclRefExpr>(E)) { if (auto FD = dyn_cast<FunctionDecl>(DRE->getDecl())) { @@ -4712,6 +4750,17 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { switch (getEvaluationKind(E->getType())) { case TEK_Scalar: { + if (auto ptrauth = E->getLHS()->getType().getPointerAuth()) { + LValue LV = EmitCheckedLValue(E->getLHS(), TCK_Store); + LValue CopiedLV = LV; + CopiedLV.getQuals().removePtrAuth(); + llvm::Value *RV = EmitPointerAuthQualify(ptrauth, E->getRHS(), + CopiedLV.getAddress()); + EmitNullabilityCheck(CopiedLV, RV, E->getExprLoc()); + EmitStoreThroughLValue(RValue::get(RV), CopiedLV); + return LV; + } + switch (E->getLHS()->getType().getObjCLifetime()) { case Qualifiers::OCL_Strong: return EmitARCStoreStrong(E, /*ignored*/ false).first; diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 819e697e52647..ad62a597f30a6 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1754,10 +1754,13 @@ namespace { struct ConstantLValue { llvm::Constant *Value; bool HasOffsetApplied; + bool HasDestPointerAuth; /*implicit*/ ConstantLValue(llvm::Constant *value, - bool hasOffsetApplied = false) - : Value(value), HasOffsetApplied(false) {} + bool hasOffsetApplied = false, + bool hasDestPointerAuth = false) + : Value(value), HasOffsetApplied(false), + HasDestPointerAuth(hasDestPointerAuth) {} /*implicit*/ ConstantLValue(ConstantAddress address) : ConstantLValue(address.getPointer()) {} @@ -1806,6 +1809,8 @@ class ConstantLValueEmitter : public ConstStmtVisitor<ConstantLValueEmitter, unsigned emitPointerAuthKey(const Expr *E); std::pair<llvm::Constant*, llvm::Constant*> emitPointerAuthDiscriminator(const Expr *E); + llvm::Constant *tryEmitConstantSignedPointer(llvm::Constant *ptr, + PointerAuthQualifier auth); bool hasNonZeroOffset() const { return !Value.getLValueOffset().isZero(); @@ -1865,6 +1870,14 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() { value = applyOffset(value); } + // Apply pointer-auth signing from the destination type. + if (auto pointerAuth = DestType.getPointerAuth()) { + if (!result.HasDestPointerAuth) { + value = tryEmitConstantSignedPointer(value, pointerAuth); + if (!value) return nullptr; + } + } + // Convert to the appropriate type; this could be an lvalue for // an integer. FIXME: performAddrSpaceCast if (isa<llvm::PointerType>(destTy)) @@ -1903,6 +1916,12 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { return CGM.GetWeakRefReference(D).getPointer(); if (auto FD = dyn_cast<FunctionDecl>(D)) { + if (auto pointerAuth = DestType.getPointerAuth()) { + llvm::Constant *C = CGM.getRawFunctionPointer(FD); + C = applyOffset(C); + C = tryEmitConstantSignedPointer(C, pointerAuth); + return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true); + } return CGM.getFunctionPointer(FD); } @@ -2010,6 +2029,42 @@ ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) { } } +/// Try to emit a constant signed pointer, given a raw pointer and the +/// destination ptrauth qualifier. +/// +/// This can fail if the qualifier needs address discrimination and the +/// emitter is in an abstract mode. +llvm::Constant * +ConstantLValueEmitter::tryEmitConstantSignedPointer( + llvm::Constant *unsignedPointer, + PointerAuthQualifier schema) { + assert(schema && "applying trivial ptrauth schema"); + auto key = schema.getKey(); + + // Create an address placeholder if we're using address discrimination. + llvm::GlobalValue *storageAddress = nullptr; + if (schema.isAddressDiscriminated()) { + // We can't do this if the emitter is in an abstract state. + if (Emitter.isAbstract()) + return nullptr; + + storageAddress = Emitter.getCurrentAddrPrivate(); + } + + // Fetch the extra discriminator. + llvm::Constant *otherDiscriminator = + llvm::ConstantInt::get(CGM.IntPtrTy, schema.getExtraDiscriminator()); + + auto signedPointer = + CGM.getConstantSignedPointer(unsignedPointer, key, storageAddress, + otherDiscriminator); + + if (schema.isAddressDiscriminated()) + Emitter.registerCurrentAddrPrivate(signedPointer, storageAddress); + + return signedPointer; +} + ConstantLValue ConstantLValueEmitter::emitPointerAuthSignConstant(const CallExpr *E) { auto unsignedPointer = emitPointerAuthPointer(E->getArg(0)); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 55a413a2a7179..91a0b61f2fda3 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1930,6 +1930,58 @@ Value *ScalarExprEmitter::VisitInitListExpr(InitListExpr *E) { return V; } +static bool isDeclRefKnownNonNull(CodeGenFunction &CGF, const ValueDecl *D) { + return !D->isWeak(); +} + +static bool isLValueKnownNonNull(CodeGenFunction &CGF, const Expr *E) { + E = E->IgnoreParens(); + + if (auto UO = dyn_cast<UnaryOperator>(E)) { + if (UO->getOpcode() == UO_Deref) { + return CGF.isPointerKnownNonNull(UO->getSubExpr()); + } + } + + if (auto DRE = dyn_cast<DeclRefExpr>(E)) { + return isDeclRefKnownNonNull(CGF, DRE->getDecl()); + } else if (auto ME = dyn_cast<MemberExpr>(E)) { + if (isa<FieldDecl>(ME->getMemberDecl())) + return true; + return isDeclRefKnownNonNull(CGF, ME->getMemberDecl()); + } + + // Array subscripts? Anything else? + + return false; +} + +bool CodeGenFunction::isPointerKnownNonNull(const Expr *E) { + assert(E->getType()->isPointerType()); + + E = E->IgnoreParens(); + + if (isa<CXXThisExpr>(E)) + return true; + + if (auto UO = dyn_cast<UnaryOperator>(E)) { + if (UO->getOpcode() == UO_AddrOf) { + return isLValueKnownNonNull(*this, UO->getSubExpr()); + } + } + + if (auto CE = dyn_cast<CastExpr>(E)) { + if (CE->getCastKind() == CK_FunctionToPointerDecay || + CE->getCastKind() == CK_ArrayToPointerDecay) { + return isLValueKnownNonNull(*this, CE->getSubExpr()); + } + } + + // Maybe honor __nonnull? + + return false; +} + bool CodeGenFunction::ShouldNullCheckClassCastValue(const CastExpr *CE) { const Expr *E = CE->getSubExpr(); @@ -3906,6 +3958,20 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { Value *RHS; LValue LHS; + if (auto ptrauth = E->getLHS()->getType().getPointerAuth()) { + LValue LV = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); + LV.getQuals().removePtrAuth(); + llvm::Value *RV = CGF.EmitPointerAuthQualify(ptrauth, E->getRHS(), + LV.getAddress()); + CGF.EmitNullabilityCheck(LV, RV, E->getExprLoc()); + CGF.EmitStoreThroughLValue(RValue::get(RV), LV); + + if (Ignore) return nullptr; + RV = CGF.EmitPointerAuthUnqualify(ptrauth, RV, LV.getType(), + LV.getAddress(), /*nonnull*/ false); + return RV; + } + switch (E->getLHS()->getType().getObjCLifetime()) { case Qualifiers::OCL_Strong: std::tie(LHS, RHS) = CGF.EmitARCStoreStrong(E, Ignore); diff --git a/clang/lib/CodeGen/CGNonTrivialStruct.cpp b/clang/lib/CodeGen/CGNonTrivialStruct.cpp index 05615aa128816..3f75e65a5d4e0 100644 --- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp +++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp @@ -261,6 +261,13 @@ struct GenBinaryFuncName : CopyStructVisitor<GenBinaryFuncName<IsMove>, IsMove>, this->appendStr("_tv" + llvm::to_string(OffsetInBits) + "w" + llvm::to_string(getFieldSize(FD, FT, this->Ctx))); } + + void visitPtrAuth(QualType FT, const FieldDecl *FD, + CharUnits CurStructOffset) { + this->appendStr("_pa"); + CharUnits FieldOffset = CurStructOffset + this->getFieldOffset(FD); + this->appendStr(llvm::to_string(FieldOffset.getQuantity())); + } }; struct GenDefaultInitializeFuncName @@ -563,6 +570,14 @@ struct GenBinaryFunc : CopyStructVisitor<Derived, IsMove>, RValue SrcVal = this->CGF->EmitLoadOfLValue(SrcLV, SourceLocation()); this->CGF->EmitStoreThroughLValue(SrcVal, DstLV); } + + void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array<Address, 2> Addrs) { + PointerAuthQualifier PtrAuth = FT.getPointerAuth(); + Addrs[DstIdx] = this->getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD); + Addrs[SrcIdx] = this->getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD); + this->CGF->EmitPointerAuthCopy(PtrAuth, FT, Addrs[DstIdx], Addrs[SrcIdx]); + } }; // These classes that emit the special functions for a non-trivial struct. diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index ed9e275daac78..ab870df65b78b 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -192,6 +192,108 @@ CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier qualifier, return CGPointerAuthInfo(qualifier.getKey(), discriminator); } +static std::pair<llvm::Value *, CGPointerAuthInfo> +emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &lv, + SourceLocation loc) { + auto value = CGF.EmitLoadOfScalar(lv, loc); + CGPointerAuthInfo authInfo; + if (auto ptrauth = lv.getQuals().getPointerAuth()) { + authInfo = CGF.EmitPointerAuthInfo(ptrauth, lv.getAddress()); + } else { + authInfo = getPointerAuthInfoForType(CGF.CGM, lv.getType()); + } + return { value, authInfo }; +} + +std::pair<llvm::Value *, CGPointerAuthInfo> +CodeGenFunction::EmitOrigPointerRValue(const Expr *E) { + assert(E->getType()->isPointerType()); + + E = E->IgnoreParens(); + if (auto load = dyn_cast<ImplicitCastExpr>(E)) { + if (load->getCastKind() == CK_LValueToRValue) { + E = load->getSubExpr()->IgnoreParens(); + + // We're semantically required to not emit loads of certain DREs naively. + if (auto refExpr = dyn_cast<DeclRefExpr>(const_cast<Expr*>(E))) { + if (auto result = tryEmitAsConstant(refExpr)) { + // Fold away a use of an intermediate variable. + if (!result.isReference()) + return { result.getValue(), + getPointerAuthInfoForType(CGM, refExpr->getType()) }; + + // Fold away a use of an intermediate reference. + auto lv = result.getReferenceLValue(*this, refExpr); + return emitLoadOfOrigPointerRValue(*this, lv, refExpr->getLocation()); + } + } + + // Otherwise, load and use the pointer + auto lv = EmitCheckedLValue(E, CodeGenFunction::TCK_Load); + return emitLoadOfOrigPointerRValue(*this, lv, E->getExprLoc()); + } + } + + // Emit direct references to functions without authentication. + if (auto DRE = dyn_cast<DeclRefExpr>(E)) { + if (auto FD = dyn_cast<FunctionDecl>(DRE->getDecl())) { + return { CGM.getRawFunctionPointer(FD), CGPointerAuthInfo() }; + } + } else if (auto ME = dyn_cast<MemberExpr>(E)) { + if (auto FD = dyn_cast<FunctionDecl>(ME->getMemberDecl())) { + EmitIgnoredExpr(ME->getBase()); + return { CGM.getRawFunctionPointer(FD), CGPointerAuthInfo() }; + } + } + + // Fallback: just use the normal rules for the type. + auto value = EmitScalarExpr(E); + return { value, getPointerAuthInfoForType(CGM, E->getType()) }; +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier, + const Expr *E, + Address destStorageAddress) { + assert(destQualifier); + + auto src = EmitOrigPointerRValue(E); + auto value = src.first; + auto curAuthInfo = src.second; + + auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress); + return EmitPointerAuthResign(value, E->getType(), curAuthInfo, destAuthInfo, + isPointerKnownNonNull(E)); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier, + llvm::Value *value, + QualType pointerType, + Address destStorageAddress, + bool isKnownNonNull) { + assert(destQualifier); + + auto curAuthInfo = getPointerAuthInfoForType(CGM, pointerType); + auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress); + return EmitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo, + isKnownNonNull); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthUnqualify(PointerAuthQualifier curQualifier, + llvm::Value *value, + QualType pointerType, + Address curStorageAddress, + bool isKnownNonNull) { + assert(curQualifier); + + auto curAuthInfo = EmitPointerAuthInfo(curQualifier, curStorageAddress); + auto destAuthInfo = getPointerAuthInfoForType(CGM, pointerType); + return EmitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo, + isKnownNonNull); +} + static bool isZeroConstant(llvm::Value *value) { if (auto ci = dyn_cast<llvm::ConstantInt>(value)) return ci->isZero(); @@ -260,6 +362,25 @@ CodeGenFunction::EmitPointerAuthResign(llvm::Value *value, QualType type, return value; } +void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier qualifier, + QualType type, + Address destAddress, + Address srcAddress) { + assert(qualifier); + + llvm::Value *value = Builder.CreateLoad(srcAddress); + + // If we're using address-discrimination, we have to re-sign the value. + if (qualifier.isAddressDiscriminated()) { + auto srcPtrAuth = EmitPointerAuthInfo(qualifier, srcAddress); + auto destPtrAuth = EmitPointerAuthInfo(qualifier, destAddress); + value = EmitPointerAuthResign(value, type, srcPtrAuth, destPtrAuth, + /*is known nonnull*/ false); + } + + Builder.CreateStore(value, destAddress); +} + /// We use an abstract, side-allocated cache for signed function pointers /// because (1) most compiler invocations will not need this cache at all, /// since they don't use signed function pointers, and (2) the diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index cba6cd6be85d5..65ebbc80f16b8 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3674,6 +3674,26 @@ class CodeGenFunction : public CodeGenTypeCache { CGPointerAuthInfo EmitPointerAuthInfo(PointerAuthQualifier qualifier, Address storageAddress); + llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier qualifier, + llvm::Value *pointer, + QualType valueType, + Address storageAddress, + bool isKnownNonNull); + llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier qualifier, + const Expr *pointerExpr, + Address storageAddress); + llvm::Value *EmitPointerAuthUnqualify(PointerAuthQualifier qualifier, + llvm::Value *pointer, + QualType pointerType, + Address storageAddress, + bool isKnownNonNull); + void EmitPointerAuthCopy(PointerAuthQualifier qualifier, QualType type, + Address destField, Address srcField); + + std::pair<llvm::Value *, CGPointerAuthInfo> + EmitOrigPointerRValue(const Expr *E); + bool isPointerKnownNonNull(const Expr *E); + // Return the copy constructor name with the prefix "__copy_constructor_" // removed. static std::string getNonTrivialCopyConstructorStr(QualType QT, diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index b11f36559a8b0..ddba494c8a79e 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -448,6 +448,7 @@ struct FormatToken { case tok::kw_noexcept: case tok::kw_static_assert: case tok::kw___attribute: + case tok::kw___ptrauth: return true; default: return false; diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index bbe05602f6da2..c3c17a24be2be 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2183,7 +2183,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) { // it is often token-pasted. while (FormatTok->isOneOf(tok::identifier, tok::coloncolon, tok::hashhash, tok::kw___attribute, tok::kw___declspec, - tok::kw_alignas) || + tok::kw_alignas, tok::kw___ptrauth) || ((Style.Language == FormatStyle::LK_Java || Style.Language == FormatStyle::LK_JavaScript) && FormatTok->isOneOf(tok::period, tok::comma))) { diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index 2fab3125edf13..2df030d000ab1 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -284,6 +284,43 @@ typedef uintptr_t ptrauth_generic_signature_t; #define ptrauth_sign_generic_data(__value, __data) \ __builtin_ptrauth_sign_generic_data(__value, __data) +/* Define some standard __ptrauth qualifiers used in the ABI. */ +#define __ptrauth_function_pointer \ + __ptrauth(ptrauth_key_function_pointer,0,0) +#define __ptrauth_return_address \ + __ptrauth(ptrauth_key_return_address,1,0) +#define __ptrauth_block_invocation_pointer \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_copy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_destroy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_byref_copy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_byref_destroy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_objc_method_list_imp \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_cxx_vtable_pointer \ + __ptrauth(ptrauth_key_cxx_vtable_pointer,0,0) +#define __ptrauth_cxx_vtt_vtable_pointer \ + __ptrauth(ptrauth_key_cxx_vtable_pointer,0,0) +#define __ptrauth_swift_heap_object_destructor \ + __ptrauth(ptrauth_key_function_pointer,1,0xbbbf) + +/* Some situations in the C++ and Swift ABIs use declaration-specific + or type-specific extra discriminators. */ +#define __ptrauth_cxx_virtual_function_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_function_pointer(__typekey) \ + __ptrauth(ptrauth_key_function_pointer,0,__typekey) +#define __ptrauth_swift_class_method_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_protocol_witness_function_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_value_witness_function_pointer(__key) \ + __ptrauth(ptrauth_key_function_pointer,1,__key) + #else #define ptrauth_strip(__value, __key) __value @@ -297,6 +334,23 @@ typedef uintptr_t ptrauth_generic_signature_t; #define ptrauth_type_discriminator(__type) ((uintptr_t)0) #define ptrauth_sign_generic_data(__value, __data) ((ptrauth_generic_signature_t)0) +#define __ptrauth_function_pointer +#define __ptrauth_return_address +#define __ptrauth_block_invocation_pointer +#define __ptrauth_block_copy_helper +#define __ptrauth_block_destroy_helper +#define __ptrauth_block_byref_copy_helper +#define __ptrauth_block_byref_destroy_helper +#define __ptrauth_objc_method_list_imp +#define __ptrauth_cxx_vtable_pointer +#define __ptrauth_cxx_vtt_vtable_pointer +#define __ptrauth_swift_heap_object_destructor +#define __ptrauth_cxx_virtual_function_pointer(__declkey) +#define __ptrauth_swift_function_pointer(__typekey) +#define __ptrauth_swift_class_method_pointer(__declkey) +#define __ptrauth_swift_protocol_witness_function_pointer(__declkey) +#define __ptrauth_swift_value_witness_function_pointer(__key) + #endif /* __PTRAUTH_INTRINSICS__ */ #endif /* __PTRAUTH_H */ diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 8483087ec31a8..d86c48edbf005 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2907,6 +2907,39 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, ParsedAttr::AS_Keyword, EllipsisLoc); } +/// type-qualifier: +/// '__ptrauth' '(' constant-expression +/// (',' constant-expression)[opt] +/// (',' constant-expression)[opt] ')' +void Parser::ParsePtrauthQualifier(ParsedAttributes &attrs) { + assert(Tok.is(tok::kw___ptrauth)); + + IdentifierInfo *kwName = Tok.getIdentifierInfo(); + SourceLocation kwLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume()) + return; + + ArgsVector argExprs; + do { + ExprResult expr = ParseAssignmentExpression(); + if (expr.isInvalid()) { + T.skipToEnd(); + return; + } + argExprs.push_back(expr.get()); + } while (TryConsumeToken(tok::comma)); + + T.consumeClose(); + SourceLocation endLoc = T.getCloseLocation(); + + attrs.addNew(kwName, SourceRange(kwLoc, endLoc), + /*scope*/ nullptr, SourceLocation(), + argExprs.data(), argExprs.size(), + ParsedAttr::AS_Keyword); +} + /// Determine whether we're looking at something that might be a declarator /// in a simple-declaration. If it can't possibly be a declarator, maybe /// diagnose a missing semicolon after a prior tag definition in the decl @@ -3510,6 +3543,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, getLangOpts()); break; + // __ptrauth qualifier. + case tok::kw___ptrauth: + ParsePtrauthQualifier(DS.getAttributes()); + continue; + case tok::kw___sptr: case tok::kw___uptr: case tok::kw___ptr64: @@ -4962,6 +5000,7 @@ bool Parser::isTypeSpecifierQualifier() { case tok::kw___ptr32: case tok::kw___pascal: case tok::kw___unaligned: + case tok::kw___ptrauth: case tok::kw__Nonnull: case tok::kw__Nullable: @@ -5159,6 +5198,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw___forceinline: case tok::kw___pascal: case tok::kw___unaligned: + case tok::kw___ptrauth: case tok::kw__Nonnull: case tok::kw__Nullable: @@ -5398,6 +5438,12 @@ void Parser::ParseTypeQualifierListOpt( ParseOpenCLQualifiers(DS.getAttributes()); break; + // __ptrauth qualifier. + case tok::kw___ptrauth: + ParsePtrauthQualifier(DS.getAttributes()); + EndLoc = PrevTokLocation; + continue; + case tok::kw___unaligned: isInvalid = DS.SetTypeQual(DeclSpec::TQ_unaligned, Loc, PrevSpec, DiagID, getLangOpts()); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 2ab0a11e53292..43ee2e18fdfe9 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -148,6 +148,14 @@ namespace { SrcExpr = src; } + void checkQualifiedDestType() { + // Destination type may not be qualified with __ptrauth. + if (DestType.getPointerAuth()) { + Self.Diag(DestRange.getBegin(), diag::err_ptrauth_qualifier_cast) + << DestType << DestRange; + } + } + /// Check for and handle non-overload placeholder expressions. void checkNonOverloadPlaceholders() { if (!isPlaceholder() || isPlaceholder(BuiltinType::Overload)) @@ -269,6 +277,8 @@ Sema::BuildCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind, Op.OpRange = SourceRange(OpLoc, Parens.getEnd()); Op.DestRange = AngleBrackets; + Op.checkQualifiedDestType(); + switch (Kind) { default: llvm_unreachable("Unknown C++ cast!"); @@ -2898,6 +2908,8 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, // -Wcast-qual DiagnoseCastQual(Op.Self, Op.SrcExpr, Op.DestType); + Op.checkQualifiedDestType(); + return Op.complete(CStyleCastExpr::Create(Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(), &Op.BasePath, CastTypeInfo, LPLoc, RPLoc)); @@ -2917,6 +2929,8 @@ ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo, if (Op.SrcExpr.isInvalid()) return ExprError(); + Op.checkQualifiedDestType(); + auto *SubExpr = Op.SrcExpr.get(); if (auto *BindExpr = dyn_cast<CXXBindTemporaryExpr>(SubExpr)) SubExpr = BindExpr->getSubExpr(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 6d376ef4d37e6..cc4e1062cba10 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -9785,6 +9785,9 @@ struct SearchNonTrivialToCopyField void visitARCWeak(QualType FT, SourceLocation SL) { S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0); } + void visitPtrAuth(QualType FT, SourceLocation SL) { + S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0); + } void visitStruct(QualType FT, SourceLocation SL) { for (const FieldDecl *FD : FT->castAs<RecordType>()->getDecl()->fields()) visit(FD->getType(), FD->getLocation()); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 2aeab6e784edb..1e3793015011c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11533,6 +11533,12 @@ struct DiagNonTrivalCUnionCopyVisitor asDerived().visit(FD->getType(), FD, InNonTrivialUnion); } + void visitPtrAuth(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) { + if (InNonTrivialUnion) + S.Diag(FD->getLocation(), diag::note_non_trivial_c_union) + << 1 << 2 << QT << FD->getName(); + } + void preVisit(QualType::PrimitiveCopyKind PCK, QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {} void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {} @@ -13245,6 +13251,12 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, New->setType(T); } + // __ptrauth is forbidden on parameters. + if (T.getPointerAuth()) { + Diag(NameLoc, diag::err_ptrauth_qualifier_param) << T; + New->setInvalidDecl(); + } + // ISO/IEC TR 18037 S6.7.3: "The type of an object with automatic storage // duration shall not be qualified by an address-space qualifier." // Since all parameters have automatic store duration, they can not have @@ -16678,8 +16690,12 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (RT->getDecl()->getArgPassingRestrictions() == RecordDecl::APK_CanNeverPassInRegs) Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); - } else if (FT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak) + } else if (FT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak) { Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); + } else if (PointerAuthQualifier Q = FT.getPointerAuth()) { + if (Q.isAddressDiscriminated()) + Record->setArgPassingRestrictions(RecordDecl::APK_CanNeverPassInRegs); + } } if (Record && FD->getType().isVolatileQualified()) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6c4e127b4fb1b..2d342cf0851f9 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7318,6 +7318,8 @@ struct SpecialMemberDeletionInfo bool shouldDeleteForVariantObjCPtrMember(FieldDecl *FD, QualType FieldType); + bool shouldDeleteForVariantPtrAuthMember(FieldDecl *FD, QualType FieldType); + bool visitBase(CXXBaseSpecifier *Base) { return shouldDeleteForBase(Base); } bool visitField(FieldDecl *Field) { return shouldDeleteForField(Field); } @@ -7466,12 +7468,36 @@ bool SpecialMemberDeletionInfo::shouldDeleteForVariantObjCPtrMember( S.Diag(FD->getLocation(), diag::note_deleted_special_member_class_subobject) << getEffectiveCSM() << ParentClass << /*IsField*/true - << FD << 4 << /*IsDtorCallInCtor*/false << /*IsObjCPtr*/true; + << FD << 4 << /*IsDtorCallInCtor*/false << 1; } return true; } +bool SpecialMemberDeletionInfo::shouldDeleteForVariantPtrAuthMember( + FieldDecl *FD, QualType FieldType) { + // Copy/move constructors/assignment operators are deleted if the field has an + // address-discriminated ptrauth qualifier. + PointerAuthQualifier Q = FieldType.getPointerAuth(); + + if (!Q || !Q.isAddressDiscriminated()) + return false; + + if (CSM == Sema::CXXDefaultConstructor || CSM == Sema::CXXDestructor) + return false; + + if (Diagnose) { + auto *ParentClass = cast<CXXRecordDecl>(FD->getParent()); + S.Diag(FD->getLocation(), + diag::note_deleted_special_member_class_subobject) + << getEffectiveCSM() << ParentClass << /*IsField*/true + << FD << 4 << /*IsDtorCallInCtor*/false << 2; + } + + return true; +} + + /// Check whether we should delete a special member function due to the class /// having a particular direct or virtual base class. bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXBaseSpecifier *Base) { @@ -7510,6 +7536,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (inUnion() && shouldDeleteForVariantObjCPtrMember(FD, FieldType)) return true; + if (inUnion() && shouldDeleteForVariantPtrAuthMember(FD, FieldType)) + return true; + if (CSM == Sema::CXXDefaultConstructor) { // For a default constructor, all references must be initialized in-class // and, if a union, it must have a non-const member. @@ -7574,6 +7603,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (shouldDeleteForVariantObjCPtrMember(&*UI, UnionFieldType)) return true; + if (shouldDeleteForVariantPtrAuthMember(&*UI, UnionFieldType)) + return true; + if (!UnionFieldType.isConstQualified()) AllVariantFieldsAreConst = false; @@ -8353,6 +8385,12 @@ void Sema::checkIllFormedTrivialABIStruct(CXXRecordDecl &RD) { return; } + // Ill-formed if the field is an address-discriminated pointer. + if (FT.hasAddressDiscriminatedPointerAuth()) { + PrintDiagAndRemoveAttr(); + return; + } + if (const auto *RT = FT->getBaseElementTypeUnsafe()->getAs<RecordType>()) if (!RT->isDependentType() && !cast<CXXRecordDecl>(RT->getDecl())->canPassInRegisters()) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 77f3c45f9fa67..8bfb89d9ac63e 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6990,6 +6990,14 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, lhQual.removeCVRQualifiers(); rhQual.removeCVRQualifiers(); + if (lhQual.getPointerAuth() != rhQual.getPointerAuth()) { + S.Diag(Loc, diag::err_typecheck_cond_incompatible_ptrauth) + << LHSTy << RHSTy + << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } + // OpenCL v2.0 specification doesn't extend compatibility of type qualifiers // (C99 6.7.3) for address spaces. We assume that the check should behave in // the same manner as it's defined for CVR qualifiers, so for OpenCL two @@ -7952,6 +7960,10 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType) { else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; + // Treat pointer-auth mismatches as fatal. + else if (lhq.getPointerAuth() != rhq.getPointerAuth()) + ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; + // For GCC/MS compatibility, other qualifier mismatches are treated // as still compatible in C. else ConvTy = Sema::CompatiblePointerDiscardsQualifiers; @@ -14722,7 +14734,9 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, if (lhq.getAddressSpace() != rhq.getAddressSpace()) { DiagKind = diag::err_typecheck_incompatible_address_space; break; - + } else if (lhq.getPointerAuth() != rhq.getPointerAuth()) { + DiagKind = diag::err_typecheck_incompatible_ptrauth; + break; } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { DiagKind = diag::err_typecheck_incompatible_ownership; break; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index a44726fe3008a..ff66a51b5baf5 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2456,6 +2456,12 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) { return true; } + // __ptrauth is illegal on a function return type. + if (T.getPointerAuth()) { + Diag(Loc, diag::err_ptrauth_qualifier_return) << T; + return true; + } + if (T.hasNonTrivialToPrimitiveDestructCUnion() || T.hasNonTrivialToPrimitiveCopyCUnion()) checkNonTrivialCUnion(T, Loc, NTCUC_FunctionReturn, @@ -2544,6 +2550,10 @@ QualType Sema::BuildFunctionType(QualType T, Diag(Loc, diag::err_parameters_retval_cannot_have_fp16_type) << 0 << FixItHint::CreateInsertion(Loc, "*"); Invalid = true; + } else if (ParamType.getPointerAuth()) { + // __ptrauth is illegal on a function return type. + Diag(Loc, diag::err_ptrauth_qualifier_param) << T; + Invalid = true; } // C++2a [dcl.fct]p4: @@ -4623,6 +4633,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } + // __ptrauth is illegal on a function return type. + if (T.getPointerAuth()) { + S.Diag(DeclType.Loc, diag::err_ptrauth_qualifier_return) << T; + } + if (LangOpts.OpenCL) { // OpenCL v2.0 s6.12.5 - A block cannot be the return value of a // function. @@ -7343,6 +7358,90 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr, CurType = S.Context.getVectorType(CurType, numElts, VecKind); } +/// Handle the __ptrauth qualifier. +static void HandlePtrAuthQualifier(QualType &type, const ParsedAttr &attr, + Sema &S) { + if (attr.getNumArgs() < 1 || attr.getNumArgs() > 3) { + S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_bad_arg_count); + attr.setInvalid(); + return; + } + + Expr *keyArg = + attr.getArgAsExpr(0); + Expr *isAddressDiscriminatedArg = + attr.getNumArgs() >= 2 ? attr.getArgAsExpr(1) : nullptr; + Expr *extraDiscriminatorArg = + attr.getNumArgs() >= 3 ? attr.getArgAsExpr(2) : nullptr; + + unsigned key; + if (S.checkConstantPointerAuthKey(keyArg, key)) { + attr.setInvalid(); + return; + } + assert(key <= PointerAuthQualifier::MaxKey && "ptrauth key is out of range"); + + bool isInvalid = false; + auto checkArg = [&](Expr *arg, unsigned argIndex) -> unsigned { + if (!arg) return 0; + + llvm::APSInt result; + if (!arg->isIntegerConstantExpr(result, S.Context)) { + isInvalid = true; + S.Diag(arg->getExprLoc(), diag::err_ptrauth_qualifier_arg_not_ice); + return 0; + } + + unsigned max = + (argIndex == 1 ? 1 : PointerAuthQualifier::MaxDiscriminator); + if (result < 0 || result > max) { + llvm::SmallString<32> value; { + llvm::raw_svector_ostream str(value); + str << result; + } + + if (argIndex == 1) { + S.Diag(arg->getExprLoc(), + diag::err_ptrauth_qualifier_address_discrimination_invalid) + << value; + } else { + S.Diag(arg->getExprLoc(), + diag::err_ptrauth_qualifier_extra_discriminator_invalid) + << value << max; + } + isInvalid = true; + } + return result.getZExtValue(); + }; + bool isAddressDiscriminated = checkArg(isAddressDiscriminatedArg, 1); + unsigned extraDiscriminator = checkArg(extraDiscriminatorArg, 2); + if (isInvalid) { + attr.setInvalid(); + return; + } + + if (!type->isPointerType()) { + S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << type; + attr.setInvalid(); + return; + } + + if (type.getPointerAuth()) { + S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_redundant) << type; + attr.setInvalid(); + return; + } + + if (!S.getLangOpts().PointerAuthIntrinsics) { + S.diagnosePointerAuthDisabled(attr.getLoc(), attr.getRange()); + attr.setInvalid(); + return; + } + + PointerAuthQualifier qual(key, isAddressDiscriminated, extraDiscriminator); + type = S.Context.getPointerAuthType(type, qual); +} + /// Handle OpenCL Access Qualifier Attribute. static void HandleOpenCLAccessAttr(QualType &CurType, const ParsedAttr &Attr, Sema &S) { @@ -7652,6 +7751,10 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, HandleOpenCLAccessAttr(type, attr, state.getSema()); attr.setUsedAsTypeAttr(); break; + case ParsedAttr::AT_PointerAuth: + HandlePtrAuthQualifier(type, attr, state.getSema()); + attr.setUsedAsTypeAttr(); + break; case ParsedAttr::AT_LifetimeBound: if (TAL == TAL_DeclChunk) HandleLifetimeBoundAttr(state, type, attr); diff --git a/clang/test/AST/ast-dump-ptrauth-json.cpp b/clang/test/AST/ast-dump-ptrauth-json.cpp new file mode 100644 index 0000000000000..d4f23fba17c31 --- /dev/null +++ b/clang/test/AST/ast-dump-ptrauth-json.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -std=c++11 -ast-dump=json %s | FileCheck %s + +// CHECK: "name": "ptrauth_type_discriminator", + +int d = __builtin_ptrauth_type_discriminator(int); diff --git a/clang/test/CodeGen/ptrauth-debuginfo.c b/clang/test/CodeGen/ptrauth-debuginfo.c new file mode 100644 index 0000000000000..807bf93a6d9f5 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-debuginfo.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios \ +// RUN: -fptrauth-calls -fptrauth-intrinsics -emit-llvm -fblocks \ +// RUN: %s -debug-info-kind=limited -o - | FileCheck %s + +// Constant initializers for data pointers. +extern int external_int; + +// CHECK: !DIDerivedType(tag: DW_TAG_APPLE_ptrauth_type, +// CHECK-SAME: ptrAuthKey: 1, +// CHECK-SAME: ptrAuthIsAddressDiscriminated: false, +// CHECK-SAME: ptrAuthExtraDiscriminator: 1234) +int * __ptrauth(1,0,1234) g1 = &external_int; + +struct A { + int value; +}; +struct A *createA(void); + +void f() { + __block struct A * __ptrauth(1, 1, 1) ptr = createA(); + ^{ ptr->value; }(); +} +// CHECK: !DIDerivedType(tag: DW_TAG_APPLE_ptrauth_type, +// CHECK-SAME: ptrAuthKey: 1, +// CHECK-SAME: ptrAuthIsAddressDiscriminated: true, +// CHECK-SAME: ptrAuthExtraDiscriminator: 1) diff --git a/clang/test/CodeGen/ptrauth-in-c-struct.c b/clang/test/CodeGen/ptrauth-in-c-struct.c new file mode 100644 index 0000000000000..c1919f981c62a --- /dev/null +++ b/clang/test/CodeGen/ptrauth-in-c-struct.c @@ -0,0 +1,129 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fblocks -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -o - %s | FileCheck %s + +#define AQ __ptrauth(1,1,50) +#define IQ __ptrauth(1,0,50) + +typedef void (^BlockTy)(void); + +// CHECK: %[[STRUCT_SA:.*]] = type { i32, i32* } +// CHECK: %[[STRUCT_SI:.*]] = type { i32* } + +typedef struct { + int f0; + int * AQ f1; // Signed using address discrimination. +} SA; + +typedef struct { + int * IQ f; // No address discrimination. +} SI; + +SA getSA(void); +void calleeSA(SA); + +// CHECK: define void @test_copy_constructor_SA(%[[STRUCT_SA]]* %{{.*}}) +// CHECK: call void @__copy_constructor_8_8_t0w4_pa8( + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// CHECK: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// CHECK: %[[V11:.*]] = load i8*, i8** %[[V10]], align 8 +// CHECK: %[[V12:.*]] = ptrtoint i8** %[[V10]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V12]], i64 50) +// CHECK: %[[V14:.*]] = ptrtoint i8** %[[V7]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V14]], i64 50) +// CHECK: %[[V17:.*]] = ptrtoint i8* %[[V11]] to i64 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V17]], i32 1, i64 %[[V13]], i32 1, i64 %[[V15]]) + +void test_copy_constructor_SA(SA *s) { + SA t = *s; +} + +// CHECK: define void @test_copy_assignment_SA( +// CHECK: call void @__copy_assignment_8_8_t0w4_pa8( + +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_pa8( + +void test_copy_assignment_SA(SA *d, SA *s) { + *d = *s; +} + +// CHECK: define void @test_move_constructor_SA( +// CHECK: define internal void @__Block_byref_object_copy_( +// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_t0w4_pa8( + +void test_move_constructor_SA(void) { + __block SA t; + BlockTy b = ^{ (void)t; }; +} + +// CHECK: define void @test_move_assignment_SA( +// CHECK: call void @__move_assignment_8_8_t0w4_pa8( +// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_pa8( + +void test_move_assignment_SA(SA *p) { + *p = getSA(); +} + +// CHECK: define void @test_parameter_SA(%[[STRUCT_SA]]* %{{.*}}) +// CHECK-NOT: call +// CHECK: ret void + +void test_parameter_SA(SA a) { +} + +// CHECK: define void @test_argument_SA(%[[STRUCT_SA]]* %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SA]], align 8 +// CHECK: store %[[STRUCT_SA]]* %[[A]], %[[STRUCT_SA]]** %[[A_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_SA]]* %[[AGG_TMP]] to i8** +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_SA]]* %[[V0]] to i8** +// CHECK: call void @__copy_constructor_8_8_t0w4_pa8(i8** %[[V1]], i8** %[[V2]]) #5 +// CHECK: call void @calleeSA(%[[STRUCT_SA]]* %[[AGG_TMP]]) +// CHECK-NOT: call +// CHECK: ret void + +void test_argument_SA(SA *a) { + calleeSA(*a); +} + +// CHECK: define void @test_return_SA(%[[STRUCT_SA]]* noalias sret %[[AGG_RESULT:.*]], %[[STRUCT_SA]]* %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: store %[[STRUCT_SA]]* %[[A]], %[[STRUCT_SA]]** %[[A_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_SA]]* %[[AGG_RESULT]] to i8** +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_SA]]* %[[V0]] to i8** +// CHECK: call void @__copy_constructor_8_8_t0w4_pa8(i8** %[[V1]], i8** %[[V2]]) #5 +// CHECK-NOT: call +// CHECK: ret void + +SA test_return_SA(SA *a) { + return *a; +} + +// CHECK: define void @test_copy_constructor_SI( +// CHECK-NOT: call +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( +// CHECK-NOT: call +// CHECK: ret void + +void test_copy_constructor_SI(SI *s) { + SI t = *s; +} + +// CHECK: define void @test_parameter_SI(i64 %{{.*}}) +// CHECK-NOT: call +// CHECK: ret void + +void test_parameter_SI(SI a) { +} diff --git a/clang/test/CodeGen/ptrauth-qualifier-loadstore.c b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c new file mode 100644 index 0000000000000..919d19a6ccfeb --- /dev/null +++ b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c @@ -0,0 +1,744 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +#define IQ __ptrauth(1,0,50) +#define AQ __ptrauth(1,1,50) +#define DIFF_IQ __ptrauth(1,0,100) +#define DIFF_AQ __ptrauth(1,1,100) +#define ZERO_IQ __ptrauth(1,0,0) +#define ZERO_AQ __ptrauth(1,1,0) + +extern int external_int; +extern int * global_upi; +extern int * IQ global_iqpi; +extern int * AQ global_aqpi; +extern void use_upi(int *ptr); + +typedef void func_t(void); +extern void external_func(void); +extern func_t *global_upf; +extern func_t * IQ global_iqpf; +extern func_t * AQ global_aqpf; +extern void use_upf(func_t *ptr); + +// Data with address-independent qualifiers. + +// CHECK-LABEL: define void @test_store_data_i_constant() +void test_store_data_i_constant() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i32* @external_int to i64), i32 1, i64 50) +// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to i32* +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * IQ iqpi = &external_int; +// CHECK-NEXT: [[T0:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i32* @external_int to i64), i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T0]] to i32* +// CHECK-NEXT: store i32* [[SIGNED]], i32** [[V]], +// CHECK-NEXT: ret void + iqpi = &external_int; +} + +// CHECK-LABEL: define void @test_store_data_iu() +void test_store_data_iu() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_upi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 [[T0]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * IQ iqpi = global_upi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_upi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 [[T0]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + iqpi = global_upi; +} + +// CHECK-LABEL: define void @test_store_data_ia() +void test_store_data_ia() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * IQ iqpi = global_aqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + iqpi = global_aqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[RESULT:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[RESULT]], i32** [[V]], +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[RESULT]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[RESULT]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 1, i64 50) +// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[RESULT:%.*]] = phi i32* [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ] +// CHECK-NEXT: call void @use_upi(i32* [[RESULT]]) + use_upi(iqpi = global_aqpi); +} + +// CHECK-LABEL: define void @test_store_data_ii_same() +void test_store_data_ii_same() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: store i32* [[LOAD]], i32** [[V]], + int * IQ iqpi = global_iqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: store i32* [[LOAD]], i32** [[V]], + iqpi = global_iqpi; +} + +// CHECK-LABEL: define void @test_store_data_ii_different() +void test_store_data_ii_different() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 100) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * DIFF_IQ iqpi = global_iqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 100) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + iqpi = global_iqpi; +} + +// CHECK-LABEL: define void @test_store_data_ii_zero() +void test_store_data_ii_zero() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * ZERO_IQ iqpi = global_iqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** [[V]] +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** @global_iqpi, + global_iqpi = iqpi; +} + +// CHECK-LABEL: define void @test_load_data_i() +void test_load_data_i() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 1, i64 50) +// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int *upi = global_iqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 1, i64 50) +// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + upi = global_iqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 1, i64 50) +// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ] +// CHECK-NEXT: call void @use_upi(i32* [[T0]]) + use_upi(global_iqpi); +} + +// Data with address-discriminated qualifiers. + +// CHECK-LABEL: define void @test_store_data_a_constant() +void test_store_data_a_constant() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i32* @external_int to i64), i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to i32* +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * AQ aqpi = &external_int; +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (i32* @external_int to i64), i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to i32* +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + aqpi = &external_int; +} + +// CHECK-LABEL: define void @test_store_data_au() +void test_store_data_au() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_upi, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 [[T0]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * AQ aqpi = global_upi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_upi, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign.i64(i64 [[T0]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + aqpi = global_upi; +} + +// CHECK-LABEL: define void @test_store_data_ai() +void test_store_data_ai() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * AQ aqpi = global_iqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_iqpi, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + aqpi = global_iqpi; +} + +// CHECK-LABEL: define void @test_store_data_aa_same() +void test_store_data_aa_same() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * AQ aqpi = global_aqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + aqpi = global_aqpi; +} + +// CHECK-LABEL: define void @test_store_data_aa_different() +void test_store_data_aa_different() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 100) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * DIFF_AQ aqpi = global_aqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 100) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + aqpi = global_aqpi; +} + +// CHECK-LABEL: define void @test_store_data_aa_zero() +void test_store_data_aa_zero() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[NEWDISC:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int * ZERO_AQ aqpi = global_aqpi; +// CHECK: [[LOAD:%.*]] = load i32*, i32** [[V]], +// CHECK-NEXT: [[OLDDISC:%.*]] = ptrtoint i32** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** @global_aqpi, + global_aqpi = aqpi; +} + +// CHECK-LABEL: define void @test_load_data_a() +void test_load_data_a() { +// CHECK: [[V:%.*]] = alloca i32*, +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]]) +// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + int *upi = global_aqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]]) +// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ] +// CHECK-NEXT: store i32* [[T0]], i32** [[V]], + upi = global_aqpi; +// CHECK-NEXT: [[LOAD:%.*]] = load i32*, i32** @global_aqpi, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (i32** @global_aqpi to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne i32* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint i32* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.auth.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]]) +// CHECK-NEXT: [[AUTHED:%.*]] = inttoptr i64 [[T1]] to i32* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi i32* [ null, {{.*}} ], [ [[AUTHED]], {{.*}} ] +// CHECK-NEXT: call void @use_upi(i32* [[T0]]) + use_upi(global_aqpi); +} + +// Function with address-independent qualifiers. + +// CHECK-LABEL: define void @test_store_function_i_constant() +void test_store_function_i_constant() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to void ()* +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * IQ iqpf = &external_func; +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to void ()* +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + iqpf = &external_func; +} + +// CHECK-LABEL: define void @test_store_function_iu() +void test_store_function_iu() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_upf, +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * IQ iqpf = global_upf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_upf, +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + iqpf = global_upf; +} + +// CHECK-LABEL: define void @test_store_function_ia() +void test_store_function_ia() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * IQ iqpf = global_aqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + iqpf = global_aqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 50) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[RESULT:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[RESULT]], void ()** [[V]], +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[RESULT]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[RESULT]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: call void @use_upf(void ()* [[T0]]) + use_upf(iqpf = global_aqpf); +} + +// CHECK-LABEL: define void @test_store_function_ii_same() +void test_store_function_ii_same() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: store void ()* [[LOAD]], void ()** [[V]], + func_t * IQ iqpf = global_iqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: store void ()* [[LOAD]], void ()** [[V]], + iqpf = global_iqpf; +} + +// CHECK-LABEL: define void @test_store_function_ii_different() +void test_store_function_ii_different() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 100) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * DIFF_IQ iqpf = global_iqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 100) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + iqpf = global_iqpf; +} + +// CHECK-LABEL: define void @test_load_function_i() +void test_load_function_i() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t *upf = global_iqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + upf = global_iqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: call void @use_upf(void ()* [[T0]]) + use_upf(global_iqpf); +} + +// Function with address-discriminated qualifiers. + +// CHECK-LABEL: define void @test_store_function_a_constant() +void test_store_function_a_constant() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to void ()* +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * AQ aqpf = &external_func; +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to void ()* +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + aqpf = &external_func; +} + +// CHECK-LABEL: define void @test_store_function_au() +void test_store_function_au() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_upf, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * AQ aqpf = global_upf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_upf, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + aqpf = global_upf; +} + +// CHECK-LABEL: define void @test_store_function_ai() +void test_store_function_ai() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * AQ aqpf = global_iqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_iqpf, +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 50, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + aqpf = global_iqpf; +} + +// CHECK-LABEL: define void @test_store_function_aa_same() +void test_store_function_aa_same() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * AQ aqpf = global_aqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + aqpf = global_aqpf; +} + +// CHECK-LABEL: define void @test_store_function_aa_different() +void test_store_function_aa_different() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 100) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t * DIFF_AQ aqpf = global_aqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = ptrtoint void ()** [[V]] to i64 +// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 [[T0]], i64 100) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + aqpf = global_aqpf; +} + +// CHECK-LABEL: define void @test_load_function_a() +void test_load_function_a() { +// CHECK: [[V:%.*]] = alloca void ()*, +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + func_t *upf = global_aqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: store void ()* [[T0]], void ()** [[V]], + upf = global_aqpf; +// CHECK-NEXT: [[LOAD:%.*]] = load void ()*, void ()** @global_aqpf, +// CHECK-NEXT: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (void ()** @global_aqpf to i64), i64 50) +// CHECK-NEXT: [[T0:%.*]] = icmp ne void ()* [[LOAD]], null +// CHECK-NEXT: br i1 [[T0]], +// CHECK: [[T0:%.*]] = ptrtoint void ()* [[LOAD]] to i64 +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign.i64(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 0) +// CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to void ()* +// CHECK-NEXT: br label +// CHECK: [[T0:%.*]] = phi void ()* [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] +// CHECK-NEXT: call void @use_upf(void ()* [[T0]]) + use_upf(global_aqpf); +} diff --git a/clang/test/CodeGen/ptrauth-qualifier.c b/clang/test/CodeGen/ptrauth-qualifier.c new file mode 100644 index 0000000000000..c123103f06d15 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-qualifier.c @@ -0,0 +1,87 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +// Constant initializers for data pointers. +extern int external_int; + +// CHECK: [[PTRAUTH_G1:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 0, i64 56 }, section "llvm.ptrauth" +// CHECK: @g1 = global i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_G1]] to i32*) +int * __ptrauth(1,0,56) g1 = &external_int; + +// CHECK: [[PTRAUTH_G2:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 ptrtoint (i32** @g2 to i64), i64 1272 }, section "llvm.ptrauth" +// CHECK: @g2 = global i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_G2]] to i32*) +int * __ptrauth(1,1,1272) g2 = &external_int; + +// CHECK: @g3 = global i32* null +int * __ptrauth(1,1,871) g3 = 0; + +// FIXME: should we make a ptrauth constant for this absolute symbol? +// CHECK: @g4 = global i32* inttoptr (i64 1230 to i32*) +int * __ptrauth(1,1,1902) g4 = (int*) 1230; + +// CHECK: [[PTRAUTH_GA0:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 ptrtoint ([3 x i32*]* @ga to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GA1:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 ptrtoint (i32** getelementptr inbounds ([3 x i32*], [3 x i32*]* @ga, i32 0, i32 1) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GA2:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 ptrtoint (i32** getelementptr inbounds ([3 x i32*], [3 x i32*]* @ga, i32 0, i32 2) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: @ga = global [3 x i32*] [i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GA0]] to i32*), i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GA1]] to i32*), i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GA2]] to i32*)] +int * __ptrauth(1,1,712) ga[3] = { &external_int, &external_int, &external_int }; + +struct A { + int * __ptrauth(1,0,431) f0; + int * __ptrauth(1,0,9182) f1; + int * __ptrauth(1,0,783) f2; +}; +// CHECK: [[PTRAUTH_GS0:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 0, i64 431 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS1:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 0, i64 9182 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS2:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 0, i64 783 }, section "llvm.ptrauth" +// CHECK: @gs1 = global %struct.A { i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GS0]] to i32*), i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GS1]] to i32*), i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GS2]] to i32*) } +struct A gs1 = { &external_int, &external_int, &external_int }; + +struct B { + int * __ptrauth(1,1,1276) f0; + int * __ptrauth(1,1,23674) f1; + int * __ptrauth(1,1,163) f2; +}; +// CHECK: [[PTRAUTH_GS0:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 ptrtoint (%struct.B* @gs2 to i64), i64 1276 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS1:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 ptrtoint (i32** getelementptr inbounds (%struct.B, %struct.B* @gs2, i32 0, i32 1) to i64), i64 23674 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS2:@external_int.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (i32* @external_int to i8*), i32 1, i64 ptrtoint (i32** getelementptr inbounds (%struct.B, %struct.B* @gs2, i32 0, i32 2) to i64), i64 163 }, section "llvm.ptrauth" +// CHECK: @gs2 = global %struct.B { i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GS0]] to i32*), i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GS1]] to i32*), i32* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_GS2]] to i32*) } +struct B gs2 = { &external_int, &external_int, &external_int }; + +// Constant initializers for function pointers. +extern void external_function(void); +typedef void (*fpt)(void); + +// CHECK: [[PTRAUTH_F1:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 0, i64 56 }, section "llvm.ptrauth" +// CHECK: @f1 = global void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_F1]] to void ()*) +fpt __ptrauth(1,0,56) f1 = &external_function; + +// CHECK: [[PTRAUTH_F2:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 ptrtoint (void ()** @f2 to i64), i64 1272 }, section "llvm.ptrauth" +// CHECK: @f2 = global void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_F2]] to void ()*) +fpt __ptrauth(1,1,1272) f2 = &external_function; + +// CHECK: [[PTRAUTH_FA0:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 ptrtoint ([3 x void ()*]* @fa to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FA1:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 ptrtoint (void ()** getelementptr inbounds ([3 x void ()*], [3 x void ()*]* @fa, i32 0, i32 1) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FA2:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 ptrtoint (void ()** getelementptr inbounds ([3 x void ()*], [3 x void ()*]* @fa, i32 0, i32 2) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: @fa = global [3 x void ()*] [void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FA0]] to void ()*), void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FA1]] to void ()*), void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FA2]] to void ()*)] +fpt __ptrauth(1,1,712) fa[3] = { &external_function, &external_function, &external_function }; + +struct C { + fpt __ptrauth(1,0,431) f0; + fpt __ptrauth(1,0,9182) f1; + fpt __ptrauth(1,0,783) f2; +}; +// CHECK: [[PTRAUTH_FS0:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 0, i64 431 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS1:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 0, i64 9182 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS2:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 0, i64 783 }, section "llvm.ptrauth" +// CHECK: @fs1 = global %struct.C { void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FS0]] to void ()*), void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FS1]] to void ()*), void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FS2]] to void ()*) } +struct C fs1 = { &external_function, &external_function, &external_function }; + +struct D { + fpt __ptrauth(1,1,1276) f0; + fpt __ptrauth(1,1,23674) f1; + fpt __ptrauth(1,1,163) f2; +}; +// CHECK: [[PTRAUTH_FS0:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 ptrtoint (%struct.D* @fs2 to i64), i64 1276 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS1:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 ptrtoint (void ()** getelementptr inbounds (%struct.D, %struct.D* @fs2, i32 0, i32 1) to i64), i64 23674 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS2:@external_function.ptrauth.*]] = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @external_function to i8*), i32 1, i64 ptrtoint (void ()** getelementptr inbounds (%struct.D, %struct.D* @fs2, i32 0, i32 2) to i64), i64 163 }, section "llvm.ptrauth" +// CHECK: @fs2 = global %struct.D { void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FS0]] to void ()*), void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FS1]] to void ()*), void ()* bitcast ({ i8*, i32, i64, i64 }* [[PTRAUTH_FS2]] to void ()*) } +struct D fs2 = { &external_function, &external_function, &external_function }; diff --git a/clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp b/clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp new file mode 100644 index 0000000000000..d91e898da7ca5 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-qualifier-struct.cpp @@ -0,0 +1,168 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -std=c++11 -emit-llvm %s -o - | FileCheck %s + +#define AQ __ptrauth(1,1,50) +#define IQ __ptrauth(1,0,50) + +// CHECK: %[[STRUCT_TRIVIALSA:.*]] = type { i32*, i32* } +// CHECK: %[[STRUCT_SA:.*]] = type { i32*, i32* } +// CHECK: %[[STRUCT_SI:.*]] = type { i32* } + +struct SA { + int * AQ m0; // Signed using address discrimination. + int * AQ m1; // Signed using address discrimination. +}; + +struct SI { + int * IQ m; // No address discrimination. +}; + +struct __attribute__((trivial_abi)) TrivialSA { + int * AQ m0; // Signed using address discrimination. + int * AQ m1; // Signed using address discrimination. +}; + +// Check that TrivialSA is passed indirectly despite being annotated with +// 'trivial_abi'. + +// CHECK: define void @_Z18testParamTrivialSA9TrivialSA(%[[STRUCT_TRIVIALSA]]* %{{.*}}) + +void testParamTrivialSA(TrivialSA a) { +} + +// CHECK: define void @_Z19testCopyConstructor2SA(%[[STRUCT_SA]]* +// CHECK: call %[[STRUCT_SA]]* @_ZN2SAC1ERKS_( + +// CHECK: define linkonce_odr %[[STRUCT_SA]]* @_ZN2SAC1ERKS_( +// CHECK: call %[[STRUCT_SA]]* @_ZN2SAC2ERKS_( + +void testCopyConstructor(SA a) { + SA t = a; +} + +// CHECK: define void @_Z19testMoveConstructor2SA(%[[STRUCT_SA]]* +// CHECK: call %[[STRUCT_SA]]* @_ZN2SAC1EOS_( + +// CHECK: define linkonce_odr %[[STRUCT_SA]]* @_ZN2SAC1EOS_( +// CHECK: call %[[STRUCT_SA]]* @_ZN2SAC2EOS_( + +void testMoveConstructor(SA a) { + SA t = static_cast<SA &&>(a); +} + +// CHECK: define void @_Z18testCopyAssignment2SA(%[[STRUCT_SA]]* +// CHECK: call dereferenceable(16) %[[STRUCT_SA]]* @_ZN2SAaSERKS_( + +// CHECK: define linkonce_odr dereferenceable(16) %[[STRUCT_SA:.*]]* @_ZN2SAaSERKS_(%[[STRUCT_SA]]* %[[THIS:.*]], %[[STRUCT_SA]]* dereferenceable(16) %0) +// CHECK: %[[THIS_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: %[[_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: store %[[STRUCT_SA]]* %[[THIS]], %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: store %[[STRUCT_SA]]* %[[V0:.*]], %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[THISI:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[THISI]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[V1]], i32 0, i32 0 +// CHECK: %[[V2:.*]] = load i32*, i32** %[[M02]], align 8 +// CHECK: %[[V3:.*]] = ptrtoint i32** %[[M02]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V3]], i64 50) +// CHECK: %[[V5:.*]] = ptrtoint i32** %[[M0]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V5]], i64 50) +// CHECK: %[[V8:.*]] = ptrtoint i32* %[[V2]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]]) + +void testCopyAssignment(SA a) { + SA t; + t = a; +} + +// CHECK: define void @_Z18testMoveAssignment2SA(%[[STRUCT_SA]]* +// CHECK: call dereferenceable(16) %[[STRUCT_SA]]* @_ZN2SAaSEOS_( + +// CHECK: define linkonce_odr dereferenceable(16) %[[STRUCT_SA:.*]]* @_ZN2SAaSEOS_(%[[STRUCT_SA]]* %[[THIS:.*]], %[[STRUCT_SA]]* dereferenceable(16) %0) +// CHECK: %[[THIS_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: %[[_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: store %[[STRUCT_SA]]* %[[THIS]], %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: store %[[STRUCT_SA]]* %[[V0:.*]], %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[THISI:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[THISI]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[V1]], i32 0, i32 0 +// CHECK: %[[V2:.*]] = load i32*, i32** %[[M02]], align 8 +// CHECK: %[[V3:.*]] = ptrtoint i32** %[[M02]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V3]], i64 50) +// CHECK: %[[V5:.*]] = ptrtoint i32** %[[M0]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V5]], i64 50) +// CHECK: %[[V8:.*]] = ptrtoint i32* %[[V2]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]]) + +void testMoveAssignment(SA a) { + SA t; + t = static_cast<SA &&>(a); +} + +// CHECK: define void @_Z19testCopyConstructor2SI(i +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( + +void testCopyConstructor(SI a) { + SI t = a; +} + +// CHECK: define void @_Z19testMoveConstructor2SI( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( + +void testMoveConstructor(SI a) { + SI t = static_cast<SI &&>(a); +} + +// CHECK: define void @_Z18testCopyAssignment2SI( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( + +void testCopyAssignment(SI a) { + SI t; + t = a; +} + +// CHECK: define void @_Z18testMoveAssignment2SI( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64( + +void testMoveAssignment(SI a) { + SI t; + t = static_cast<SI &&>(a); +} + +// CHECK: define linkonce_odr %[[STRUCT_SA:.*]]* @_ZN2SAC2ERKS_(%[[STRUCT_SA]]* %[[THIS:.*]], %[[STRUCT_SA]]* dereferenceable(16) %0) +// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: %[[THIS_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: %[[_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: store %[[STRUCT_SA]]* %[[THIS]], %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: store %[[STRUCT_SA]]* %[[V0:.*]], %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: store %[[STRUCT_SA]]* %[[THIS1]], %[[STRUCT_SA]]** %[[RETVAL]], align 8 +// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[V1]], i32 0, i32 0 +// CHECK: %[[V2:.*]] = load i32*, i32** %[[M02]], align 8 +// CHECK: %[[V3:.*]] = ptrtoint i32** %[[M02]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V3]], i64 50) +// CHECK: %[[V5:.*]] = ptrtoint i32** %[[M0]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V5]], i64 50) +// CHECK: %[[V8:.*]] = ptrtoint i32* %[[V2]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]]) + +// CHECK: define linkonce_odr %[[STRUCT_SA:.*]]* @_ZN2SAC2EOS_(%[[STRUCT_SA]]* %[[THIS:.*]], %[[STRUCT_SA]]* dereferenceable(16) %0) +// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: %[[THIS_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: %[[_ADDR:.*]] = alloca %[[STRUCT_SA]]*, align 8 +// CHECK: store %[[STRUCT_SA]]* %[[THIS]], %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: store %[[STRUCT_SA]]* %[[V0:.*]], %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[THIS_ADDR]], align 8 +// CHECK: store %[[STRUCT_SA]]* %[[THIS1]], %[[STRUCT_SA]]** %[[RETVAL]], align 8 +// CHECK: %[[M0:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[_ADDR]], align 8 +// CHECK: %[[M02:.*]] = getelementptr inbounds %[[STRUCT_SA]], %[[STRUCT_SA]]* %[[V1]], i32 0, i32 0 +// CHECK: %[[V2:.*]] = load i32*, i32** %[[M02]], align 8 +// CHECK: %[[V3:.*]] = ptrtoint i32** %[[M02]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V3]], i64 50) +// CHECK: %[[V5:.*]] = ptrtoint i32** %[[M0]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V5]], i64 50) +// CHECK: %[[V8:.*]] = ptrtoint i32* %[[V2]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V8]], i32 1, i64 %[[V4]], i32 1, i64 %[[V6]]) diff --git a/clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm b/clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm new file mode 100644 index 0000000000000..f259a56f93c85 --- /dev/null +++ b/clang/test/CodeGenObjCXX/ptrauth-struct-cxx-abi.mm @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fptrauth-calls -fptrauth-intrinsics -std=c++11 -fobjc-arc -emit-llvm -o - %s | FileCheck %s + +// CHECK: %[[STRUCT_ADDRDISCSTRONG0:.*]] = type { i32*, i8* } +// CHECK: %[[STRUCT_ADDRDISCSTRONG1:.*]] = type { i32*, i8* } + +#define AQ __ptrauth(1,1,50) + +struct AddrDiscStrong0 { + int * AQ f0; // Signed using address discrimination. + __strong id f1; +}; + +struct AddrDiscStrong1 { + AddrDiscStrong1(const AddrDiscStrong1 &); + int * AQ f0; // Signed using address discrimination. + __strong id f1; +}; + +// Check that AddrDiscStrong0 is destructed in the callee. + +// CHECK: define void @_Z24testParamAddrDiscStrong015AddrDiscStrong0(%[[STRUCT_ADDRDISCSTRONG0]]* %[[A:.*]]) +// CHECK: call %[[STRUCT_ADDRDISCSTRONG0]]* @_ZN15AddrDiscStrong0D1Ev(%[[STRUCT_ADDRDISCSTRONG0]]* %[[A]]) +// CHECK: ret void + +// CHECK: define linkonce_odr %[[STRUCT_ADDRDISCSTRONG0]]* @_ZN15AddrDiscStrong0D1Ev( + +void testParamAddrDiscStrong0(AddrDiscStrong0 a) { +} + +// Check that AddrDiscStrong1 is not destructed in the callee because it has a +// non-trivial copy constructor. + +// CHECK: define void @_Z24testParamAddrDiscStrong115AddrDiscStrong1(%[[STRUCT_ADDRDISCSTRONG1]]* %{{.*}}) +// CHECK-NOT: call +// CHECK: ret void + +void testParamAddrDiscStrong1(AddrDiscStrong1 a) { +} diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c index 0f082441c412d..6e26933ab6be6 100644 --- a/clang/test/Sema/ptrauth-intrinsics-macro.c +++ b/clang/test/Sema/ptrauth-intrinsics-macro.c @@ -21,4 +21,20 @@ void test(int *dp, int (*fp)(int), int value) { dp = ptrauth_auth_data(dp, VALID_DATA_KEY, 0); int t1 = ptrauth_string_discriminator("string"); int t2 = ptrauth_sign_generic_data(dp, 0); + + void * __ptrauth_function_pointer p0; + void * __ptrauth_return_address p1; + void * __ptrauth_block_invocation_pointer p2; + void * __ptrauth_block_copy_helper p3; + void * __ptrauth_block_destroy_helper p4; + void * __ptrauth_block_byref_copy_helper p5; + void * __ptrauth_block_byref_destroy_helper p6; + void * __ptrauth_objc_method_list_imp p7; + void * __ptrauth_cxx_vtable_pointer p8; + void * __ptrauth_cxx_vtt_vtable_pointer p9; + void * __ptrauth_swift_heap_object_destructor p10; + void * __ptrauth_swift_function_pointer(VALID_CODE_KEY) p11; + void * __ptrauth_swift_class_method_pointer(VALID_CODE_KEY) p12; + void * __ptrauth_swift_protocol_witness_function_pointer(VALID_CODE_KEY) p13; + void * __ptrauth_swift_value_witness_function_pointer(VALID_CODE_KEY) p14; } diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c new file mode 100644 index 0000000000000..458bab554fb5f --- /dev/null +++ b/clang/test/Sema/ptrauth-qualifier.c @@ -0,0 +1,127 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s + +#if __has_feature(ptrauth_qualifier) +#warning __ptrauth qualifier enabled! +// expected-warning@-1 {{__ptrauth qualifier enabled!}} +#endif + +#if __aarch64__ +#define VALID_CODE_KEY 0 +#define VALID_DATA_KEY 2 +#define INVALID_KEY 200 +#else +#error Provide these constants if you port this test +#endif + +int * __ptrauth(VALID_DATA_KEY) valid0; + +typedef int *intp; + +int nonConstantGlobal = 5; + +__ptrauth int invalid0; // expected-error{{expected '('}} +__ptrauth() int invalid1; // expected-error{{expected expression}} +__ptrauth(INVALID_KEY) int invalid2; // expected-error{{200 does not identify a valid pointer authentication key for the current target}} +__ptrauth(VALID_DATA_KEY) int invalid3; // expected-error{{__ptrauth qualifier may only be applied to pointer types}} +__ptrauth(VALID_DATA_KEY) int *invalid4; // expected-error{{__ptrauth qualifier may only be applied to pointer types}} +int * (__ptrauth(VALID_DATA_KEY) invalid5); // expected-error{{expected identifier or '('}} expected-error{{expected ')'}} expected-note {{to match this '('}} +int * __ptrauth(VALID_DATA_KEY) __ptrauth(VALID_DATA_KEY) invalid6; // expected-error{{type 'int *__ptrauth(2,0,0)' is already __ptrauth-qualified}} +int * __ptrauth(VALID_DATA_KEY, 2) invalid7; // expected-error{{address discrimination flag for __ptrauth must be 0 or 1; value is 2}} +int * __ptrauth(VALID_DATA_KEY, -1) invalid8; // expected-error{{address discrimination flag for __ptrauth must be 0 or 1; value is -1}} +int * __ptrauth(VALID_DATA_KEY, 1, -1) invalid9; // expected-error{{extra discriminator for __ptrauth must between 0 and 65535; value is -1}} +int * __ptrauth(VALID_DATA_KEY, 1, 100000) invalid10; // expected-error{{extra discriminator for __ptrauth must between 0 and 65535; value is 100000}} +int * __ptrauth(VALID_DATA_KEY, 1, 65535, 41) invalid11; // expected-error{{__ptrauth qualifier must take between 1 and 3 arguments}} +int * __ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // expected-error{{argument to __ptrauth must be an integer constant expression}} + +int * __ptrauth(VALID_DATA_KEY) valid0; +int * __ptrauth(VALID_DATA_KEY) *valid1; +__ptrauth(VALID_DATA_KEY) intp valid2; +__ptrauth(VALID_DATA_KEY) intp *valid3; +intp __ptrauth(VALID_DATA_KEY) valid4; +intp __ptrauth(VALID_DATA_KEY) *valid5; +int * __ptrauth(VALID_DATA_KEY, 0) valid6; +int * __ptrauth(VALID_DATA_KEY, 1) valid7; +int * __ptrauth(VALID_DATA_KEY, (_Bool) 1) valid8; +int * __ptrauth(VALID_DATA_KEY, 1, 0) valid9; +int * __ptrauth(VALID_DATA_KEY, 1, 65535) valid10; + +extern intp redeclaration0; // expected-note {{previous declaration}} +extern intp __ptrauth(VALID_DATA_KEY) redeclaration0; // expected-error{{redeclaration of 'redeclaration0' with a different type: '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)') vs 'intp' (aka 'int *')}} + +extern intp redeclaration1; // expected-note {{previous declaration}} +extern intp __ptrauth(VALID_DATA_KEY) redeclaration1; // expected-error{{redeclaration of 'redeclaration1' with a different type: '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)') vs 'intp' (aka 'int *')}} + +intp __ptrauth(VALID_DATA_KEY) redeclaration2; // expected-note {{previous definition}} +intp redeclaration2 = 0; // expected-error{{redefinition of 'redeclaration2' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}} + +intp __ptrauth(VALID_DATA_KEY) redeclaration3; // expected-note {{previous definition}} +intp redeclaration3 = 0; // expected-error{{redefinition of 'redeclaration3' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}} + +void illegal0(intp __ptrauth(VALID_DATA_KEY)); // expected-error{{parameter types may not be qualified with __ptrauth}} +intp __ptrauth(VALID_DATA_KEY) illegal1(void); // expected-error{{return types may not be qualified with __ptrauth}} + +void test_code(intp p) { + p = (intp __ptrauth(VALID_DATA_KEY)) 0; // expected-error{{cast types may not be qualified with __ptrauth}} + + __ptrauth(VALID_DATA_KEY) intp pSpecial = p; + pSpecial = p; + intp pNormal = pSpecial; + pNormal = pSpecial; + + intp __ptrauth(VALID_DATA_KEY) *ppSpecial0 = &pSpecial; + intp __ptrauth(VALID_DATA_KEY) *ppSpecial1 = &pNormal; // expected-error {{initializing '__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') with an expression of type 'intp *' (aka 'int **') changes pointer-authentication of pointee type}} + intp *ppNormal0 = &pSpecial; // expected-error {{initializing 'intp *' (aka 'int **') with an expression of type '__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') changes pointer-authentication of pointee type}} + intp *ppNormal1 = &pNormal; + + intp *pp5 = (p ? &pSpecial : &pNormal); // expected-error {{__ptrauth qualification mismatch ('__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') and 'intp *' (aka 'int **'))}} +} + +void test_array(void) { + intp __ptrauth(VALID_DATA_KEY) pSpecialArray[10]; + intp __ptrauth(VALID_DATA_KEY) *ppSpecial0 = pSpecialArray; + intp __ptrauth(VALID_DATA_KEY) *ppSpecial1 = &pSpecialArray[0]; +} + +struct S0 { // expected-note 4 {{struct S0' has subobjects that are non-trivial to copy}} + intp __ptrauth(1, 1, 50) f0; // expected-note 4 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}} +}; + +union U0 { // expected-note 4 {{union U0' has subobjects that are non-trivial to copy}} + struct S0 s0; +}; + +struct S1 { + intp __ptrauth(1, 0, 50) f0; +}; + +union U1 { + struct S1 s1; +}; + +union U2 { // expected-note 2 {{union U2' has subobjects that are non-trivial to copy}} + intp __ptrauth(1, 1, 50) f0; // expected-note 2 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}} + intp __ptrauth(1, 0, 50) f1; +}; + +// Test for r353556. +struct S2 { // expected-note 2 {{struct S2' has subobjects that are non-trivial to copy}} + intp __ptrauth(1, 1, 50) f0[4]; // expected-note 2 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}} +}; + +union U3 { // expected-note 2 {{union U3' has subobjects that are non-trivial to copy}} + struct S2 s2; +}; + +struct S4 { + union U0 u0; +}; + +union U0 foo0(union U0); // expected-error {{cannot use type 'union U0' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U0' for a function/method parameter since it is a union that is non-trivial to copy}} + +union U1 foo1(union U1); + +union U2 foo2(union U2); // expected-error {{cannot use type 'union U2' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U2' for a function/method parameter since it is a union that is non-trivial to copy}} + +union U3 foo3(union U3); // expected-error {{cannot use type 'union U3' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U3' for a function/method parameter since it is a union that is non-trivial to copy}} + +struct S4 foo4(struct S4); // expected-error {{cannot use type 'struct S4' for function/method return since it contains a union that is non-trivial to copy}} expected-error {{cannot use type 'struct S4' for a function/method parameter since it contains a union that is non-trivial to copy}} diff --git a/clang/test/SemaCXX/ptrauth-qualifier.cpp b/clang/test/SemaCXX/ptrauth-qualifier.cpp new file mode 100644 index 0000000000000..e1fd10121ec84 --- /dev/null +++ b/clang/test/SemaCXX/ptrauth-qualifier.cpp @@ -0,0 +1,120 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++11 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s + +#define AQ __ptrauth(1,1,50) +#define IQ __ptrauth(1,0,50) + +struct __attribute__((trivial_abi)) AddrDisc { // expected-warning {{'trivial_abi' cannot be applied to 'AddrDisc'}} + int * AQ m0; +}; + +struct __attribute__((trivial_abi)) NoAddrDisc { + int * IQ m0; +}; + +namespace test_union { + + union U0 { + int * AQ f0; // expected-note 4 {{'U0' is implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}} + + // ptrauth fields that don't have an address-discriminated qualifier don't + // delete the special functions. + int * IQ f1; + }; + + union U1 { + int * AQ f0; // expected-note 8 {{'U1' is implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}} + U1() = default; + ~U1() = default; + U1(const U1 &) = default; // expected-warning {{explicitly defaulted copy constructor is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}} + U1(U1 &&) = default; // expected-warning {{explicitly defaulted move constructor is implicitly deleted}} + U1 & operator=(const U1 &) = default; // expected-warning {{explicitly defaulted copy assignment operator is implicitly deleted}} expected-note 2 {{explicitly defaulted function was implicitly deleted here}} + U1 & operator=(U1 &&) = default; // expected-warning {{explicitly defaulted move assignment operator is implicitly deleted}} + }; + + // It's fine if the user has explicitly defined the special functions. + union U2 { + int * AQ f0; + U2() = default; + ~U2() = default; + U2(const U2 &); + U2(U2 &&); + U2 & operator=(const U2 &); + U2 & operator=(U2 &&); + }; + + // Address-discriminated ptrauth fields in anonymous union fields delete the + // defaulted copy/move constructors/assignment operators of the containing + // class. + struct S0 { + union { + int * AQ f0; // expected-note 4 {{'' is implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}} + char f1; + }; + }; + + struct S1 { + union { + union { // expected-note 2 {{'S1' is implicitly deleted because variant field '' has a non-trivial}} expected-note 2 {{'S1' is implicitly deleted because field '' has a deleted}} + int * AQ f0; + char f1; + }; + int f2; + }; + }; + + U0 *x0; + U1 *x1; + U2 *x2; + S0 *x3; + S1 *x4; + + // No diagnostics since constructors/destructors of the unions aren't deleted by default. + void testDefaultConstructor() { + U0 u0; + U1 u1; + U2 u2; + S0 s0; + S1 s1; + } + + // No diagnostics since destructors of the unions aren't deleted by default. + void testDestructor(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) { + delete u0; + delete u1; + delete u2; + delete s0; + delete s1; + } + + void testCopyConstructor(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) { + U0 t0(*u0); // expected-error {{call to implicitly-deleted copy constructor}} + U1 t1(*u1); // expected-error {{call to implicitly-deleted copy constructor}} + U2 t2(*u2); + S0 t3(*s0); // expected-error {{call to implicitly-deleted copy constructor}} + S1 t4(*s1); // expected-error {{call to implicitly-deleted copy constructor}} + } + + void testCopyAssignment(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) { + *x0 = *u0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x1 = *u1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x2 = *u2; + *x3 = *s0; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x4 = *s1; // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + } + + void testMoveConstructor(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) { + U0 t0(static_cast<U0 &&>(*u0)); // expected-error {{call to implicitly-deleted copy constructor}} + U1 t1(static_cast<U1 &&>(*u1)); // expected-error {{call to implicitly-deleted copy constructor}} + U2 t2(static_cast<U2 &&>(*u2)); + S0 t3(static_cast<S0 &&>(*s0)); // expected-error {{call to implicitly-deleted copy constructor}} + S1 t4(static_cast<S1 &&>(*s1)); // expected-error {{call to implicitly-deleted copy constructor}} + } + + void testMoveAssignment(U0 *u0, U1 *u1, U2 *u2, S0 *s0, S1 *s1) { + *x0 = static_cast<U0 &&>(*u0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x1 = static_cast<U1 &&>(*u1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x2 = static_cast<U2 &&>(*u2); + *x3 = static_cast<S0 &&>(*s0); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + *x4 = static_cast<S1 &&>(*s1); // expected-error {{cannot be assigned because its copy assignment operator is implicitly deleted}} + } +} diff --git a/clang/test/SemaObjC/warn-nontrivial-struct-memaccess-ptrauth.m b/clang/test/SemaObjC/warn-nontrivial-struct-memaccess-ptrauth.m new file mode 100644 index 0000000000000..179ed478ce187 --- /dev/null +++ b/clang/test/SemaObjC/warn-nontrivial-struct-memaccess-ptrauth.m @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -verify %s + +void *memset(void *, int, __SIZE_TYPE__); +void bzero(void *, __SIZE_TYPE__); +void *memcpy(void *, const void *, __SIZE_TYPE__); +void *memmove(void *, const void *, __SIZE_TYPE__); + +#define AQ __ptrauth(1,1,50) +#define IQ __ptrauth(1,0,50) + +struct PtrAuthTrivial { + int f0; + int * IQ f1; +}; + +struct PtrAuthNonTrivial0 { + int f0; + int * AQ f1; // expected-note 2 {{non-trivial to copy}} + int f2; +}; + +struct PtrAuthNonTrivial1 { + int * AQ f0; // expected-note 2 {{non-trivial to copy}} + int f1; + struct PtrAuthNonTrivial0 f2; +}; + +void testPtrAuthTrivial(struct PtrAuthTrivial *d, struct PtrAuthTrivial *s) { + memset(d, 0, sizeof(struct PtrAuthTrivial)); + bzero(d, sizeof(struct PtrAuthTrivial)); + memcpy(d, s, sizeof(struct PtrAuthTrivial)); + memmove(d, s, sizeof(struct PtrAuthTrivial)); +} + +void testPtrAuthNonTrivial1(struct PtrAuthNonTrivial1 *d, + struct PtrAuthNonTrivial1 *s) { + memset(d, 0, sizeof(struct PtrAuthNonTrivial1)); + bzero(d, sizeof(struct PtrAuthNonTrivial1)); + memcpy(d, s, sizeof(struct PtrAuthNonTrivial1)); // expected-warning {{that is not trivial to primitive-copy}} expected-note {{explicitly cast the pointer to silence}} + memmove(d, s, sizeof(struct PtrAuthNonTrivial1)); // expected-warning {{that is not trivial to primitive-copy}} expected-note {{explicitly cast the pointer to silence}} +} From 34229b5deadce644ce557e20b1d1bcbe16b4ca95 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:39:44 -0400 Subject: [PATCH 542/582] Make the CGPointerAuthInfo parameter to the general CGCallee constructor mandatory. --- clang/lib/CodeGen/CGCall.h | 2 +- clang/lib/CodeGen/CGObjCGNU.cpp | 6 ++++-- clang/lib/CodeGen/MicrosoftCXXABI.cpp | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index b27f41dc4065c..8f90474304ac3 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -149,7 +149,7 @@ class CGCalleeInfo { /// Construct a callee. Call this constructor directly when this /// isn't a direct call. CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr, - const CGPointerAuthInfo &pointerAuthInfo = /*FIXME*/ CGPointerAuthInfo()) + const CGPointerAuthInfo &pointerAuthInfo) : KindOrFunctionPointer(SpecialKind(uintptr_t(functionPtr))) { OrdinaryInfo.AbstractInfo = abstractInfo; OrdinaryInfo.PointerAuthInfo = pointerAuthInfo; diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index d2c089d0360e1..f4a6fa94cfd41 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -2600,7 +2600,8 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGenFunction &CGF, llvm::Type::getInt1Ty(VMContext), IsClassMessage))}; llvm::MDNode *node = llvm::MDNode::get(VMContext, impMD); - CGCallee callee(CGCalleeInfo(), imp); + CGPointerAuthInfo pointerAuth; // TODO + CGCallee callee(CGCalleeInfo(), imp, pointerAuth); llvm::CallBase *call; RValue msgRet = CGF.EmitCall(MSI.CallInfo, callee, Return, ActualArgs, &call); @@ -2720,7 +2721,8 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF, imp = EnforceType(Builder, imp, MSI.MessengerType); llvm::CallBase *call; - CGCallee callee(CGCalleeInfo(), imp); + CGPointerAuthInfo pointerAuth; // TODO + CGCallee callee(CGCalleeInfo(), imp, pointerAuth); RValue msgRet = CGF.EmitCall(MSI.CallInfo, callee, Return, ActualArgs, &call); call->setMetadata(msgSendMDKind, node); diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 2d8b538bc2eec..c9eada5a4ae28 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1896,7 +1896,7 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, VFunc = Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); } - CGCallee Callee(GD, VFunc); + CGCallee Callee(GD, VFunc, /*unsigned*/ CGPointerAuthInfo()); return Callee; } @@ -3369,7 +3369,7 @@ CGCallee MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer( FunctionPointer = Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo()); - CGCallee Callee(FPT, FunctionPointer); + CGCallee Callee(FPT, FunctionPointer, /*unsigned*/ CGPointerAuthInfo()); return Callee; } From e398fa8318cf18d5ead0fcfbe762509814f098d7 Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Wed, 18 Sep 2019 02:40:29 -0400 Subject: [PATCH 543/582] Language documentation for the pointer authentication feature. --- clang/docs/LanguageExtensions.rst | 5 + clang/docs/PointerAuthentication.rst | 877 +++++++++++++++++++++++++++ 2 files changed, 882 insertions(+) create mode 100644 clang/docs/PointerAuthentication.rst diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 51e6a4460336f..22c735c91125a 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -13,6 +13,7 @@ Clang Language Extensions BlockLanguageSpec Block-ABI-Apple AutomaticReferenceCounting + PointerAuthentication Introduction ============ @@ -2828,6 +2829,10 @@ reordering of memory accesses and side effect instructions. Other instructions like simple arithmetic may be reordered around the intrinsic. If you expect to have no reordering at all, use inline assembly instead. +Pointer Authentication +^^^^^^^^^^^^^^^^^^^^^^ +See :doc:`PointerAuthentication`. + X86/X86-64 Language Extensions ------------------------------ diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst new file mode 100644 index 0000000000000..04adc52c900a0 --- /dev/null +++ b/clang/docs/PointerAuthentication.rst @@ -0,0 +1,877 @@ +Pointer Authentication +====================== + +.. contents:: + :local: + +Introduction +------------ + +Pointer authentication is a technology which offers strong probabilistic protection against exploiting a broad class of memory bugs to take control of program execution. When adopted consistently in a language ABI, it provides a form of relatively fine-grained control flow integrity (CFI) check that resists both return-oriented programming (ROP) and jump-oriented programming (JOP) attacks. + +While pointer authentication can be implemented purely in software, direct hardware support (e.g. as provided by ARMv8.3) can dramatically lower the execution speed and code size costs. Similarly, while pointer authentication can be implemented on any architecture, taking advantage of the (typically) excess addressing range of a target with 64-bit pointers minimizes the impact on memory performance and can allow interoperation with existing code (by disabling pointer authentication dynamically). This document will generally attempt to present the pointer authentication feature independent of any hardware implementation or ABI. Considerations that are implementation-specific are clearly identified throughout. + +Note that there are several different terms in use: + +- **Pointer authentication** is a target-independent language technology. + +- **ARMv8.3** is an AArch64 architecture revision of that provides hardware support for pointer authentication. It is implemented on several shipping processors, including the Apple A12 and later. + +* **arm64e** is a specific ABI for (not yet fully stable) for implementing pointer authentication on ARMv8.3 on certain Apple operating systems. + +This document serves four purposes: + +- It describes the basic ideas of pointer authentication. + +- It documents several language extensions that are useful on targets using pointer authentication. + +- It presents a theory of operation for the security mitigation, describing the basic requirements for correctness, various weaknesses in the mechanism, and ways in which programmers can strengthen its protections (including recommendations for language implementors). + +- It documents the language ABIs currently used for C, C++, Objective-C, and Swift on arm64e, although these are not yet stable on any target. + +Basic Concepts +-------------- + +The simple address of an object or function is a **raw pointer**. A raw pointer can be **signed** to produce a **signed pointer**. A signed pointer can be then **authenticated** in order to verify that it was **validly signed** and extract the original raw pointer. These terms reflect the most likely implementation technique: computing and storing a cryptographic signature along with the pointer. The security of pointer authentication does not rely on attackers not being able to separately overwrite the signature. + +An **abstract signing key** is a name which refers to a secret key which can used to sign and authenticate pointers. The key value for a particular name is consistent throughout a process. + +A **discriminator** is an arbitrary value used to **diversify** signed pointers so that one validly-signed pointer cannot simply be copied over another. A discriminator is simply opaque data of some implementation-defined size that is included in the signature as a salt. + +Nearly all aspects of pointer authentication use just these two primary operations: + +- ``sign(raw_pointer, key, discriminator)`` produces a signed pointer given a raw pointer, an abstract signing key, and a discriminator. + +- ``auth(signed_pointer, key, discriminator)`` produces a raw pointer given a signed pointer, an abstract signing key, and a discriminator. + +``auth(sign(raw_pointer, key, discriminator), key, discriminator)`` must succeed and produce ``raw_pointer``. ``auth`` applied to a value that was ultimately produced in any other way is expected to immediately halt the program. However, it is permitted for ``auth`` to fail to detect that a signed pointer was not produced in this way, in which case it may return anything; this is what makes pointer authentication a probabilistic mitigation rather than a perfect one. + +There are two secondary operations which are required only to implement certain intrinsics in ``<ptrauth.h>``: + +- ``strip(signed_pointer, key)`` produces a raw pointer given a signed pointer and a key it was presumptively signed with. This is useful for certain kinds of tooling, such as crash backtraces; it should generally not be used in the basic language ABI except in very careful ways. + +- ``sign_generic(value)`` produces a cryptographic signature for arbitrary data, not necessarily a pointer. This is useful for efficiently verifying that non-pointer data has not been tampered with. + +Whenever any of these operations is called for, the key value must be known statically. This is because the layout of a signed pointer may vary according to the signing key. (For example, in ARMv8.3, the layout of a signed pointer depends on whether TBI is enabled, which can be set independently for code and data pointers.) + +.. admonition:: Note for API designers and language implementors + + These are the *primitive* operations of pointer authentication, provided for clarity of description. They are not suitable either as high-level interfaces or as primitives in a compiler IR because they expose raw pointers. Raw pointers require special attention in the language implementation to avoid the accidental creation of exploitable code sequences; see the section on `Attackable code sequences`_. + +The following details are all implementation-defined: + +- the nature of a signed pointer +- the size of a discriminator +- the number and nature of the signing keys +- the implementation of the ``sign``, ``auth``, ``strip``, and ``sign_generic`` operations + +While the use of the terms "sign" and "signed pointer" suggest the use of a cryptographic signature, other implementations may be possible. See `Alternative implementations`_ for an exploration of implementation options. + +.. admonition:: Implementation example: ARMv8.3 + + Readers may find it helpful to know how these terms map to ARMv8.3: + + - A signed pointer is a pointer with a signature stored in the otherwise-unused high bits. The kernel configures the signature width based on the system's addressing needs, accounting for whether the AArch64 TBI feature is enabled for the kind of pointer (code or data). + + - A discriminator is a 64-bit integer. Constant discriminators are 16-bit integers. Blending a constant discriminator into an address consists of replacing the top 16 bits of the address with the constant. + + - There are five 128-bit signing-key registers, each of which can only be directly read or set by privileged code. Of these, four are used for signing pointers, and the fifth is used only for ``sign_generic``. The key data is simply a pepper added to the hash, not an encryption key, and so can be initialized using random data. + + - ``sign`` computes a cryptographic hash of the pointer, discriminator, and signing key, and stores it in the high bits as the signature. ``auth`` removes the signature, computes the same hash, and compares the result with the stored signature. ``strip`` removes the signature without authenticating it. While ARMv8.3's ``aut*`` instructions do not themselves trap on failure, the compiler only ever emits them in sequences that will trap. + + - ``sign_generic`` corresponds to the ``pacga`` instruction, which takes two 64-bit values and produces a 64-bit cryptographic hash. Implementations of this instruction may not produce meaningful data in all bits of the result. + +Discriminators +~~~~~~~~~~~~~~ + +A discriminator is arbitrary extra data which alters the signature on a pointer. When two pointers are signed differently --- either with different keys or with different discriminators --- an attacker cannot simply replace one pointer with the other. For more information on why discriminators are important and how to use them effectively, see the section on `Substitution attacks`_. + +To use standard cryptographic terminology, a discriminator acts as a salt in the signing of a pointer, and the key data acts as a pepper. That is, both the discriminator and key data are ultimately just added as inputs to the signing algorithm along with the pointer, but they serve significantly different roles. The key data is a common secret added to every signature, whereas the discriminator is a signing-specific value that can be derived from the circumstances of how a pointer is signed. However, unlike a password salt, it's important that discriminators be *independently* derived from the circumstances of the signing; they should never simply be stored alongside a pointer. + +The intrinsic interface in ``<ptrauth.h>`` allows an arbitrary discriminator value to be provided, but can only be used when running normal code. The discriminators used by language ABIs must be restricted to make it feasible for the loader to sign pointers stored in global memory without needing excessive amounts of metadata. Under these restrictions, a discriminator may consist of either or both of the following: + +- The address at which the pointer is stored in memory. A pointer signed with a discriminator which incorporates its storage address is said to have **address diversity**. In general, using address diversity means that a pointer cannot be reliably replaced by an attacker or used to reliably replace a different pointer. However, an attacker may still be able to attack a larger call sequence if they can alter the address through which the pointer is accessed. Furthermore, some situations cannot use address diversity because of language or other restrictions. + +- A constant integer, called a **constant discriminator**. A pointer signed with a non-zero constant discriminator is said to have **constant diversity**. If the discriminator is specific to a single declaration, it is said to have **declaration diversity**; if the discriminator is specific to a type of value, it is said to have **type diversity**. For example, C++ v-tables on arm64e sign their component functions using a hash of their method names and signatures, which provides declaration diversity; similarly, C++ member function pointers sign their invocation functions using a hash of the member pointer type, which provides type diversity. + +The implementation may need to restrict constant discriminators to be significantly smaller than the full size of a discriminator. For example, on arm64e, constant discriminators are only 16-bit values. This is believed to not significantly weaken the mitigation, since collisions remain uncommon. + +The algorithm for blending a constant discriminator with a storage address is implementation-defined. + +.. _Signing schemas: + +Signing schemas +~~~~~~~~~~~~~~~ + +Correct use of pointer authentication requires the signing code and the authenticating code to agree about the **signing schema** for the pointer: + +- the abstract signing key with which the pointer should be signed and +- an algorithm for computing the discriminator. + +As described in the section above on `Discriminators`_, in most situations, the discriminator is produced by taking a constant discriminator and optionally blending it with the storage address of the pointer. In these situations, the signing schema breaks down even more simply: + +- the abstract signing key, +- a constant discriminator, and +- whether to use address diversity. + +It is important that the signing schema be independently derived at all signing and authentication sites. Preferably, the schema should be hard-coded everywhere it is needed, but at the very least, it must not be derived by inspecting information stored along with the pointer. See the section on `Attacks on pointer authentication`_ for more information. + + + + + +Language Features +----------------- + +There are three levels of the pointer authentication language feature: + +- The language implementation automatically signs and authenticates function pointers (and certain data pointers) across a variety of standard situations, including return addresses, function pointers, and C++ virtual functions. The intent is for all pointers to code in program memory to be signed in some way and for all branches to code in program text to authenticate those signatures. + +- The language also provides extensions to override the default rules used by the language implementation. For example, the ``__ptrauth`` type qualifier can be used to change how pointers are signed when they are stored in a particular variable or field; this provides much stronger protection than is guaranteed by the default rules for C function and data pointers. + +- FInally, the language provides the ``<ptrauth.h>`` intrinsic interface for manually signing and authenticating pointers in code. These can be used in circumstances where very specific behavior is required. + +Language implementation +~~~~~~~~~~~~~~~~~~~~~~~ + +For the most part, pointer authentication is an unobserved detail of the implementation of the programming language. Any element of the language implementation that would perform an indirect branch to a pointer is implicitly altered so that the pointer is signed when first constructed and authenticated when the branch is performed. This includes: + +- indirect-call features in the programming language, such as C function pointers, C++ virtual functions, C++ member function pointers, the "blocks" C extension, and so on; + +- returning from a function, no matter how it is called; and + +- indirect calls introduced by the implementation, such as branches through the global offset table (GOT) used to implement direct calls to functions defined outside of the current shared object. + +For more information about this, see the `Language ABI`_ section. + +However, some aspects of the implementation are observable by the programmer or otherwise require special notice. + +C data pointers +^^^^^^^^^^^^^^^ + +The current implementation in Clang does not sign pointers to ordinary data by default. For a partial explanation of the reasoning behind this, see the `Theory of Operation`_ section. + +A specific data pointer which is more security-sensitive than most can be signed using the `__ptrauth qualifier`_ or using the ``<ptrauth.h>`` intrinsics. + +C function pointers +^^^^^^^^^^^^^^^^^^^ + +The C standard imposes restrictions on the representation and semantics of function pointer types which make it difficult to achieve satisfactory signature diversity in the default language rules. See `Attacks on pointer authentication`_ for more information about signature diversity. Programmers should strongly consider using the ``__ptrauth`` qualifier to improve the protections for important function pointers, such as the components of of a hand-rolled "v-table"; see the section on the `__ptrauth qualifier`_ for details. + +The value of a pointer to a C function includes a signature, even when the value is cast to a non-function-pointer type like ``void*`` or ``intptr_t``. On implementations that use high bits to store the signature, this means that relational comparisons and hashes will vary according to the exact signature value, which is likely to change between executions of a program. In some implementations, it may also vary based on the exact function pointer type. + +Null pointers +^^^^^^^^^^^^^ + +In principle, an implementation could derive the signed null pointer value simply by applying the standard signing algorithm to the raw null pointer value. However, for likely signing algorithms, this would mean that the signed null pointer value would no longer be statically known, which would have many negative consequences. For one, it would become substantially more expensive to emit null pointer values or to perform null-pointer checks. For another, the pervasive (even if technically unportable) assumption that null pointers are bitwise zero would be invalidated, making it substantially more difficult to adopt pointer authentication, as well as weakening common optimizations for zero-initialized memory such as the use of ``.bzz`` sections. Therefore it is beneficial to treat null pointers specially by giving them their usual representation. On AArch64, this requires additional code when working with possibly-null pointers, such as when copying a pointer field that has been signed with address diversity. + +Return addresses and frame pointers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The current implementation in Clang implicitly signs both return addresses and frame pointers. While these values are technically implementation details of a function, there are some important libraries and development tools which rely on manually walking the chain of stack frames. These tools must be updated to correctly account for pointer authentication, either by stripping signatures (if security is not important for the tool, e.g. if it is capturing a stack trace during a crash) or properly authenticating them. More information about how these values are signed is available in the `Language ABI`_ section. + +C++ virtual functions +^^^^^^^^^^^^^^^^^^^^^ + +The current implementation in Clang signs virtual function pointers with a discriminator derived from the full signature of the overridden method, including the method name and parameter types. It is possible to write C++ code that relies on v-table layout remaining constant despite changes to a method signature; for example, a parameter might be a ``typedef`` that resolves to a different type based on a build setting. Such code violates C++'s One Definition Rule (ODR), but that violation is not normally detected; however, pointer authentication will detect it. + + +Language extensions +~~~~~~~~~~~~~~~~~~~ + +Feature testing +^^^^^^^^^^^^^^^ + +Whether the current target uses pointer authentication can be tested for with a number of different tests. + +- ``__has_feature(ptrauth_intrinsics)`` is true if ``<ptrauth.h>`` provides its normal interface. This may be true even on targets where pointer authentication is not enabled by default. + +- ``__has_feature(ptrauth_returns)`` is true if the target uses pointer authentication to protect return addresses. + +- ``__has_feature(ptrauth_calls)`` is true if the target uses pointer authentication to protect indirect branches. This implies ``__has_feature(ptrauth_returns)`` and ``__has_feature(ptrauth_intrinsics)``. + +Clang provides several other tests only for historical purposes; for current purposes they are all equivalent to ``ptrauth_calls``. + +__ptrauth qualifier +^^^^^^^^^^^^^^^^^^^ + +``__ptrauth(key, address, discriminator)`` is an extended type qualifier which causes so-qualified objects to hold pointers signed using the specified schema rather than the default schema for such types. + +In the current implementation in Clang, the qualified type must be a C pointer type, either to a function or to an object. It currently cannot be an Objective-C pointer type, a C++ reference type, or a block pointer type; these restrictions may be lifted in the future. + +The current implementation in Clang is known to not provide adequate safety guarantees against the creation of `signing oracles`_ when assigning data pointers to ``__ptrauth``-qualified gl-values. See the section on `safe derivation`_ for more information. + +The qualifier's operands are as follows: + +- ``key`` - an expression evaluating to a key value from ``<ptrauth.h>``; must be a constant expression + +- ``address`` - whether to use address diversity (1) or not (0); must be a constant expression with one of these two values + +- ``discriminator`` - a constant discriminator; must be a constant expression + +See `Discriminators`_ for more information about discriminators. + +Currently the operands must be constant-evaluable even within templates. In the future this restriction may be lifted to allow value-dependent expressions as long as they instantiate to a constant expression. + +Consistent with the ordinary C/C++ rule for parameters, top-level ``__ptrauth`` qualifiers on a parameter (after parameter type adjustment) are ignored when deriving the type of the function. The parameter will be passed using the default ABI for the unqualified pointer type. + +If ``x`` is an object of type ``__ptrauth(key, address, discriminator) T``, then the signing schema of the value stored in ``x`` is a key of ``key`` and a discriminator determined as follows: + +- if ``address`` is 0, then the discriminator is ``discriminator``; + +- if ``address`` is 1 and ``discriminator`` is 0, then the discriminator is ``&x``; otherwise + +- if ``address`` is 1 and ``discriminator`` is non-zero, then the discriminator is ``ptrauth_blend_discriminator(&x, discriminator)``; see `ptrauth_blend_discriminator`_. + +Non-triviality from address diversity ++++++++++++++++++++++++++++++++++++++ + +Address diversity must impose additional restrictions in order to allow the implementation to correctly copy values. In C++, a type qualified with address diversity is treated like a class type with non-trivial copy/move constructors and assignment operators, with the usual effect on containing classes and unions. C does not have a standard concept of non-triviality, and so we must describe the basic rules here, with the intention of imitating the emergent rules of C++: + +- A type may be **non-trivial to copy**. + +- A type may also be **illegal to copy**. Types that are illegal to copy are always non-trivial to copy. + +- A type may also be **address-sensitive**. + +- A type qualified with a ``ptrauth`` qualifier that requires address diversity is non-trivial to copy and address-sensitive. + +- An array type is illegal to copy, non-trivial to copy, or address-sensitive if its element type is illegal to copy, non-trivial to copy, or address-sensitive, respectively. + +- A struct type is illegal to copy, non-trivial to copy, or address-sensitive if it has a field whose type is illegal to copy, non-trivial to copy, or address-sensitive, respectively. + +- A union type is both illegal and non-trivial to copy if it has a field whose type is non-trivial or illegal to copy. + +- A union type is address-sensitive if it has a field whose type is address-sensitive. + +- A program is ill-formed if it uses a type that is illegal to copy as a function parameter, argument, or return type. + +- A program is ill-formed if an expression requires a type to be copied that is illegal to copy. + +- Otherwise, copying a type that is non-trivial to copy correctly copies its subobjects. + +- Types that are address-sensitive must always be passed and returned indirectly. Thus, changing the address-sensitivity of a type may be ABI-breaking even if its size and alignment do not change. + +``<ptrauth.h>`` +~~~~~~~~~~~~~~~ + +This header defines the following types and operations: + +``ptrauth_key`` +^^^^^^^^^^^^^^^ + +This ``enum`` is the type of abstract signing keys. In addition to defining the set of implementation-specific signing keys (for example, ARMv8.3 defines ``ptrauth_key_asia``), it also defines some portable aliases for those keys. For example, ``ptrauth_key_function_pointer`` is the key generally used for C function pointers, which will generally be suitable for other function-signing schemas. + +In all the operation descriptions below, key values must be constant values corresponding to one of the implementation-specific abstract signing keys from this ``enum``. + +``ptrauth_extra_data_t`` +^^^^^^^^^^^^^^^^^^^^^^^^ + +This is a ``typedef`` of a standard integer type of the correct size to hold a discriminator value. + +In the signing and authentication operation descriptions below, discriminator values must have either pointer type or integer type. If the discriminator is an integer, it will be coerced to ``ptrauth_extra_data_t``. + +``ptrauth_blend_discriminator`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_blend_discriminator(pointer, integer) + +Produce a discriminator value which blends information from the given pointer and the given integer. + +Implementations may ignore some bits from each value, which is to say, the blending algorithm may be chosen for speed and convenience over theoretical strength as a hash-combining algorithm. For example, arm64e simply overwrites the high 16 bits of the pointer with the low 16 bits of the integer, which can be done in a single instruction with an immediate integer. + +``pointer`` must have pointer type, and ``integer`` must have integer type. The result has type ``ptrauth_extra_data_t``. + +``ptrauth_string_discriminator`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_string_discriminator(string) + +Produce a discriminator value for the given string. ``string`` must be a string literal of ``char`` character type. The result has type ``ptrauth_extra_data_t``. + +The result is always a constant expression. The result value is never zero and always within range for both the ``__ptrauth`` qualifier and ``ptrauth_blend_discriminator``. + +``ptrauth_strip`` +^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_strip(signedPointer, key) + +Given that ``signedPointer`` matches the layout for signed pointers signed with the given key, extract the raw pointer from it. This operation does not trap and cannot fail, even if the pointer is not validly signed. + +``ptrauth_sign_constant`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_sign_constant(pointer, key, discriminator) + +Return a signed pointer for a constant address in a manner which guarantees a non-attackable sequence. + +``pointer`` must be a constant expression of pointer type which evaluates to a non-null pointer. The result will have the same type as ``discriminator``. + +Calls to this are constant expressions if the discriminator is a null-pointer constant expression or an integer constant expression. Implementations may make allow other pointer expressions as well. + +``ptrauth_sign_unauthenticated`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_sign_unauthenticated(pointer, key, discriminator) + +Produce a signed pointer for the given raw pointer without applying any authentication or extra treatment. This operation is not required to have the same behavior on a null pointer that the language implementation would. + +This is a treacherous operation that can easily result in `signing oracles`_. Programs should use it seldom and carefully. + +``ptrauth_auth_and_resign`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_auth_and_resign(pointer, oldKey, oldDiscriminator, newKey, newDiscriminator) + +Authenticate that ``pointer`` is signed with ``oldKey`` and ``oldDiscriminator`` and then resign the raw-pointer result of that authentication with ``newKey`` and ``newDiscriminator``. + +``pointer`` must have pointer type. The result will have the same type as ``pointer``. This operation is not required to have the same behavior on a null pointer that the language implementation would. + +The code sequence produced for this operation must not be directly attackable. However, if the discriminator values are not constant integers, their computations may still be attackable. In the future, Clang should be enhanced to guaranteed non-attackability if these expressions are :ref:`safely-derived<Safe derivation>`. + +``ptrauth_auth_function`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_auth_function(pointer, key, discriminator) + +Authenticate that ``pointer`` is signed with ``key`` and ``discriminator`` and re-sign it to the standard schema for a function pointer of its type. + +``pointer`` must have function pointer type. The result will have the same type as ``pointer``. This operation is not required to have the same behavior on a null pointer that the language implementation would. + +This operation makes the same attackability guarantees as ``ptrauth_auth_and_resign``. + +If this operation appears syntactically as the function operand of a call, Clang guarantees that the call will directly authenticate the function value using the given schema rather than re-signing to the standard schema. + +``ptrauth_auth_data`` +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_auth_data(pointer, key, discriminator) + +Authenticate that ``pointer`` is signed with ``key`` and ``discriminator`` and remove the signature. + +``pointer`` must have object pointer type. The result will have the same type as ``pointer``. This operation is not required to have the same behavior on a null pointer that the language implementation would. + +In the future when Clang makes `safe derivation`_ guarantees, the result of this operation should be considered safely-derived. + +``ptrauth_sign_generic_data`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_sign_generic_data(value1, value2) + +Computes a signature for the given pair of values, incorporating a secret signing key. + +This operation can be used to verify that arbitrary data has not be tampered with by computing a signature for the data, storing that signature, and then repeating this process and verifying that it yields the same result. This can be reasonably done in any number of ways; for example, a library could compute an ordinary checksum of the data and just sign the result in order to get the tamper-resistance advantages of the secret signing key (since otherwise an attacker could reliably overwrite both the data and the checksum). + +``value1`` and ``value2`` must be either pointers or integers. If the integers are larger than ``uintptr_t`` then data not representable in ``uintptr_t`` may be discarded. + +The result will have type ``ptrauth_generic_signature_t``, which is an integer type. Implementations are not required to make all bits of the result equally significant; in particular, some implementations are known to not leave meaningful data in the low bits. + +Standard ``__ptrauth`` qualifiers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``<ptrauth.h>`` additionally provides several macros which expand to ``__ptrauth`` qualifiers for common ABI situations. + +For convenience, these macros expand to nothing when pointer authentication is disabled. + +These macros can be found in the header; some details of these macros may be unstable or implementation-specific. + + + + + +Theory of Operation +------------------- + +The threat model of pointer authentication is as follows: + +- The attacker has the ability to read and write to a certain range of addresses, possibly the entire address space. However, they are constrained by the normal rules of the process: for example, they cannot write to memory that is mapped read-only, and if they access unmapped memory it will trigger a trap. + +- The attacker has no ability to add arbitrary executable code to the program. For example, the program does not include malicious code to begin with, and the attacker cannot alter existing instructions, load a malicious shared library, or remap writable pages as executable. If the attacker wants to get the process to perform a specific sequence of actions, they must somehow subvert the normal control flow of the process. + +In both of the above paragraphs, it is merely assumed that the attacker's *current* capabilities are restricted; that is, their current exploit does not directly give them the power to do these things. The attacker's immediate goal may well be to leverage their exploit to gain these capabilities, e.g. to load a malicious dynamic library into the process, even though the process does not directly contain code to do so. + +Note that any bug that fits the above threat model can be immediately exploited as a denial-of-service attack by simply performing an illegal access and crashing the program. Pointer authentication cannot protect against this. While denial-of-service attacks are unfortunate, they are also unquestionably the best possible result of a bug this severe. Therefore, pointer authentication enthusiastically embraces the idea of halting the program on a pointer authentication failure rather than continuing in a possibly-compromised state. + +Pointer authentication is a form of control-flow integrity (CFI) enforcement. The basic security hypothesis behind CFI enforcement is that many bugs can only be usefully exploited (other than as a denial-of-service) by leveraging them to subvert the control flow of the program. If this is true, then by inhibiting or limiting that subversion, it may be possible to largely mitigate the security consequences of those bugs by rendering them impractical (or, ideally, impossible) to exploit. + +Every indirect branch in a program has a purpose. Using human intelligence, a programmer can describe where a particular branch *should* go according to this purpose: a ``return`` in ``printf`` should return to the call site, a particular call in ``qsort`` should call the comparator that was passed in as an argument, and so on. But for CFI to enforce that every branch in a program goes where it *should* in this sense would require CFI to perfectly enforce every semantic rule of the program's abstract machine; that is, it would require making the programming environment perfectly sound. That is out of scope. Instead, the goal of CFI is merely to catch attempts to make a branch go somewhere that its obviously *shouldn't* for its purpose: for example, to stop a call from branching into the middle of a function rather than its beginning. As the information available to CFI gets better about the purpose of the branch, CFI can enforce tighter and tighter restrictions on where the branch is permitted to go. Still, ultimately CFI cannot make the program sound. This may help explain why pointer authentication makes some of the choices it does: for example, to sign and authenticate mostly code pointers rather than every pointer in the program. Preventing attackers from redirecting branches is both particularly important and particularly approachable as a goal. Detecting corruption more broadly is infeasible with these techniques, and the attempt would have far higher cost. + +Attacks on pointer authentication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pointer authentication works as follows. Every indirect branch in a program has a purpose. For every purpose, the implementation chooses a :ref:`signing schema<Signing schemas>`. At some place where a pointer is known to be correct for its purpose, it is signed according to the purpose's schema. At every place where the pointer is needed for its purpose, it is authenticated according to the purpose's schema. If that authentication fails, the program is halted. + +There are a variety of ways to attack this. + +Attacks of interest to programmers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These attacks arise from weaknesses in the default protections offered by pointer authentication. They can be addressed by using attributes or intrinsics to opt in to stronger protection. + +Substitution attacks +++++++++++++++++++++ + +An attacker can simply overwrite a pointer intended for one purpose with a pointer intended for another purpose if both purposes use the same signing schema and that schema does not use address diversity. + +The most common source of this weakness is when code relies on using the default language rules for C function pointers. The current implementation uses the exact same signing schema for all C function pointers, even for functions of substantially different type. While efforts are ongoing to improve constant diversity for C function pointers of different type, there are necessary limits to this. The C standard requires function pointers to be copyable with ``memcpy``, which means that function pointers can never use address diversity. Furthermore, even if a function pointer can only be replaced with another function of the exact same type, that can still be useful to an attacker, as in the following example of a hand-rolled "v-table": + +.. code-block:: c + + struct ObjectOperations { + void (*retain)(Object *); + void (*release)(Object *); + void (*deallocate)(Object *); + void (*logStatus)(Object *); + }; + +This weakness can be mitigated by using a more specific signing schema for each purpose. For example, in this example, the ``__ptrauth`` qualifier can be used with a different constant discriminator for each field. Since there's no particular reason it's important for this v-table to be copyable with ``memcpy``, the functions can also be signed with address diversity: + +.. code-block:: c + + #if __has_feature(ptrauth_calls) + #define objectOperation(discriminator) \ + __ptrauth(ptrauth_key_function_pointer, 1, discriminator) + #else + #define objectOperation(discriminator) + #endif + + struct ObjectOperations { + void (*objectOperation(0xf017) retain)(Object *); + void (*objectOperation(0x2639) release)(Object *); + void (*objectOperation(0x8bb0) deallocate)(Object *); + void (*objectOperation(0xc5d4) logStatus)(Object *); + }; + +This weakness can also sometimes be mitigated by simply keeping the signed pointer in constant memory, but this is less effective than using better signing diversity. + +.. _Access path attacks: + +Access path attacks ++++++++++++++++++++ + +If a signed pointer is often accessed indirectly (that is, by first loading the address of the object where the signed pointer is stored), an attacker can affect uses of it by overwriting the intermediate pointer in the access path. + +The most common scenario exhibiting this weakness is an object with a pointer to a "v-table" (a structure holding many function pointers). An attacker does not need to replace a signed function pointer in the v-table if they can instead simply replace the v-table pointer in the object with their own pointer --- perhaps to memory where they've constructed their own v-table, or to existing memory that coincidentally happens to contain a signed pointer at the right offset that's been signed with the right signing schema. + +This attack arises because data pointers are not signed by default. It works even if the signed pointer uses address diversity: address diversity merely means that each pointer is signed with its own storage address, which (by design) is invariant to changes in the accessing pointer. + +Using sufficiently diverse signing schemas within the v-table can provide reasonably strong mitigation against this weakness. Always use address diversity in v-tables to prevent attackers from assembling their own v-table. Avoid re-using constant discriminators to prevent attackers from replacing a v-table pointer with a pointer to totally unrelated memory that just happens to contain an similarly-signed pointer. + +Further mitigation can be attained by signing pointers to v-tables. Any signature at all should prevent attackers from forging v-table pointers; they will need to somehow harvest an existing signed pointer from elsewhere in memory. Using a meaningful constant discriminator will force this to be harvested from an object with similar structure (e.g. a different implementation of the same interface). Using address diversity will prevent such harvesting entirely. However, care must be taken when sourcing the v-table pointer originally; do not blindly sign a pointer that is not :ref:`safely derived<Safe derivation>`. + +.. _Signing oracles: + +Signing oracles ++++++++++++++++ + +A signing oracle is a bit of code which can be exploited by an attacker to sign an arbitrary pointer in a way that can later be recovered. Such oracles can be used by attackers to forge signatures matching the oracle's signing schema, which is likely to cause a total compromise of pointer authentication's effectiveness. + +This attack only affects ordinary programmers if they are using certain treacherous patterns of code. Currently this includes: + +- all uses of the ``__ptrauth_sign_unauthenticated`` intrinsic and +- assigning data pointers to ``__ptrauth``-qualified l-values. + +Care must be taken in these situations to ensure that the pointer being signed has been :ref:`safely derived<Safe derivation>` or is otherwise not possible to attack. (In some cases, this may be challenging without compiler support.) + +A diagnostic will be added in the future for implicitly dangerous patterns of code, such as assigning a non-safely-derived data pointer to a ``__ptrauth``-qualified l-value. + +.. _Authentication oracles: + +Authentication oracles +++++++++++++++++++++++ + +An authentication oracle is a bit of code which can be exploited by an attacker to leak whether a signed pointer is validly signed without halting the program if it isn't. Such oracles can be used to forge signatures matching the oracle's signing schema if the attacker can repeatedly invoke the oracle for different candidate signed pointers. This is likely to cause a total compromise of pointer authentication's effectiveness. + +There should be no way for an ordinary programmer to create an authentication oracle using the current set of operations. However, implementation flaws in the past have occasionally given rise to authentication oracles due to a failure to immediately trap on authentication failure. + +The likelihood of creating an authentication oracle is why there is currently no intrinsic which queries whether a signed pointer is validly signed. + + +Attacks of interest to implementors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These attacks are not inherent to the model; they arise from mistakes in either implementing or using the `sign` and `auth` operations. Avoiding these mistakes requires careful work throughout the system. + +Failure to trap on authentication failure ++++++++++++++++++++++++++++++++++++++++++ + +Any failure to halt the program on an authentication failure is likely to be exploitable by attackers to create an :ref:`authentication oracle<Authentication oracles>`. + +There are several different ways to introduce this problem: + +- The implementation might try to halt the program in some way that can be intercepted. + + For example, the ``auth`` instruction in ARMv8.3 does not directly trap; instead it corrupts its result so that it is always an invalid pointer. If the program subsequently attempts to use that pointer, that will be a bad memory access, and it will trap into the kernel. However, kernels do not usually immediately halt programs that trigger traps due to bad memory accesses; instead they notify the process to give it an opportunity to recover. If this happens with an ``auth`` failure, the attacker may be able to exploit the recovery path in a way that creates an oracle. Kernels should ensure that these sorts of traps are not recoverable. + +- A compiler might use an intermediate representation (IR) for ``sign`` and ``auth`` operations that cannot make adequate correctness guarantees. + + For example, suppose that an IR uses ARMv8.3-like semantics for ``auth``: the operation merely corrupts its result on failure instead of promising the trap. A frontend might emit patterns of IR that always follow an ``auth`` with a memory access, thinking that this ensures correctness. But if the IR can be transformed to insert code between the ``auth`` and the access, or if the ``auth`` can be speculated, then this potentially creates an oracle. It is better for ``auth`` to semantically guarantee to trap, potentially requiring an explicit check in the generated code. An ARMv8.3-like target can avoid this explicit check in the common case by recognizing the pattern of an ``auth`` followed immediately by an access. + +Attackable code sequences ++++++++++++++++++++++++++ + +If code that is part of a pointer authentication operation is interleaved with code that may itself be vulnerable to attacks, an attacker may be able to use this to create a :ref:`signing<Signing oracles>` or :ref:`authentication<Authentication oracles>` oracle. + +For example, suppose that the compiler is generating a call to a function and passing two arguments: a signed constant pointer and a value derived from a call. In ARMv8.3, this code might look like so: + +.. code-block:: asm + + adr x19, _callback. ; compute &_callback + paciza x19 ; sign it with a constant discriminator of 0 + blr _argGenerator ; call _argGenerator() (returns in x0) + mov x1, x0 ; move call result to second arg register + mov x0, x19 ; move signed &_callback to first arg register + blr _function ; call _function + +This code is correct, as would be a sequencing that does *both* the ``adr`` and the ``paciza`` after the call to ``_argGenerator``. But a sequence that computes the address of ``_callback`` but leaves it as a raw pointer in a register during the call to ``_argGenerator`` would be vulnerable: + +.. code-block:: asm + + adr x19, _callback. ; compute &_callback + blr _argGenerator ; call _argGenerator() (returns in x0) + mov x1, x0 ; move call result to second arg register + paciza x19 ; sign &_callback + mov x0, x19 ; move signed &_callback to first arg register + blr _function ; call _function + +If ``_argGenerator`` spills ``x19`` (a callee-save register), and if the attacker can perform a write during this call, then the attacker can overwrite the spill slot with an arbitrary pointer that will eventually be unconditionally signed after the function returns. This would be a signing oracle. + +The implementation can avoid this by obeying two basic rules: + +- The compiler's intermediate representations (IR) should not provide operations that expose intermediate raw pointers. This may require providing extra operations that perform useful combinations of operations. + + For example, there should be an "atomic" auth-and-resign operation that should be used instead of emitting an ``auth`` operation whose result is fed into a ``sign``. + + Similarly, if a pointer should be authenticated as part of doing a memory access or a call, then the access or call should be decorated with enough information to perform the authentication; there should not be a separate ``auth`` whose result is used as the pointer operand for the access or call. (In LLVM IR, we do this for calls, but not yet for loads or stores.) + + "Operations" includes things like materializing a signed pointer to a known function or global variable. The compiler must be able to recognize and emit this as a unified operation, rather than potentially splitting it up as in the example above. + +- The compiler backend should not be too aggressive about scheduling instructions that are part of a pointer authentication operation. This may require custom code-generation of these operations in some cases. + +Register clobbering ++++++++++++++++++++ + +As a refinement of the section on `Attackable code sequences`_, if the attacker has the ability to modify arbitrary *register* state at arbitrary points in the program, then special care must be taken. + +For example, ARMv8.3 might materialize a signed function pointer like so: + +.. code-block:: asm + + adr x0, _callback. ; compute &_callback + paciza x0 ; sign it with a constant discriminator of 0 + +If an attacker has the ability to overwrite ``x0`` between these two instructions, this code sequence is vulnerable to becoming a signing oracle. + +For the most part, this sort of attack is not possible: it is a basic element of the design of modern computation that register state is private and inviolable. However, in systems that support asynchronous interrupts, this property requires the cooperation of the interrupt-handling code. If that code saves register state to memory, and that memory can be overwritten by an attacker, then essentially the attack can overwrite arbitrary register state at an arbitrary point. This could be a concern if the threat model includes attacks on the kernel or if the program uses user-space preemptive multitasking. + +(Readers might object that an attacker cannot rely on asynchronous interrupts triggering at an exact instruction boundary. In fact, researchers have had some success in doing exactly that. Even ignoring that, though, we should aim to protect against lucky attackers just as much as good ones.) + +To protect against this, saved register state must be at least partially signed (using something like `ptrauth_sign_generic_data`_). This is required for correctness anyway because saved thread states include security-critical registers such as SP, FP, PC, and LR (where applicable). Ideally, this signature would cover all the registers, but since saving and restoring registers can be very performance-sensitive, that may not be acceptable. It is sufficient to set aside a small number of scratch registers that will be guaranteed to be preserved correctly; the compiler can then be careful to only store critical values like intermediate raw pointers in those registers. + +``setjmp`` and ``longjmp`` should sign and authenticate the core registers (SP, FP, PC, and LR), but they do not need to worry about intermediate values because ``setjmp`` can only be called synchronously, and the compiler should never schedule pointer-authentication operations interleaved with arbitrary calls. + +.. _Relative addresses: + +Attacks on relative addressing +++++++++++++++++++++++++++++++ + +Relative addressing is a technique used to compress and reduce the load-time cost of infrequently-used global data. The pointer authentication system is unlikely to support signing or authenticating a relative address, and in most cases it would defeat the point to do so: it would take additional storage space, and applying the signature would take extra work at load time. + +Relative addressing is not precluded by the use of pointer authentication, but it does take extra considerations to make it secure: + +- Relative addresses must only be stored in read-only memory. A writable relative address can be overwritten to point nearly anywhere, making it inherently insecure; this danger can only be compensated for with techniques for protecting arbitrary data like `ptrauth_sign_generic_data`_. + +- Relative addresses must only be accessed through signed pointers with adequate diversity. If an attacker can perform an `access path attack` to replace the pointer through which the relative address is accessed, they can easily cause the relative address to point wherever they want. + +Signature forging ++++++++++++++++++ + +If an attacker can exactly reproduce the behavior of the signing algorithm, and they know all the correct inputs to it, then they can perfectly forge a signature on an arbitrary pointer. + +There are three components to avoiding this mistake: + +- The abstract signing algorithm should be good: it should not have glaring flaws which would allow attackers to predict its result with better than random accuracy without knowing all the inputs (like the key values). + +- The key values should be kept secret. If at all possible, they should never be stored in accessible memory, or perhaps only stored encrypted. + +- Contexts that are meant to be independently protected should use different key values. For example, the kernel should not use the same keys as user processes. Different user processes should also use different keys from each other as much as possible, although this may pose its own technical challenges. + +Remapping ++++++++++ + +If an attacker can change the memory protections on certain pages of the program's memory, that can substantially weaken the protections afforded by pointer authentication. + +- If an attacker can inject their own executable code, they can also certainly inject code that can be used as a :ref:`signing oracle<Signing Oracles>`. The same is true if they can write to the instruction stream. + +- If an attacker can remap read-only program sections to be writable, then any use of :ref:`relative addresses` in global data becomes insecure. + +- If an attacker can remap read-only program sections to be writable, then it is unsafe to use unsigned pointers in `global offset tables`_. + +Remapping memory in this way often requires the attacker to have already substantively subverted the control flow of the process. Nonetheless, if the operating system has a mechanism for mapping pages in a way that cannot be remapped, this should be used wherever possible. + + + +.. _Safe Derivation: + +Safe derivation +~~~~~~~~~~~~~~~ + +Whether a data pointer is stored, even briefly, as a raw pointer can affect the security-correctness of a program. (Function pointers are never implicitly stored as raw pointers; raw pointers to functions can only be produced with the ``<ptrauth.h>`` intrinsics.) Repeated re-signing can also impact performance. Clang makes a modest set of guarantees in this area: + +- An expression of pointer type is said to be **safely derived** if: + + - it takes the address of a global variable or function, or + + - it is a load from a gl-value of ``__ptrauth``-qualified type. + +- If a value that is safely derived is assigned to a ``__ptrauth``-qualified object, including by initialization, then the value will be directly signed as appropriate for the target qualifier and will not be stored as a raw pointer. + +- If the function expression of a call is a gl-value of ``__ptrauth``-qualified type, then the call will be authenticated directly according to the source qualifier and will not be resigned to the default rule for a function pointer of its type. + +These guarantees are known to be inadequate for data pointer security. In particular, Clang should be enhanced to make the following guarantees: + +- A pointer should additionally be considered safely derived if it is: + + - the address of a gl-value that is safely derived, + + - the result of pointer arithmetic on a pointer that is safely derived (with some restrictions on the integer operand), + + - the result of a comma operator where the second operand is safely derived, + + - the result of a conditional operator where the selected operand is safely derived, or + + - the result of loading from a safely derived gl-value. + +- A gl-value should be considered safely derived if it is: + + - a dereference of a safely derived pointer, + + - a member access into a safely derived gl-value, or + + - a reference to a variable. + +- An access to a safely derived gl-value should be guaranteed to not allow replacement of any of the safely-derived component values at any point in the access. "Access" should include loading a function pointer. + +- Assignments should include pointer-arithmetic operators like ``+=``. + +Making these guarantees will require further work, including significant new support in LLVM IR. + +Furthermore, Clang should implement a warning when assigning a data pointer that is not safely derived to a ``__ptrauth``-qualified gl-value. + + + +Language ABI +------------ + +This section describes the pointer-authentication ABI currently implemented in Clang for the Apple arm64e target. As other targets adopt pointer authentication, this section should be generalized to express their ABIs as well. + +Key assignments +~~~~~~~~~~~~~~~ + +ARMv8.3 provides four abstract signing keys: ``IA``, ``IB``, ``DA``, and ``DB``. The architecture designates ``IA`` and ``IB`` for signing code pointers and ``DA`` and ``DB`` for signing data pointers; this is reinforced by two properties: + +- The ISA provides instructions that perform combined auth+call and auth+load operations; these instructions can only use the ``I`` keys and ``D`` keys, respectively. + +- AArch64's TBI feature can be separately enabled for code pointers (controlling whether indirect-branch instructions ignore those bits) and data pointers (controlling whether memory-access instructions) ignore those bits. If TBI is enabled for a kind of pointer, the sign and auth operations preserve the TBI bits when signing with an associated keys (at the cost of shrinking the number of available signing bits by 8). + +arm64e then further subdivides the keys as follows: + +- The ``A`` keys are used for primarily "global" purposes like signing v-tables and function pointers. These keys are sometimes called *process-independent* or *cross-process* because on existing OSes they are not changed when changing processes, although this is not a platform guarantee. + +- The ``B`` keys are used for primarily "local" purposes like signing return addresses and frame pointers. These keys are sometimes called *process-specific* because they are typically different between processes. However, they are in fact shared across processes in one situation: systems which provide ``fork`` cannot change these keys in the child process; they can only be changed during ``exec``. + +Implementation-defined algorithms and quantities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cryptographic hash algorithm used to compute signatures in ARMv8.3 is a private detail of the hardware implementation. + +arm64e restricts constant discriminators (used in ``__ptrauth`` and ``ptrauth_blend_discriminator``) to the range from 0 to 65535, inclusive. A 0 discriminator generally signifies that no blending is required; see the documentation for ``ptrauth_blend_discriminator``. This range is somewhat narrow but has two advantages: + +- The AArch64 ISA allows an arbitrary 16-bit immediate to be written over the top 16 bits of a register in a single instruction: + + .. code-block:: asm + + movk xN, #0x4849, LSL 48 + + This is ideal for the discriminator blending operation because it adds minimal code-size overhead and avoids overwriting any interesting bits from the pointer. Blending in a wider constant discriminator would either clobber interesting bits (e.g. if it was loaded with ``movk xN, #0x4c4f, LSL 32``) or require significantly more code (e.g. if the discriminator was loaded with a ``mov+bfi`` sequence). + +- It is possible to pack a 16-bit discriminator into loader metadata with minimal compromises, whereas a wider discriminator would require extra metadata storage and therefore significantly impact load times. + +The string hash used by ``ptrauth_string_discriminator`` is a 64-bit SipHash-2-4 using the constant seed ``b5d4c9eb79104a796fec8b1b428781d4`` (big-endian), with the result reduced by modulo to the range of non-zero discriminators (i.e. ``(rawHash % 65535) + 1``). + +Return addresses +~~~~~~~~~~~~~~~~ + +The kernel must ensure that attackers cannot replace LR due to an asynchronous exception; see `Register clobbering`_. If this is done by generally protecting LR, then functions which don't spill LR to the stack can avoid signing it entirely. Otherwise, the return address must be signed; on arm64e it is signed with the ``IB`` key using the stack pointer on entry as the discriminator. + +Protecting return addresses is of such particular importance that the ``IB`` key is almost entirely reserved for this purpose. + +Global offset tables +~~~~~~~~~~~~~~~~~~~~ + +The global offset table (GOT) is not ABI, but it is a common implementation technique for dynamic linking which deserves special discussion here. + +Whenever possible, signed pointers should be materialized directly in code rather than via the GOT, e.g. using an ``adrp+add+pac`` sequence on ARMv8.3. This decreases the amount of work necessary at load time to initialize the GOT, but more importantly, it defines away the potential for several attacks: + +- Attackers cannot change instructions, so there is no way to cause this code sequence to materialize a different pointer, whereas an access via the GOT always has *at minimum* a probabilistic chance to be the target of successful `substitution attacks`_. + +- The GOT is a dense pool of fixed pointers at a fixed offset relative to code; attackers can search this pool for useful pointers that can be used in `substitution attacks`_, whereas pointers that are only materialized directly are not so easily available. + +- Similarly, attackers can use `access path attacks`_ to replace a pointer to a signed pointer with a pointer to the GOT if the signing schema used within the GOT happens to be the same as the original pointer. This kind of collision becomes much less likely to be useful the fewer pointers are in the GOT in the first place. + +If this can be done for a symbol, then the compiler need only ensure that it materializes the signed pointer using registers that are safe against `register clobbering`_. + +However, many symbols can only be accessed via the GOT, e.g. because they resolve to definitions outside of the current image. In this case, care must be taken to ensure that using the GOT does not introduce weaknesses. + +- If the entire GOT can be mapped read-only after loading, then no signing is required within the GOT. In fact, not signing pointers in the GOT is preferable in this case because it makes the GOT useless for the harvesting and access-path attacks above. Storing raw pointers in this way is usually extremely unsafe, but for the special case of an immutable GOT entry it's fine because the GOT is always accessed via an address that is directly materialized in code and thus provably unattackable. (But see `Remapping`_.) + +- Otherwise, GOT entries which are used for producing a signed pointer constant must be signed. The signing schema used in the GOT need not match the target signing schema for the signed constant. To counteract the threats of substitution attacks, it's best if GOT entries can be signed with address diversity. Using a good constant discriminator as well (perhaps derived from the symbol name) can make it less useful to use a pointer to the GOT as the replacement in an :ref:`access path attack<Access path attacks>`. + +In either case, the compiler must ensure that materializing the address of a GOT entry as part of producing a signed pointer constant is not vulnerable to `register clobbering`_. If the linker also generates code for this, e.g. for call stubs, this generated code must take the same precautions. + +C function pointers +~~~~~~~~~~~~~~~~~~~ + +On arm64e, C function pointers are currently signed with the ``IA`` key without address diversity and with a constant discriminator of 0. + +The C and C++ standards do not permit C function pointers to be signed with address diversity by default: in C++ terms, function pointer types are required to be trivially copyable, which means they must be copyable with ``memcpy``. + +The use of a uniform constant discriminator is seen as a serious defect which should be remedied, and improving this is under investigation. + +C++ virtual tables +~~~~~~~~~~~~~~~~~~ + +The pointer to a C++ virtual table is currently signed with the ``DA`` key, no address diversity, and a constant discriminator of 0. The use of no address diversity, as well as the uniform constant discriminator, are seen as weaknesses. Not using address diversity allows attackers to simply copy valid v-table pointers from one object to another. However, using a uniform discriminator of 0 does have positive performance and code-size implications on ARMv8.3, and diversity for the most important v-table access pattern (virtual dispatch) is already better assured by the signing schemas used on the virtual functions. It is also known that some code in practice copies objects containing v-tables with ``memcpy``, and while this is not permitted formally, it is something that may be invasive to eliminate. + +Virtual functions in a C++ virtual table are signed with the ``IA`` key, address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangled name of the function which originally gave rise to the v-table slot. + +C++ member function pointers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A member function pointer is signed with the ``IA`` key, no address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the member pointer type. Address diversity is not permitted by C++ for member function pointers because they must be trivially-copyable types. + +The Itanium C++ ABI specifies that member function pointers to virtual functions simply store an offset to the correct v-table slot. This ABI cannot be used securely with pointer authentication because there is no safe place to store the constant discriminator for the target v-table slot: if it's stored with the offset, an attacker can simply overwrite it with the right discriminator for the offset. Even if the programmer never uses pointers to virtual functions, the existence of this code path makes all member function pointer dereferences insecure. + +arm64e changes this ABI so that virtual function pointers are stored using dispatch thunks with vague linkage. Because arm64e supports interoperation with ``arm64`` code when pointer authentication is disabled, an arm64e member function pointer dereference still recognizes the virtual-function representation but uses an bogus discriminator on that path that should always trap if pointer authentication is enabled dynamically. + +The use of dispatch thunks means that ``==`` on member function pointers is no longer reliable for virtual functions, but this is acceptable because the standard makes no guarantees about it in the first place. + +The use of dispatch thunks also potentially enables v-tables to be signed using a declaration-specific constant discriminator in the future; otherwise this discriminator would also need to be stored in the member pointer. + +Blocks +~~~~~~ + +Block pointers are data pointers which must interoperate with the ObjC `id` type and therefore cannot be signed themselves. + +The invocation pointer in a block is signed with the ``IA`` key using address diversity and a constant dicriminator of 0. Using a uniform discriminator is seen as a weakness to be potentially improved, but this is tricky due to the subtype polymorphism directly permitted for blocks. + +Block descriptors and ``__block`` variables can contain pointers to functions that can be used to copy or destroy the object. These functions are signed with the ``IA`` key, address diversity, and a constant discriminator of 0. The structure of block descriptors is under consideration for improvement. + +Objective-C methods +~~~~~~~~~~~~~~~~~~~ + +Objective-C method lists sign methods with the ``IA`` key using address diversity and a constant discriminator of 0. Using a uniform constant discriminator is believed to be acceptable because these tables are only accessed internally to the Objective-C runtime. + +The Objective-C runtime provides additional protection to methods that have been loaded into the Objective-C method cache; this protection is private to the runtime. + +Pointer authentication cannot protect against access-path atacks against the Objective-C ``isa`` pointer, through which all dispatch occurs, because of compatibility requirements and existing and important usage of high bits in the pointer. + +Swift class methods +~~~~~~~~~~~~~~~~~~~ + +Class methods in Swift are signed in the class object with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the original overridable method. + +Resilient class-method lookup relies on passing a method descriptor; this method descriptor should be signed but currently isn't. The lookup function returns a function pointer that is signed using ``IA`` without address diversity and with the correct constant discriminator for the looked-up method. + +Swift's equivalent of a C++ v-table pointer is the ``isa`` pointer of an object. On arm64e, this is constrained by Objective-C compatibility and cannot be a signed pointer. + +Swift heap destructors +~~~~~~~~~~~~~~~~~~~~~~ + +Objects that are retained and released with Swift's native reference-counting system, including both native classes and temporary "box" allocations, must provide a destructor function in their metadata. This destructor function is signed with the ``IA`` key using address diversity and a constant discriminator of ``0xbbbf``. + +Swift protocol requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Protocol function requirements are signed in the protocol witness table with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the protocol requirement. + +Swift function types +~~~~~~~~~~~~~~~~~~~~ + +The invocation pointers of Swift function values are signed using the ``IA`` key without address diversity and with a constant discriminator derived loosely from the function type. + +Address diversity cannot be used by default for function values because function types are intended to be a "loadable" type which can be held and passed in registers. + +The constant discriminator currently accounts for potential abstraction in the function signature in ways that decrease the diversity of signatures; improving this is under investigation. + +Swift metadata +~~~~~~~~~~~~~~ + +Type metadata pointers in Swift are not signed. + +Type context descriptors must be signed because they frequently contain `relative addresses`_. Type context descriptors are signed with the ``DA`` key without address diversity (except when stored in type metadata) and with a constant discriminator of ``0xae86``. + +Swift value witnesses +~~~~~~~~~~~~~~~~~~~~~ + +Value witness functions in Swift are signed in the value witness table using the ``IA`` key with address diversity and an operation-specific constant discriminator which can be found in the Swift project headers. + +Swift coroutines +~~~~~~~~~~~~~~~~ + +Resumption functions for Swift coroutines are signed using the ``IA`` key without address diversity and with a constant discriminator derived from the yield type of the coroutine. Resumption functions cannot be signed with address diversity as they are returned directly in registers from the coroutine. + + + + + +Alternative implementations +--------------------------- + +Signature storage +~~~~~~~~~~~~~~~~~ + +It is not critical for the security of pointer authentication that the signature be stored "together" with the pointer, as it is in ARMv8.3. An implementation could just as well store the signature in a separate word, so that the ``sizeof`` a signed pointer would be larger than the ``sizeof`` a raw pointer. + +Storing the signature in the high bits, as ARMv8.3 does, has several trade-offs: + +- Disadvantage: there are substantially fewer bits available for the signature, weakening the mitigation by making it much easier for an attacker to simply guess the correct signature. + +- Disadvantage: future growth of the address space will necessarily further weaken the mitigation. + +- Advantage: memory layouts don't change, so it's possible for pointer-authentication-enabled code (for example, in a system library) to efficiently interoperate with existing code, as long as pointer authentication can be disabled dynamically. + +- Advantage: the size of a signed pointer doesn't grow, which might significantly increase memory requirements, code size, and register pressure. + +- Advantage: the size of a signed pointer is the same as a raw pointer, so generic APIs which work in types like `void *` (such as `dlsym`) can still return signed pointers. This means that clients of these APIs will not require insecure code in order to correctly receive a function pointer. + +Hashing vs. encrypting pointers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ARMv8.3 implements ``sign`` by computing a cryptographic hash and storing that in the spare bits of the pointer. This means that there are relatively few possible values for the valid signed pointer, since the bits corresponding to the raw pointer are known. Together with an ``auth`` oracle, this can make it computationally feasible to discover the correct signature with brute force. (The implementation should of course endeavor not to introduce ``auth`` oracles, but this can be difficult, and attackers can be devious.) + +If the implementation can instead *encrypt* the pointer during ``sign`` and *decrypt* it during ``auth``, this brute-force attack becomes far less feasible, even with an ``auth`` oracle. However, there are several problems with this idea: + +- It's unclear whether this kind of encryption is even possible without increasing the storage size of a signed pointer. If the storage size can be increased, brute-force atacks can be equally well mitigated by simply storing a larger signature. + +- It would likely be impossible to implement a ``strip`` operation, which might make debuggers and other out-of-process tools far more difficult to write, as well as generally making primitive debugging more challenging. + +- Implementations can benefit from being able to extract the raw pointer immediately from a signed pointer. An ARMv8.3 processor executing an ``auth``-and-load instruction can perform the load and ``auth`` in parallel; a processor which instead encrypted the pointer would be forced to perform these operations serially. From 0a1b1118cfd0716d1373438528b8e80914b436dd Mon Sep 17 00:00:00 2001 From: Jeremy Morse <jeremy.morse@sony.com> Date: Wed, 30 Oct 2019 13:24:55 +0000 Subject: [PATCH 544/582] Rename a flang test case On Windows and macOS, the filesystem is case insensitive, and these files interfere with each other. Reading through, the case of the file extension is part of the test. I've altered the rest of the name instead. (cherry picked from commit 6c0a160c2d33e54aecf1538bf7c85d8da92051be) --- clang/test/Driver/flang/flang.f90 | 2 +- clang/test/Driver/flang/{flang.F90 => flang_ucase.F90} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename clang/test/Driver/flang/{flang.F90 => flang_ucase.F90} (98%) diff --git a/clang/test/Driver/flang/flang.f90 b/clang/test/Driver/flang/flang.f90 index 56c1ace6f8600..9d47c7c90225c 100644 --- a/clang/test/Driver/flang/flang.f90 +++ b/clang/test/Driver/flang/flang.f90 @@ -1,6 +1,6 @@ ! Check that flang -fc1 is invoked when in --driver-mode=flang. -! This is a copy of flang.F90 because the driver has logic in it which +! This is a copy of flang_ucase.F90 because the driver has logic in it which ! differentiates between F90 and f90 files. Flang will not treat these files ! differently. diff --git a/clang/test/Driver/flang/flang.F90 b/clang/test/Driver/flang/flang_ucase.F90 similarity index 98% rename from clang/test/Driver/flang/flang.F90 rename to clang/test/Driver/flang/flang_ucase.F90 index 37553c7c27605..323afb21dccf5 100644 --- a/clang/test/Driver/flang/flang.F90 +++ b/clang/test/Driver/flang/flang_ucase.F90 @@ -48,4 +48,4 @@ ! CHECK-EMIT-OBJ-DAG: "-o" "{{[^"]*}}.o" ! Should end in the input file. -! ALL: "{{.*}}flang.F90"{{$}} +! ALL: "{{.*}}flang_ucase.F90"{{$}} From 2334bd4ea89ca00ee02e7849982f048b3e2659e9 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 30 Oct 2019 18:58:22 -0700 Subject: [PATCH 545/582] Fix build post-merge of Michael's scan-deps commit --- clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 7f20ec7056c6e..89ef2b5f51f86 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -126,7 +126,7 @@ void ModuleDepCollectorPP::addModuleDep(const Module *M, ModuleDeps &MD) { ModuleDepCollector::ModuleDepCollector(CompilerInstance &I, DependencyConsumer &C) - : Instance(I), Consumer(C), ContextHash(I.getInvocation().getModuleHash()) { + : Instance(I), Consumer(C), ContextHash(I.getInvocation().getModuleHash(I.getDiagnostics())) { } void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { From 0e9cdfe5ffec2f2372bb034abfa637bb15a66841 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Wed, 30 Oct 2019 19:24:53 -0700 Subject: [PATCH 546/582] Try to fix second issue in downstream getModuleHash --- .../lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index edf2cf8bd70f0..d9e078d462f6e 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -147,7 +147,7 @@ class DependencyScanningAction : public tooling::ToolAction { break; } - Consumer.handleContextHash(Compiler.getInvocation().getModuleHash()); + Consumer.handleContextHash(Compiler.getInvocation().getModuleHash(Compiler.getDiagnostics())); auto Action = std::make_unique<PreprocessOnlyAction>(); const bool Result = Compiler.ExecuteAction(*Action); From fccc03fdc27b2cb32e9a501584aa496975b2eba4 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Thu, 31 Oct 2019 11:39:42 -0700 Subject: [PATCH 547/582] Fix merge error. --- clang/lib/CodeGen/CGCall.h | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index e2ace48e4afed..6cdd5fb1a0728 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -103,6 +103,10 @@ class CGCallee { Last = Virtual }; + struct OrdinaryInfoStorage { + CGCalleeInfo AbstractInfo; + CGPointerAuthInfo PointerAuthInfo; + }; struct BuiltinInfoStorage { const FunctionDecl *Decl; unsigned ID; @@ -119,7 +123,7 @@ class CGCallee { SpecialKind KindOrFunctionPointer; union { - CGCalleeInfo AbstractInfo; + OrdinaryInfoStorage OrdinaryInfo; BuiltinInfoStorage BuiltinInfo; PseudoDestructorInfoStorage PseudoDestructorInfo; VirtualInfoStorage VirtualInfo; @@ -138,9 +142,11 @@ class CGCallee { /// Construct a callee. Call this constructor directly when this /// isn't a direct call. - CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr) + CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr, + const CGPointerAuthInfo &pointerAuthInfo) : KindOrFunctionPointer(SpecialKind(uintptr_t(functionPtr))) { - AbstractInfo = abstractInfo; + OrdinaryInfo.AbstractInfo = abstractInfo; + OrdinaryInfo.PointerAuthInfo = pointerAuthInfo; assert(functionPtr && "configuring callee without function pointer"); assert(functionPtr->getType()->isPointerTy()); assert(functionPtr->getType()->getPointerElementType()->isFunctionTy()); @@ -162,12 +168,12 @@ class CGCallee { static CGCallee forDirect(llvm::Constant *functionPtr, const CGCalleeInfo &abstractInfo = CGCalleeInfo()) { - return CGCallee(abstractInfo, functionPtr); + return CGCallee(abstractInfo, functionPtr, CGPointerAuthInfo()); } static CGCallee forDirect(llvm::FunctionCallee functionPtr, const CGCalleeInfo &abstractInfo = CGCalleeInfo()) { - return CGCallee(abstractInfo, functionPtr.getCallee()); + return CGCallee(abstractInfo, functionPtr.getCallee(), CGPointerAuthInfo()); } static CGCallee forVirtual(const CallExpr *CE, GlobalDecl MD, Address Addr, @@ -207,7 +213,11 @@ class CGCallee { if (isVirtual()) return VirtualInfo.MD; assert(isOrdinary()); - return AbstractInfo; + return OrdinaryInfo.AbstractInfo; + } + const CGPointerAuthInfo &getPointerAuthInfo() const { + assert(isOrdinary()); + return OrdinaryInfo.PointerAuthInfo; } llvm::Value *getFunctionPointer() const { assert(isOrdinary()); @@ -217,6 +227,10 @@ class CGCallee { assert(isOrdinary()); KindOrFunctionPointer = SpecialKind(uintptr_t(functionPtr)); } + void setPointerAuthInfo(CGPointerAuthInfo pointerAuth) { + assert(isOrdinary()); + OrdinaryInfo.PointerAuthInfo = pointerAuth; + } bool isVirtual() const { return KindOrFunctionPointer == SpecialKind::Virtual; From b20f4deeb58dc77d6dffc6f164836a7786605a43 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 1 Nov 2019 14:38:47 -0700 Subject: [PATCH 548/582] apple-llvm-config: add pr.json config for git-apple-llvm pr tool --- apple-llvm-config/pr.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 apple-llvm-config/pr.json diff --git a/apple-llvm-config/pr.json b/apple-llvm-config/pr.json new file mode 100644 index 0000000000000..9de9ce6265850 --- /dev/null +++ b/apple-llvm-config/pr.json @@ -0,0 +1,9 @@ +{ +"type": "github", +"domain": "github.com", +"user": "apple", +"repo": "llvm-project", +"test": { + "type":"swift-ci" +} +} From c7bb59aa7457f542b067e1b05d092c1c736dfe8f Mon Sep 17 00:00:00 2001 From: Ben Langmuir <blangmuir@apple.com> Date: Fri, 1 Nov 2019 14:29:54 -0700 Subject: [PATCH 549/582] [darwin] Set 'darwin' feature to fix tests using UNSUPPORTED/REQUIRES This should ultimately be upstreamed, but it depends on other changes to lit that have not been upstreamed yet (rdar://26780128). rdar://56817666 --- clang/test/lit.cfg.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 1ffb6d094d72c..3fc75105fb797 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -155,6 +155,9 @@ def is_filesystem_case_insensitive(): if platform.system() not in ['Windows']: config.available_features.add('can-remove-opened-file') +# *-apple-macosx should also be XFAILED when 'darwin' is XFAILED. +if lit.util.isMacOSTriple(config.target_triple): + config.available_features.add('darwin') def calculate_arch_features(arch_string): features = [] From e7d488c3dc6dd18a4211f818772c607239fd9ef3 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai <smeenai@fb.com> Date: Mon, 11 Nov 2019 17:43:23 -0800 Subject: [PATCH 550/582] [arm64e] Make test work on non-Darwin hosts -arch only applies to Darwin triples, which won't be the default triple on non-Darwin hosts. Use an explicit -target instead to be agnostic to the default triple. --- clang/test/Driver/arch-arm64e.c | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c index 40b471c8f130d..8d875e8dd175d 100644 --- a/clang/test/Driver/arch-arm64e.c +++ b/clang/test/Driver/arch-arm64e.c @@ -1,6 +1,6 @@ // Check that we can manually enable specific ptrauth features. -// RUN: %clang -arch arm64 -c %s -### 2>&1 | FileCheck %s --check-prefix NONE +// RUN: %clang -target arm64-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix NONE // NONE: "-cc1" // NONE-NOT: "-fptrauth-intrinsics" // NONE-NOT: "-fptrauth-calls" @@ -9,61 +9,61 @@ // NONE-NOT: "-fptrauth-auth-traps" // NONE-NOT: "-fptrauth-soft" -// RUN: %clang -arch arm64 -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL +// RUN: %clang -target arm64-apple-darwin -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL // CALL: "-cc1"{{.*}} {{.*}} "-fptrauth-calls" -// RUN: %clang -arch arm64 -fptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix INTRIN +// RUN: %clang -target arm64-apple-darwin -fptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix INTRIN // INTRIN: "-cc1"{{.*}} {{.*}} "-fptrauth-intrinsics" -// RUN: %clang -arch arm64 -fptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix RETURN +// RUN: %clang -target arm64-apple-darwin -fptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix RETURN // RETURN: "-cc1"{{.*}} {{.*}} "-fptrauth-returns" -// RUN: %clang -arch arm64 -fptrauth-indirect-gotos -c %s -### 2>&1 | FileCheck %s --check-prefix INDGOTO +// RUN: %clang -target arm64-apple-darwin -fptrauth-indirect-gotos -c %s -### 2>&1 | FileCheck %s --check-prefix INDGOTO // INDGOTO: "-cc1"{{.*}} {{.*}} "-fptrauth-indirect-gotos" -// RUN: %clang -arch arm64 -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS +// RUN: %clang -target arm64-apple-darwin -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS // TRAPS: "-cc1"{{.*}} {{.*}} "-fptrauth-auth-traps" -// RUN: %clang -arch arm64 -fptrauth-soft -c %s -### 2>&1 | FileCheck %s --check-prefix SOFT +// RUN: %clang -target arm64-apple-darwin -fptrauth-soft -c %s -### 2>&1 | FileCheck %s --check-prefix SOFT // SOFT: "-cc1"{{.*}} {{.*}} "-fptrauth-soft" // Check the arm64e defaults. -// RUN: %clang -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT -// RUN: %clang -mkernel -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT -// RUN: %clang -fapple-kext -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -mkernel -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -fapple-kext -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT // DEFAULT: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex"{{.*}} -// RUN: %clang -arch arm64e -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL -// RUN: %clang -mkernel -arch arm64e -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL -// RUN: %clang -fapple-kext -arch arm64e -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -mkernel -target arm64e-apple-darwin -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -fapple-kext -target arm64e-apple-darwin -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL // DEFAULT-NOCALL-NOT: "-fptrauth-calls" // DEFAULT-NOCALL: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex" -// RUN: %clang -arch arm64e -fno-ptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix NORET +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix NORET // NORET-NOT: "-fptrauth-returns" // NORET: "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex" -// RUN: %clang -arch arm64e -fno-ptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix NOINTRIN +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix NOINTRIN // NOINTRIN: "-fptrauth-returns" // NOINTRIN-NOT: "-fptrauth-intrinsics" // NOINTRIN: "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex"{{.*}} -// RUN: %clang -arch arm64e -fno-ptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix NOTRAP +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix NOTRAP // NOTRAP: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-target-cpu" "vortex" // Check the CPU defaults and overrides. -// RUN: %clang -arch arm64e -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX -// RUN: %clang -arch arm64e -mcpu=vortex -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX -// RUN: %clang -arch arm64e -mcpu=cyclone -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX -// RUN: %clang -arch arm64e -mcpu=lightning -c %s -### 2>&1 | FileCheck %s --check-prefix LIGHTNING +// RUN: %clang -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -target arm64e-apple-darwin -mcpu=vortex -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -target arm64e-apple-darwin -mcpu=cyclone -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -target arm64e-apple-darwin -mcpu=lightning -c %s -### 2>&1 | FileCheck %s --check-prefix LIGHTNING // VORTEX: "-cc1"{{.*}} "-target-cpu" "vortex" // LIGHTNING: "-cc1"{{.*}} "-target-cpu" "lightning" From 0981651bb89ad6f9bfa2520737408931bc0738f7 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Thu, 14 Nov 2019 11:59:32 -0800 Subject: [PATCH 551/582] Update soft ptrauth pass to match upstream change 05da2fe5216. rdar://57177492 --- llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp b/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp index 486fe57122e6d..030c6af2b086f 100644 --- a/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp +++ b/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp @@ -39,6 +39,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h" From 752558d42de3d78405f71ebb46ac80b5ba614994 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" <dexonsmith@apple.com> Date: Thu, 14 Nov 2019 18:53:46 -0800 Subject: [PATCH 552/582] apple-docs: Document Apple's branching scheme The trick will be to keep the stabilization branches relatively up-to-date, but this should be useful even if it bitrots a little. --- README.md | 8 +++ apple-docs/AppleBranchingScheme.md | 105 +++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 apple-docs/AppleBranchingScheme.md diff --git a/README.md b/README.md index c8013e0426bd9..4cb0167148129 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# Apple's fork of llvm-project + +This is Apple's fork of llvm-project. For more information on Apple's +branching scheme, please see +[apple-docs/AppleBranchingScheme.md](https://github.com/apple/llvm-project/tree/apple/master/apple-docs/AppleBranchingScheme.md). + +The LLVM project's main README follows. + # The LLVM Compiler Infrastructure This directory and its subdirectories contain source code for LLVM, diff --git a/apple-docs/AppleBranchingScheme.md b/apple-docs/AppleBranchingScheme.md new file mode 100644 index 0000000000000..806d0a39416ff --- /dev/null +++ b/apple-docs/AppleBranchingScheme.md @@ -0,0 +1,105 @@ +# Apple's branching scheme for llvm-project + +There are currently three namespaces for branches on +[github.com/apple/llvm-project](https://github.com/apple/llvm-project): + + 1. `llvm.org/*`, for forwarded branches from + [github.com/llvm](https://github.com/llvm/llvm-project); + 2. `apple/*`, for standalone downstream content; and + 3. `swift/*`, for downstream content that depends on + [Swift](https://github.com/apple/swift). + +## Forwarded branches from [github.com/llvm](https://github.com/llvm/llvm-project) + +The `llvm.org/*` branches are forwarded, unchanged, from +[github.com/llvm/llvm-project](https://github.com/llvm/llvm-project). These +are read-only, exact copies of the upstream LLVM project's branches. They are +forwarded here as a convenience for easy reference, to avoid the need for extra +remotes. + +- [llvm.org/master](https://github.com/apple/llvm-project/tree/llvm.org/master) + is the most important branch here, matching the LLVM project's + [master](https://github.com/llvm/llvm-project/tree/master) branch. + +## Downstream branches that are standalone + +The `apple/*` branches have downstream content, besides what is in the LLVM +project. This content includes some patches that have not yet been fully +upstreamed to the LLVM project, including some special support for Swift. +Critically, however, none of these branches *depend on* the +[github.com/apple/swift](https://github.com/apple/swift) repository. + +Although there are a few non-trivial differences from LLVM, the goal is to +minimize this difference, and do almost all development upstream. + +- [apple/master](https://github.com/apple/llvm-project/tree/apple/master) is + directly downstream of + [llvm.org/master](https://github.com/apple/llvm-project/tree/llvm.org/master). + There is a gated automerger that does testing before merging in. Most + changes to this branch should be redirected to <https://reviews.llvm.org/> + (see also <http://llvm.org/docs/Contributing.html>). +- `apple/stable/*`: These branches are periodic stabilization branches, where + fixes are cherry-picked from LLVM. At time of writing: + - [apple/stable/20191106](https://github.com/apple/llvm-project/tree/apple/stable/20191106) + is the most recent stabilization branch. + - [apple/stable/20190619](https://github.com/apple/llvm-project/tree/apple/stable/20190619) + is the current stabilization branch for + [swift/master](https://github.com/apple/llvm-project/tree/swift/master) + (see below). + +## Downstream branches that depend on [Swift](https://github.com/apple/swift) + +The `swift/*` branches are downstream of `apple/*`, and include content that +depends [Swift](https://github.com/apple/swift). The naming scheme is +`swift/<swift-branch>`, where `<swift-branch>` is the aligned Swift branch. + +The branches are automerged from a branch in the `apple/*` namespace. They are +expected to have zero differences outside the `lldb/` and `apple-llvm-config/` +directories. + +These are the most important branches: + +- [swift/master-next](https://github.com/apple/llvm-project/tree/swift/master-next) + is downstream of + [apple/master](https://github.com/apple/llvm-project/tree/apple/master) and + aligned with Swift's + [master-next](https://github.com/apple/swift/tree/master-next) branch. +- [swift/master](https://github.com/apple/llvm-project/tree/swift/master) is + downstream of a stabilization branch in `apple/stable/*` + ([apple/stable/20190619](https://github.com/apple/llvm-project/tree/apple/stable/20190619), + as of time of writing) and aligned with Swift's + [master](https://github.com/apple/swift/tree/master) branch. + +## Historical trivia: mappings to branches from before the monorepo transition + +Before the LLVM project's monorepo transition, Apple maintained downstream +forks of various split repositories. Here is a mapping from a few of the new +branches in the llvm-project monorepo to their original split repositories. + +- [apple/master](https://github.com/apple/llvm-project/tree/apple/master) was + generated from the `upstream-with-swift` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/master-next](https://github.com/apple/llvm-project/tree/swift/master-next) + was generated from the `upstream-with-swift` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from [apple/master](https://github.com/apple/llvm-project/tree/apple/master). +- [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104) + was generated from the `swift-5.1-branch` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/swift-5.1-branch](https://github.com/apple/llvm-project/tree/swift/swift-5.1-branch) + was generated from the `swift-5.1-branch` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from + [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104). +- [swift/master](https://github.com/apple/llvm-project/tree/swift/master) was + generated from the `stable` branch from all six split repos. From 2a5d08b5ad82e0cc9eeec03a4f7c2ac4c8b7b63a Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Fri, 15 Nov 2019 13:37:12 -0800 Subject: [PATCH 553/582] [CMake] s/clangToolingRefactor/clangToolingRefactoring/ Account for bab1d8edcf4b29c3529db3c6f665d2131fc917fa --- clang-tools-extra/clang-apply-replacements/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt index 02da0851a72be..5bfdcb487e17a 100644 --- a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt +++ b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt @@ -10,7 +10,7 @@ add_clang_library(clangApplyReplacements clangBasic clangRewrite clangToolingCore - clangToolingRefactor + clangToolingRefactoring ) include_directories( From b0b4052c350ba72bd2af2360862e5234d5f9b22e Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Sun, 17 Nov 2019 13:58:27 -0800 Subject: [PATCH 554/582] Fix the ast-dump-decl-json.m test on apple/master --- clang/test/AST/ast-dump-decl-json.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/AST/ast-dump-decl-json.m b/clang/test/AST/ast-dump-decl-json.m index 25ca792429bef..afbd3d5248b6d 100644 --- a/clang/test/AST/ast-dump-decl-json.m +++ b/clang/test/AST/ast-dump-decl-json.m @@ -1037,7 +1037,7 @@ void f() { // CHECK: "kind": "ObjCCompatibleAliasDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 926, +// CHECK-NEXT: "offset": 947, // CHECK-NEXT: "file": "{{.*}}", // CHECK-NEXT: "line": 60, // CHECK-NEXT: "col": 22, From f59e83e3b54353a526e271eae8ed82d2d6dbcb6f Mon Sep 17 00:00:00 2001 From: David Zarzycki <dave@znu.io> Date: Tue, 19 Nov 2019 15:57:45 +0200 Subject: [PATCH 555/582] [CMake] Add missing dep when building with BUILD_SHARED_LIBS --- clang/lib/APINotes/CMakeLists.txt | 1 + clang/lib/Index/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/clang/lib/APINotes/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt index da9d0d1e55078..6b1200273d985 100644 --- a/clang/lib/APINotes/CMakeLists.txt +++ b/clang/lib/APINotes/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS BitReader + BitstreamReader Support ) diff --git a/clang/lib/Index/CMakeLists.txt b/clang/lib/Index/CMakeLists.txt index 18ba9ec6113d9..7bb53a5f967ba 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS BitReader + BitstreamReader Core Support ) From 23391df492588e5dee6f8caa42ffc8f0c4c84ab9 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Fri, 8 Nov 2019 11:32:28 -0800 Subject: [PATCH 556/582] fix indexstore.h header for gcc --- clang/include/indexstore/indexstore.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h index ac365e770116a..5f190635bd29d 100644 --- a/clang/include/indexstore/indexstore.h +++ b/clang/include/indexstore/indexstore.h @@ -98,9 +98,13 @@ #define INDEXSTORE_OPTIONS_ATTRS INDEXSTORE_OPEN_ENUM_ATTR INDEXSTORE_FLAG_ENUM_ATTR +#if defined(__has_extension) #if __has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum) # define INDEXSTORE_OPTIONS(_type, _name) enum INDEXSTORE_OPTIONS_ATTRS _name : _type _name; enum INDEXSTORE_OPTIONS_ATTRS _name : _type -#else +#endif +#endif + +#ifndef INDEXSTORE_OPTIONS # define INDEXSTORE_OPTIONS(_type, _name) _type _name; enum INDEXSTORE_OPTIONS_ATTRS #endif From f6c355687816272efa0bf1cfbf8a6f4b36e42870 Mon Sep 17 00:00:00 2001 From: Azharuddin Mohammed <azhar@apple.com> Date: Tue, 26 Nov 2019 18:08:44 -0800 Subject: [PATCH 557/582] Fix build after 20d51b2f14ac --- clang/lib/Index/IndexingAction.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 8275859aafeed..a3f181e7be7b4 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -401,7 +401,7 @@ class SourceFilesIndexDependencyCollector : public DependencyCollector, visitor) override { HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); - if (auto Reader = CI.getModuleManager()) { + if (auto Reader = CI.getASTReader()) { Reader->getModuleManager().visit( [&](serialization::ModuleFile &Mod) -> bool { bool isSystemMod = false; @@ -749,7 +749,7 @@ class ModuleFileIndexDependencyCollector : public IndexDependencyProvider { const CompilerInstance &CI, llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) override { - auto Reader = CI.getModuleManager(); + auto Reader = CI.getASTReader(); Reader->visitInputFiles( ModFile, RecordOpts.RecordSystemDependencies, /*Complain=*/false, @@ -810,7 +810,7 @@ static void indexModule(serialization::ModuleFile &Mod, IndexCtx.setSysrootPath(SysrootPath); Recorder.init(&IndexCtx, CI); - for (const Decl *D : CI.getModuleManager()->getModuleFileLevelDecls(Mod)) { + for (const Decl *D : CI.getASTReader()->getModuleFileLevelDecls(Mod)) { IndexCtx.indexTopLevelDecl(D); } Recorder.finish(); @@ -889,7 +889,7 @@ bool index::emitIndexDataForModuleFile(const Module *Mod, std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(CI.getFrontendOpts()); - auto astReader = CI.getModuleManager(); + auto astReader = CI.getASTReader(); serialization::ModuleFile *ModFile = astReader->getModuleManager().lookup(Mod->getASTFile()); assert(ModFile && "no module file loaded for module ?"); From f579ea359d358f855616e85c0fc57c1a8aaef649 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Wed, 4 Dec 2019 10:20:57 -0800 Subject: [PATCH 558/582] Pass reference to CodeGenFunction after changes in open source rdar://problem/57627174 --- clang/lib/CodeGen/CGDecl.cpp | 2 +- clang/lib/CodeGen/CGExpr.cpp | 8 +++++--- clang/lib/CodeGen/CGExprScalar.cpp | 4 ++-- clang/lib/CodeGen/CGPointerAuth.cpp | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 437e2015e2838..22b161634a5a6 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -744,7 +744,7 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D, if (!lifetime) { llvm::Value *value; if (auto ptrauth = lvalue.getQuals().getPointerAuth()) { - value = EmitPointerAuthQualify(ptrauth, init, lvalue.getAddress()); + value = EmitPointerAuthQualify(ptrauth, init, lvalue.getAddress(*this)); lvalue.getQuals().removePtrAuth(); } else { value = EmitScalarExpr(init); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 558ed3c636abb..253d54cef41bf 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1777,7 +1777,8 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) { LV.getQuals().removePtrAuth(); auto value = EmitLoadOfLValue(LV, Loc).getScalarVal(); return RValue::get(EmitPointerAuthUnqualify(ptrauth, value, - LV.getType(), LV.getAddress(), + LV.getType(), + LV.getAddress(*this), /*known nonnull*/ false)); } @@ -1962,7 +1963,8 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, // Handle __ptrauth qualification by re-signing the value. if (auto pointerAuth = Dst.getQuals().getPointerAuth()) { Src = RValue::get(EmitPointerAuthQualify(pointerAuth, Src.getScalarVal(), - Dst.getType(), Dst.getAddress(), + Dst.getType(), + Dst.getAddress(*this), /*known nonnull*/ false)); } @@ -4803,7 +4805,7 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { LValue CopiedLV = LV; CopiedLV.getQuals().removePtrAuth(); llvm::Value *RV = EmitPointerAuthQualify(ptrauth, E->getRHS(), - CopiedLV.getAddress()); + CopiedLV.getAddress(*this)); EmitNullabilityCheck(CopiedLV, RV, E->getExprLoc()); EmitStoreThroughLValue(RValue::get(RV), CopiedLV); return LV; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 6d8e3ec925bd8..fce4058ab2e04 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -4023,13 +4023,13 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { LValue LV = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); LV.getQuals().removePtrAuth(); llvm::Value *RV = CGF.EmitPointerAuthQualify(ptrauth, E->getRHS(), - LV.getAddress()); + LV.getAddress(CGF)); CGF.EmitNullabilityCheck(LV, RV, E->getExprLoc()); CGF.EmitStoreThroughLValue(RValue::get(RV), LV); if (Ignore) return nullptr; RV = CGF.EmitPointerAuthUnqualify(ptrauth, RV, LV.getType(), - LV.getAddress(), /*nonnull*/ false); + LV.getAddress(CGF), /*nonnull*/ false); return RV; } diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index ab870df65b78b..f3debeaca0279 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -198,7 +198,7 @@ emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &lv, auto value = CGF.EmitLoadOfScalar(lv, loc); CGPointerAuthInfo authInfo; if (auto ptrauth = lv.getQuals().getPointerAuth()) { - authInfo = CGF.EmitPointerAuthInfo(ptrauth, lv.getAddress()); + authInfo = CGF.EmitPointerAuthInfo(ptrauth, lv.getAddress(CGF)); } else { authInfo = getPointerAuthInfoForType(CGF.CGM, lv.getType()); } From ded65a9a0b8cf6a554e1c1d443791191c56f9dd6 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Fri, 6 Dec 2019 16:52:55 -0800 Subject: [PATCH 559/582] Include Attr.h to fix build error rdar://problem/57717200 --- clang/lib/Index/IndexRecordHasher.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp index ee22ec8bcf3c3..1d1556f4a52c6 100644 --- a/clang/lib/Index/IndexRecordHasher.cpp +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -10,6 +10,7 @@ #include "IndexRecordHasher.h" #include "FileIndexRecord.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclVisitor.h" #include "llvm/Support/Path.h" From 1c6ddedf949db6fb047873c7b2a4b1640bf3e06e Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha <ahmed@bougacha.org> Date: Tue, 10 Dec 2019 13:23:31 -0800 Subject: [PATCH 560/582] [AArch64] Adopt upstream ldraa syntax revert. NFC. acd7fe8636a disabled the emission of the ldraa alias, for reasons that don't apply to apple/master, but this isn't an easy thing to diverge on, so we're stuck with that behavior for now. rdar://57644612 --- llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll index d7ed3c78e2b42..b62592793b6cf 100644 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll +++ b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll @@ -152,7 +152,7 @@ define i64* @test_load_auth_db_m256_pre(i64* %ptr, i64* %dst) { define i64* @test_load_auth_da_0_pre(i64* %ptr, i64* %dst) { ; CHECK-LABEL: test_load_auth_da_0_pre: ; CHECK: ; %bb.0: -; CHECK-NEXT: ldraa x8, [x0]! +; CHECK-NEXT: ldraa x8, [x0, #0]! ; CHECK-NEXT: str x8, [x1] ; CHECK-NEXT: ret %tmp0 = ptrtoint i64* %ptr to i64 From 771fed1834c9bc8f64fe92de27f929cb4b20f0ff Mon Sep 17 00:00:00 2001 From: Erik Pilkington <erik.pilkington@gmail.com> Date: Thu, 21 Nov 2019 13:32:05 -0800 Subject: [PATCH 561/582] NFC: get rid of the unnecessary union in PointerAuthSchema --- .../include/clang/Basic/PointerAuthOptions.h | 92 ++++++------------- 1 file changed, 30 insertions(+), 62 deletions(-) diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index d712c17ee2bea..7e69f17a456eb 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -27,14 +27,15 @@ namespace clang { class PointerAuthSchema { public: - enum class Kind { + enum class Kind : unsigned { None, Soft, ARM8_3, }; - /// Software pointer-signing "keys". - enum class SoftKey { + /// Software pointer-signing "keys". If you add a new key, make sure this->Key + /// has a large enough bit-width. + enum class SoftKey : unsigned { FunctionPointers = 0, BlockInvocationFunctionPointers = 1, BlockHelperFunctionPointers = 2, @@ -47,7 +48,7 @@ class PointerAuthSchema { /// Hardware pointer-signing keys in ARM8.3. /// /// These values are the same used in ptrauth.h. - enum class ARM8_3Key { + enum class ARM8_3Key : unsigned { ASIA = 0, ASIB = 1, ASDA = 2, @@ -55,7 +56,7 @@ class PointerAuthSchema { }; /// Forms of extra discrimination. - enum class Discrimination { + enum class Discrimination : unsigned { /// No additional discrimination. None, @@ -67,68 +68,33 @@ class PointerAuthSchema { }; private: - enum { - NumKindBits = 2 - }; - union { - /// A common header shared by all pointer authentication kinds. - struct { - unsigned Kind : NumKindBits; - unsigned AddressDiscriminated : 1; - unsigned Discrimination : 2; - } Common; - - struct { - unsigned Kind : NumKindBits; - unsigned AddressDiscriminated : 1; - unsigned Discrimination : 2; - unsigned Key : 3; - } Soft; - - struct { - unsigned Kind : NumKindBits; - unsigned AddressDiscriminated : 1; - unsigned Discrimination : 2; - unsigned Key : 2; - } ARM8_3; - }; + Kind TheKind : 2; + unsigned IsAddressDiscriminated : 1; + Discrimination DiscriminationKind : 2; + unsigned Key : 3; public: - PointerAuthSchema() { - Common.Kind = unsigned(Kind::None); - } + PointerAuthSchema() : TheKind(Kind::None) {} PointerAuthSchema(SoftKey key, bool isAddressDiscriminated, - Discrimination otherDiscrimination) { - Common.Kind = unsigned(Kind::Soft); - Common.AddressDiscriminated = isAddressDiscriminated; - Common.Discrimination = unsigned(otherDiscrimination); - Soft.Key = unsigned(key); - } + Discrimination otherDiscrimination) + : TheKind(Kind::Soft), IsAddressDiscriminated(isAddressDiscriminated), + DiscriminationKind(otherDiscrimination), Key(unsigned(key)) {} PointerAuthSchema(ARM8_3Key key, bool isAddressDiscriminated, - Discrimination otherDiscrimination) { - Common.Kind = unsigned(Kind::ARM8_3); - Common.AddressDiscriminated = isAddressDiscriminated; - Common.Discrimination = unsigned(otherDiscrimination); - ARM8_3.Key = unsigned(key); - } + Discrimination otherDiscrimination) + : TheKind(Kind::ARM8_3), IsAddressDiscriminated(isAddressDiscriminated), + DiscriminationKind(otherDiscrimination), Key(unsigned(key)) {} - Kind getKind() const { - return Kind(Common.Kind); - } + Kind getKind() const { return TheKind; } - explicit operator bool() const { - return isEnabled(); - } + explicit operator bool() const { return isEnabled(); } - bool isEnabled() const { - return getKind() != Kind::None; - } + bool isEnabled() const { return getKind() != Kind::None; } bool isAddressDiscriminated() const { assert(getKind() != Kind::None); - return Common.AddressDiscriminated; + return IsAddressDiscriminated; } bool hasOtherDiscrimination() const { @@ -137,30 +103,32 @@ class PointerAuthSchema { Discrimination getOtherDiscrimination() const { assert(getKind() != Kind::None); - return Discrimination(Common.Discrimination); + return DiscriminationKind; } unsigned getKey() const { switch (getKind()) { - case Kind::None: llvm_unreachable("calling getKey() on disabled schema"); - case Kind::Soft: return unsigned(getSoftKey()); - case Kind::ARM8_3: return unsigned(getARM8_3Key()); + case Kind::None: + llvm_unreachable("calling getKey() on disabled schema"); + case Kind::Soft: + return unsigned(getSoftKey()); + case Kind::ARM8_3: + return unsigned(getARM8_3Key()); } llvm_unreachable("bad key kind"); } SoftKey getSoftKey() const { assert(getKind() == Kind::Soft); - return SoftKey(Soft.Key); + return SoftKey(Key); } ARM8_3Key getARM8_3Key() const { assert(getKind() == Kind::ARM8_3); - return ARM8_3Key(ARM8_3.Key); + return ARM8_3Key(Key); } }; - struct PointerAuthOptions { /// Do member function pointers to virtual functions need to be built /// as thunks? From eb9d2faaa3dc5583d49ffc3fd9055d3fd027e5f4 Mon Sep 17 00:00:00 2001 From: Erik Pilkington <erik.pilkington@gmail.com> Date: Thu, 21 Nov 2019 13:55:44 -0800 Subject: [PATCH 562/582] Add support for constant discriminators in PointerAuthSchema --- .../include/clang/Basic/PointerAuthOptions.h | 31 ++++++++++++++++--- clang/lib/CodeGen/CGPointerAuth.cpp | 3 ++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 7e69f17a456eb..936ba12148b8d 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -65,6 +65,9 @@ class PointerAuthSchema { /// Include a hash of the entity's identity. Decl, + + /// Discriminate using a constant value. + Constant, }; private: @@ -72,19 +75,34 @@ class PointerAuthSchema { unsigned IsAddressDiscriminated : 1; Discrimination DiscriminationKind : 2; unsigned Key : 3; + unsigned ConstantDiscriminator : 16; public: PointerAuthSchema() : TheKind(Kind::None) {} PointerAuthSchema(SoftKey key, bool isAddressDiscriminated, - Discrimination otherDiscrimination) + Discrimination otherDiscrimination, + Optional<uint16_t> constantDiscriminator = None) : TheKind(Kind::Soft), IsAddressDiscriminated(isAddressDiscriminated), - DiscriminationKind(otherDiscrimination), Key(unsigned(key)) {} + DiscriminationKind(otherDiscrimination), Key(unsigned(key)) { + assert((getOtherDiscrimination() != Discrimination::Constant || + constantDiscriminator) && + "constant discrimination requires a constant!"); + if (constantDiscriminator) + ConstantDiscriminator = *constantDiscriminator; + } PointerAuthSchema(ARM8_3Key key, bool isAddressDiscriminated, - Discrimination otherDiscrimination) + Discrimination otherDiscrimination, + Optional<uint16_t> constantDiscriminator = None) : TheKind(Kind::ARM8_3), IsAddressDiscriminated(isAddressDiscriminated), - DiscriminationKind(otherDiscrimination), Key(unsigned(key)) {} + DiscriminationKind(otherDiscrimination), Key(unsigned(key)) { + assert((getOtherDiscrimination() != Discrimination::Constant || + constantDiscriminator) && + "constant discrimination requires a constant!"); + if (constantDiscriminator) + ConstantDiscriminator = *constantDiscriminator; + } Kind getKind() const { return TheKind; } @@ -106,6 +124,11 @@ class PointerAuthSchema { return DiscriminationKind; } + uint16_t getConstantDiscrimination() const { + assert(getOtherDiscrimination() == Discrimination::Constant); + return (uint16_t)ConstantDiscriminator; + } + unsigned getKey() const { switch (getKind()) { case Kind::None: diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index f3debeaca0279..e9ad8d06463ae 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -50,6 +50,9 @@ CodeGenModule::getPointerAuthOtherDiscriminator(const PointerAuthSchema &schema, "declaration not provided for decl-discriminated schema"); return llvm::ConstantInt::get(IntPtrTy, getPointerAuthDeclDiscriminator(decl)); + + case PointerAuthSchema::Discrimination::Constant: + return llvm::ConstantInt::get(IntPtrTy, schema.getConstantDiscrimination()); } llvm_unreachable("bad discrimination kind"); } From 2fe2a3f923125e4c86bc5d140f327b5904a5bc97 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Wed, 11 Dec 2019 15:45:35 -0800 Subject: [PATCH 563/582] Fix build error. rdar://problem/57856413 --- clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 6715b504a7894..cfa4889017c9e 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -171,9 +171,9 @@ void ModuleDepCollectorPP::addModuleDep( for (const Module *Import : M->Imports) { if (Import->getTopLevelModule() != M->getTopLevelModule()) { if (AddedModules.insert(Import->getTopLevelModule()).second) - MD.ClangModuleDeps.push_back( - {Import->getTopLevelModuleName(), - Instance.getInvocation().getModuleHash()}); + MD.ClangModuleDeps.push_back({Import->getTopLevelModuleName(), + Instance.getInvocation().getModuleHash( + Instance.getDiagnostics())}); handleTopLevelModule(Import->getTopLevelModule()); } } From f2d0c6207fce05efbdb3e4f9ce6777bf5b731ead Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Thu, 12 Dec 2019 16:12:27 -0800 Subject: [PATCH 564/582] Fix check strings in test case Tail call elimination no longer runs at -O1 after the following commit: commit fd39b1bb20cec32c310ae9b6f1b4603c17a5f832 Author: Eric Christopher <echristo@gmail.com> Date: Tue Nov 26 20:28:52 2019 -0800 Revert "Revert "As a follow-up to my initial mail to llvm-dev here's a first pass at the O1 described there."" --- clang/test/CodeGenCXX/ptrauth-thunks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/CodeGenCXX/ptrauth-thunks.cpp b/clang/test/CodeGenCXX/ptrauth-thunks.cpp index 8bc0ba8fb6065..826a8fbb7fa6f 100644 --- a/clang/test/CodeGenCXX/ptrauth-thunks.cpp +++ b/clang/test/CodeGenCXX/ptrauth-thunks.cpp @@ -24,4 +24,4 @@ namespace Test1 { // CHECK-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test11DD0Ev(%"struct.Test1::D"* %this) // CHECK: %[[BitcastThis:.*]] = bitcast %"struct.Test1::D"* %this to i64* // CHECK: %[[SignedVTable:.*]] = load i64, i64* %[[BitcastThis]], align 8 -// CHECK: %[[VTable:.*]] = tail call i64 @llvm.ptrauth.auth.i64(i64 %[[SignedVTable]], i32 2, i64 0) \ No newline at end of file +// CHECK: %[[VTable:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[SignedVTable]], i32 2, i64 0) From 6201ae66b8f64f65095b64ce9270fb87fd2e6647 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Thu, 12 Dec 2019 15:57:38 -0800 Subject: [PATCH 565/582] Don't forget to parse ptrauth options when the input is a precompiled file rdar://problem/55410771 --- clang/lib/Frontend/CompilerInvocation.cpp | 18 +++++++++++------- clang/test/CodeGen/ptrauth.c | 11 +++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index eae4c4290b3d6..c567c638f72dd 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2310,6 +2310,15 @@ static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args, Opts.ModuleSearchPaths.push_back(A->getValue()); } +static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args) { + Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics); + Opts.PointerAuthCalls = Args.hasArg(OPT_fptrauth_calls); + Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns); + Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); + Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps); + Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, const llvm::Triple &T, PreprocessorOptions &PPOpts, @@ -3244,13 +3253,6 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, if (InlineArg->getOption().matches(options::OPT_fno_inline)) Opts.NoInlineDefine = true; - Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics); - Opts.PointerAuthCalls = Args.hasArg(OPT_fptrauth_calls); - Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns); - Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); - Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps); - Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); - Opts.FastMath = Args.hasArg(OPT_ffast_math) || Args.hasArg(OPT_cl_fast_relaxed_math); Opts.FiniteMathOnly = Args.hasArg(OPT_ffinite_math_only) || @@ -3660,6 +3662,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Res.getFileSystemOpts().WorkingDir); ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags); + ParsePointerAuthArgs(LangOpts, Args); + llvm::Triple T(Res.getTargetOpts().Triple); if (DashX.getFormat() == InputKind::Precompiled || DashX.getLanguage() == Language::LLVM_IR) { diff --git a/clang/test/CodeGen/ptrauth.c b/clang/test/CodeGen/ptrauth.c index 097e876647fd9..7ad8566a0f55c 100644 --- a/clang/test/CodeGen/ptrauth.c +++ b/clang/test/CodeGen/ptrauth.c @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=NOPCH %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK -check-prefix=PCH %s #define FNPTRKEY 0 @@ -82,9 +84,10 @@ struct InitiallyIncomplete; extern struct InitiallyIncomplete returns_initially_incomplete(void); // CHECK-LABEL: define void @use_while_incomplete() void use_while_incomplete() { - // CHECK: [[VAR:%.*]] = alloca {}*, - // CHECK-NEXT: store {}* bitcast ({ i8*, i32, i64, i64 }* @returns_initially_incomplete.ptrauth to {}*), {}** [[VAR]], - // CHECK-NEXT: ret void + // NOPCH: [[VAR:%.*]] = alloca {}*, + // NOPCH-NEXT: store {}* bitcast ({ i8*, i32, i64, i64 }* @returns_initially_incomplete.ptrauth to {}*), {}** [[VAR]], + // PCH: [[VAR:%.*]] = alloca i64 ()*, + // PCH-NEXT: store i64 ()* bitcast ({ i8*, i32, i64, i64 }* @returns_initially_incomplete.ptrauth to i64 ()*), i64 ()** [[VAR]], struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; } struct InitiallyIncomplete { int x; }; From a94321f13043a39b02fa7106b8270767651354cd Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Mon, 16 Dec 2019 20:31:25 -0500 Subject: [PATCH 566/582] Abstract serialization fixes for the Apple Clang changes. --- clang/include/clang/AST/AbstractBasicReader.h | 4 ++-- clang/include/clang/AST/AbstractBasicWriter.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h index d7b3a9da88ec2..1ee63d0640ca0 100644 --- a/clang/include/clang/AST/AbstractBasicReader.h +++ b/clang/include/clang/AST/AbstractBasicReader.h @@ -178,9 +178,9 @@ class DataStreamBasicReader : public BasicReaderBase<Impl> { } Qualifiers readQualifiers() { - static_assert(sizeof(Qualifiers().getAsOpaqueValue()) <= sizeof(uint32_t), + static_assert(sizeof(Qualifiers().getAsOpaqueValue()) <= sizeof(uint64_t), "update this if the value size changes"); - uint32_t value = asImpl().readUInt32(); + uint64_t value = asImpl().readUInt64(); return Qualifiers::fromOpaqueValue(value); } diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h index 0a6730c86bbfe..f16add0c96cd1 100644 --- a/clang/include/clang/AST/AbstractBasicWriter.h +++ b/clang/include/clang/AST/AbstractBasicWriter.h @@ -164,9 +164,9 @@ class DataStreamBasicWriter : public BasicWriterBase<Impl> { } void writeQualifiers(Qualifiers value) { - static_assert(sizeof(value.getAsOpaqueValue()) <= sizeof(uint32_t), + static_assert(sizeof(value.getAsOpaqueValue()) <= sizeof(uint64_t), "update this if the value size changes"); - asImpl().writeUInt32(value.getAsOpaqueValue()); + asImpl().writeUInt64(value.getAsOpaqueValue()); } void writeExceptionSpecInfo( From 751c0ed2bc855ba8e9d1882dcb72bfe8cb9c816d Mon Sep 17 00:00:00 2001 From: John McCall <rjmccall@apple.com> Date: Mon, 16 Dec 2019 21:15:55 -0500 Subject: [PATCH 567/582] Apparently they corrected the spelling in trunk. --- clang/lib/Index/IndexingAction.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 850deb1c0a309..d9c4f97809771 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -245,9 +245,9 @@ class IndexDataRecorder : public IndexDataConsumer { bool record_empty() const { return RecordByFile.empty(); } private: - bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, - ArrayRef<SymbolRelation> Relations, - SourceLocation Loc, ASTNodeInfo ASTNode) override { + bool handleDeclOccurrence(const Decl *D, SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) override { SourceManager &SM = PP->getSourceManager(); Loc = SM.getFileLoc(Loc); if (Loc.isInvalid()) From a0544ee8925688f09ee344c3f75f054eb6f696de Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Wed, 18 Dec 2019 13:43:14 -0800 Subject: [PATCH 568/582] Use hasOffsetApplied to initialize member HasOffsetApplied --- clang/lib/CodeGen/CGExprConstant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index ae930da18a641..332ef402c5a07 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1760,7 +1760,7 @@ struct ConstantLValue { /*implicit*/ ConstantLValue(llvm::Constant *value, bool hasOffsetApplied = false, bool hasDestPointerAuth = false) - : Value(value), HasOffsetApplied(false), + : Value(value), HasOffsetApplied(hasOffsetApplied), HasDestPointerAuth(hasDestPointerAuth) {} /*implicit*/ ConstantLValue(ConstantAddress address) From 4a4ae8ba5058b8c98b98523a1aa851441508a6e1 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahatanaka@apple.com> Date: Thu, 19 Dec 2019 23:49:33 -0800 Subject: [PATCH 569/582] Include ptrauth key and diversity information in the mangled name of non-trivial C copy constructors rdar://problem/58101919 --- clang/lib/CodeGen/CGNonTrivialStruct.cpp | 3 ++ clang/test/CodeGen/ptrauth-in-c-struct.c | 57 +++++++++++++++++++----- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/clang/lib/CodeGen/CGNonTrivialStruct.cpp b/clang/lib/CodeGen/CGNonTrivialStruct.cpp index fca24607c56d7..4d0992410478a 100644 --- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp +++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp @@ -265,6 +265,9 @@ struct GenBinaryFuncName : CopyStructVisitor<GenBinaryFuncName<IsMove>, IsMove>, void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStructOffset) { this->appendStr("_pa"); + PointerAuthQualifier PtrAuth = FT.getPointerAuth(); + this->appendStr(llvm::to_string(PtrAuth.getKey()) + "_"); + this->appendStr(llvm::to_string(PtrAuth.getExtraDiscriminator()) + "_"); CharUnits FieldOffset = CurStructOffset + this->getFieldOffset(FD); this->appendStr(llvm::to_string(FieldOffset.getQuantity())); } diff --git a/clang/test/CodeGen/ptrauth-in-c-struct.c b/clang/test/CodeGen/ptrauth-in-c-struct.c index c1919f981c62a..a6fd4ba2bd7c9 100644 --- a/clang/test/CodeGen/ptrauth-in-c-struct.c +++ b/clang/test/CodeGen/ptrauth-in-c-struct.c @@ -1,18 +1,25 @@ // RUN: %clang_cc1 -triple arm64-apple-ios -fblocks -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -o - %s | FileCheck %s -#define AQ __ptrauth(1,1,50) +#define AQ1_50 __ptrauth(1,1,50) +#define AQ2_30 __ptrauth(2,1,30) #define IQ __ptrauth(1,0,50) typedef void (^BlockTy)(void); // CHECK: %[[STRUCT_SA:.*]] = type { i32, i32* } +// CHECK: %[[STRUCT_SA2:.*]] = type { i32, i32* } // CHECK: %[[STRUCT_SI:.*]] = type { i32* } typedef struct { int f0; - int * AQ f1; // Signed using address discrimination. + int * AQ1_50 f1; // Signed using address discrimination. } SA; +typedef struct { + int f0; + int * AQ2_30 f1; // Signed using address discrimination. +} SA2; + typedef struct { int * IQ f; // No address discrimination. } SI; @@ -21,9 +28,9 @@ SA getSA(void); void calleeSA(SA); // CHECK: define void @test_copy_constructor_SA(%[[STRUCT_SA]]* %{{.*}}) -// CHECK: call void @__copy_constructor_8_8_t0w4_pa8( +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8( -// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa1_50_8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) // CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 // CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 // CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 @@ -48,10 +55,38 @@ void test_copy_constructor_SA(SA *s) { SA t = *s; } +// CHECK: define void @test_copy_constructor_SA2(%[[STRUCT_SA2]]* %{{.*}}) +// CHECK: call void @__copy_constructor_8_8_t0w4_pa2_30_8( + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa2_30_8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// CHECK: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// CHECK: %[[V11:.*]] = load i8*, i8** %[[V10]], align 8 +// CHECK: %[[V12:.*]] = ptrtoint i8** %[[V10]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V12]], i64 30) +// CHECK: %[[V14:.*]] = ptrtoint i8** %[[V7]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V14]], i64 30) +// CHECK: %[[V17:.*]] = ptrtoint i8* %[[V11]] to i64 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V17]], i32 2, i64 %[[V13]], i32 2, i64 %[[V15]]) + +void test_copy_constructor_SA2(SA2 *s) { + SA2 t = *s; +} + // CHECK: define void @test_copy_assignment_SA( -// CHECK: call void @__copy_assignment_8_8_t0w4_pa8( +// CHECK: call void @__copy_assignment_8_8_t0w4_pa1_50_8( -// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_pa8( +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_pa1_50_8( void test_copy_assignment_SA(SA *d, SA *s) { *d = *s; @@ -59,7 +94,7 @@ void test_copy_assignment_SA(SA *d, SA *s) { // CHECK: define void @test_move_constructor_SA( // CHECK: define internal void @__Block_byref_object_copy_( -// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_t0w4_pa8( +// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_t0w4_pa1_50_8( void test_move_constructor_SA(void) { __block SA t; @@ -67,8 +102,8 @@ void test_move_constructor_SA(void) { } // CHECK: define void @test_move_assignment_SA( -// CHECK: call void @__move_assignment_8_8_t0w4_pa8( -// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_pa8( +// CHECK: call void @__move_assignment_8_8_t0w4_pa1_50_8( +// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_pa1_50_8( void test_move_assignment_SA(SA *p) { *p = getSA(); @@ -88,7 +123,7 @@ void test_parameter_SA(SA a) { // CHECK: %[[V0:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[A_ADDR]], align 8 // CHECK: %[[V1:.*]] = bitcast %[[STRUCT_SA]]* %[[AGG_TMP]] to i8** // CHECK: %[[V2:.*]] = bitcast %[[STRUCT_SA]]* %[[V0]] to i8** -// CHECK: call void @__copy_constructor_8_8_t0w4_pa8(i8** %[[V1]], i8** %[[V2]]) #5 +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8(i8** %[[V1]], i8** %[[V2]]) #5 // CHECK: call void @calleeSA(%[[STRUCT_SA]]* %[[AGG_TMP]]) // CHECK-NOT: call // CHECK: ret void @@ -103,7 +138,7 @@ void test_argument_SA(SA *a) { // CHECK: %[[V0:.*]] = load %[[STRUCT_SA]]*, %[[STRUCT_SA]]** %[[A_ADDR]], align 8 // CHECK: %[[V1:.*]] = bitcast %[[STRUCT_SA]]* %[[AGG_RESULT]] to i8** // CHECK: %[[V2:.*]] = bitcast %[[STRUCT_SA]]* %[[V0]] to i8** -// CHECK: call void @__copy_constructor_8_8_t0w4_pa8(i8** %[[V1]], i8** %[[V2]]) #5 +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8(i8** %[[V1]], i8** %[[V2]]) #5 // CHECK-NOT: call // CHECK: ret void From dfbcbcc8a57ad781503f2cee555aac3ded226ad5 Mon Sep 17 00:00:00 2001 From: Tim Northover <t.p.northover@gmail.com> Date: Wed, 8 Jan 2020 13:08:39 +0000 Subject: [PATCH 570/582] AArch64: move Apple codename CPUs to be based on the OSS numbered definitions. --- llvm/lib/Target/AArch64/AArch64.td | 43 ++------------------ llvm/lib/Target/AArch64/AArch64Subtarget.cpp | 2 - llvm/lib/Target/AArch64/AArch64Subtarget.h | 2 - 3 files changed, 3 insertions(+), 44 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64.td b/llvm/lib/Target/AArch64/AArch64.td index da5449bbfcfda..af515c419db4c 100644 --- a/llvm/lib/Target/AArch64/AArch64.td +++ b/llvm/lib/Target/AArch64/AArch64.td @@ -582,43 +582,6 @@ def ProcAppleA7 : SubtargetFeature<"apple-a7", "ARMProcFamily", "AppleA7", FeatureZCZeroingFPWorkaround ]>; -def ProcVortex : SubtargetFeature<"vortex", "ARMProcFamily", "Vortex", - "Vortex", [ - FeatureAlternateSExtLoadCVTF32Pattern, - FeatureArithmeticBccFusion, - FeatureArithmeticCbzFusion, - FeatureCrypto, - FeatureDisableLatencySchedHeuristic, - FeatureFullFP16, - FeatureFuseAES, - FeatureFuseCryptoEOR, - FeatureNEON, - FeaturePerfMon, - FeatureZCRegMove, - FeatureZCZeroing, - HasV8_3aOps - ]>; - -def ProcLightning : SubtargetFeature<"lightning", "ARMProcFamily", "Lightning", - "Lightning", [ - FeatureAlternateSExtLoadCVTF32Pattern, - FeatureArithmeticBccFusion, - FeatureArithmeticCbzFusion, - FeatureCrypto, - FeatureDisableLatencySchedHeuristic, - FeatureFP16FML, - FeatureFullFP16, - FeatureFuseAES, - FeatureFuseCryptoEOR, - FeatureNEON, - FeaturePerfMon, - FeatureSHA3, - FeatureSM4, - FeatureZCRegMove, - FeatureZCZeroing, - HasV8_4aOps - ]>; - def ProcAppleA10 : SubtargetFeature<"apple-a10", "ARMProcFamily", "AppleA10", "Apple A10", [ FeatureAlternateSExtLoadCVTF32Pattern, @@ -918,10 +881,10 @@ def : ProcessorModel<"thunderx2t99", ThunderX2T99Model, [ProcThunderX2T99]>; // FIXME: HiSilicon TSV110 is currently modeled as a Cortex-A57. def : ProcessorModel<"tsv110", CortexA57Model, [ProcTSV110]>; -// Support cyclone as an alias for apple-a7 so we can still LTO old bitcode. +// Support some CPU codenames as aliases for apple-aN so we can still LTO old bitcode. def : ProcessorModel<"cyclone", CycloneModel, [ProcAppleA7]>; -def : ProcessorModel<"vortex", CycloneModel, [ProcVortex]>; -def : ProcessorModel<"lightning", CycloneModel, [ProcLightning]>; +def : ProcessorModel<"vortex", CycloneModel, [ProcAppleA12]>; +def : ProcessorModel<"lightning", CycloneModel, [ProcAppleA13]>; // iPhone and iPad CPUs def : ProcessorModel<"apple-a7", CycloneModel, [ProcAppleA7]>; diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp index 9d3baa2c068db..3636d8d2b628c 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp @@ -88,8 +88,6 @@ void AArch64Subtarget::initializeProperties() { case CortexA76: PrefFunctionLogAlignment = 4; break; - case Vortex: - case Lightning: case AppleA7: case AppleA10: case AppleA11: diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h index 5d5968f26f7e4..7977dc18498e4 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.h +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -55,7 +55,6 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo { ExynosM3, Falkor, Kryo, - Lightning, NeoverseE1, NeoverseN1, Saphira, @@ -65,7 +64,6 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo { ThunderXT83, ThunderXT88, TSV110, - Vortex }; protected: From df42f6946f514b91fc2ab0f956ee3cd14a7985bf Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Tue, 7 Jan 2020 15:09:23 -0800 Subject: [PATCH 571/582] [macCatalyst] add clang driver support for macCatalyst --- .../clang/Basic/DiagnosticDriverKinds.td | 2 + clang/include/clang/Driver/DarwinSDKInfo.h | 15 ++ clang/lib/Driver/DarwinSDKInfo.cpp | 44 ++++- clang/lib/Driver/ToolChains/Darwin.cpp | 184 ++++++++++++------ clang/lib/Driver/ToolChains/Darwin.h | 39 ++-- .../SDKSettings.json | 25 +++ .../SDKSettings.json | 13 ++ .../darwin-ld-platform-version-maccatalyst.c | 12 ++ clang/test/Driver/darwin-ld.c | 12 ++ .../darwin-mac-catalyst-32bit-not-supported.c | 4 + clang/test/Driver/darwin-maccatalyst.c | 6 + clang/test/Driver/darwin-objc-defaults.m | 8 + .../Driver/darwin-objc-runtime-maccatalyst.m | 13 ++ clang/test/Driver/darwin-sanitizer-ld.c | 12 ++ .../Driver/darwin-sdk-version-maccatalyst.c | 4 + 15 files changed, 318 insertions(+), 75 deletions(-) create mode 100644 clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json create mode 100644 clang/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json create mode 100644 clang/test/Driver/darwin-ld-platform-version-maccatalyst.c create mode 100644 clang/test/Driver/darwin-mac-catalyst-32bit-not-supported.c create mode 100644 clang/test/Driver/darwin-maccatalyst.c create mode 100644 clang/test/Driver/darwin-objc-runtime-maccatalyst.m create mode 100644 clang/test/Driver/darwin-sdk-version-maccatalyst.c diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 39242c972ea28..fbce69f109cbc 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -206,6 +206,8 @@ def warn_invalid_ios_deployment_target : Warning< "invalid iOS deployment version '%0', iOS 10 is the maximum deployment " "target for 32-bit targets">, InGroup<InvalidIOSDeploymentTarget>, DefaultError; +def err_invalid_macos_32bit_deployment_target : Error< + "32-bit targets are not supported when building for Mac Catalyst">; def err_drv_conflicting_deployment_targets : Error< "conflicting deployment targets, both '%0' and '%1' are present in environment">; def err_arc_unsupported_on_runtime : Error< diff --git a/clang/include/clang/Driver/DarwinSDKInfo.h b/clang/include/clang/Driver/DarwinSDKInfo.h index f7075a8d3b7f9..3f8ae7391e2de 100644 --- a/clang/include/clang/Driver/DarwinSDKInfo.h +++ b/clang/include/clang/Driver/DarwinSDKInfo.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_DRIVER_DARWIN_SDK_INFO_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Support/Error.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualFileSystem.h" @@ -20,12 +21,26 @@ namespace driver { /// The information about the darwin SDK that was used during this compilation. class DarwinSDKInfo { public: + /// Represents the mapping between macOS and IosMac versions. + class IOSMacVersionMapping { + public: + VersionTuple getiOSMacVersionForMacOSVersion(); + + llvm::StringMap<VersionTuple> MacOS2iOSMacMapping; + llvm::StringMap<VersionTuple> IosMac2macOSMapping; + }; + DarwinSDKInfo(llvm::VersionTuple Version) : Version(Version) {} const llvm::VersionTuple &getVersion() const { return Version; } + IOSMacVersionMapping &getVersionMap() { return VersionMap; } + + const IOSMacVersionMapping &getVersionMap() const { return VersionMap; } + private: llvm::VersionTuple Version; + IOSMacVersionMapping VersionMap; }; /// Parse the SDK information from the SDKSettings.json file. diff --git a/clang/lib/Driver/DarwinSDKInfo.cpp b/clang/lib/Driver/DarwinSDKInfo.cpp index 761c6717266bf..06e2d438d09c9 100644 --- a/clang/lib/Driver/DarwinSDKInfo.cpp +++ b/clang/lib/Driver/DarwinSDKInfo.cpp @@ -15,6 +15,42 @@ using namespace clang::driver; using namespace clang; +static Optional<DarwinSDKInfo> +parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj) { + auto VersionString = Obj->getString("Version"); + if (!VersionString) + return None; + VersionTuple Version; + if (Version.tryParse(*VersionString)) + return None; + DarwinSDKInfo SDKInfo(Version); + if (const auto *VM = Obj->getObject("VersionMap")) { + auto parseVersionMap = [](const llvm::json::Object &Obj, + llvm::StringMap<VersionTuple> &Mapping) -> bool { + for (const auto &KV : Obj) { + if (auto Val = KV.getSecond().getAsString()) { + llvm::VersionTuple Version; + if (Version.tryParse(*Val)) + return true; + Mapping[KV.getFirst()] = Version; + } + } + return false; + }; + if (const auto *Mapping = VM->getObject("macOS_iOSMac")) { + if (parseVersionMap(*Mapping, + SDKInfo.getVersionMap().MacOS2iOSMacMapping)) + return None; + } + if (const auto *Mapping = VM->getObject("iOSMac_macOS")) { + if (parseVersionMap(*Mapping, + SDKInfo.getVersionMap().IosMac2macOSMapping)) + return None; + } + } + return std::move(SDKInfo); +} + Expected<Optional<DarwinSDKInfo>> driver::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { llvm::SmallString<256> Filepath = SDKRootPath; @@ -31,12 +67,8 @@ driver::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { return Result.takeError(); if (const auto *Obj = Result->getAsObject()) { - auto VersionString = Obj->getString("Version"); - if (VersionString) { - VersionTuple Version; - if (!Version.tryParse(*VersionString)) - return DarwinSDKInfo(Version); - } + if (auto SDKInfo = parseDarwinSDKSettingsJSON(Obj)) + return std::move(SDKInfo); } return llvm::make_error<llvm::StringError>("invalid SDKSettings.json", llvm::inconvertibleErrorCode()); diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 344a14fe1ea7c..892e352ce7474 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -754,9 +754,9 @@ bool MachO::HasNativeLLVMSupport() const { return true; } ToolChain::CXXStdlibType Darwin::GetDefaultCXXStdlibType() const { // Default to use libc++ on OS X 10.9+ and iOS 7+. - if ((isTargetMacOS() && !isMacosxVersionLT(10, 9)) || - (isTargetIOSBased() && !isIPhoneOSVersionLT(7, 0)) || - isTargetWatchOSBased()) + if ((isTargetMacOSBased() && !isMacosxVersionLT(10, 9)) || + (isTargetIOSBased() && !isIPhoneOSVersionLT(7, 0)) || + isTargetWatchOSBased()) return ToolChain::CST_Libcxx; return ToolChain::CST_Libstdcxx; @@ -780,7 +780,7 @@ bool Darwin::hasBlocksRuntime() const { else if (isTargetIOSBased()) return !isIPhoneOSVersionLT(3, 2); else { - assert(isTargetMacOS() && "unexpected darwin target"); + assert(isTargetMacOSBased() && "unexpected darwin target"); return !isMacosxVersionLT(10, 6); } } @@ -873,13 +873,12 @@ std::string Darwin::ComputeEffectiveClangTriple(const ArgList &Args, Str += "watchos"; else if (isTargetTvOSBased()) Str += "tvos"; - else if (isTargetIOSBased()) + else if (isTargetIOSBased() || isTargetMacABI()) Str += "ios"; else Str += "macosx"; - Str += getTargetVersion().getAsString(); + Str += getOSTargetVersion().getAsString(); Triple.setOSName(Str); - return Triple.getTriple(); } @@ -922,7 +921,7 @@ void DarwinClang::addClangWarningOptions(ArgStringList &CC1Args) const { // For iOS and watchOS, also error about implicit function declarations, // as that can impact calling conventions. - if (!isTargetMacOS()) + if (!isTargetMacOSBased()) CC1Args.push_back("-Werror=implicit-function-declaration"); } } @@ -942,7 +941,7 @@ static StringRef getXcodeDeveloperPath(StringRef PathIntoXcode) { void DarwinClang::AddLinkARCArgs(const ArgList &Args, ArgStringList &CmdArgs) const { // Avoid linking compatibility stubs on i386 mac. - if (isTargetMacOS() && getArch() == llvm::Triple::x86) + if (isTargetMacOSBased() && getArch() == llvm::Triple::x86) return; ObjCRuntime runtime = getDefaultObjCRuntime(/*nonfragile*/ true); @@ -995,7 +994,7 @@ void DarwinClang::AddLinkARCArgs(const ArgList &Args, unsigned DarwinClang::GetDefaultDwarfVersion() const { // Default to use DWARF 2 on OS X 10.10 / iOS 8 and lower. - if ((isTargetMacOS() && isMacosxVersionLT(10, 11)) || + if ((isTargetMacOSBased() && isMacosxVersionLT(10, 11)) || (isTargetIOSBased() && isIPhoneOSVersionLT(9))) return 2; return 4; @@ -1083,6 +1082,8 @@ StringRef Darwin::getOSLibraryNameSuffix(bool IgnoreSim) const { case DarwinPlatformKind::MacOS: return "osx"; case DarwinPlatformKind::IPhoneOS: + if (TargetEnvironment == MacABI) + return "osx"; return TargetEnvironment == NativeEnvironment || IgnoreSim ? "ios" : "iossim"; case DarwinPlatformKind::TvOS: @@ -1307,6 +1308,12 @@ struct DarwinPlatform { DarwinEnvironmentKind getEnvironment() const { return Environment; } + VersionTuple getNativeTargetVersion() const { + assert(Environment == DarwinEnvironmentKind::MacABI && + "native target version is specified only for macabi"); + return NativeTargetVersion; + } + void setEnvironment(DarwinEnvironmentKind Kind) { Environment = Kind; InferSimulatorFromArch = false; @@ -1371,21 +1378,38 @@ struct DarwinPlatform { llvm_unreachable("Unsupported Darwin Source Kind"); } - static DarwinPlatform createFromTarget(const llvm::Triple &TT, - StringRef OSVersion, Arg *A) { + static DarwinPlatform + createFromTarget(const llvm::Triple &TT, StringRef OSVersion, Arg *A, + const Optional<DarwinSDKInfo> &SDKInfo) { DarwinPlatform Result(TargetArg, getPlatformFromOS(TT.getOS()), OSVersion, A); + unsigned Major, Minor, Micro; + TT.getOSVersion(Major, Minor, Micro); + if (Major == 0) + Result.HasOSVersion = false; switch (TT.getEnvironment()) { case llvm::Triple::Simulator: Result.Environment = DarwinEnvironmentKind::Simulator; break; + case llvm::Triple::MacABI: { + auto NativeTargetVersion = VersionTuple(10, 15); + if (Result.HasOSVersion && SDKInfo) { + const auto &VM = SDKInfo->getVersionMap().IosMac2macOSMapping; + // FIXME: make it a more robust lookup when upstreaming. + VersionTuple VV = Micro ? VersionTuple(Major, Minor, Micro) + : VersionTuple(Major, Minor); + std::string VersionString = VV.getAsString(); + auto It = VM.find(VersionString); + if (It != VM.end()) + NativeTargetVersion = It->getValue(); + } + Result.Environment = DarwinEnvironmentKind::MacABI; + Result.NativeTargetVersion = NativeTargetVersion; + break; + } default: break; } - unsigned Major, Minor, Micro; - TT.getOSVersion(Major, Minor, Micro); - if (Major == 0) - Result.HasOSVersion = false; return Result; } static DarwinPlatform createOSVersionArg(DarwinPlatformKind Platform, @@ -1451,6 +1475,7 @@ struct DarwinPlatform { SourceKind Kind; DarwinPlatformKind Platform; DarwinEnvironmentKind Environment = DarwinEnvironmentKind::NativeEnvironment; + VersionTuple NativeTargetVersion; std::string OSVersion; bool HasOSVersion = true, InferSimulatorFromArch = true; Arg *Argument; @@ -1660,7 +1685,8 @@ inferDeploymentTargetFromArch(DerivedArgList &Args, const Darwin &Toolchain, /// Returns the deployment target that's specified using the -target option. Optional<DarwinPlatform> getDeploymentTargetFromTargetArg( - DerivedArgList &Args, const llvm::Triple &Triple, const Driver &TheDriver) { + DerivedArgList &Args, const llvm::Triple &Triple, const Driver &TheDriver, + const Optional<DarwinSDKInfo> &SDKInfo) { if (!Args.hasArg(options::OPT_target)) return None; if (Triple.getOS() == llvm::Triple::Darwin || @@ -1668,7 +1694,8 @@ Optional<DarwinPlatform> getDeploymentTargetFromTargetArg( return None; std::string OSVersion = getOSVersion(Triple.getOS(), Triple, TheDriver); return DarwinPlatform::createFromTarget(Triple, OSVersion, - Args.getLastArg(options::OPT_target)); + Args.getLastArg(options::OPT_target), + SDKInfo); } Optional<DarwinSDKInfo> parseSDKSettings(llvm::vfs::FileSystem &VFS, @@ -1717,7 +1744,7 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { // The OS and the version can be specified using the -target argument. Optional<DarwinPlatform> OSTarget = - getDeploymentTargetFromTargetArg(Args, getTriple(), getDriver()); + getDeploymentTargetFromTargetArg(Args, getTriple(), getDriver(), SDKInfo); if (OSTarget) { Optional<DarwinPlatform> OSVersionArgTarget = getDeploymentTargetFromOSVersionArg(Args, getDriver()); @@ -1808,8 +1835,11 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { if (getTriple().isArch32Bit() && Major >= 11) { // If the deployment target is explicitly specified, print a diagnostic. if (OSTarget->isExplicitlySpecified()) { - getDriver().Diag(diag::warn_invalid_ios_deployment_target) - << OSTarget->getAsString(Args, Opts); + if (OSTarget->getEnvironment() == MacABI) + getDriver().Diag(diag::err_invalid_macos_32bit_deployment_target); + else + getDriver().Diag(diag::warn_invalid_ios_deployment_target) + << OSTarget->getAsString(Args, Opts); // Otherwise, set it to 10.99.99. } else { Major = 10; @@ -1838,14 +1868,19 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { OSTarget->canInferSimulatorFromArch() && getTriple().isX86()) Environment = Simulator; - setTarget(Platform, Environment, Major, Minor, Micro); + VersionTuple NativeTargetVersion; + if (Environment == MacABI) + NativeTargetVersion = OSTarget->getNativeTargetVersion(); + setTarget(Platform, Environment, Major, Minor, Micro, NativeTargetVersion); if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { StringRef SDK = getSDKName(A->getValue()); if (SDK.size() > 0) { size_t StartVer = SDK.find_first_of("0123456789"); StringRef SDKName = SDK.slice(0, StartVer); - if (!SDKName.startswith(getPlatformFamily())) + // Don't warn about the macabi SDK. + // FIXME: Can we warn here? + if (!SDKName.startswith(getPlatformFamily()) && Environment != MacABI) getDriver().Diag(diag::warn_incompatible_sysroot) << SDKName << getPlatformFamily(); } @@ -2333,6 +2368,8 @@ void MachO::AddLinkRuntimeLibArgs(const ArgList &Args, bool Darwin::isAlignedAllocationUnavailable() const { llvm::Triple::OSType OS; + if (isTargetMacABI()) + return TargetVersion < alignedAllocMinVersion(llvm::Triple::MacOSX); switch (TargetPlatform) { case MacOS: // Earlier than 10.13. OS = llvm::Triple::MacOSX; @@ -2362,11 +2399,26 @@ void Darwin::addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, CC1Args.push_back("-faligned-alloc-unavailable"); if (SDKInfo) { + VersionTuple TargetVariantSDKVersion; + bool UseTVSDKVersionAsPrimary = + getTriple().getOS() == llvm::Triple::IOS && + getTriple().getEnvironment() == llvm::Triple::MacABI; + if (UseTVSDKVersionAsPrimary) { + const auto &VM = SDKInfo->getVersionMap().MacOS2iOSMacMapping; + std::string VersionString = SDKInfo->getVersion().getAsString(); + auto It = VM.find(VersionString); + if (It != VM.end()) + TargetVariantSDKVersion = It->getValue(); + } + /// Pass the SDK version to the compiler when the SDK information is /// available. std::string Arg; llvm::raw_string_ostream OS(Arg); - OS << "-target-sdk-version=" << SDKInfo->getVersion(); + VersionTuple SDKVersion = SDKInfo->getVersion(); + if (UseTVSDKVersionAsPrimary) + std::swap(SDKVersion, TargetVariantSDKVersion); + OS << "-target-sdk-version=" << SDKVersion; CC1Args.push_back(DriverArgs.MakeArgString(OS.str())); } } @@ -2492,7 +2544,7 @@ bool MachO::SupportsProfiling() const { void Darwin::addMinVersionArgs(const ArgList &Args, ArgStringList &CmdArgs) const { - VersionTuple TargetVersion = getTargetVersion(); + VersionTuple TargetVersion = getOSTargetVersion(); if (isTargetWatchOS()) CmdArgs.push_back("-watchos_version_min"); @@ -2504,10 +2556,12 @@ void Darwin::addMinVersionArgs(const ArgList &Args, CmdArgs.push_back("-tvos_simulator_version_min"); else if (isTargetIOSSimulator()) CmdArgs.push_back("-ios_simulator_version_min"); + else if (isTargetMacABI()) + CmdArgs.push_back("-maccatalyst_version_min"); else if (isTargetIOSBased()) CmdArgs.push_back("-iphoneos_version_min"); else { - assert(isTargetMacOS() && "unexpected target"); + assert(isTargetMacOSBased() && "unexpected target"); CmdArgs.push_back("-macosx_version_min"); } @@ -2520,11 +2574,9 @@ static const char *getPlatformName(Darwin::DarwinPlatformKind Platform, case Darwin::MacOS: return "macos"; case Darwin::IPhoneOS: - if (Environment == Darwin::NativeEnvironment || - Environment == Darwin::Simulator) - return "ios"; - // FIXME: Add macCatalyst support here ("\"mac catalyst\""). - llvm_unreachable("macCatalyst isn't yet supported"); + if (Environment == Darwin::MacABI) + return "mac catalyst"; + return "ios"; case Darwin::TvOS: return "tvos"; case Darwin::WatchOS: @@ -2535,22 +2587,41 @@ static const char *getPlatformName(Darwin::DarwinPlatformKind Platform, void Darwin::addPlatformVersionArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const { - // -platform_version <platform> <target_version> <sdk_version> - // Both the target and SDK version support only up to 3 components. - CmdArgs.push_back("-platform_version"); - std::string PlatformName = getPlatformName(TargetPlatform, TargetEnvironment); - if (TargetEnvironment == Darwin::Simulator) - PlatformName += "-simulator"; - CmdArgs.push_back(Args.MakeArgString(PlatformName)); - VersionTuple TargetVersion = getTargetVersion().withoutBuild(); - CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); - if (SDKInfo) { - VersionTuple SDKVersion = SDKInfo->getVersion().withoutBuild(); - CmdArgs.push_back(Args.MakeArgString(SDKVersion.getAsString())); - } else { - // Use a blank SDK version if it's not present. - CmdArgs.push_back("0.0.0"); - } + auto EmitPlatformVersionArg = [&](const VersionTuple &TV, + Darwin::DarwinPlatformKind Platform, + Darwin::DarwinEnvironmentKind Environment) { + // -platform_version <platform> <target_version> <sdk_version> + // Both the target and SDK version support only up to 3 components. + CmdArgs.push_back("-platform_version"); + std::string PlatformName = getPlatformName(Platform, Environment); + if (Environment == Darwin::Simulator) + PlatformName += "-simulator"; + CmdArgs.push_back(Args.MakeArgString(PlatformName)); + VersionTuple TargetVersion = TV.withoutBuild(); + // FIXME: This should be an error. + if (Platform == Darwin::IPhoneOS && Environment == Darwin::MacABI && + TargetVersion.getMajor() < 13) + TargetVersion = VersionTuple(13, 0); + CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); + if (SDKInfo) { + VersionTuple SDKVersion = SDKInfo->getVersion().withoutBuild(); + // Use the appropriate SDK version for macCatalyst. + if (Platform == Darwin::IPhoneOS && Environment == Darwin::MacABI) { + const auto &VM = SDKInfo->getVersionMap().MacOS2iOSMacMapping; + auto It = VM.find(SDKVersion.getAsString()); + if (It != VM.end()) + SDKVersion = It->getValue(); + else + SDKVersion = VersionTuple(0, 0, 0); + } + CmdArgs.push_back(Args.MakeArgString(SDKVersion.getAsString())); + } else { + // Use a blank SDK version if it's not present. + CmdArgs.push_back("0.0.0"); + } + }; + EmitPlatformVersionArg(getOSTargetVersion(), TargetPlatform, + TargetEnvironment); } void Darwin::addStartObjectFileArgs(const ArgList &Args, @@ -2589,7 +2660,7 @@ void Darwin::addStartObjectFileArgs(const ArgList &Args, } } else { if (Args.hasArg(options::OPT_pg) && SupportsProfiling()) { - if (isTargetMacOS() && isMacosxVersionLT(10, 9)) { + if (isTargetMacOSBased() && isMacosxVersionLT(10, 9)) { if (Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_object) || Args.hasArg(options::OPT_preload)) { @@ -2604,11 +2675,11 @@ void Darwin::addStartObjectFileArgs(const ArgList &Args, // when compiling with -pg, we need to link with the gcrt1.o file, // so pass the -no_new_main option to tell the linker to use the // "start" symbol as the entry point. - if (isTargetMacOS() && !isMacosxVersionLT(10, 8)) + if (isTargetMacOSBased() && !isMacosxVersionLT(10, 8)) CmdArgs.push_back("-no_new_main"); } else { getDriver().Diag(diag::err_drv_clang_unsupported_opt_pg_darwin) - << isTargetMacOS(); + << isTargetMacOSBased(); } } else { if (Args.hasArg(options::OPT_static) || @@ -2652,7 +2723,7 @@ void Darwin::addStartObjectFileArgs(const ArgList &Args, void Darwin::CheckObjCARC() const { if (isTargetIOSBased() || isTargetWatchOSBased() || - (isTargetMacOS() && !isMacosxVersionLT(10, 6))) + (isTargetMacOSBased() && !isMacosxVersionLT(10, 6))) return; getDriver().Diag(diag::err_arc_unsupported_on_toolchain); } @@ -2671,16 +2742,13 @@ SanitizerMask Darwin::getSupportedSanitizers() const { // Prior to 10.9, macOS shipped a version of the C++ standard library without // C++11 support. The same is true of iOS prior to version 5. These OS'es are // incompatible with -fsanitize=vptr. - if (!(isTargetMacOS() && isMacosxVersionLT(10, 9)) + if (!(isTargetMacOSBased() && isMacosxVersionLT(10, 9)) && !(isTargetIPhoneOS() && isIPhoneOSVersionLT(5, 0))) Res |= SanitizerKind::Vptr; - if (isTargetMacOS()) { - if (IsX86_64) - Res |= SanitizerKind::Thread; - } else if (isTargetIOSSimulator() || isTargetTvOSSimulator()) { - if (IsX86_64) - Res |= SanitizerKind::Thread; + if (IsX86_64 && (isTargetMacOSBased() || isTargetIOSSimulator() || + isTargetTvOSSimulator())) { + Res |= SanitizerKind::Thread; } return Res; } diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h index 1b193a4c4eb96..d7ca424be0579 100644 --- a/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -288,13 +288,16 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { enum DarwinEnvironmentKind { NativeEnvironment, Simulator, + MacABI, }; mutable DarwinPlatformKind TargetPlatform; mutable DarwinEnvironmentKind TargetEnvironment; - /// The OS version we are targeting. + /// The native OS version we are targeting. mutable VersionTuple TargetVersion; + /// The OS version we are targeting as specified in the triple. + mutable VersionTuple OSTargetVersion; /// The information about the darwin SDK that was used. mutable Optional<DarwinSDKInfo> SDKInfo; @@ -340,12 +343,14 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { // FIXME: Eliminate these ...Target functions and derive separate tool chains // for these targets and put version in constructor. void setTarget(DarwinPlatformKind Platform, DarwinEnvironmentKind Environment, - unsigned Major, unsigned Minor, unsigned Micro) const { + unsigned Major, unsigned Minor, unsigned Micro, + VersionTuple NativeTargetVersion) const { // FIXME: For now, allow reinitialization as long as values don't // change. This will go away when we move away from argument translation. if (TargetInitialized && TargetPlatform == Platform && TargetEnvironment == Environment && - TargetVersion == VersionTuple(Major, Minor, Micro)) + (Environment == MacABI ? OSTargetVersion : TargetVersion) == + VersionTuple(Major, Minor, Micro)) return; assert(!TargetInitialized && "Target already initialized!"); @@ -355,6 +360,11 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { TargetVersion = VersionTuple(Major, Minor, Micro); if (Environment == Simulator) const_cast<Darwin *>(this)->setTripleEnvironment(llvm::Triple::Simulator); + else if (Environment == MacABI) { + const_cast<Darwin *>(this)->setTripleEnvironment(llvm::Triple::MacABI); + TargetVersion = NativeTargetVersion; + OSTargetVersion = VersionTuple(Major, Minor, Micro); + } } bool isTargetIPhoneOS() const { @@ -404,16 +414,23 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { return TargetPlatform == WatchOS; } - bool isTargetMacOS() const { + bool isTargetMacABI() const { assert(TargetInitialized && "Target not initialized!"); - return TargetPlatform == MacOS; + return TargetPlatform == IPhoneOS && TargetEnvironment == MacABI; + } + + bool isTargetMacOSBased() const { + assert(TargetInitialized && "Target not initialized!"); + return TargetPlatform == MacOS || isTargetMacABI(); } bool isTargetInitialized() const { return TargetInitialized; } - VersionTuple getTargetVersion() const { + /// The version of the OS that's used by the OS specified in the target + /// triple. + VersionTuple getOSTargetVersion() const { assert(TargetInitialized && "Target not initialized!"); - return TargetVersion; + return isTargetMacABI() ? OSTargetVersion : TargetVersion; } bool isIPhoneOSVersionLT(unsigned V0, unsigned V1 = 0, @@ -423,7 +440,7 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { } bool isMacosxVersionLT(unsigned V0, unsigned V1 = 0, unsigned V2 = 0) const { - assert(isTargetMacOS() && "Unexpected call for non OS X target!"); + assert(isTargetMacOSBased() && "Unexpected call for non OS X target!"); return TargetVersion < VersionTuple(V0, V1, V2); } @@ -466,7 +483,7 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { // This is only used with the non-fragile ABI and non-legacy dispatch. // Mixed dispatch is used everywhere except OS X before 10.6. - return !(isTargetMacOS() && isMacosxVersionLT(10, 6)); + return !(isTargetMacOSBased() && isMacosxVersionLT(10, 6)); } unsigned GetDefaultStackProtectorLevel(bool KernelOrKext) const override { @@ -474,9 +491,9 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { // and for everything in 10.6 and beyond if (isTargetIOSBased() || isTargetWatchOSBased()) return 1; - else if (isTargetMacOS() && !isMacosxVersionLT(10, 6)) + else if (isTargetMacOSBased() && !isMacosxVersionLT(10, 6)) return 1; - else if (isTargetMacOS() && !isMacosxVersionLT(10, 5) && !KernelOrKext) + else if (isTargetMacOSBased() && !isMacosxVersionLT(10, 5) && !KernelOrKext) return 1; return 0; diff --git a/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json b/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json new file mode 100644 index 0000000000000..90b25e1f1d674 --- /dev/null +++ b/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json @@ -0,0 +1,25 @@ +{ + "Version":"10.14", + "VersionMap" : { + "macOS_iOSMac" : { + "10.14.4" : "12.4", + "10.14.3" : "12.3", + "10.14.2" : "12.2", + "10.14.1" : "12.1", + "10.15" : "13.0", + "10.14" : "12.0", + "10.14.5" : "12.5", + "10.15.1" : "13.2" + }, + "iOSMac_macOS" : { + "13.0" : "10.15", + "12.3" : "10.14.3", + "12.0" : "10.14", + "12.4" : "10.14.4", + "12.1" : "10.14.1", + "12.5" : "10.14.5", + "12.2" : "10.14.2", + "13.2" : "10.15.1" + } + } +} diff --git a/clang/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json b/clang/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json new file mode 100644 index 0000000000000..cc26f6bcfac37 --- /dev/null +++ b/clang/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json @@ -0,0 +1,13 @@ +{ + "Version":"10.15", + "VersionMap" : { + "macOS_iOSMac" : { + "10.15" : "13.1", + "10.15.1" : "13.2" + }, + "iOSMac_macOS" : { + "13.1" : "10.15", + "13.2" : "10.15.1" + } + } +} diff --git a/clang/test/Driver/darwin-ld-platform-version-maccatalyst.c b/clang/test/Driver/darwin-ld-platform-version-maccatalyst.c new file mode 100644 index 0000000000000..201a7e03fedeb --- /dev/null +++ b/clang/test/Driver/darwin-ld-platform-version-maccatalyst.c @@ -0,0 +1,12 @@ +// RUN: touch %t.o + +// RUN: %clang -target x86_64-apple-ios13.3-macabi -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck %s +// RUN: %clang -target x86_64-apple-ios13.3-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-MAPPED-SDK %s +// RUN: %clang -target x86_64-apple-ios12.0-macabi -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-OLD %s + +// CHECK: "-platform_version" "mac catalyst" "13.3.0" "0.0.0" +// CHECK-MAPPED-SDK: "-platform_version" "mac catalyst" "13.3.0" "12.0" +// CHECK-OLD: "-platform_version" "mac catalyst" "13.0" "0.0.0" diff --git a/clang/test/Driver/darwin-ld.c b/clang/test/Driver/darwin-ld.c index 001ed871efe64..dee3df0337e92 100644 --- a/clang/test/Driver/darwin-ld.c +++ b/clang/test/Driver/darwin-ld.c @@ -140,6 +140,13 @@ // LINK_VERSION_MIN: {{ld(.exe)?"}} // LINK_VERSION_MIN: "-macosx_version_min" "10.7.0" +// RUN: %clang -target x86_64-apple-ios13-macabi -mlinker-version=400 -### %t.o 2>> %t.log +// RUN: FileCheck -check-prefix=LINK_VERSION_MIN_MACABI %s < %t.log +// LINK_VERSION_MIN_MACABI: {{ld(.exe)?"}} +// LINK_VERSION_MIN_MACABI: "-maccatalyst_version_min" "13.0.0" +// LINK_VERSION_MIN_MACABI-NOT: macosx_version_min +// LINK_VERSION_MIN_MACABI-NOT: macos_version_min + // RUN: %clang -target x86_64-apple-darwin12 -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=LINK_NO_CRT1 %s < %t.log // LINK_NO_CRT1-NOT: crt @@ -158,6 +165,11 @@ // LINK_IOSSIM_PROFILE: libclang_rt.profile_iossim.a // LINK_IOSSIM_PROFILE: libclang_rt.ios.a +// RUN: %clang -target x86_64-apple-ios13-macabi -mlinker-version=400 -fprofile-instr-generate -### %t.o 2> %t.log +// RUN: FileCheck -check-prefix=LINK_MACABI_PROFILE %s < %t.log +// LINK_MACABI_PROFILE: {{ld(.exe)?"}} +// LINK_MACABI_PROFILE: libclang_rt.profile_osx.a + // RUN: %clang -target arm64-apple-tvos8.3 -mlinker-version=400 -mtvos-version-min=8.3 -resource-dir=%S/Inputs/resource_dir -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=LINK_TVOS_ARM64 %s < %t.log // LINK_TVOS_ARM64: {{ld(.exe)?"}} diff --git a/clang/test/Driver/darwin-mac-catalyst-32bit-not-supported.c b/clang/test/Driver/darwin-mac-catalyst-32bit-not-supported.c new file mode 100644 index 0000000000000..03ff51cbb47d3 --- /dev/null +++ b/clang/test/Driver/darwin-mac-catalyst-32bit-not-supported.c @@ -0,0 +1,4 @@ +// RUN: %clang --target=i386-apple-ios13.0-macabi -c -### %s 2>&1 \ +// RUN: | FileCheck %s + +// CHECK: error: 32-bit targets are not supported when building for Mac Catalyst diff --git a/clang/test/Driver/darwin-maccatalyst.c b/clang/test/Driver/darwin-maccatalyst.c new file mode 100644 index 0000000000000..c023b11d7f103 --- /dev/null +++ b/clang/test/Driver/darwin-maccatalyst.c @@ -0,0 +1,6 @@ +// RUN: %clang -target x86_64-apple-ios13-macabi -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-VERSION1 %s + +// CHECK-VERSION1: "x86_64-apple-ios13.0.0-macabi" + +// FIXME: refuse versions earlier than ios13. diff --git a/clang/test/Driver/darwin-objc-defaults.m b/clang/test/Driver/darwin-objc-defaults.m index 1b3f7a844445d..331369ff5c0b3 100644 --- a/clang/test/Driver/darwin-objc-defaults.m +++ b/clang/test/Driver/darwin-objc-defaults.m @@ -92,3 +92,11 @@ // CHECK-CHECK-ARMV7_IPHONE3_0: -fobjc-runtime=ios-3.0 // CHECK-CHECK-ARMV7_IPHONE3_0-NOT: -fobjc-dispatch-method // CHECK-CHECK-ARMV7_IPHONE3_0: darwin-objc-defaults + +// RUN: %clang -target x86_64-apple-ios13-macabi -S -### %s 2> %t +// RUN: FileCheck --check-prefix CHECK-CHECK-MACCATALYST < %t %s + +// CHECK-CHECK-MACCATALYST: "-cc1" +// CHECK-CHECK-MACCATALYST: -fobjc-runtime=macosx-10.15 +// CHECK-CHECK-MACCATALYST-NOT: -fobjc-dispatch-method +// CHECK-CHECK-MACCATALYST: darwin-objc-defaults diff --git a/clang/test/Driver/darwin-objc-runtime-maccatalyst.m b/clang/test/Driver/darwin-objc-runtime-maccatalyst.m new file mode 100644 index 0000000000000..ce5d887af9d46 --- /dev/null +++ b/clang/test/Driver/darwin-objc-runtime-maccatalyst.m @@ -0,0 +1,13 @@ +// RUN: %clang -target x86_64-apple-ios13.2-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck %s +// RUN: %clang -target x86_64-apple-ios13.2.0-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck %s +// RUN: %clang -target x86_64-apple-ios13.2-macabi -isysroot %S/Inputs/MacOSX10.14.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=FALLBACK-DEFAULT %s +// RUN: %clang -target x86_64-apple-ios12.99.99-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=FALLBACK-DEFAULT %s +// RUN: %clang -target x86_64-apple-ios-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=FALLBACK-DEFAULT %s + +// CHECK: -fobjc-runtime=macosx-10.15.1 +// FALLBACK-DEFAULT: -fobjc-runtime=macosx-10.15 diff --git a/clang/test/Driver/darwin-sanitizer-ld.c b/clang/test/Driver/darwin-sanitizer-ld.c index 53c7fce115e71..6388766bb8a9c 100644 --- a/clang/test/Driver/darwin-sanitizer-ld.c +++ b/clang/test/Driver/darwin-sanitizer-ld.c @@ -93,6 +93,18 @@ // CHECK-ASAN-WATCHOSSIM: "-rpath" "@executable_path" // CHECK-ASAN-WATCHOSSIM: "-rpath" "{{.*}}lib{{.*}}darwin" +// RUN: %clang -no-canonical-prefixes -### -target x86_64-apple-ios13-macabi \ +// RUN: -stdlib=platform -fsanitize=address \ +// RUN: -resource-dir %S/Inputs/resource_dir \ +// RUN: %s -o %t.o 2>&1 | FileCheck --check-prefix=CHECK-ASAN-MACCATALYST %s + +// CHECK-ASAN-MACCATALYST: "{{.*}}ld{{(.exe)?}}" +// CHECK-ASAN-MACCATALYST-NOT: "-lstdc++" +// CHECK-ASAN-MACCATALYST-NOT: "-lc++" +// CHECK-ASAN-MACCATALYST: libclang_rt.asan_osx_dynamic.dylib" +// CHECK-ASAN-MACCATALYST: "-rpath" "@executable_path" +// CHECK-ASAN-MACCATALYST: "-rpath" "{{.*}}lib{{.*}}darwin" + // RUN: %clang -no-canonical-prefixes -### -target armv7-apple-ios \ // RUN: -stdlib=platform -fsanitize=address -miphoneos-version-min=7 \ // RUN: %s -o %t.o 2>&1 | FileCheck --check-prefix=CHECK-ASAN-IOS %s diff --git a/clang/test/Driver/darwin-sdk-version-maccatalyst.c b/clang/test/Driver/darwin-sdk-version-maccatalyst.c new file mode 100644 index 0000000000000..40597aefdd67a --- /dev/null +++ b/clang/test/Driver/darwin-sdk-version-maccatalyst.c @@ -0,0 +1,4 @@ +// RUN: %clang -target x86_64-apple-ios13-macabi -isysroot %S/Inputs/MacOSX10.15.versioned.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck %s + +// CHECK: "-target-sdk-version=13.1" From e54af192450dc8d37a8d81b725df7a2f2a699b89 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com> Date: Thu, 12 Dec 2019 00:24:37 -0800 Subject: [PATCH 572/582] Handle tag types and complain about bad merges in C/Objective-C mode Summary: Take `struct Z {...}` defined differently and imported from both modules X and Y. While in C/ObjC mode, clang used to pick one of the definitions and ignore the other even though they might not be structurally equivalent. - Instead of using the structural equivalence code for checking compatibility, use the ODR hash mechanism. This should significantly speed up the checks. - Hook the necessary name lookup bits to allow this logic in C. - Teach diagnoseOdrViolations to check the differences. Instead of silently compiling, clang now emits: In module 'Y' imported from t.m:2: ./y.h:3:10: error: 'Z::m' from module 'Y' is not present in definition of 'struct Z' in module 'X' double m; ^ ./x.h:3:7: note: declaration of 'm' does not match int m; ^ rdar://problem/56764293 Reviewers: rsmith, arphaman, vsapsai, martong, jdoerfert Subscribers: rnkovacs, jkorous, dexonsmith, ributzka, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D71734 --- clang/include/clang/AST/Decl.h | 11 + clang/include/clang/AST/DeclBase.h | 5 +- clang/include/clang/AST/ODRHash.h | 4 + clang/include/clang/Serialization/ASTReader.h | 4 + clang/lib/AST/Decl.cpp | 15 + clang/lib/AST/DeclCXX.cpp | 2 +- clang/lib/AST/ODRHash.cpp | 15 + clang/lib/Serialization/ASTReader.cpp | 523 ++++++++++++++---- clang/lib/Serialization/ASTReaderDecl.cpp | 29 +- clang/lib/Serialization/ASTWriterDecl.cpp | 5 + clang/test/Modules/odr_hash-record.c | 293 ++++++++++ 11 files changed, 791 insertions(+), 115 deletions(-) create mode 100644 clang/test/Modules/odr_hash-record.c diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index cd97c6dcf8d5c..10beb79071e85 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3716,6 +3716,7 @@ class RecordDecl : public TagDecl { // to save some space. Use the provided accessors to access it. public: friend class DeclContext; + friend class ASTDeclReader; /// Enum that represents the different ways arguments are passed to and /// returned from function calls. This takes into account the target-specific /// and version-specific rules along with the rules determined by the @@ -3960,9 +3961,19 @@ class RecordDecl : public TagDecl { /// nullptr is returned if no named data member exists. const FieldDecl *findFirstNamedDataMember() const; + /// Get precomputed ODRHash or add a new one. + unsigned getODRHash(); + private: /// Deserialize just the fields. void LoadFieldsFromExternalStorage() const; + + /// True if a valid hash is stored in ODRHash. + bool hasODRHash() const { return RecordDeclBits.HasODRHash; } + void setHasODRHash(bool Hash = true) { RecordDeclBits.HasODRHash = Hash; } + + /// Store the ODR hash for this decl. + unsigned ODRHash; }; class FileScopeAsmDecl : public Decl { diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 91c372585b071..badf688aeba8d 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1452,10 +1452,13 @@ class DeclContext { /// Represents the way this type is passed to a function. uint64_t ArgPassingRestrictions : 2; + + /// True if a valid hash is stored in ODRHash. + uint64_t HasODRHash : 1; }; /// Number of non-inherited bits in RecordDeclBitfields. - enum { NumRecordDeclBits = 14 }; + enum { NumRecordDeclBits = 15 }; /// Stores the bits used by OMPDeclareReductionDecl. /// If modified NumOMPDeclareReductionDeclBits and the accessor diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h index cd4a6f37f5db3..61c79a76ad7d4 100644 --- a/clang/include/clang/AST/ODRHash.h +++ b/clang/include/clang/AST/ODRHash.h @@ -55,6 +55,10 @@ class ODRHash { // more information than the AddDecl class. void AddCXXRecordDecl(const CXXRecordDecl *Record); + // Use this for ODR checking records in C/Objective-C between modules. This + // method compares more information than the AddDecl class. + void AddRecordDecl(const RecordDecl *Record); + // Use this for ODR checking functions between modules. This method compares // more information than the AddDecl class. SkipBody will process the // hash as if the function has no body. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index d993fdb3e2e34..2abe0a54b1aa3 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1100,6 +1100,10 @@ class ASTReader llvm::SmallDenseMap<EnumDecl *, llvm::SmallVector<EnumDecl *, 2>, 2> PendingEnumOdrMergeFailures; + /// C/ObjC definitions in which the structural equivalence check fails + llvm::SmallDenseMap<RecordDecl *, llvm::SmallVector<RecordDecl *, 2>, 2> + PendingRecordOdrMergeFailures; + /// DeclContexts in which we have diagnosed an ODR violation. llvm::SmallPtrSet<DeclContext*, 2> DiagnosedOdrMergeFailures; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 6cfd4c2a2a218..afd82b5a4e7ee 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4360,6 +4360,8 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, setHasNonTrivialToPrimitiveCopyCUnion(false); setParamDestroyedInCallee(false); setArgPassingRestrictions(APK_CanPassInRegs); + setHasODRHash(false); + ODRHash = 0; } RecordDecl *RecordDecl::Create(const ASTContext &C, TagKind TK, DeclContext *DC, @@ -4507,6 +4509,19 @@ const FieldDecl *RecordDecl::findFirstNamedDataMember() const { return nullptr; } +unsigned RecordDecl::getODRHash() { + if (hasODRHash()) + return ODRHash; + + // Only calculate hash on first call of getODRHash per record. + class ODRHash Hash; + Hash.AddRecordDecl(this); + setHasODRHash(); + ODRHash = Hash.CalculateHash(); + + return ODRHash; +} + //===----------------------------------------------------------------------===// // BlockDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index c94ab5ccd8b51..b143dace4525d 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -484,7 +484,7 @@ unsigned CXXRecordDecl::getODRHash() const { return DefinitionData->ODRHash; // Only calculate hash on first call of getODRHash per record. - ODRHash Hash; + class ODRHash Hash; Hash.AddCXXRecordDecl(getDefinition()); DefinitionData->HasODRHash = true; DefinitionData->ODRHash = Hash.CalculateHash(); diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 3b89c630b451e..8376766b46e55 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -464,6 +464,21 @@ void ODRHash::AddSubDecl(const Decl *D) { ODRDeclVisitor(ID, *this).Visit(D); } +void ODRHash::AddRecordDecl(const RecordDecl *Record) { + AddDecl(Record); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector<const Decl *, 16> Decls; + for (Decl *SubDecl : Record->decls()) + if (isWhitelistedDecl(SubDecl, Record)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto SubDecl : Decls) + AddSubDecl(SubDecl); +} + void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { assert(Record && Record->hasDefinition() && "Expected non-null record to be a definition."); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 2ff1ff6b24ddc..0daf211105139 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -9269,7 +9269,8 @@ void ASTReader::finishPendingActions() { void ASTReader::diagnoseOdrViolations() { if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && PendingFunctionOdrMergeFailures.empty() && - PendingEnumOdrMergeFailures.empty()) + PendingEnumOdrMergeFailures.empty() && + PendingRecordOdrMergeFailures.empty()) return; // Trigger the import of the full definition of each class that had any @@ -9291,6 +9292,15 @@ void ASTReader::diagnoseOdrViolations() { } } + // Trigger the import of the full definition of each record in C/ObjC. + auto RecordOdrMergeFailures = std::move(PendingRecordOdrMergeFailures); + PendingRecordOdrMergeFailures.clear(); + for (auto &Merge : RecordOdrMergeFailures) { + Merge.first->decls_begin(); + for (auto &D : Merge.second) + D->decls_begin(); + } + // Trigger the import of functions. auto FunctionOdrMergeFailures = std::move(PendingFunctionOdrMergeFailures); PendingFunctionOdrMergeFailures.clear(); @@ -9333,6 +9343,11 @@ void ASTReader::diagnoseOdrViolations() { DeclContext *CanonDef = D->getDeclContext(); + // Skip ODR checking for structs without a definition for C/ObjC mode. + if (RecordDecl *RD = dyn_cast<RecordDecl>(CanonDef)) + if (!RD->isCompleteDefinition()) + continue; + bool Found = false; const Decl *DCanon = D->getCanonicalDecl(); @@ -9398,7 +9413,7 @@ void ASTReader::diagnoseOdrViolations() { } if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() && - EnumOdrMergeFailures.empty()) + EnumOdrMergeFailures.empty() && RecordOdrMergeFailures.empty()) return; // Ensure we don't accidentally recursively enter deserialization while @@ -9441,6 +9456,113 @@ void ASTReader::diagnoseOdrViolations() { return Hash.CalculateHash(); }; + // Used with err_module_odr_violation_mismatch_decl and + // note_module_odr_violation_mismatch_decl + // This list should be the same Decl's as in ODRHash::isWhiteListedDecl + enum RecordDiffType { + EndOfClass, + PublicSpecifer, + PrivateSpecifer, + ProtectedSpecifer, + StaticAssert, + Field, + CXXMethod, + TypeAlias, + TypeDef, + Var, + Friend, + FunctionTemplate, + Other + }; + + // Used with err_module_odr_violation_mismatch_decl_diff and + // note_module_odr_violation_mismatch_decl_diff + enum ODRDeclDifference { + StaticAssertCondition, + StaticAssertMessage, + StaticAssertOnlyMessage, + FieldName, + FieldTypeName, + FieldSingleBitField, + FieldDifferentWidthBitField, + FieldSingleMutable, + FieldSingleInitializer, + FieldDifferentInitializers, + MethodName, + MethodDeleted, + MethodDefaulted, + MethodVirtual, + MethodStatic, + MethodVolatile, + MethodConst, + MethodInline, + MethodNumberParameters, + MethodParameterType, + MethodParameterName, + MethodParameterSingleDefaultArgument, + MethodParameterDifferentDefaultArgument, + MethodNoTemplateArguments, + MethodDifferentNumberTemplateArguments, + MethodDifferentTemplateArgument, + MethodSingleBody, + MethodDifferentBody, + TypedefName, + TypedefType, + VarName, + VarType, + VarSingleInitializer, + VarDifferentInitializer, + VarConstexpr, + FriendTypeFunction, + FriendType, + FriendFunction, + FunctionTemplateDifferentNumberParameters, + FunctionTemplateParameterDifferentKind, + FunctionTemplateParameterName, + FunctionTemplateParameterSingleDefaultArgument, + FunctionTemplateParameterDifferentDefaultArgument, + FunctionTemplateParameterDifferentType, + FunctionTemplatePackParameter, + }; + + auto DifferenceSelector = [](Decl *D) { + assert(D && "valid Decl required"); + switch (D->getKind()) { + default: + return Other; + case Decl::AccessSpec: + switch (D->getAccess()) { + case AS_public: + return PublicSpecifer; + case AS_private: + return PrivateSpecifer; + case AS_protected: + return ProtectedSpecifer; + case AS_none: + break; + } + llvm_unreachable("Invalid access specifier"); + case Decl::StaticAssert: + return StaticAssert; + case Decl::Field: + return Field; + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + return CXXMethod; + case Decl::TypeAlias: + return TypeAlias; + case Decl::Typedef: + return TypeDef; + case Decl::Var: + return Var; + case Decl::Friend: + return Friend; + case Decl::FunctionTemplate: + return FunctionTemplate; + } + }; + // Issue any pending ODR-failure diagnostics. for (auto &Merge : OdrMergeFailures) { // If we've already pointed out a specific problem with this class, don't @@ -9778,64 +9900,7 @@ void ASTReader::diagnoseOdrViolations() { PopulateHashes(FirstHashes, FirstRecord); PopulateHashes(SecondHashes, SecondRecord); - // Used with err_module_odr_violation_mismatch_decl and - // note_module_odr_violation_mismatch_decl - // This list should be the same Decl's as in ODRHash::isWhiteListedDecl - enum { - EndOfClass, - PublicSpecifer, - PrivateSpecifer, - ProtectedSpecifer, - StaticAssert, - Field, - CXXMethod, - TypeAlias, - TypeDef, - Var, - Friend, - FunctionTemplate, - Other - } FirstDiffType = Other, - SecondDiffType = Other; - - auto DifferenceSelector = [](Decl *D) { - assert(D && "valid Decl required"); - switch (D->getKind()) { - default: - return Other; - case Decl::AccessSpec: - switch (D->getAccess()) { - case AS_public: - return PublicSpecifer; - case AS_private: - return PrivateSpecifer; - case AS_protected: - return ProtectedSpecifer; - case AS_none: - break; - } - llvm_unreachable("Invalid access specifier"); - case Decl::StaticAssert: - return StaticAssert; - case Decl::Field: - return Field; - case Decl::CXXMethod: - case Decl::CXXConstructor: - case Decl::CXXDestructor: - return CXXMethod; - case Decl::TypeAlias: - return TypeAlias; - case Decl::Typedef: - return TypeDef; - case Decl::Var: - return Var; - case Decl::Friend: - return Friend; - case Decl::FunctionTemplate: - return FunctionTemplate; - } - }; - + RecordDiffType FirstDiffType = Other, SecondDiffType = Other; Decl *FirstDecl = nullptr; Decl *SecondDecl = nullptr; auto FirstIt = FirstHashes.begin(); @@ -9917,56 +9982,6 @@ void ASTReader::diagnoseOdrViolations() { assert(FirstDiffType == SecondDiffType); - // Used with err_module_odr_violation_mismatch_decl_diff and - // note_module_odr_violation_mismatch_decl_diff - enum ODRDeclDifference { - StaticAssertCondition, - StaticAssertMessage, - StaticAssertOnlyMessage, - FieldName, - FieldTypeName, - FieldSingleBitField, - FieldDifferentWidthBitField, - FieldSingleMutable, - FieldSingleInitializer, - FieldDifferentInitializers, - MethodName, - MethodDeleted, - MethodDefaulted, - MethodVirtual, - MethodStatic, - MethodVolatile, - MethodConst, - MethodInline, - MethodNumberParameters, - MethodParameterType, - MethodParameterName, - MethodParameterSingleDefaultArgument, - MethodParameterDifferentDefaultArgument, - MethodNoTemplateArguments, - MethodDifferentNumberTemplateArguments, - MethodDifferentTemplateArgument, - MethodSingleBody, - MethodDifferentBody, - TypedefName, - TypedefType, - VarName, - VarType, - VarSingleInitializer, - VarDifferentInitializer, - VarConstexpr, - FriendTypeFunction, - FriendType, - FriendFunction, - FunctionTemplateDifferentNumberParameters, - FunctionTemplateParameterDifferentKind, - FunctionTemplateParameterName, - FunctionTemplateParameterSingleDefaultArgument, - FunctionTemplateParameterDifferentDefaultArgument, - FunctionTemplateParameterDifferentType, - FunctionTemplatePackParameter, - }; - // These lambdas have the common portions of the ODR diagnostics. This // has the same return as Diag(), so addition parameters can be passed // in with operator<< @@ -10991,6 +11006,294 @@ void ASTReader::diagnoseOdrViolations() { } } + // Issue any pending ODR-failure diagnostics. + for (auto &Merge : RecordOdrMergeFailures) { + // If we've already pointed out a specific problem with this class, don't + // bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + bool Diagnosed = false; + RecordDecl *FirstRecord = Merge.first; + if (!FirstRecord->isCompleteDefinition()) + continue; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); + for (auto *SecondRecord : Merge.second) { + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstRecord == SecondRecord) + continue; + if (!SecondRecord->isCompleteDefinition()) + continue; + + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + using DeclHashes = llvm::SmallVector<std::pair<Decl *, unsigned>, 4>; + DeclHashes FirstHashes; + DeclHashes SecondHashes; + + auto PopulateHashes = [&ComputeSubDeclODRHash, FirstRecord]( + DeclHashes &Hashes, RecordDecl *Record) { + for (auto *D : Record->decls()) { + if (!ODRHash::isWhitelistedDecl(D, FirstRecord)) + continue; + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); + } + }; + PopulateHashes(FirstHashes, FirstRecord); + PopulateHashes(SecondHashes, SecondRecord); + + RecordDiffType FirstDiffType = Other, SecondDiffType = Other; + Decl *FirstDecl = nullptr; + Decl *SecondDecl = nullptr; + auto FirstIt = FirstHashes.begin(); + auto SecondIt = SecondHashes.begin(); + + // If there is a diagnoseable difference, FirstDiffType and + // SecondDiffType will not be Other and FirstDecl and SecondDecl will be + // filled in if not EndOfClass. + while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { + if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && + FirstIt->second == SecondIt->second) { + ++FirstIt; + ++SecondIt; + continue; + } + + FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; + SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; + + FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass; + SecondDiffType = + SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass; + + break; + } + + if (FirstDiffType == Other || SecondDiffType == Other) { + // Reaching this point means an unexpected Decl was encountered + // or no difference was detected. This causes a generic error + // message to be emitted. + Diag(FirstRecord->getLocation(), + diag::err_module_odr_violation_different_definitions) + << FirstRecord << FirstModule.empty() << FirstModule; + + if (FirstDecl) { + Diag(FirstDecl->getLocation(), diag::note_first_module_difference) + << FirstRecord << FirstDecl->getSourceRange(); + } + + Diag(SecondRecord->getLocation(), + diag::note_module_odr_violation_different_definitions) + << SecondModule; + + if (SecondDecl) { + Diag(SecondDecl->getLocation(), diag::note_second_module_difference) + << SecondDecl->getSourceRange(); + } + + Diagnosed = true; + break; + } + + if (FirstDiffType != SecondDiffType) { + SourceLocation FirstLoc; + SourceRange FirstRange; + if (FirstDiffType == EndOfClass) { + FirstLoc = FirstRecord->getBraceRange().getEnd(); + } else { + FirstLoc = FirstIt->first->getLocation(); + FirstRange = FirstIt->first->getSourceRange(); + } + Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl) + << FirstRecord << FirstModule.empty() << FirstModule << FirstRange + << FirstDiffType; + + SourceLocation SecondLoc; + SourceRange SecondRange; + if (SecondDiffType == EndOfClass) { + SecondLoc = SecondRecord->getBraceRange().getEnd(); + } else { + SecondLoc = SecondDecl->getLocation(); + SecondRange = SecondDecl->getSourceRange(); + } + Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl) + << SecondModule << SecondRange << SecondDiffType; + Diagnosed = true; + break; + } + + assert(FirstDiffType == SecondDiffType); + + // These lambdas have the common portions of the ODR diagnostics. This + // has the same return as Diag(), so addition parameters can be passed + // in with operator<< + auto ODRDiagError = [FirstRecord, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRDeclDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff) + << FirstRecord << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto ODRDiagNote = [&SecondModule, this](SourceLocation Loc, + SourceRange Range, + ODRDeclDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << Range << DiffType; + }; + + switch (FirstDiffType) { + case EndOfClass: + case Other: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + llvm_unreachable("Invalid diff type"); + + case Field: { + FieldDecl *FirstField = cast<FieldDecl>(FirstDecl); + FieldDecl *SecondField = cast<FieldDecl>(SecondDecl); + IdentifierInfo *FirstII = FirstField->getIdentifier(); + IdentifierInfo *SecondII = SecondField->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), + FieldName) + << FirstII; + ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), + FieldName) + << SecondII; + + Diagnosed = true; + break; + } + + assert(getContext().hasSameType(FirstField->getType(), + SecondField->getType())); + + QualType FirstType = FirstField->getType(); + QualType SecondType = SecondField->getType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), + FieldTypeName) + << FirstII << FirstType; + ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), + FieldTypeName) + << SecondII << SecondType; + + Diagnosed = true; + break; + } + + const bool IsFirstBitField = FirstField->isBitField(); + const bool IsSecondBitField = SecondField->isBitField(); + if (IsFirstBitField != IsSecondBitField) { + ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), + FieldSingleBitField) + << FirstII << IsFirstBitField; + ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), + FieldSingleBitField) + << SecondII << IsSecondBitField; + Diagnosed = true; + break; + } + + if (IsFirstBitField && IsSecondBitField) { + ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), + FieldDifferentWidthBitField) + << FirstII << FirstField->getBitWidth()->getSourceRange(); + ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), + FieldDifferentWidthBitField) + << SecondII << SecondField->getBitWidth()->getSourceRange(); + Diagnosed = true; + break; + } + break; + } + case TypeDef: { + TypedefNameDecl *FirstTD = cast<TypedefNameDecl>(FirstDecl); + TypedefNameDecl *SecondTD = cast<TypedefNameDecl>(SecondDecl); + auto FirstName = FirstTD->getDeclName(); + auto SecondName = SecondTD->getDeclName(); + if (FirstName != SecondName) { + ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(), + TypedefName) + << (FirstDiffType == TypeAlias) << FirstName; + ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(), + TypedefName) + << (FirstDiffType == TypeAlias) << SecondName; + Diagnosed = true; + break; + } + + QualType FirstType = FirstTD->getUnderlyingType(); + QualType SecondType = SecondTD->getUnderlyingType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(), + TypedefType) + << (FirstDiffType == TypeAlias) << FirstName << FirstType; + ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(), + TypedefType) + << (FirstDiffType == TypeAlias) << SecondName << SecondType; + Diagnosed = true; + break; + } + break; + } + case Var: { + VarDecl *FirstVD = cast<VarDecl>(FirstDecl); + VarDecl *SecondVD = cast<VarDecl>(SecondDecl); + auto FirstName = FirstVD->getDeclName(); + auto SecondName = SecondVD->getDeclName(); + if (FirstName != SecondName) { + ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), + VarName) + << FirstName; + ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), + VarName) + << SecondName; + Diagnosed = true; + break; + } + + QualType FirstType = FirstVD->getType(); + QualType SecondType = SecondVD->getType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), + VarType) + << FirstName << FirstType; + ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), + VarType) + << SecondName << SecondType; + Diagnosed = true; + break; + } + break; + } + } + + if (Diagnosed) + continue; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + Diagnosed = true; + } + } + // Issue ODR failures diagnostics for functions. for (auto &Merge : FunctionOdrMergeFailures) { enum ODRFunctionDifference { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 0106e509367fc..e086f0a0dd435 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -723,8 +723,11 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitTagDecl(TagDecl *TD) { llvm_unreachable("unexpected tag info kind"); } - if (!isa<CXXRecordDecl>(TD)) - mergeRedeclarable(TD, Redecl); + if (isa<CXXRecordDecl>(TD)) + return Redecl; + + // Handle merging in C and Objective-C + mergeRedeclarable(TD, Redecl); return Redecl; } @@ -794,6 +797,22 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) { RD->setHasNonTrivialToPrimitiveCopyCUnion(Record.readInt()); RD->setParamDestroyedInCallee(Record.readInt()); RD->setArgPassingRestrictions((RecordDecl::ArgPassingKind)Record.readInt()); + RD->setHasODRHash(true); + RD->ODRHash = Record.readInt(); + + // C++ applies ODR checking in VisitCXXRecordDecl instead. Note that + // structural equivalence is the usual way to check for ODR-like semantics + // in ObjC/C, but using ODRHash is prefered if possible because of better + // performance. + if (!Reader.getContext().getLangOpts().CPlusPlus) { + RecordDecl *Canon = static_cast<RecordDecl *>(RD->getCanonicalDecl()); + if (RD == Canon || Canon->getODRHash() == RD->getODRHash()) + return Redecl; + Reader.PendingRecordOdrMergeFailures[Canon].push_back(RD); + // Track that we merged the definitions. + Reader.MergedDeclContexts.insert(std::make_pair(RD, Canon)); + } + return Redecl; } @@ -2563,7 +2582,7 @@ static bool allowODRLikeMergeInC(NamedDecl *ND) { if (!ND) return false; // TODO: implement merge for other necessary decls. - if (isa<EnumConstantDecl>(ND)) + if (isa<EnumConstantDecl>(ND) || isa<FieldDecl>(ND)) return true; return false; } @@ -3201,6 +3220,10 @@ DeclContext *ASTDeclReader::getPrimaryContextForMerging(ASTReader &Reader, return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition() : nullptr; + if (auto *RD = dyn_cast<RecordDecl>(DC)) + if (!RD->getASTContext().getLangOpts().CPlusPlus) + return RD->getCanonicalDecl()->getDefinition(); + // We can see the TU here only if we have no Sema object. In that case, // there's no TU scope to look in, so using the DC alone is sufficient. if (auto *TU = dyn_cast<TranslationUnitDecl>(DC)) diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index e6d2f0c438f65..b56fe647e8a04 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -483,6 +483,9 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) { Record.push_back(D->hasNonTrivialToPrimitiveCopyCUnion()); Record.push_back(D->isParamDestroyedInCallee()); Record.push_back(D->getArgPassingRestrictions()); + // Only compute this for C/Objective-C, in C++ this is computed as part + // of CXXRecordDecl. + Record.push_back(Writer.getLangOpts().CPlusPlus ? 0UL : D->getODRHash()); if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() && @@ -2048,6 +2051,8 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // getArgPassingRestrictions Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); + // ODRHash + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // DC Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset diff --git a/clang/test/Modules/odr_hash-record.c b/clang/test/Modules/odr_hash-record.c new file mode 100644 index 0000000000000..abfb4e0f195ab --- /dev/null +++ b/clang/test/Modules/odr_hash-record.c @@ -0,0 +1,293 @@ +// Clear and create directories +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: mkdir %t/cache +// RUN: mkdir %t/Inputs + +// Build first header file +// RUN: echo "#define FIRST" >> %t/Inputs/first.h +// RUN: cat %s >> %t/Inputs/first.h + +// Build second header file +// RUN: echo "#define SECOND" >> %t/Inputs/second.h +// RUN: cat %s >> %t/Inputs/second.h + +// Test that each header can compile +// RUN: %clang_cc1 -fsyntax-only -x c %t/Inputs/first.h +// RUN: %clang_cc1 -fsyntax-only -x c %t/Inputs/second.h + +// Build module map file +// RUN: echo "module FirstModule {" >> %t/Inputs/module.map +// RUN: echo " header \"first.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map +// RUN: echo "module SecondModule {" >> %t/Inputs/module.map +// RUN: echo " header \"second.h\"" >> %t/Inputs/module.map +// RUN: echo "}" >> %t/Inputs/module.map + +// Run test +// RUN: %clang_cc1 -triple x86_64-linux-gnu -x c \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: -I%t/Inputs -verify %s + +#if !defined(FIRST) && !defined(SECOND) +#include "first.h" +#include "second.h" +#endif + +#if defined(FIRST) +struct S1 {}; +struct S1 s1a; +#elif defined(SECOND) +struct S1 {}; +#else +struct S1 s1; +#endif + +#if defined(FIRST) +struct S2 { + int x; + int y; +}; +#elif defined(SECOND) +struct S2 { + int y; + int x; +}; +#else +struct S2 s2; +// expected-error@first.h:* {{'S2' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x'}} +// expected-note@second.h:* {{but in 'SecondModule' found field 'y'}} +#endif + +#if defined(FIRST) +struct S3 { + double x; +}; +#elif defined(SECOND) +struct S3 { + int x; +}; +#else +struct S3 s3; +// expected-error@second.h:* {{'S3::x' from module 'SecondModule' is not present in definition of 'struct S3' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +typedef int A; +struct S4 { + A x; +}; + +struct S5 { + A x; +}; +#elif defined(SECOND) +typedef int B; +struct S4 { + B x; +}; + +struct S5 { + int x; +}; +#else +struct S4 s4; +// expected-error@first.h:* {{'S4' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x' with type 'A' (aka 'int')}} +// expected-note@second.h:* {{but in 'SecondModule' found field 'x' with type 'B' (aka 'int')}} + +struct S5 s5; +// expected-error@first.h:* {{'S5' has different definitions in different modules; first difference is definition in module 'FirstModule' found field 'x' with type 'A' (aka 'int')}} +// expected-note@second.h:* {{but in 'SecondModule' found field 'x' with type 'int'}} +#endif + +#if defined(FIRST) +struct S6 { + unsigned x; +}; +#elif defined(SECOND) +struct S6 { + unsigned x : 1; +}; +#else +struct S6 s6; +// expected-error@first.h:* {{'S6' has different definitions in different modules; first difference is definition in module 'FirstModule' found non-bitfield 'x'}} +// expected-note@second.h:* {{but in 'SecondModule' found bitfield 'x'}} +#endif + +#if defined(FIRST) +struct S7 { + unsigned x : 2; +}; +#elif defined(SECOND) +struct S7 { + unsigned x : 1; +}; +#else +struct S7 s7; +// expected-error@first.h:* {{'S7' has different definitions in different modules; first difference is definition in module 'FirstModule' found bitfield 'x' with one width expression}} +// expected-note@second.h:* {{but in 'SecondModule' found bitfield 'x' with different width expression}} +#endif + +#if defined(FIRST) +struct S8 { + unsigned x : 2; +}; +#elif defined(SECOND) +struct S8 { + unsigned x : 1 + 1; +}; +#else +struct S8 s8; +// expected-error@first.h:* {{'S8' has different definitions in different modules; first difference is definition in module 'FirstModule' found bitfield 'x' with one width expression}} +// expected-note@second.h:* {{but in 'SecondModule' found bitfield 'x' with different width expression}} +#endif + +#if defined(FIRST) +struct S12 { + unsigned x[5]; +}; +#elif defined(SECOND) +struct S12 { + unsigned x[7]; +}; +#else +struct S12 s12; +// expected-error@second.h:* {{'S12::x' from module 'SecondModule' is not present in definition of 'struct S12' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +struct S13 { + unsigned x[7]; +}; +#elif defined(SECOND) +struct S13 { + double x[7]; +}; +#else +struct S13 s13; +// expected-error@second.h:* {{'S13::x' from module 'SecondModule' is not present in definition of 'struct S13' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +struct B1 {}; +struct SS1 { + struct B1 x; +}; +#elif defined(SECOND) +struct A1 {}; +struct SS1 { + struct A1 x; +}; +#else +struct SS1 ss1; +// expected-error@second.h:* {{'SS1::x' from module 'SecondModule' is not present in definition of 'struct SS1' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +enum E1 { x42 }; +struct SE1 { + enum E1 x; +}; +#elif defined(SECOND) +enum E2 { x42 }; +struct SE1 { + enum E2 x; +}; +#else +struct SE1 se1; +// expected-error@second.h:* {{'SE1::x' from module 'SecondModule' is not present in definition of 'struct SE1' in module 'FirstModule'}} +// expected-note@first.h:* {{declaration of 'x' does not match}} +#endif + +// struct with forward declaration +#if defined(FIRST) +struct P {}; +struct S { + struct P *ptr; +}; +#elif defined(SECOND) +struct S { + struct P *ptr; +}; +#else +struct S s; +#endif + +// struct with forward declaration and no definition +#if defined(FIRST) +struct PA; +struct SA { + struct PA *ptr; +}; +#elif defined(SECOND) +struct SA { + struct PA *ptr; +}; +#else +struct SA sa; +#endif + +// struct with multiple typedefs +#if defined(FIRST) +typedef int BB1; +typedef BB1 AA1; +struct TS1 { + AA1 x; +}; +#elif defined(SECOND) +typedef int AA1; +struct TS1 { + AA1 x; +}; +#else +struct TS1 ts1; +#endif + +#if defined(FIRST) +struct T2 { + int x; +}; +typedef struct T2 B2; +typedef struct B2 A2; +struct TS2 { + struct T2 x; +}; +#elif defined(SECOND) +struct T2 { + int x; +}; +typedef struct T2 A2; +struct TS2 { + struct T2 x; +}; +#else +struct TS2 ts2; +#endif + +#if defined(FIRST) +struct T3; +struct TS3 { + struct T3 *t; +}; +#elif defined(SECOND) +typedef struct T3 { +} T3; +struct TS3 { + struct T3 *t; +}; +#else +struct TS3 ts3; +#endif + +// Keep macros contained to one file. +#ifdef FIRST +#undef FIRST +#endif + +#ifdef SECOND +#undef SECOND +#endif From a59db64a41bd61cffc5127a101d35c5b04eb6fd6 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Wed, 8 Jan 2020 13:34:55 -0800 Subject: [PATCH 573/582] [lldb/SWIG] Refactor extensions to be non Python-specific The current SWIG extensions for the string conversion operator is Python specific because it uses the PythonObjects. This means that the code cannot be reused for other SWIG supported languages such as Lua. This reimplements the extensions in a more generic way that can be reused. Differential revision: https://reviews.llvm.org/D72377 (cherry picked from commit 0341c11e08504acef8c16ab07210bc253dadf2d9) --- lldb/scripts/Python/python-extensions.swig | 13 ------------- lldb/scripts/interface/SBTarget.i | 17 ++++++++++++++++- lldb/scripts/lldb.swig | 1 + lldb/scripts/lldb_lua.swig | 1 + 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lldb/scripts/Python/python-extensions.swig b/lldb/scripts/Python/python-extensions.swig index c10c32b448774..dbd4b1d79d005 100644 --- a/lldb/scripts/Python/python-extensions.swig +++ b/lldb/scripts/Python/python-extensions.swig @@ -1,4 +1,3 @@ - %extend lldb::SBAddress { %nothreadallow; PyObject *lldb::SBAddress::__str__ (){ @@ -502,18 +501,6 @@ } %extend lldb::SBTarget { - %nothreadallow; - PyObject *lldb::SBTarget::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; - %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): diff --git a/lldb/scripts/interface/SBTarget.i b/lldb/scripts/interface/SBTarget.i index b31622889e52f..02c70b6e1cd6f 100644 --- a/lldb/scripts/interface/SBTarget.i +++ b/lldb/scripts/interface/SBTarget.i @@ -8,7 +8,6 @@ namespace lldb { - %feature("docstring", "Represents the target program running under the debugger. @@ -968,6 +967,22 @@ public: lldb::SBValue EvaluateExpression (const char *expr, const lldb::SBExpressionOptions &options); + %extend { + %nothreadallow; + std::string lldb::SBTarget::__str__(){ + lldb::SBStream stream; + $self->GetDescription (stream, lldb::eDescriptionLevelBrief); + + const char *desc = stream.GetData(); + size_t desc_len = stream.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + + return std::string(desc, desc_len); + } + %clearnothreadallow; + } + #ifdef SWIGPYTHON %pythoncode %{ class modules_access(object): diff --git a/lldb/scripts/lldb.swig b/lldb/scripts/lldb.swig index f030116b6cb8e..c3b9083327410 100644 --- a/lldb/scripts/lldb.swig +++ b/lldb/scripts/lldb.swig @@ -93,6 +93,7 @@ def lldb_iter(obj, getsize, getelem): yield elem(i) %} +%include <std_string.i> %include "./Python/python-typemaps.swig" %include "./headers.swig" diff --git a/lldb/scripts/lldb_lua.swig b/lldb/scripts/lldb_lua.swig index 85edefff76f27..bf8809015d9a3 100644 --- a/lldb/scripts/lldb_lua.swig +++ b/lldb/scripts/lldb_lua.swig @@ -8,6 +8,7 @@ %module lldb +%include <std_string.i> %include "./headers.swig" %{ From 1150259ed146f7043eb153fb05f9039681763b7b Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Wed, 8 Jan 2020 16:13:03 -0800 Subject: [PATCH 574/582] [lldb/SWIG] Refactor extensions to be non Python-specific (2/2) The current SWIG extensions for the string conversion operator is Python specific because it uses the PythonObjects. This means that the code cannot be reused for other SWIG supported languages such as Lua. This reimplements the extensions in a more generic way that can be reused. It uses a SWIG macro to reduce code duplication. Differential revision: https://reviews.llvm.org/D72377 (cherry picked from commit ae47a3d8107856c84c104f3c2e43a553f4e36748) --- lldb/scripts/Python/python-extensions.swig | 276 +----------------- lldb/scripts/interface/SBAddress.i | 2 + lldb/scripts/interface/SBBlock.i | 2 + lldb/scripts/interface/SBBreakpoint.i | 2 + lldb/scripts/interface/SBBreakpointLocation.i | 2 + lldb/scripts/interface/SBBreakpointName.i | 1 + .../scripts/interface/SBCommandReturnObject.i | 2 + lldb/scripts/interface/SBCompileUnit.i | 2 + lldb/scripts/interface/SBData.i | 2 + lldb/scripts/interface/SBDebugger.i | 2 + lldb/scripts/interface/SBDeclaration.i | 2 + lldb/scripts/interface/SBError.i | 2 + lldb/scripts/interface/SBFileSpec.i | 2 + lldb/scripts/interface/SBFrame.i | 2 + lldb/scripts/interface/SBFunction.i | 2 + lldb/scripts/interface/SBInstruction.i | 2 + lldb/scripts/interface/SBInstructionList.i | 2 + lldb/scripts/interface/SBLineEntry.i | 2 + lldb/scripts/interface/SBMemoryRegionInfo.i | 1 + lldb/scripts/interface/SBModule.i | 2 + lldb/scripts/interface/SBModuleSpec.i | 2 + lldb/scripts/interface/SBTarget.i | 16 +- lldb/scripts/lldb.swig | 18 ++ lldb/scripts/lldb_lua.swig | 1 + lldb/scripts/macros.swig | 33 +++ 25 files changed, 95 insertions(+), 287 deletions(-) create mode 100644 lldb/scripts/macros.swig diff --git a/lldb/scripts/Python/python-extensions.swig b/lldb/scripts/Python/python-extensions.swig index dbd4b1d79d005..36dac19644bd0 100644 --- a/lldb/scripts/Python/python-extensions.swig +++ b/lldb/scripts/Python/python-extensions.swig @@ -1,42 +1,4 @@ -%extend lldb::SBAddress { - %nothreadallow; - PyObject *lldb::SBAddress::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBBlock { - %nothreadallow; - PyObject *lldb::SBBlock::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} %extend lldb::SBBreakpoint { - %nothreadallow; - PyObject *lldb::SBBreakpoint::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; - %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -50,34 +12,6 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} - -} -%extend lldb::SBBreakpointLocation { - %nothreadallow; - PyObject *lldb::SBBreakpointLocation::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelFull); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} - -%extend lldb::SBBreakpointName { - %nothreadallow; - PyObject *lldb::SBBreakpointName::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; } %extend lldb::SBBroadcaster { @@ -97,18 +31,6 @@ } %extend lldb::SBCommandReturnObject { - %nothreadallow; - PyObject *lldb::SBCommandReturnObject::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; - /* the write() and flush() calls are not part of the SB API proper, and are solely for Python usage they are meant to make an SBCommandReturnObject into a file-like object so that instructions of the sort print >>sb_command_return_object, "something" @@ -122,18 +44,8 @@ void lldb::SBCommandReturnObject::flush () {} } + %extend lldb::SBCompileUnit { - %nothreadallow; - PyObject *lldb::SBCompileUnit::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -148,45 +60,8 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} } -%extend lldb::SBData { - %nothreadallow; - PyObject *lldb::SBData::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBDebugger { - %nothreadallow; - PyObject *lldb::SBDebugger::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBDeclaration { - %nothreadallow; - PyObject *lldb::SBDeclaration::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; +%extend lldb::SBDeclaration { %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -200,60 +75,9 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} - -} -%extend lldb::SBError { - %nothreadallow; - PyObject *lldb::SBError::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBFileSpec { - %nothreadallow; - PyObject *lldb::SBFileSpec::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; } -%extend lldb::SBFrame { - %nothreadallow; - PyObject *lldb::SBFrame::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBFunction { - %nothreadallow; - PyObject *lldb::SBFunction::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; +%extend lldb::SBFunction { %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -267,47 +91,9 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} - -} -%extend lldb::SBInstruction { - %nothreadallow; - PyObject *lldb::SBInstruction::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBInstructionList { - %nothreadallow; - PyObject *lldb::SBInstructionList::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; } -%extend lldb::SBLineEntry { - %nothreadallow; - PyObject *lldb::SBLineEntry::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; +%extend lldb::SBLineEntry { %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -323,33 +109,7 @@ %} } -%extend lldb::SBMemoryRegionInfo { - %nothreadallow; - PyObject *lldb::SBMemoryRegionInfo::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} - %extend lldb::SBModule { - %nothreadallow; - PyObject *lldb::SBModule::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; - %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -365,34 +125,6 @@ %} } -%extend lldb::SBModuleSpec { - %nothreadallow; - PyObject *lldb::SBModuleSpec::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} - -%extend lldb::SBModuleSpecList { - %nothreadallow; - PyObject *lldb::SBModuleSpecList::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} - %extend lldb::SBProcess { %nothreadallow; PyObject *lldb::SBProcess::__str__ (){ diff --git a/lldb/scripts/interface/SBAddress.i b/lldb/scripts/interface/SBAddress.i index 6c5352bac6d7c..4658534d153ea 100644 --- a/lldb/scripts/interface/SBAddress.i +++ b/lldb/scripts/interface/SBAddress.i @@ -140,6 +140,8 @@ public: lldb::SBLineEntry GetLineEntry (); + STRING_EXTENSION(SBAddress) + #ifdef SWIGPYTHON %pythoncode %{ def __get_load_addr_property__ (self): diff --git a/lldb/scripts/interface/SBBlock.i b/lldb/scripts/interface/SBBlock.i index 73079a11760c6..8bd8e37953cfc 100644 --- a/lldb/scripts/interface/SBBlock.i +++ b/lldb/scripts/interface/SBBlock.i @@ -100,6 +100,8 @@ public: bool locals, bool statics); + STRING_EXTENSION(SBBlock) + #ifdef SWIGPYTHON %pythoncode %{ def get_range_at_index(self, idx): diff --git a/lldb/scripts/interface/SBBreakpoint.i b/lldb/scripts/interface/SBBreakpoint.i index f84f2ada3d329..20354346be900 100644 --- a/lldb/scripts/interface/SBBreakpoint.i +++ b/lldb/scripts/interface/SBBreakpoint.i @@ -249,6 +249,8 @@ public: bool IsHardware (); + STRING_EXTENSION(SBBreakpoint) + #ifdef SWIGPYTHON %pythoncode %{ diff --git a/lldb/scripts/interface/SBBreakpointLocation.i b/lldb/scripts/interface/SBBreakpointLocation.i index 44fd42b514f7f..dc39c83c2d67b 100644 --- a/lldb/scripts/interface/SBBreakpointLocation.i +++ b/lldb/scripts/interface/SBBreakpointLocation.i @@ -134,6 +134,8 @@ public: SBBreakpoint GetBreakpoint (); + + STRING_EXTENSION_LEVEL(SBBreakpointLocation, lldb::eDescriptionLevelFull) }; } // namespace lldb diff --git a/lldb/scripts/interface/SBBreakpointName.i b/lldb/scripts/interface/SBBreakpointName.i index 2a06d0a2105f0..e280d42245915 100644 --- a/lldb/scripts/interface/SBBreakpointName.i +++ b/lldb/scripts/interface/SBBreakpointName.i @@ -108,6 +108,7 @@ public: bool GetDescription(lldb::SBStream &description); + STRING_EXTENSION(SBBreakpointName) }; } // namespace lldb diff --git a/lldb/scripts/interface/SBCommandReturnObject.i b/lldb/scripts/interface/SBCommandReturnObject.i index 73d4001aaba59..affa16520f28d 100644 --- a/lldb/scripts/interface/SBCommandReturnObject.i +++ b/lldb/scripts/interface/SBCommandReturnObject.i @@ -96,6 +96,8 @@ public: void SetImmediateOutputFile(lldb::FileSP BORROWED); void SetImmediateErrorFile(lldb::FileSP BORROWED); + STRING_EXTENSION(SBCommandReturnObject) + %extend { // transfer_ownership does nothing, and is here for compatibility with // old scripts. Ownership is tracked by reference count in the ordinary way. diff --git a/lldb/scripts/interface/SBCompileUnit.i b/lldb/scripts/interface/SBCompileUnit.i index bc2d45ae8e56c..d6a4c07038c65 100644 --- a/lldb/scripts/interface/SBCompileUnit.i +++ b/lldb/scripts/interface/SBCompileUnit.i @@ -116,6 +116,8 @@ public: bool operator != (const lldb::SBCompileUnit &rhs) const; + STRING_EXTENSION(SBCompileUnit) + #ifdef SWIGPYTHON %pythoncode %{ def __iter__(self): diff --git a/lldb/scripts/interface/SBData.i b/lldb/scripts/interface/SBData.i index fdaa6962f0eca..3e74240329e05 100644 --- a/lldb/scripts/interface/SBData.i +++ b/lldb/scripts/interface/SBData.i @@ -134,6 +134,8 @@ public: bool SetDataFromDoubleArray (double* array, size_t array_len); + STRING_EXTENSION(SBData) + #ifdef SWIGPYTHON %pythoncode %{ diff --git a/lldb/scripts/interface/SBDebugger.i b/lldb/scripts/interface/SBDebugger.i index 52f65841893c6..f2e23a7ed7804 100644 --- a/lldb/scripts/interface/SBDebugger.i +++ b/lldb/scripts/interface/SBDebugger.i @@ -479,6 +479,8 @@ public: lldb::SBTypeSynthetic GetSyntheticForType (lldb::SBTypeNameSpecifier); + STRING_EXTENSION(SBDebugger) + %feature("docstring", "Launch a command interpreter session. Commands are read from standard input or from the input handle specified for the debugger object. Output/errors are diff --git a/lldb/scripts/interface/SBDeclaration.i b/lldb/scripts/interface/SBDeclaration.i index cdaec85676461..621c1a0ab7c87 100644 --- a/lldb/scripts/interface/SBDeclaration.i +++ b/lldb/scripts/interface/SBDeclaration.i @@ -53,6 +53,8 @@ namespace lldb { bool operator != (const lldb::SBDeclaration &rhs) const; + STRING_EXTENSION(SBDeclaration) + #ifdef SWIGPYTHON %pythoncode %{ file = property(GetFileSpec, None, doc='''A read only property that returns an lldb object that represents the file (lldb.SBFileSpec) for this line entry.''') diff --git a/lldb/scripts/interface/SBError.i b/lldb/scripts/interface/SBError.i index 96cd6c4886f5f..ea48e2263a77a 100644 --- a/lldb/scripts/interface/SBError.i +++ b/lldb/scripts/interface/SBError.i @@ -105,6 +105,8 @@ public: bool GetDescription (lldb::SBStream &description); + STRING_EXTENSION(SBError) + #ifdef SWIGPYTHON %pythoncode %{ value = property(GetError, None, doc='''A read only property that returns the same result as GetError().''') diff --git a/lldb/scripts/interface/SBFileSpec.i b/lldb/scripts/interface/SBFileSpec.i index 07a7630ebbac8..d287a940c051a 100644 --- a/lldb/scripts/interface/SBFileSpec.i +++ b/lldb/scripts/interface/SBFileSpec.i @@ -80,6 +80,8 @@ public: void AppendPathComponent (const char *file_or_directory); + STRING_EXTENSION(SBFileSpec) + #ifdef SWIGPYTHON %pythoncode %{ def __get_fullpath__(self): diff --git a/lldb/scripts/interface/SBFrame.i b/lldb/scripts/interface/SBFrame.i index 811f7f22f9b4d..c65b88f863e7d 100644 --- a/lldb/scripts/interface/SBFrame.i +++ b/lldb/scripts/interface/SBFrame.i @@ -285,6 +285,8 @@ public: bool GetDescription (lldb::SBStream &description); + STRING_EXTENSION(SBFrame) + #ifdef SWIGPYTHON %pythoncode %{ def get_all_variables(self): diff --git a/lldb/scripts/interface/SBFunction.i b/lldb/scripts/interface/SBFunction.i index 7b157bb388169..630c4db22c55d 100644 --- a/lldb/scripts/interface/SBFunction.i +++ b/lldb/scripts/interface/SBFunction.i @@ -111,6 +111,8 @@ public: bool operator != (const lldb::SBFunction &rhs) const; + STRING_EXTENSION(SBFunction) + #ifdef SWIGPYTHON %pythoncode %{ def get_instructions_from_current_target (self): diff --git a/lldb/scripts/interface/SBInstruction.i b/lldb/scripts/interface/SBInstruction.i index 09688214630b1..d50a080fd0454 100644 --- a/lldb/scripts/interface/SBInstruction.i +++ b/lldb/scripts/interface/SBInstruction.i @@ -74,6 +74,8 @@ public: bool TestEmulation (lldb::SBStream &output_stream, const char *test_file); + STRING_EXTENSION(SBInstruction) + #ifdef SWIGPYTHON %pythoncode %{ def __mnemonic_property__ (self): diff --git a/lldb/scripts/interface/SBInstructionList.i b/lldb/scripts/interface/SBInstructionList.i index d50deba4f5e1f..1357323027573 100644 --- a/lldb/scripts/interface/SBInstructionList.i +++ b/lldb/scripts/interface/SBInstructionList.i @@ -66,6 +66,8 @@ public: bool DumpEmulationForAllInstructions (const char *triple); + STRING_EXTENSION(SBInstructionList) + #ifdef SWIGPYTHON %pythoncode %{ def __iter__(self): diff --git a/lldb/scripts/interface/SBLineEntry.i b/lldb/scripts/interface/SBLineEntry.i index 90f60df23247d..be365377ba8b0 100644 --- a/lldb/scripts/interface/SBLineEntry.i +++ b/lldb/scripts/interface/SBLineEntry.i @@ -84,6 +84,8 @@ public: bool operator != (const lldb::SBLineEntry &rhs) const; + STRING_EXTENSION(SBLineEntry) + #ifdef SWIGPYTHON %pythoncode %{ file = property(GetFileSpec, None, doc='''A read only property that returns an lldb object that represents the file (lldb.SBFileSpec) for this line entry.''') diff --git a/lldb/scripts/interface/SBMemoryRegionInfo.i b/lldb/scripts/interface/SBMemoryRegionInfo.i index 7a59d0051ceac..6a2ad6a3e3649 100644 --- a/lldb/scripts/interface/SBMemoryRegionInfo.i +++ b/lldb/scripts/interface/SBMemoryRegionInfo.i @@ -55,6 +55,7 @@ public: bool GetDescription (lldb::SBStream &description); + STRING_EXTENSION(SBMemoryRegionInfo) }; } // namespace lldb diff --git a/lldb/scripts/interface/SBModule.i b/lldb/scripts/interface/SBModule.i index 03c8aeb2bed9e..a9d9480cd7cf1 100644 --- a/lldb/scripts/interface/SBModule.i +++ b/lldb/scripts/interface/SBModule.i @@ -344,6 +344,8 @@ public: lldb::SBAddress GetObjectFileEntryPointAddress() const; + STRING_EXTENSION(SBModule) + #ifdef SWIGPYTHON %pythoncode %{ def __len__(self): diff --git a/lldb/scripts/interface/SBModuleSpec.i b/lldb/scripts/interface/SBModuleSpec.i index ec4e9bb7fbf72..64d0aa641a774 100644 --- a/lldb/scripts/interface/SBModuleSpec.i +++ b/lldb/scripts/interface/SBModuleSpec.i @@ -91,6 +91,7 @@ public: bool GetDescription (lldb::SBStream &description); + STRING_EXTENSION(SBModuleSpec) }; @@ -127,6 +128,7 @@ public: bool GetDescription (lldb::SBStream &description); + STRING_EXTENSION(SBModuleSpecList) }; } // namespace lldb diff --git a/lldb/scripts/interface/SBTarget.i b/lldb/scripts/interface/SBTarget.i index 02c70b6e1cd6f..371bf5c35ebd0 100644 --- a/lldb/scripts/interface/SBTarget.i +++ b/lldb/scripts/interface/SBTarget.i @@ -967,21 +967,7 @@ public: lldb::SBValue EvaluateExpression (const char *expr, const lldb::SBExpressionOptions &options); - %extend { - %nothreadallow; - std::string lldb::SBTarget::__str__(){ - lldb::SBStream stream; - $self->GetDescription (stream, lldb::eDescriptionLevelBrief); - - const char *desc = stream.GetData(); - size_t desc_len = stream.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - - return std::string(desc, desc_len); - } - %clearnothreadallow; - } + STRING_EXTENSION_LEVEL(SBTarget, lldb::eDescriptionLevelBrief) #ifdef SWIGPYTHON %pythoncode %{ diff --git a/lldb/scripts/lldb.swig b/lldb/scripts/lldb.swig index c3b9083327410..bebf9bf534a34 100644 --- a/lldb/scripts/lldb.swig +++ b/lldb/scripts/lldb.swig @@ -59,6 +59,23 @@ except ImportError: // Parameter types will be used in the autodoc string. %feature("autodoc", "1"); +%define ARRAYHELPER(type,name) +%inline %{ +type *new_ ## name (int nitems) { + return (type *) malloc(sizeof(type)*nitems); +} +void delete_ ## name(type *t) { + free(t); +} +type name ## _get(type *t, int index) { + return t[index]; +} +void name ## _set(type *t, int index, type val) { + t[index] = val; +} +%} +%enddef + %pythoncode%{ import uuid import re @@ -95,6 +112,7 @@ def lldb_iter(obj, getsize, getelem): %include <std_string.i> %include "./Python/python-typemaps.swig" +%include "./macros.swig" %include "./headers.swig" %{ diff --git a/lldb/scripts/lldb_lua.swig b/lldb/scripts/lldb_lua.swig index bf8809015d9a3..3b279a6b69e7f 100644 --- a/lldb/scripts/lldb_lua.swig +++ b/lldb/scripts/lldb_lua.swig @@ -9,6 +9,7 @@ %module lldb %include <std_string.i> +%include "./macros.swig" %include "./headers.swig" %{ diff --git a/lldb/scripts/macros.swig b/lldb/scripts/macros.swig new file mode 100644 index 0000000000000..e0756c2f17932 --- /dev/null +++ b/lldb/scripts/macros.swig @@ -0,0 +1,33 @@ +%define STRING_EXTENSION_LEVEL(Class, Level) +%extend { + %nothreadallow; + std::string lldb:: ## Class ## ::__str__(){ + lldb::SBStream stream; + $self->GetDescription (stream, Level); + const char *desc = stream.GetData(); + size_t desc_len = stream.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == 'n' || desc[desc_len-1] == 'r')) { + --desc_len; + } + return std::string(desc, desc_len); + } + %clearnothreadallow; +} +%enddef + +%define STRING_EXTENSION(Class) +%extend { + %nothreadallow; + std::string lldb:: ## Class ## ::__str__(){ + lldb::SBStream stream; + $self->GetDescription (stream); + const char *desc = stream.GetData(); + size_t desc_len = stream.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == 'n' || desc[desc_len-1] == 'r')) { + --desc_len; + } + return std::string(desc, desc_len); + } + %clearnothreadallow; +} +%enddef From 4d202ac615240c8ab9c406998592b2bc38ea4a6d Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Wed, 8 Jan 2020 20:56:11 -0800 Subject: [PATCH 575/582] [lldb/SWIG] Refactor extensions to be non Python-specific (3/3) The current SWIG extensions for the string conversion operator is Python specific because it uses the PythonObjects. This means that the code cannot be reused for other SWIG supported languages such as Lua. This reimplements the extensions in a more generic way that can be reused. It uses a SWIG macro to reduce code duplication. Differential revision: https://reviews.llvm.org/D72377 (cherry picked from commit 51bdd98b8a52d07004bcaddff26caf376a1c32bf) --- lldb/scripts/Python/python-extensions.swig | 257 +------------------ lldb/scripts/interface/SBProcess.i | 2 + lldb/scripts/interface/SBSection.i | 2 + lldb/scripts/interface/SBSymbol.i | 2 + lldb/scripts/interface/SBSymbolContext.i | 1 + lldb/scripts/interface/SBSymbolContextList.i | 2 + lldb/scripts/interface/SBThread.i | 2 + lldb/scripts/interface/SBType.i | 5 + lldb/scripts/interface/SBTypeCategory.i | 2 + lldb/scripts/interface/SBTypeEnumMember.i | 1 + lldb/scripts/interface/SBTypeFilter.i | 2 + lldb/scripts/interface/SBTypeFormat.i | 2 + lldb/scripts/interface/SBTypeNameSpecifier.i | 2 + lldb/scripts/interface/SBTypeSummary.i | 2 + lldb/scripts/interface/SBTypeSynthetic.i | 2 + lldb/scripts/interface/SBValue.i | 2 + lldb/scripts/interface/SBValueList.i | 23 ++ lldb/scripts/interface/SBWatchpoint.i | 1 + 18 files changed, 59 insertions(+), 253 deletions(-) diff --git a/lldb/scripts/Python/python-extensions.swig b/lldb/scripts/Python/python-extensions.swig index 36dac19644bd0..0b23fdd400068 100644 --- a/lldb/scripts/Python/python-extensions.swig +++ b/lldb/scripts/Python/python-extensions.swig @@ -125,32 +125,7 @@ %} } -%extend lldb::SBProcess { - %nothreadallow; - PyObject *lldb::SBProcess::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} %extend lldb::SBSection { - %nothreadallow; - PyObject *lldb::SBSection::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; - %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -180,17 +155,6 @@ {} } %extend lldb::SBSymbol { - %nothreadallow; - PyObject *lldb::SBSymbol::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -205,32 +169,6 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} } -%extend lldb::SBSymbolContext { - %nothreadallow; - PyObject *lldb::SBSymbolContext::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBSymbolContextList { - %nothreadallow; - PyObject *lldb::SBSymbolContextList::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} %extend lldb::SBTarget { %pythoncode %{ @@ -248,44 +186,7 @@ %} } -%extend lldb::SBType { - %nothreadallow; - PyObject *lldb::SBType::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBTypeCategory { - %nothreadallow; - PyObject *lldb::SBTypeCategory::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} %extend lldb::SBTypeFilter { - %nothreadallow; - PyObject *lldb::SBTypeFilter::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -300,70 +201,8 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} } -%extend lldb::SBTypeFormat { - %nothreadallow; - PyObject *lldb::SBTypeFormat::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBTypeMember { - %nothreadallow; - PyObject *lldb::SBTypeMember::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBTypeMemberFunction { - %nothreadallow; - PyObject *lldb::SBTypeMemberFunction::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBTypeEnumMember { - %nothreadallow; - PyObject *lldb::SBTypeEnumMember::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} + %extend lldb::SBTypeNameSpecifier { - %nothreadallow; - PyObject *lldb::SBTypeNameSpecifier::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -378,18 +217,8 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} } + %extend lldb::SBTypeSummary { - %nothreadallow; - PyObject *lldb::SBTypeSummary::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -404,18 +233,8 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} } + %extend lldb::SBTypeSynthetic { - %nothreadallow; - PyObject *lldb::SBTypeSynthetic::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelBrief); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -430,18 +249,8 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} } + %extend lldb::SBThread { - %nothreadallow; - PyObject *lldb::SBThread::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; %pythoncode %{ def __eq__(self, rhs): if not isinstance(rhs, type(self)): @@ -456,64 +265,6 @@ return getattr(_lldb,self.__class__.__name__+"___ne__")(self, rhs) %} } -%extend lldb::SBValue { - %nothreadallow; - PyObject *lldb::SBValue::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBValueList { - %nothreadallow; - PyObject *lldb::SBValueList::__str__ (){ - lldb::SBStream description; - const size_t n = $self->GetSize(); - if (n) - { - for (size_t i=0; i<n; ++i) - $self->GetValueAtIndex(i).GetDescription(description); - } - else - { - description.Printf("<empty> lldb.SBValueList()"); - } - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} -%extend lldb::SBWatchpoint { - %nothreadallow; - PyObject *lldb::SBWatchpoint::__str__ (){ - lldb::SBStream description; - $self->GetDescription (description, lldb::eDescriptionLevelVerbose); - const char *desc = description.GetData(); - size_t desc_len = description.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) - --desc_len; - return PythonString(llvm::StringRef(desc, desc_len)).release(); - } - %clearnothreadallow; -} - - -// %extend lldb::SBDebugger { -// // FIXME: We can't get the callback and baton -// PyObject *lldb::SBDebugger (){ -// // Only call Py_XDECREF if we have a Python object (or NULL) -// if (LLDBSwigPythonCallPythonLogOutputCallback == $self->GetLogOutPutCallback()) -// Py_XDECREF($self->GetCallbackBaton()); -// } -// } %pythoncode %{ diff --git a/lldb/scripts/interface/SBProcess.i b/lldb/scripts/interface/SBProcess.i index c5ebc24686155..ac6a265faec9f 100644 --- a/lldb/scripts/interface/SBProcess.i +++ b/lldb/scripts/interface/SBProcess.i @@ -417,6 +417,8 @@ public: lldb::SBProcessInfo GetProcessInfo(); + STRING_EXTENSION(SBProcess) + #ifdef SWIGPYTHON %pythoncode %{ def __get_is_alive__(self): diff --git a/lldb/scripts/interface/SBSection.i b/lldb/scripts/interface/SBSection.i index c1a84acc4f144..3d1c900917fd8 100644 --- a/lldb/scripts/interface/SBSection.i +++ b/lldb/scripts/interface/SBSection.i @@ -114,6 +114,8 @@ public: bool operator != (const lldb::SBSection &rhs); + STRING_EXTENSION(SBSection) + #ifdef SWIGPYTHON %pythoncode %{ def __iter__(self): diff --git a/lldb/scripts/interface/SBSymbol.i b/lldb/scripts/interface/SBSymbol.i index e5880e66d300f..4e17ab5af0fd6 100644 --- a/lldb/scripts/interface/SBSymbol.i +++ b/lldb/scripts/interface/SBSymbol.i @@ -72,6 +72,8 @@ public: bool operator != (const lldb::SBSymbol &rhs) const; + STRING_EXTENSION(SBSymbol) + #ifdef SWIGPYTHON %pythoncode %{ def get_instructions_from_current_target (self): diff --git a/lldb/scripts/interface/SBSymbolContext.i b/lldb/scripts/interface/SBSymbolContext.i index a6aa4d78bfe39..b6b336516c949 100644 --- a/lldb/scripts/interface/SBSymbolContext.i +++ b/lldb/scripts/interface/SBSymbolContext.i @@ -81,6 +81,7 @@ public: bool GetDescription (lldb::SBStream &description); + STRING_EXTENSION(SBSymbolContext) #ifdef SWIGPYTHON %pythoncode %{ diff --git a/lldb/scripts/interface/SBSymbolContextList.i b/lldb/scripts/interface/SBSymbolContextList.i index 54adc659fa212..f5adcfcebfb56 100644 --- a/lldb/scripts/interface/SBSymbolContextList.i +++ b/lldb/scripts/interface/SBSymbolContextList.i @@ -60,6 +60,8 @@ public: void Clear(); + STRING_EXTENSION(SBSymbolContextList) + #ifdef SWIGPYTHON %pythoncode %{ def __iter__(self): diff --git a/lldb/scripts/interface/SBThread.i b/lldb/scripts/interface/SBThread.i index c1c045487fc1d..95b15b182ec26 100644 --- a/lldb/scripts/interface/SBThread.i +++ b/lldb/scripts/interface/SBThread.i @@ -402,6 +402,8 @@ public: bool SafeToCallFunctions (); + STRING_EXTENSION(SBThread) + #ifdef SWIGPYTHON %pythoncode %{ def __iter__(self): diff --git a/lldb/scripts/interface/SBType.i b/lldb/scripts/interface/SBType.i index d9da9e39b9560..3cd82452084b4 100644 --- a/lldb/scripts/interface/SBType.i +++ b/lldb/scripts/interface/SBType.i @@ -43,6 +43,8 @@ public: uint32_t GetBitfieldSizeInBits(); + STRING_EXTENSION_LEVEL(SBTypeMember, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ name = property(GetName, None, doc='''A read only property that returns the name for this member as a string.''') @@ -100,6 +102,7 @@ public: GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level); + STRING_EXTENSION_LEVEL(SBTypeMemberFunction, lldb::eDescriptionLevelBrief) protected: lldb::TypeMemberFunctionImplSP m_opaque_sp; }; @@ -314,6 +317,8 @@ public: bool operator!=(lldb::SBType &rhs); + STRING_EXTENSION_LEVEL(SBType, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ def template_arg_array(self): diff --git a/lldb/scripts/interface/SBTypeCategory.i b/lldb/scripts/interface/SBTypeCategory.i index 43fe9faf70f52..b762bf8a95a36 100644 --- a/lldb/scripts/interface/SBTypeCategory.i +++ b/lldb/scripts/interface/SBTypeCategory.i @@ -124,6 +124,8 @@ namespace lldb { bool DeleteTypeSynthetic (lldb::SBTypeNameSpecifier); + STRING_EXTENSION_LEVEL(SBTypeCategory, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ diff --git a/lldb/scripts/interface/SBTypeEnumMember.i b/lldb/scripts/interface/SBTypeEnumMember.i index b2d8617117823..006bdeaa8cee1 100644 --- a/lldb/scripts/interface/SBTypeEnumMember.i +++ b/lldb/scripts/interface/SBTypeEnumMember.i @@ -43,6 +43,7 @@ public: GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level); + STRING_EXTENSION_LEVEL(SBTypeEnumMember, lldb::eDescriptionLevelBrief) #ifdef SWIGPYTHON %pythoncode %{ name = property(GetName, None, doc='''A read only property that returns the name for this enum member as a string.''') diff --git a/lldb/scripts/interface/SBTypeFilter.i b/lldb/scripts/interface/SBTypeFilter.i index 3759e0a23d418..c1d282c6d4fbe 100644 --- a/lldb/scripts/interface/SBTypeFilter.i +++ b/lldb/scripts/interface/SBTypeFilter.i @@ -61,6 +61,8 @@ namespace lldb { bool operator != (lldb::SBTypeFilter &rhs); + STRING_EXTENSION_LEVEL(SBTypeFilter, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ options = property(GetOptions, SetOptions) diff --git a/lldb/scripts/interface/SBTypeFormat.i b/lldb/scripts/interface/SBTypeFormat.i index 5efd135b73261..765a2a7bb99dc 100644 --- a/lldb/scripts/interface/SBTypeFormat.i +++ b/lldb/scripts/interface/SBTypeFormat.i @@ -61,6 +61,8 @@ namespace lldb { bool operator != (lldb::SBTypeFormat &rhs); + STRING_EXTENSION_LEVEL(SBTypeFormat, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ format = property(GetFormat, SetFormat) diff --git a/lldb/scripts/interface/SBTypeNameSpecifier.i b/lldb/scripts/interface/SBTypeNameSpecifier.i index bb16e86b0bc50..772f7c174093f 100644 --- a/lldb/scripts/interface/SBTypeNameSpecifier.i +++ b/lldb/scripts/interface/SBTypeNameSpecifier.i @@ -53,6 +53,8 @@ namespace lldb { bool operator != (lldb::SBTypeNameSpecifier &rhs); + STRING_EXTENSION_LEVEL(SBTypeNameSpecifier, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ name = property(GetName) diff --git a/lldb/scripts/interface/SBTypeSummary.i b/lldb/scripts/interface/SBTypeSummary.i index 225a404cf73c4..adcc79b5a6ee8 100644 --- a/lldb/scripts/interface/SBTypeSummary.i +++ b/lldb/scripts/interface/SBTypeSummary.i @@ -101,6 +101,8 @@ namespace lldb { bool operator != (lldb::SBTypeSummary &rhs); + STRING_EXTENSION_LEVEL(SBTypeSummary, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ options = property(GetOptions, SetOptions) diff --git a/lldb/scripts/interface/SBTypeSynthetic.i b/lldb/scripts/interface/SBTypeSynthetic.i index d9d75e4c9efae..f57139ebf9f17 100644 --- a/lldb/scripts/interface/SBTypeSynthetic.i +++ b/lldb/scripts/interface/SBTypeSynthetic.i @@ -63,6 +63,8 @@ namespace lldb { bool operator != (lldb::SBTypeSynthetic &rhs); + STRING_EXTENSION_LEVEL(SBTypeSynthetic, lldb::eDescriptionLevelBrief) + #ifdef SWIGPYTHON %pythoncode %{ options = property(GetOptions, SetOptions) diff --git a/lldb/scripts/interface/SBValue.i b/lldb/scripts/interface/SBValue.i index 8647854e89c15..fb899805c3951 100644 --- a/lldb/scripts/interface/SBValue.i +++ b/lldb/scripts/interface/SBValue.i @@ -440,6 +440,8 @@ public: const SBExpressionOptions &options, const char *name) const; + STRING_EXTENSION(SBValue) + #ifdef SWIGPYTHON %pythoncode %{ def __get_dynamic__ (self): diff --git a/lldb/scripts/interface/SBValueList.i b/lldb/scripts/interface/SBValueList.i index 56ef19054e4c5..17ba2056f0c23 100644 --- a/lldb/scripts/interface/SBValueList.i +++ b/lldb/scripts/interface/SBValueList.i @@ -101,6 +101,29 @@ public: lldb::SBValue GetFirstValueByName (const char* name) const; + %extend { + %nothreadallow; + std::string lldb::SBValueList::__str__ (){ + lldb::SBStream description; + const size_t n = $self->GetSize(); + if (n) + { + for (size_t i=0; i<n; ++i) + $self->GetValueAtIndex(i).GetDescription(description); + } + else + { + description.Printf("<empty> lldb.SBValueList()"); + } + const char *desc = description.GetData(); + size_t desc_len = description.GetSize(); + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) + --desc_len; + return std::string(desc, desc_len); + } + %clearnothreadallow; + } + #ifdef SWIGPYTHON %pythoncode %{ def __iter__(self): diff --git a/lldb/scripts/interface/SBWatchpoint.i b/lldb/scripts/interface/SBWatchpoint.i index e11c4f213ca2e..cb0bc5f9859ac 100644 --- a/lldb/scripts/interface/SBWatchpoint.i +++ b/lldb/scripts/interface/SBWatchpoint.i @@ -90,6 +90,7 @@ public: static lldb::SBWatchpoint GetWatchpointFromEvent (const lldb::SBEvent& event); + STRING_EXTENSION_LEVEL(SBWatchpoint, lldb::eDescriptionLevelVerbose) }; } // namespace lldb From 17a79c5df5f5f0b682585153ef7aee2e74b5adb8 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 9 Jan 2020 08:13:25 -0800 Subject: [PATCH 576/582] [lldb/SWIG] Add missing '\' in macros again Making the string conversion operator a macro unintentionally dropped the backslash before '\n' and '\r' and was therefore incorrectly stripping 'n' and 'r' from the object description. (cherry picked from commit 93a1e9c90c96a9130352bf358d7777f0379ebb48) --- lldb/scripts/macros.swig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/scripts/macros.swig b/lldb/scripts/macros.swig index e0756c2f17932..0387f27f3cb9b 100644 --- a/lldb/scripts/macros.swig +++ b/lldb/scripts/macros.swig @@ -6,7 +6,7 @@ $self->GetDescription (stream, Level); const char *desc = stream.GetData(); size_t desc_len = stream.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == 'n' || desc[desc_len-1] == 'r')) { + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) { --desc_len; } return std::string(desc, desc_len); @@ -23,7 +23,7 @@ $self->GetDescription (stream); const char *desc = stream.GetData(); size_t desc_len = stream.GetSize(); - if (desc_len > 0 && (desc[desc_len-1] == 'n' || desc[desc_len-1] == 'r')) { + if (desc_len > 0 && (desc[desc_len-1] == '\n' || desc[desc_len-1] == '\r')) { --desc_len; } return std::string(desc, desc_len); From 25b00e62b46440cdba9f2e5be79e1f733bf12624 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 9 Jan 2020 07:57:59 -0800 Subject: [PATCH 577/582] [lldb/Bindings] Move bindings into their own subdirectory All the code required to generate the language bindings for Python and Lua lives under scripts, even though the majority of this code aren't scripts at all, and surrounded by scripts that are totally unrelated. I've reorganized these files and moved everything related to the language bindings into a new top-level directory named bindings. This makes the corresponding files self contained and much more discoverable. Differential revision: https://reviews.llvm.org/D72437 (cherry picked from commit 6498aff249a1c3c6bad33137df3b90e2973722d6) --- lldb/CMakeLists.txt | 10 +++++----- lldb/{scripts => bindings}/CMakeLists.txt | 6 +++--- lldb/{scripts => bindings}/headers.swig | 0 lldb/{scripts => bindings}/interface/SBAddress.i | 0 lldb/{scripts => bindings}/interface/SBAttachInfo.i | 0 lldb/{scripts => bindings}/interface/SBBlock.i | 0 lldb/{scripts => bindings}/interface/SBBreakpoint.i | 0 .../interface/SBBreakpointLocation.i | 0 .../{scripts => bindings}/interface/SBBreakpointName.i | 0 lldb/{scripts => bindings}/interface/SBBroadcaster.i | 0 .../interface/SBCommandInterpreter.i | 0 .../interface/SBCommandReturnObject.i | 0 lldb/{scripts => bindings}/interface/SBCommunication.i | 0 lldb/{scripts => bindings}/interface/SBCompileUnit.i | 0 lldb/{scripts => bindings}/interface/SBData.i | 0 lldb/{scripts => bindings}/interface/SBDebugger.i | 0 lldb/{scripts => bindings}/interface/SBDeclaration.i | 0 lldb/{scripts => bindings}/interface/SBError.i | 0 lldb/{scripts => bindings}/interface/SBEvent.i | 0 .../interface/SBExecutionContext.i | 0 .../interface/SBExpressionOptions.i | 0 lldb/{scripts => bindings}/interface/SBFile.i | 0 lldb/{scripts => bindings}/interface/SBFileSpec.i | 0 lldb/{scripts => bindings}/interface/SBFileSpecList.i | 0 lldb/{scripts => bindings}/interface/SBFrame.i | 0 lldb/{scripts => bindings}/interface/SBFunction.i | 0 lldb/{scripts => bindings}/interface/SBHostOS.i | 0 lldb/{scripts => bindings}/interface/SBInstruction.i | 0 .../interface/SBInstructionList.i | 0 .../interface/SBLanguageRuntime.i | 0 lldb/{scripts => bindings}/interface/SBLaunchInfo.i | 0 lldb/{scripts => bindings}/interface/SBLineEntry.i | 0 lldb/{scripts => bindings}/interface/SBListener.i | 0 .../interface/SBMemoryRegionInfo.i | 0 .../interface/SBMemoryRegionInfoList.i | 0 lldb/{scripts => bindings}/interface/SBModule.i | 0 lldb/{scripts => bindings}/interface/SBModuleSpec.i | 0 lldb/{scripts => bindings}/interface/SBPlatform.i | 0 lldb/{scripts => bindings}/interface/SBProcess.i | 0 lldb/{scripts => bindings}/interface/SBProcessInfo.i | 0 lldb/{scripts => bindings}/interface/SBQueue.i | 0 lldb/{scripts => bindings}/interface/SBQueueItem.i | 0 lldb/{scripts => bindings}/interface/SBSection.i | 0 lldb/{scripts => bindings}/interface/SBSourceManager.i | 0 lldb/{scripts => bindings}/interface/SBStream.i | 0 lldb/{scripts => bindings}/interface/SBStringList.i | 0 .../{scripts => bindings}/interface/SBStructuredData.i | 0 lldb/{scripts => bindings}/interface/SBSymbol.i | 0 lldb/{scripts => bindings}/interface/SBSymbolContext.i | 0 .../interface/SBSymbolContextList.i | 0 lldb/{scripts => bindings}/interface/SBTarget.i | 0 lldb/{scripts => bindings}/interface/SBThread.i | 0 .../interface/SBThreadCollection.i | 0 lldb/{scripts => bindings}/interface/SBThreadPlan.i | 0 lldb/{scripts => bindings}/interface/SBTrace.i | 0 lldb/{scripts => bindings}/interface/SBTraceOptions.i | 0 lldb/{scripts => bindings}/interface/SBType.i | 0 lldb/{scripts => bindings}/interface/SBTypeCategory.i | 0 .../{scripts => bindings}/interface/SBTypeEnumMember.i | 0 lldb/{scripts => bindings}/interface/SBTypeFilter.i | 0 lldb/{scripts => bindings}/interface/SBTypeFormat.i | 0 .../interface/SBTypeNameSpecifier.i | 0 lldb/{scripts => bindings}/interface/SBTypeSummary.i | 0 lldb/{scripts => bindings}/interface/SBTypeSynthetic.i | 0 lldb/{scripts => bindings}/interface/SBUnixSignals.i | 0 lldb/{scripts => bindings}/interface/SBValue.i | 0 lldb/{scripts => bindings}/interface/SBValueList.i | 0 .../interface/SBVariablesOptions.i | 0 lldb/{scripts => bindings}/interface/SBWatchpoint.i | 0 lldb/{scripts => bindings}/interfaces.swig | 5 ++--- lldb/{scripts/lldb_lua.swig => bindings/lua.swig} | 0 lldb/{scripts => bindings}/macros.swig | 0 lldb/{scripts/lldb.swig => bindings/python.swig} | 2 +- .../Python => bindings/python}/createPythonInit.py | 0 .../Python => bindings/python}/python-extensions.swig | 0 .../python}/python-swigsafecast.swig | 0 .../Python => bindings/python}/python-typemaps.swig | 0 .../Python => bindings/python}/python-wrapper.swig | 0 lldb/docs/CMakeLists.txt | 4 ++-- lldb/source/API/CMakeLists.txt | 8 ++++---- 80 files changed, 17 insertions(+), 18 deletions(-) rename lldb/{scripts => bindings}/CMakeLists.txt (93%) rename lldb/{scripts => bindings}/headers.swig (100%) rename lldb/{scripts => bindings}/interface/SBAddress.i (100%) rename lldb/{scripts => bindings}/interface/SBAttachInfo.i (100%) rename lldb/{scripts => bindings}/interface/SBBlock.i (100%) rename lldb/{scripts => bindings}/interface/SBBreakpoint.i (100%) rename lldb/{scripts => bindings}/interface/SBBreakpointLocation.i (100%) rename lldb/{scripts => bindings}/interface/SBBreakpointName.i (100%) rename lldb/{scripts => bindings}/interface/SBBroadcaster.i (100%) rename lldb/{scripts => bindings}/interface/SBCommandInterpreter.i (100%) rename lldb/{scripts => bindings}/interface/SBCommandReturnObject.i (100%) rename lldb/{scripts => bindings}/interface/SBCommunication.i (100%) rename lldb/{scripts => bindings}/interface/SBCompileUnit.i (100%) rename lldb/{scripts => bindings}/interface/SBData.i (100%) rename lldb/{scripts => bindings}/interface/SBDebugger.i (100%) rename lldb/{scripts => bindings}/interface/SBDeclaration.i (100%) rename lldb/{scripts => bindings}/interface/SBError.i (100%) rename lldb/{scripts => bindings}/interface/SBEvent.i (100%) rename lldb/{scripts => bindings}/interface/SBExecutionContext.i (100%) rename lldb/{scripts => bindings}/interface/SBExpressionOptions.i (100%) rename lldb/{scripts => bindings}/interface/SBFile.i (100%) rename lldb/{scripts => bindings}/interface/SBFileSpec.i (100%) rename lldb/{scripts => bindings}/interface/SBFileSpecList.i (100%) rename lldb/{scripts => bindings}/interface/SBFrame.i (100%) rename lldb/{scripts => bindings}/interface/SBFunction.i (100%) rename lldb/{scripts => bindings}/interface/SBHostOS.i (100%) rename lldb/{scripts => bindings}/interface/SBInstruction.i (100%) rename lldb/{scripts => bindings}/interface/SBInstructionList.i (100%) rename lldb/{scripts => bindings}/interface/SBLanguageRuntime.i (100%) rename lldb/{scripts => bindings}/interface/SBLaunchInfo.i (100%) rename lldb/{scripts => bindings}/interface/SBLineEntry.i (100%) rename lldb/{scripts => bindings}/interface/SBListener.i (100%) rename lldb/{scripts => bindings}/interface/SBMemoryRegionInfo.i (100%) rename lldb/{scripts => bindings}/interface/SBMemoryRegionInfoList.i (100%) rename lldb/{scripts => bindings}/interface/SBModule.i (100%) rename lldb/{scripts => bindings}/interface/SBModuleSpec.i (100%) rename lldb/{scripts => bindings}/interface/SBPlatform.i (100%) rename lldb/{scripts => bindings}/interface/SBProcess.i (100%) rename lldb/{scripts => bindings}/interface/SBProcessInfo.i (100%) rename lldb/{scripts => bindings}/interface/SBQueue.i (100%) rename lldb/{scripts => bindings}/interface/SBQueueItem.i (100%) rename lldb/{scripts => bindings}/interface/SBSection.i (100%) rename lldb/{scripts => bindings}/interface/SBSourceManager.i (100%) rename lldb/{scripts => bindings}/interface/SBStream.i (100%) rename lldb/{scripts => bindings}/interface/SBStringList.i (100%) rename lldb/{scripts => bindings}/interface/SBStructuredData.i (100%) rename lldb/{scripts => bindings}/interface/SBSymbol.i (100%) rename lldb/{scripts => bindings}/interface/SBSymbolContext.i (100%) rename lldb/{scripts => bindings}/interface/SBSymbolContextList.i (100%) rename lldb/{scripts => bindings}/interface/SBTarget.i (100%) rename lldb/{scripts => bindings}/interface/SBThread.i (100%) rename lldb/{scripts => bindings}/interface/SBThreadCollection.i (100%) rename lldb/{scripts => bindings}/interface/SBThreadPlan.i (100%) rename lldb/{scripts => bindings}/interface/SBTrace.i (100%) rename lldb/{scripts => bindings}/interface/SBTraceOptions.i (100%) rename lldb/{scripts => bindings}/interface/SBType.i (100%) rename lldb/{scripts => bindings}/interface/SBTypeCategory.i (100%) rename lldb/{scripts => bindings}/interface/SBTypeEnumMember.i (100%) rename lldb/{scripts => bindings}/interface/SBTypeFilter.i (100%) rename lldb/{scripts => bindings}/interface/SBTypeFormat.i (100%) rename lldb/{scripts => bindings}/interface/SBTypeNameSpecifier.i (100%) rename lldb/{scripts => bindings}/interface/SBTypeSummary.i (100%) rename lldb/{scripts => bindings}/interface/SBTypeSynthetic.i (100%) rename lldb/{scripts => bindings}/interface/SBUnixSignals.i (100%) rename lldb/{scripts => bindings}/interface/SBValue.i (100%) rename lldb/{scripts => bindings}/interface/SBValueList.i (100%) rename lldb/{scripts => bindings}/interface/SBVariablesOptions.i (100%) rename lldb/{scripts => bindings}/interface/SBWatchpoint.i (100%) rename lldb/{scripts => bindings}/interfaces.swig (99%) rename lldb/{scripts/lldb_lua.swig => bindings/lua.swig} (100%) rename lldb/{scripts => bindings}/macros.swig (100%) rename lldb/{scripts/lldb.swig => bindings/python.swig} (98%) rename lldb/{scripts/Python => bindings/python}/createPythonInit.py (100%) rename lldb/{scripts/Python => bindings/python}/python-extensions.swig (100%) rename lldb/{scripts/Python => bindings/python}/python-swigsafecast.swig (100%) rename lldb/{scripts/Python => bindings/python}/python-typemaps.swig (100%) rename lldb/{scripts/Python => bindings/python}/python-wrapper.swig (100%) diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 6170ab625c54d..573b8556989e4 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -56,7 +56,7 @@ if (LLDB_ENABLE_PYTHON) endif () if (LLDB_ENABLE_PYTHON OR LLDB_ENABLE_LUA) - add_subdirectory(scripts) + add_subdirectory(bindings) endif () # We need the headers generated by instrinsics_gen before we can compile @@ -97,7 +97,7 @@ if(LLDB_INCLUDE_TESTS) endif() if (LLDB_ENABLE_PYTHON) - get_target_property(lldb_scripts_dir swig_wrapper BINARY_DIR) + get_target_property(lldb_bindings_dir swig_wrapper BINARY_DIR) if(LLDB_BUILD_FRAMEWORK) set(lldb_python_build_path "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Resources/Python/lldb") @@ -109,7 +109,7 @@ if (LLDB_ENABLE_PYTHON) # to liblldb.so for the Python API(hardlink on Windows). add_custom_target(finish_swig ALL VERBATIM COMMAND ${CMAKE_COMMAND} -E make_directory ${lldb_python_build_path} - DEPENDS ${lldb_scripts_dir}/lldb.py + DEPENDS ${lldb_bindings_dir}/lldb.py COMMENT "Python script sym-linking LLDB Python API") if(NOT LLDB_USE_SYSTEM_SIX) @@ -121,7 +121,7 @@ if (LLDB_ENABLE_PYTHON) add_custom_command(TARGET finish_swig POST_BUILD VERBATIM COMMAND ${CMAKE_COMMAND} -E copy - "${lldb_scripts_dir}/lldb.py" + "${lldb_bindings_dir}/lldb.py" "${lldb_python_build_path}/__init__.py") function(create_python_package pkg_dir) @@ -131,7 +131,7 @@ if (LLDB_ENABLE_PYTHON) endif() if(NOT ARG_NOINIT) set(init_cmd COMMAND ${PYTHON_EXECUTABLE} - "${LLDB_SOURCE_DIR}/scripts/Python/createPythonInit.py" + "${LLDB_SOURCE_DIR}/bindings/python/createPythonInit.py" "${pkg_dir}" ${ARG_FILES}) endif() add_custom_command(TARGET finish_swig POST_BUILD VERBATIM diff --git a/lldb/scripts/CMakeLists.txt b/lldb/bindings/CMakeLists.txt similarity index 93% rename from lldb/scripts/CMakeLists.txt rename to lldb/bindings/CMakeLists.txt index 515c63293bc20..92ae402c478e9 100644 --- a/lldb/scripts/CMakeLists.txt +++ b/lldb/bindings/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB SWIG_INTERFACES interface/*.i) +file(GLOB SWIG_INTERFACES interfaces/*.i) file(GLOB_RECURSE SWIG_SOURCES *.swig) file(GLOB SWIG_HEADERS ${LLDB_SOURCE_DIR}/include/lldb/API/*.h @@ -46,7 +46,7 @@ if (LLDB_ENABLE_PYTHON) -python -threads -o ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp - ${LLDB_SOURCE_DIR}/scripts/lldb.swig + ${LLDB_SOURCE_DIR}/bindings/python.swig VERBATIM COMMENT "Builds LLDB Python wrapper") @@ -67,7 +67,7 @@ if (LLDB_ENABLE_LUA) -lua -w503 -o ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapLua.cpp - ${LLDB_SOURCE_DIR}/scripts/lldb_lua.swig + ${LLDB_SOURCE_DIR}/bindings/lua.swig VERBATIM COMMENT "Builds LLDB Lua wrapper") diff --git a/lldb/scripts/headers.swig b/lldb/bindings/headers.swig similarity index 100% rename from lldb/scripts/headers.swig rename to lldb/bindings/headers.swig diff --git a/lldb/scripts/interface/SBAddress.i b/lldb/bindings/interface/SBAddress.i similarity index 100% rename from lldb/scripts/interface/SBAddress.i rename to lldb/bindings/interface/SBAddress.i diff --git a/lldb/scripts/interface/SBAttachInfo.i b/lldb/bindings/interface/SBAttachInfo.i similarity index 100% rename from lldb/scripts/interface/SBAttachInfo.i rename to lldb/bindings/interface/SBAttachInfo.i diff --git a/lldb/scripts/interface/SBBlock.i b/lldb/bindings/interface/SBBlock.i similarity index 100% rename from lldb/scripts/interface/SBBlock.i rename to lldb/bindings/interface/SBBlock.i diff --git a/lldb/scripts/interface/SBBreakpoint.i b/lldb/bindings/interface/SBBreakpoint.i similarity index 100% rename from lldb/scripts/interface/SBBreakpoint.i rename to lldb/bindings/interface/SBBreakpoint.i diff --git a/lldb/scripts/interface/SBBreakpointLocation.i b/lldb/bindings/interface/SBBreakpointLocation.i similarity index 100% rename from lldb/scripts/interface/SBBreakpointLocation.i rename to lldb/bindings/interface/SBBreakpointLocation.i diff --git a/lldb/scripts/interface/SBBreakpointName.i b/lldb/bindings/interface/SBBreakpointName.i similarity index 100% rename from lldb/scripts/interface/SBBreakpointName.i rename to lldb/bindings/interface/SBBreakpointName.i diff --git a/lldb/scripts/interface/SBBroadcaster.i b/lldb/bindings/interface/SBBroadcaster.i similarity index 100% rename from lldb/scripts/interface/SBBroadcaster.i rename to lldb/bindings/interface/SBBroadcaster.i diff --git a/lldb/scripts/interface/SBCommandInterpreter.i b/lldb/bindings/interface/SBCommandInterpreter.i similarity index 100% rename from lldb/scripts/interface/SBCommandInterpreter.i rename to lldb/bindings/interface/SBCommandInterpreter.i diff --git a/lldb/scripts/interface/SBCommandReturnObject.i b/lldb/bindings/interface/SBCommandReturnObject.i similarity index 100% rename from lldb/scripts/interface/SBCommandReturnObject.i rename to lldb/bindings/interface/SBCommandReturnObject.i diff --git a/lldb/scripts/interface/SBCommunication.i b/lldb/bindings/interface/SBCommunication.i similarity index 100% rename from lldb/scripts/interface/SBCommunication.i rename to lldb/bindings/interface/SBCommunication.i diff --git a/lldb/scripts/interface/SBCompileUnit.i b/lldb/bindings/interface/SBCompileUnit.i similarity index 100% rename from lldb/scripts/interface/SBCompileUnit.i rename to lldb/bindings/interface/SBCompileUnit.i diff --git a/lldb/scripts/interface/SBData.i b/lldb/bindings/interface/SBData.i similarity index 100% rename from lldb/scripts/interface/SBData.i rename to lldb/bindings/interface/SBData.i diff --git a/lldb/scripts/interface/SBDebugger.i b/lldb/bindings/interface/SBDebugger.i similarity index 100% rename from lldb/scripts/interface/SBDebugger.i rename to lldb/bindings/interface/SBDebugger.i diff --git a/lldb/scripts/interface/SBDeclaration.i b/lldb/bindings/interface/SBDeclaration.i similarity index 100% rename from lldb/scripts/interface/SBDeclaration.i rename to lldb/bindings/interface/SBDeclaration.i diff --git a/lldb/scripts/interface/SBError.i b/lldb/bindings/interface/SBError.i similarity index 100% rename from lldb/scripts/interface/SBError.i rename to lldb/bindings/interface/SBError.i diff --git a/lldb/scripts/interface/SBEvent.i b/lldb/bindings/interface/SBEvent.i similarity index 100% rename from lldb/scripts/interface/SBEvent.i rename to lldb/bindings/interface/SBEvent.i diff --git a/lldb/scripts/interface/SBExecutionContext.i b/lldb/bindings/interface/SBExecutionContext.i similarity index 100% rename from lldb/scripts/interface/SBExecutionContext.i rename to lldb/bindings/interface/SBExecutionContext.i diff --git a/lldb/scripts/interface/SBExpressionOptions.i b/lldb/bindings/interface/SBExpressionOptions.i similarity index 100% rename from lldb/scripts/interface/SBExpressionOptions.i rename to lldb/bindings/interface/SBExpressionOptions.i diff --git a/lldb/scripts/interface/SBFile.i b/lldb/bindings/interface/SBFile.i similarity index 100% rename from lldb/scripts/interface/SBFile.i rename to lldb/bindings/interface/SBFile.i diff --git a/lldb/scripts/interface/SBFileSpec.i b/lldb/bindings/interface/SBFileSpec.i similarity index 100% rename from lldb/scripts/interface/SBFileSpec.i rename to lldb/bindings/interface/SBFileSpec.i diff --git a/lldb/scripts/interface/SBFileSpecList.i b/lldb/bindings/interface/SBFileSpecList.i similarity index 100% rename from lldb/scripts/interface/SBFileSpecList.i rename to lldb/bindings/interface/SBFileSpecList.i diff --git a/lldb/scripts/interface/SBFrame.i b/lldb/bindings/interface/SBFrame.i similarity index 100% rename from lldb/scripts/interface/SBFrame.i rename to lldb/bindings/interface/SBFrame.i diff --git a/lldb/scripts/interface/SBFunction.i b/lldb/bindings/interface/SBFunction.i similarity index 100% rename from lldb/scripts/interface/SBFunction.i rename to lldb/bindings/interface/SBFunction.i diff --git a/lldb/scripts/interface/SBHostOS.i b/lldb/bindings/interface/SBHostOS.i similarity index 100% rename from lldb/scripts/interface/SBHostOS.i rename to lldb/bindings/interface/SBHostOS.i diff --git a/lldb/scripts/interface/SBInstruction.i b/lldb/bindings/interface/SBInstruction.i similarity index 100% rename from lldb/scripts/interface/SBInstruction.i rename to lldb/bindings/interface/SBInstruction.i diff --git a/lldb/scripts/interface/SBInstructionList.i b/lldb/bindings/interface/SBInstructionList.i similarity index 100% rename from lldb/scripts/interface/SBInstructionList.i rename to lldb/bindings/interface/SBInstructionList.i diff --git a/lldb/scripts/interface/SBLanguageRuntime.i b/lldb/bindings/interface/SBLanguageRuntime.i similarity index 100% rename from lldb/scripts/interface/SBLanguageRuntime.i rename to lldb/bindings/interface/SBLanguageRuntime.i diff --git a/lldb/scripts/interface/SBLaunchInfo.i b/lldb/bindings/interface/SBLaunchInfo.i similarity index 100% rename from lldb/scripts/interface/SBLaunchInfo.i rename to lldb/bindings/interface/SBLaunchInfo.i diff --git a/lldb/scripts/interface/SBLineEntry.i b/lldb/bindings/interface/SBLineEntry.i similarity index 100% rename from lldb/scripts/interface/SBLineEntry.i rename to lldb/bindings/interface/SBLineEntry.i diff --git a/lldb/scripts/interface/SBListener.i b/lldb/bindings/interface/SBListener.i similarity index 100% rename from lldb/scripts/interface/SBListener.i rename to lldb/bindings/interface/SBListener.i diff --git a/lldb/scripts/interface/SBMemoryRegionInfo.i b/lldb/bindings/interface/SBMemoryRegionInfo.i similarity index 100% rename from lldb/scripts/interface/SBMemoryRegionInfo.i rename to lldb/bindings/interface/SBMemoryRegionInfo.i diff --git a/lldb/scripts/interface/SBMemoryRegionInfoList.i b/lldb/bindings/interface/SBMemoryRegionInfoList.i similarity index 100% rename from lldb/scripts/interface/SBMemoryRegionInfoList.i rename to lldb/bindings/interface/SBMemoryRegionInfoList.i diff --git a/lldb/scripts/interface/SBModule.i b/lldb/bindings/interface/SBModule.i similarity index 100% rename from lldb/scripts/interface/SBModule.i rename to lldb/bindings/interface/SBModule.i diff --git a/lldb/scripts/interface/SBModuleSpec.i b/lldb/bindings/interface/SBModuleSpec.i similarity index 100% rename from lldb/scripts/interface/SBModuleSpec.i rename to lldb/bindings/interface/SBModuleSpec.i diff --git a/lldb/scripts/interface/SBPlatform.i b/lldb/bindings/interface/SBPlatform.i similarity index 100% rename from lldb/scripts/interface/SBPlatform.i rename to lldb/bindings/interface/SBPlatform.i diff --git a/lldb/scripts/interface/SBProcess.i b/lldb/bindings/interface/SBProcess.i similarity index 100% rename from lldb/scripts/interface/SBProcess.i rename to lldb/bindings/interface/SBProcess.i diff --git a/lldb/scripts/interface/SBProcessInfo.i b/lldb/bindings/interface/SBProcessInfo.i similarity index 100% rename from lldb/scripts/interface/SBProcessInfo.i rename to lldb/bindings/interface/SBProcessInfo.i diff --git a/lldb/scripts/interface/SBQueue.i b/lldb/bindings/interface/SBQueue.i similarity index 100% rename from lldb/scripts/interface/SBQueue.i rename to lldb/bindings/interface/SBQueue.i diff --git a/lldb/scripts/interface/SBQueueItem.i b/lldb/bindings/interface/SBQueueItem.i similarity index 100% rename from lldb/scripts/interface/SBQueueItem.i rename to lldb/bindings/interface/SBQueueItem.i diff --git a/lldb/scripts/interface/SBSection.i b/lldb/bindings/interface/SBSection.i similarity index 100% rename from lldb/scripts/interface/SBSection.i rename to lldb/bindings/interface/SBSection.i diff --git a/lldb/scripts/interface/SBSourceManager.i b/lldb/bindings/interface/SBSourceManager.i similarity index 100% rename from lldb/scripts/interface/SBSourceManager.i rename to lldb/bindings/interface/SBSourceManager.i diff --git a/lldb/scripts/interface/SBStream.i b/lldb/bindings/interface/SBStream.i similarity index 100% rename from lldb/scripts/interface/SBStream.i rename to lldb/bindings/interface/SBStream.i diff --git a/lldb/scripts/interface/SBStringList.i b/lldb/bindings/interface/SBStringList.i similarity index 100% rename from lldb/scripts/interface/SBStringList.i rename to lldb/bindings/interface/SBStringList.i diff --git a/lldb/scripts/interface/SBStructuredData.i b/lldb/bindings/interface/SBStructuredData.i similarity index 100% rename from lldb/scripts/interface/SBStructuredData.i rename to lldb/bindings/interface/SBStructuredData.i diff --git a/lldb/scripts/interface/SBSymbol.i b/lldb/bindings/interface/SBSymbol.i similarity index 100% rename from lldb/scripts/interface/SBSymbol.i rename to lldb/bindings/interface/SBSymbol.i diff --git a/lldb/scripts/interface/SBSymbolContext.i b/lldb/bindings/interface/SBSymbolContext.i similarity index 100% rename from lldb/scripts/interface/SBSymbolContext.i rename to lldb/bindings/interface/SBSymbolContext.i diff --git a/lldb/scripts/interface/SBSymbolContextList.i b/lldb/bindings/interface/SBSymbolContextList.i similarity index 100% rename from lldb/scripts/interface/SBSymbolContextList.i rename to lldb/bindings/interface/SBSymbolContextList.i diff --git a/lldb/scripts/interface/SBTarget.i b/lldb/bindings/interface/SBTarget.i similarity index 100% rename from lldb/scripts/interface/SBTarget.i rename to lldb/bindings/interface/SBTarget.i diff --git a/lldb/scripts/interface/SBThread.i b/lldb/bindings/interface/SBThread.i similarity index 100% rename from lldb/scripts/interface/SBThread.i rename to lldb/bindings/interface/SBThread.i diff --git a/lldb/scripts/interface/SBThreadCollection.i b/lldb/bindings/interface/SBThreadCollection.i similarity index 100% rename from lldb/scripts/interface/SBThreadCollection.i rename to lldb/bindings/interface/SBThreadCollection.i diff --git a/lldb/scripts/interface/SBThreadPlan.i b/lldb/bindings/interface/SBThreadPlan.i similarity index 100% rename from lldb/scripts/interface/SBThreadPlan.i rename to lldb/bindings/interface/SBThreadPlan.i diff --git a/lldb/scripts/interface/SBTrace.i b/lldb/bindings/interface/SBTrace.i similarity index 100% rename from lldb/scripts/interface/SBTrace.i rename to lldb/bindings/interface/SBTrace.i diff --git a/lldb/scripts/interface/SBTraceOptions.i b/lldb/bindings/interface/SBTraceOptions.i similarity index 100% rename from lldb/scripts/interface/SBTraceOptions.i rename to lldb/bindings/interface/SBTraceOptions.i diff --git a/lldb/scripts/interface/SBType.i b/lldb/bindings/interface/SBType.i similarity index 100% rename from lldb/scripts/interface/SBType.i rename to lldb/bindings/interface/SBType.i diff --git a/lldb/scripts/interface/SBTypeCategory.i b/lldb/bindings/interface/SBTypeCategory.i similarity index 100% rename from lldb/scripts/interface/SBTypeCategory.i rename to lldb/bindings/interface/SBTypeCategory.i diff --git a/lldb/scripts/interface/SBTypeEnumMember.i b/lldb/bindings/interface/SBTypeEnumMember.i similarity index 100% rename from lldb/scripts/interface/SBTypeEnumMember.i rename to lldb/bindings/interface/SBTypeEnumMember.i diff --git a/lldb/scripts/interface/SBTypeFilter.i b/lldb/bindings/interface/SBTypeFilter.i similarity index 100% rename from lldb/scripts/interface/SBTypeFilter.i rename to lldb/bindings/interface/SBTypeFilter.i diff --git a/lldb/scripts/interface/SBTypeFormat.i b/lldb/bindings/interface/SBTypeFormat.i similarity index 100% rename from lldb/scripts/interface/SBTypeFormat.i rename to lldb/bindings/interface/SBTypeFormat.i diff --git a/lldb/scripts/interface/SBTypeNameSpecifier.i b/lldb/bindings/interface/SBTypeNameSpecifier.i similarity index 100% rename from lldb/scripts/interface/SBTypeNameSpecifier.i rename to lldb/bindings/interface/SBTypeNameSpecifier.i diff --git a/lldb/scripts/interface/SBTypeSummary.i b/lldb/bindings/interface/SBTypeSummary.i similarity index 100% rename from lldb/scripts/interface/SBTypeSummary.i rename to lldb/bindings/interface/SBTypeSummary.i diff --git a/lldb/scripts/interface/SBTypeSynthetic.i b/lldb/bindings/interface/SBTypeSynthetic.i similarity index 100% rename from lldb/scripts/interface/SBTypeSynthetic.i rename to lldb/bindings/interface/SBTypeSynthetic.i diff --git a/lldb/scripts/interface/SBUnixSignals.i b/lldb/bindings/interface/SBUnixSignals.i similarity index 100% rename from lldb/scripts/interface/SBUnixSignals.i rename to lldb/bindings/interface/SBUnixSignals.i diff --git a/lldb/scripts/interface/SBValue.i b/lldb/bindings/interface/SBValue.i similarity index 100% rename from lldb/scripts/interface/SBValue.i rename to lldb/bindings/interface/SBValue.i diff --git a/lldb/scripts/interface/SBValueList.i b/lldb/bindings/interface/SBValueList.i similarity index 100% rename from lldb/scripts/interface/SBValueList.i rename to lldb/bindings/interface/SBValueList.i diff --git a/lldb/scripts/interface/SBVariablesOptions.i b/lldb/bindings/interface/SBVariablesOptions.i similarity index 100% rename from lldb/scripts/interface/SBVariablesOptions.i rename to lldb/bindings/interface/SBVariablesOptions.i diff --git a/lldb/scripts/interface/SBWatchpoint.i b/lldb/bindings/interface/SBWatchpoint.i similarity index 100% rename from lldb/scripts/interface/SBWatchpoint.i rename to lldb/bindings/interface/SBWatchpoint.i diff --git a/lldb/scripts/interfaces.swig b/lldb/bindings/interfaces.swig similarity index 99% rename from lldb/scripts/interfaces.swig rename to lldb/bindings/interfaces.swig index cc6bb91febdac..780fe34392ff5 100644 --- a/lldb/scripts/interfaces.swig +++ b/lldb/bindings/interfaces.swig @@ -32,8 +32,8 @@ %include "./interface/SBEvent.i" %include "./interface/SBExecutionContext.i" %include "./interface/SBExpressionOptions.i" -%include "./interface/SBFileSpec.i" %include "./interface/SBFile.i" +%include "./interface/SBFileSpec.i" %include "./interface/SBFileSpecList.i" %include "./interface/SBFrame.i" %include "./interface/SBFunction.i" @@ -75,9 +75,8 @@ %include "./interface/SBTypeNameSpecifier.i" %include "./interface/SBTypeSummary.i" %include "./interface/SBTypeSynthetic.i" +%include "./interface/SBUnixSignals.i" %include "./interface/SBValue.i" %include "./interface/SBValueList.i" %include "./interface/SBVariablesOptions.i" %include "./interface/SBWatchpoint.i" -%include "./interface/SBUnixSignals.i" - diff --git a/lldb/scripts/lldb_lua.swig b/lldb/bindings/lua.swig similarity index 100% rename from lldb/scripts/lldb_lua.swig rename to lldb/bindings/lua.swig diff --git a/lldb/scripts/macros.swig b/lldb/bindings/macros.swig similarity index 100% rename from lldb/scripts/macros.swig rename to lldb/bindings/macros.swig diff --git a/lldb/scripts/lldb.swig b/lldb/bindings/python.swig similarity index 98% rename from lldb/scripts/lldb.swig rename to lldb/bindings/python.swig index bebf9bf534a34..cf716da4a477a 100644 --- a/lldb/scripts/lldb.swig +++ b/lldb/bindings/python.swig @@ -117,7 +117,7 @@ def lldb_iter(obj, getsize, getelem): %{ #include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" -#include "../scripts/Python/python-swigsafecast.swig" +#include "../bindings/python/python-swigsafecast.swig" using namespace lldb_private; using namespace lldb_private::python; using namespace lldb; diff --git a/lldb/scripts/Python/createPythonInit.py b/lldb/bindings/python/createPythonInit.py similarity index 100% rename from lldb/scripts/Python/createPythonInit.py rename to lldb/bindings/python/createPythonInit.py diff --git a/lldb/scripts/Python/python-extensions.swig b/lldb/bindings/python/python-extensions.swig similarity index 100% rename from lldb/scripts/Python/python-extensions.swig rename to lldb/bindings/python/python-extensions.swig diff --git a/lldb/scripts/Python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig similarity index 100% rename from lldb/scripts/Python/python-swigsafecast.swig rename to lldb/bindings/python/python-swigsafecast.swig diff --git a/lldb/scripts/Python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig similarity index 100% rename from lldb/scripts/Python/python-typemaps.swig rename to lldb/bindings/python/python-typemaps.swig diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig similarity index 100% rename from lldb/scripts/Python/python-wrapper.swig rename to lldb/bindings/python/python-wrapper.swig diff --git a/lldb/docs/CMakeLists.txt b/lldb/docs/CMakeLists.txt index 0082d004bd0d6..8fa46860e5cec 100644 --- a/lldb/docs/CMakeLists.txt +++ b/lldb/docs/CMakeLists.txt @@ -30,9 +30,9 @@ if (LLDB_ENABLE_PYTHON) # Because we don't build liblldb, epydoc will complain that the import of # _lldb.so failed, but that doesn't prevent it from generating the docs. file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lldb) - get_target_property(lldb_scripts_dir swig_wrapper BINARY_DIR) + get_target_property(lldb_bindings_dir swig_wrapper BINARY_DIR) add_custom_target(lldb-python-doc-package - COMMAND "${CMAKE_COMMAND}" -E copy "${lldb_scripts_dir}/lldb.py" "${CMAKE_CURRENT_BINARY_DIR}/lldb/__init__.py" + COMMAND "${CMAKE_COMMAND}" -E copy "${lldb_bindings_dir}/lldb.py" "${CMAKE_CURRENT_BINARY_DIR}/lldb/__init__.py" COMMENT "Copying lldb.py to pretend package.") add_dependencies(lldb-python-doc-package swig_wrapper) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index eea409bed185a..e0ecf29b502b7 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -5,13 +5,13 @@ endif() get_property(LLDB_ALL_PLUGINS GLOBAL PROPERTY LLDB_PLUGINS) if(LLDB_ENABLE_PYTHON) - get_target_property(lldb_scripts_dir swig_wrapper BINARY_DIR) - set(lldb_python_wrapper ${lldb_scripts_dir}/LLDBWrapPython.cpp) + get_target_property(lldb_bindings_dir swig_wrapper BINARY_DIR) + set(lldb_python_wrapper ${lldb_bindings_dir}/LLDBWrapPython.cpp) endif() if(LLDB_ENABLE_LUA) - get_target_property(lldb_scripts_dir swig_wrapper_lua BINARY_DIR) - set(lldb_lua_wrapper ${lldb_scripts_dir}/LLDBWrapLua.cpp) + get_target_property(lldb_bindings_dir swig_wrapper_lua BINARY_DIR) + set(lldb_lua_wrapper ${lldb_bindings_dir}/LLDBWrapLua.cpp) endif() if(LLDB_BUILD_FRAMEWORK) From dc1058b019410742b75c42426e800458c47d0b7f Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 9 Jan 2020 09:22:04 -0800 Subject: [PATCH 578/582] [lldb/SWIG] Fix capitalization for case sensitive file systems. When moving the Python directory I renamed it to python (lowercase) but didn't update the python.swig file. (cherry picked from commit 5e0bf6772e2ca450d3433fca8b47ce7bac5a6cc7) --- lldb/bindings/python.swig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/bindings/python.swig b/lldb/bindings/python.swig index cf716da4a477a..56fab9ff17951 100644 --- a/lldb/bindings/python.swig +++ b/lldb/bindings/python.swig @@ -111,12 +111,12 @@ def lldb_iter(obj, getsize, getelem): %} %include <std_string.i> -%include "./Python/python-typemaps.swig" +%include "./python/python-typemaps.swig" %include "./macros.swig" %include "./headers.swig" %{ -#include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" +#include "../source/Plugins/ScriptInterpreter/python/PythonDataObjects.h" #include "../bindings/python/python-swigsafecast.swig" using namespace lldb_private; using namespace lldb_private::python; @@ -124,8 +124,8 @@ using namespace lldb; %} %include "./interfaces.swig" -%include "./Python/python-extensions.swig" -%include "./Python/python-wrapper.swig" +%include "./python/python-extensions.swig" +%include "./python/python-wrapper.swig" %pythoncode%{ debugger_unique_id = 0 From 93743a2b468593830f8a7b40e9ed407bcb2f2d36 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <jonas@devlieghere.com> Date: Thu, 9 Jan 2020 09:54:46 -0800 Subject: [PATCH 579/582] [lldb/SWIG] Undo incorrect substitution The Python directory for the script interpreter is still capitalized. (cherry picked from commit 7bbd4076c1984165568c978ff15b77dbfe52b6f0) --- lldb/bindings/python.swig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/bindings/python.swig b/lldb/bindings/python.swig index 56fab9ff17951..b086d436e57ba 100644 --- a/lldb/bindings/python.swig +++ b/lldb/bindings/python.swig @@ -116,7 +116,7 @@ def lldb_iter(obj, getsize, getelem): %include "./headers.swig" %{ -#include "../source/Plugins/ScriptInterpreter/python/PythonDataObjects.h" +#include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" #include "../bindings/python/python-swigsafecast.swig" using namespace lldb_private; using namespace lldb_private::python; From bef57e7d55544f34ce3cc252280d04f29161c7aa Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 9 Jan 2020 12:49:35 -0800 Subject: [PATCH 580/582] [macOS] fix a refactor test by adjusting for AST changes (cherry picked from commit 17269e24f9750bd2cf951cf6bf1c9de746a1ace7) --- clang/lib/Tooling/Refactor/TypeUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Tooling/Refactor/TypeUtils.cpp b/clang/lib/Tooling/Refactor/TypeUtils.cpp index 03c8a5bc45b39..dc365a6d9ebb5 100644 --- a/clang/lib/Tooling/Refactor/TypeUtils.cpp +++ b/clang/lib/Tooling/Refactor/TypeUtils.cpp @@ -133,7 +133,7 @@ static QualType canonicalizeStdOperatorReturnType(const Expr *E, QualType T) { } else return T; for (unsigned I = 0, E = OCE->getNumArgs(); I < E; ++I) { - const Expr *Arg = OCE->getArgs()[I]; + const Expr *Arg = OCE->getArgs()[I]->IgnoreImpCasts(); QualType T = Arg->getType(); if (const auto *ET = dyn_cast<ElaboratedType>(T)) T = ET->desugar(); From afa441599e8ffb30399a7344973fe3e71a792caf Mon Sep 17 00:00:00 2001 From: Julian Lettner <julian.lettner@apple.com> Date: Wed, 4 Dec 2019 15:32:32 -0800 Subject: [PATCH 581/582] Fix `ignore_noninstrumented_modules` on Linux My initial implementation of `ignore_noninstrumented_modules` for Linux (landed in 61592420d3e2a896ff64b6bcd9728dd359d7d479) did not consider that the GNU symbol table could be present but empty. "Empty" means: void of real symbol entries, but potential "terminator/placeholder" symbols that are skipped via `header->symoffset`. In this case `last_symbol` is zero and we should avoid computing `chains[last_symbol - header->symoffset]`. This bug seems to only manifest in combination with `LD_PRELOAD` (counterpart of `DYLD_INSERT_LIBRARIES` for Linux) and for small binaries, (e.g., the "not" utility from the llvm test suite) that have segments without any real symbols. This should fix the remaining failing ASan tests on the Swift Linux CI. rdar://57566645 (cherry picked from commit 2fb2445d99cd38a64d6aa8ff08cb6986d3fa96bf) --- compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp index 4e9a5c60de4fc..43073a3a0c476 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp @@ -621,6 +621,10 @@ class DynamicSegment { last_symbol = Max(buckets[i], last_symbol); } + if (last_symbol < header->symoffset) { + return header->symoffset; + } + // Walk the bucket's chain to add the chain length to the total. uint32_t chain_entry; do { From 84c5ccbfa2666b121a9c93579f9a68f1d2861c72 Mon Sep 17 00:00:00 2001 From: Julian Lettner <julian.lettner@apple.com> Date: Mon, 2 Dec 2019 10:40:16 -0800 Subject: [PATCH 582/582] [TSan] Fix TSan flag for Swift on Linux testing For Swift, we turn on `ignore_noninstrumented_modules` and `ignore_interceptors_accesses` even on Linux (where they are usually disabled). We have to account for that when running LLVM-Swift on Linux. rdar://57436887 (cherry picked from commit eb6441cbc94aaeafa9fe5fca5a96173672bda033) --- compiler-rt/test/sanitizer_common/lit.common.cfg.py | 3 +++ compiler-rt/test/tsan/Unit/lit.site.cfg.py.in | 2 ++ compiler-rt/test/tsan/lit.cfg.py | 3 +++ 3 files changed, 8 insertions(+) diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg.py b/compiler-rt/test/sanitizer_common/lit.common.cfg.py index 3e4edf12ab6d7..5b77e7db60103 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg.py +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg.py @@ -37,6 +37,9 @@ default_tool_options += ['abort_on_error=0'] if config.tool_name == "tsan": default_tool_options += ['ignore_interceptors_accesses=0'] +elif config.host_os == 'Linux' and config.tool_name == 'tsan': + # For Swift, the above also applies on Linux. + default_tool_options += ['ignore_interceptors_accesses=0'] elif config.android: # The same as on Darwin, we default to "abort_on_error=1" which slows down # testing. Also, all existing tests are using "not" instead of "not --crash" diff --git a/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in b/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in index 714501a0d76de..6c81ec54e2458 100644 --- a/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in +++ b/compiler-rt/test/tsan/Unit/lit.site.cfg.py.in @@ -15,6 +15,8 @@ config.test_source_root = config.test_exec_root if config.host_os == 'Darwin': config.parallelism_group = config.darwin_sanitizer_parallelism_group_func +# For Swift, the following also applies on Linux. +if config.host_os == 'Darwin' or config.host_os == 'Linux': # On Darwin, we default to ignore_noninstrumented_modules=1, which also # suppresses some races the tests are supposed to find. See tsan/lit.cfg.py. if 'TSAN_OPTIONS' in config.environment: diff --git a/compiler-rt/test/tsan/lit.cfg.py b/compiler-rt/test/tsan/lit.cfg.py index b7a00117823e9..536ae43afa375 100644 --- a/compiler-rt/test/tsan/lit.cfg.py +++ b/compiler-rt/test/tsan/lit.cfg.py @@ -24,6 +24,9 @@ def get_required_attr(config, attr_name): # On Darwin, we default to `abort_on_error=1`, which would make tests run # much slower. Let's override this and run lit tests with 'abort_on_error=0'. default_tsan_opts += ':abort_on_error=0' + +# For Swift, the following also applies on Linux. +if config.host_os == 'Darwin' or config.host_os == 'Linux': # On Darwin, we default to ignore_noninstrumented_modules=1, which also # suppresses some races the tests are supposed to find. Let's run without this # setting, but turn it back on for Darwin tests (see Darwin/lit.local.cfg.py).