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. + diff --git a/apple-llvm-config/am/apple-master.json b/apple-llvm-config/am/apple-master.json new file mode 100644 index 0000000000000..427b8b4bbf218 --- /dev/null +++ b/apple-llvm-config/am/apple-master.json @@ -0,0 +1,3 @@ +{ + "upstream": "llvm.org/master" +} 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" +} +} diff --git a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt index 5bfdcb487e17a..02da0851a72be 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 - clangToolingRefactoring + clangToolingRefactor ) include_directories( 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). 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/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index a9fb85fa0d8d7..775fa33ebaa39 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 ============ @@ -2829,6 +2830,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 ````: + +- ``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 ```` 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 ```` 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 ```` 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 ```` 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 ````; 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. + +```` +~~~~~~~~~~~~~~~ + +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`. + +``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 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +```` 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`. 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`. + +.. _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` 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`. + +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` or :ref:`authentication` 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`. 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 ```` 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`. + +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. diff --git a/clang/docs/index.rst b/clang/docs/index.rst index 493f736f2be4f..1c91f13b9375a 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -44,6 +44,7 @@ Using Clang as a Compiler OpenCLSupport OpenMPSupport ThinLTO + APINotes CommandGuide/index FAQ diff --git a/clang/include/clang-c/CXErrorCode.h b/clang/include/clang-c/CXErrorCode.h index fed195ec1f33d..139948f3eda3f 100644 --- a/clang/include/clang-c/CXErrorCode.h +++ b/clang/include/clang-c/CXErrorCode.h @@ -54,7 +54,25 @@ enum CXErrorCode { /** * 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..a196403cb09f6 --- /dev/null +++ b/clang/include/clang-c/Refactor.h @@ -0,0 +1,1313 @@ +/*==-- 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(void); + +/** + * \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 The sub-action of 'extract' that extracts source expression into a + * new variable. + */ + CXRefactor_Extract_Expression = 11, +}; + +/** + * \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 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 + * 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 + * - ObjC class: CXCursor_ObjCInterfaceDecl + * 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. + */ +CINDEX_LINKAGE +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 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. + * + * 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/APINotes/APINotesManager.h b/clang/include/clang/APINotes/APINotesManager.h new file mode 100644 index 0000000000000..faedf2d44db23 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesManager.h @@ -0,0 +1,144 @@ +//===--- 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 APINotesManager interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H +#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" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/VersionTuple.h" +#include +#include + +namespace clang { + +class DirectoryEntry; +class FileEntry; +class LangOptions; +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 + ReaderEntry; + + SourceManager &SourceMgr; + + /// Whether to implicitly search for API notes files based on the + /// source file from which an entity was declared. + bool ImplicitAPINotes; + + /// The Swift version to use when interpreting versioned API notes. + llvm::VersionTuple SwiftVersion; + + /// 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 }; + + /// 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 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 loadAPINotes(const FileEntry *apiNotesFile); + + /// 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); + + /// Look for API notes in the given directory. + /// + /// This might find either a binary or source API notes. + const FileEntry *findAPINotesFile(const DirectoryEntry *directory, + StringRef filename, + bool wantPublic = true); + + /// 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, const LangOptions &langOpts); + ~APINotesManager(); + + /// Set the Swift version to use when filtering API notes. + void setSwiftVersion(llvm::VersionTuple swiftVersion) { + SwiftVersion = swiftVersion; + } + + /// Load the API notes for 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 true if API notes were successfully loaded, \c false otherwise. + bool loadCurrentModuleAPINotes(const Module *module, + bool lookInModule, + ArrayRef searchPaths); + + /// Retrieve the set of API notes readers for the current module. + ArrayRef getCurrentModuleReaders() const { + unsigned numReaders = static_cast(CurrentModuleReaders[0] != nullptr) + + static_cast(CurrentModuleReaders[1] != nullptr); + return llvm::makeArrayRef(CurrentModuleReaders).slice(0, numReaders); + } + + /// Find the API notes readers that correspond to the given source location. + llvm::SmallVector findAPINotes(SourceLocation Loc); +}; + +} // end namespace api_notes +} // end namespace clang + +#endif diff --git a/clang/include/clang/APINotes/APINotesOptions.h b/clang/include/clang/APINotes/APINotesOptions.h new file mode 100644 index 0000000000000..4c15e7fc56e15 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesOptions.h @@ -0,0 +1,40 @@ +//===--- 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 +#include +#include "llvm/Support/VersionTuple.h" + +namespace clang { + +/// APINotesOptions - Track various options which control how API +/// notes are found and handled. +class APINotesOptions { +public: + /// The Swift version which should be used for API notes. + llvm::VersionTuple SwiftVersion; + + /// The set of search paths where we API notes can be found for + /// particular modules. + /// + /// The API notes in this directory are stored as .apinotes, + /// and are only applied when building the module . + std::vector ModuleSearchPaths; +}; + +} // 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..f3ad7533b6b3f --- /dev/null +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -0,0 +1,214 @@ +//===--- 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 "llvm/Support/VersionTuple.h" +#include + +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(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, + llvm::VersionTuple swiftVersion, 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 + get(std::unique_ptr inputBuffer, + llvm::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 + getUnmanaged(llvm::MemoryBuffer *inputBuffer, + llvm::VersionTuple swiftVersion); + + ~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; + + /// Retrieve the size and modification time of the source file from + /// which this API notes file was created, if known. + Optional> getSourceFileSizeAndModTime() const; + + /// 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 + class VersionedInfo { + /// The complete set of results. + SmallVector, 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(llvm::VersionTuple version, + SmallVector, 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 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 *begin() const { return Results.begin(); } + const std::pair *end() const { return Results.end(); } + + /// Access a specific versioned result. + const std::pair &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. + /// + /// \returns The ID, if known. + Optional 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 information about the class, if known. + VersionedInfo 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 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 information about the protocol, if known. + VersionedInfo lookupObjCProtocolInfo(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. + /// \param isInstance Whether we are looking for an instance property (vs. + /// a class property). + /// + /// \returns Information about the property, if known. + VersionedInfo lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance); + + /// 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. + VersionedInfo 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. + VersionedInfo 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. + VersionedInfo lookupGlobalFunction(StringRef name); + + /// Look for information regarding the given enumerator. + /// + /// \param name The name of the enumerator. + /// + /// \returns information about the enumerator, if known. + VersionedInfo lookupEnumConstant(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. + VersionedInfo lookupTag(StringRef name); + + /// Look for information regarding the given typedef. + /// + /// \param name The name of the typedef. + /// + /// \returns information about the typedef, if known. + VersionedInfo lookupTypedef(StringRef name); +}; + +} // 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..222f33b728a13 --- /dev/null +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -0,0 +1,126 @@ +//===--- 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" +#include "llvm/Support/VersionTuple.h" + +namespace llvm { + class raw_ostream; +} + +namespace clang { + +class FileEntry; + +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 and + /// (optional) source file. + APINotesWriter(StringRef moduleName, const FileEntry *sourceFile); + ~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 or protocol. + /// + /// \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 or protocol, which can be used to add + /// properties and methods to the class/protocol. + ContextID addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + llvm::VersionTuple swiftVersion); + + /// 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, + bool isInstanceProperty, + const ObjCPropertyInfo &info, + llvm::VersionTuple swiftVersion); + + /// 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, + 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, + 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, + 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, + 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, + 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, + llvm::VersionTuple swiftVersion); + + /// Add module options + void addModuleOptions(ModuleOptions opts); +}; + +} // 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..fa991d3a3d0fc --- /dev/null +++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h @@ -0,0 +1,49 @@ +//=== 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 + +namespace llvm { + class raw_ostream; + class MemoryBuffer; +} + +namespace clang { + +class FileEntry; + +namespace api_notes { + + enum class ActionType { + None, + YAMLToBinary, + BinaryToYAML, + Dump, + }; + + /// Converts API notes from YAML format to binary format. + bool compileAPINotes(llvm::StringRef yamlInput, + const FileEntry *sourceFile, + llvm::raw_ostream &os, + llvm::SourceMgr::DiagHandlerTy diagHandler = nullptr, + void *diagHandlerCtxt = nullptr); + + bool parseAndDumpAPINotes(llvm::StringRef yamlInput); +} // 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..b2ca595b0c3b0 --- /dev/null +++ b/clang/include/clang/APINotes/Types.h @@ -0,0 +1,783 @@ +//===--- 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/LLVM.h" +#include "clang/Basic/Specifiers.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include +#include + +namespace clang { +namespace api_notes { + +/// The file extension used for the source representation of API notes. +static const char SOURCE_APINOTES_EXTENSION[] = "apinotes"; + +/// Opaque context ID used to refer to an Objective-C class or protocol. +class ContextID { +public: + unsigned Value; + + 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. +class CommonEntityInfo { +public: + /// Message to use when this entity is unavailable. + std::string UnavailableMsg; + + /// Whether this entity is marked unavailable. + unsigned Unavailable : 1; + + /// 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), SwiftPrivateSpecified(0), + SwiftPrivate(0) { } + + Optional isSwiftPrivate() const { + if (!SwiftPrivateSpecified) return None; + return SwiftPrivate; + } + + void setSwiftPrivate(Optional 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; + } + + 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; + } + } + + if (rhs.UnavailableInSwift) { + lhs.UnavailableInSwift = true; + if (rhs.UnavailableMsg.length() != 0 && + lhs.UnavailableMsg.length() == 0) { + lhs.UnavailableMsg = rhs.UnavailableMsg; + } + } + + if (rhs.SwiftPrivateSpecified && !lhs.SwiftPrivateSpecified) { + lhs.SwiftPrivateSpecified = 1; + lhs.SwiftPrivate = rhs.SwiftPrivate; + } + + if (rhs.SwiftName.length() != 0 && + lhs.SwiftName.length() == 0) + lhs.SwiftName = rhs.SwiftName; + + 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. + Optional SwiftBridge; + + /// The NS error domain for this type. + Optional NSErrorDomain; + +public: + CommonTypeInfo() : CommonEntityInfo() { } + + const Optional &getSwiftBridge() const { return SwiftBridge; } + + void setSwiftBridge(const Optional &swiftType) { + SwiftBridge = swiftType; + } + + void setSwiftBridge(const Optional &swiftType) { + if (swiftType) + SwiftBridge = *swiftType; + else + SwiftBridge = None; + } + + const Optional &getNSErrorDomain() const { + return NSErrorDomain; + } + + void setNSErrorDomain(const Optional &domain) { + NSErrorDomain = domain; + } + + void setNSErrorDomain(const Optional &domain) { + if (domain) + NSErrorDomain = *domain; + else + NSErrorDomain = None; + } + + friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + static_cast(lhs) |= rhs; + if (!lhs.SwiftBridge && rhs.SwiftBridge) + lhs.SwiftBridge = rhs.SwiftBridge; + if (!lhs.NSErrorDomain && rhs.NSErrorDomain) + lhs.NSErrorDomain = rhs.NSErrorDomain; + return lhs; + } + + friend bool operator==(const CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.SwiftBridge == rhs.SwiftBridge && + lhs.NSErrorDomain == rhs.NSErrorDomain; + } + + 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 CommonTypeInfo { + /// 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; + + unsigned SwiftImportAsNonGenericSpecified : 1; + unsigned SwiftImportAsNonGeneric : 1; + + unsigned SwiftObjCMembersSpecified : 1; + unsigned SwiftObjCMembers : 1; + +public: + ObjCContextInfo() + : CommonTypeInfo(), + HasDefaultNullability(0), + DefaultNullability(0), + HasDesignatedInits(0), + SwiftImportAsNonGenericSpecified(false), + SwiftImportAsNonGeneric(false), + SwiftObjCMembersSpecified(false), + SwiftObjCMembers(false) + { } + + /// Determine the default nullability for properties and methods of this + /// class. + /// + /// \returns the default nullability, if implied, or None if there is no + Optional getDefaultNullability() const { + if (HasDefaultNullability) + return static_cast(DefaultNullability); + + return None; + } + + /// Set the default nullability for properties and methods of this class. + void setDefaultNullability(NullabilityKind kind) { + HasDefaultNullability = true; + DefaultNullability = static_cast(kind); + } + + bool hasDesignatedInits() const { return HasDesignatedInits; } + void setHasDesignatedInits(bool value) { HasDesignatedInits = value; } + + Optional getSwiftImportAsNonGeneric() const { + if (SwiftImportAsNonGenericSpecified) + return SwiftImportAsNonGeneric; + return None; + } + void setSwiftImportAsNonGeneric(Optional value) { + if (value.hasValue()) { + SwiftImportAsNonGenericSpecified = true; + SwiftImportAsNonGeneric = value.getValue(); + } else { + SwiftImportAsNonGenericSpecified = false; + SwiftImportAsNonGeneric = false; + } + } + + Optional getSwiftObjCMembers() const { + if (SwiftObjCMembersSpecified) + return SwiftObjCMembers; + return None; + } + void setSwiftObjCMembers(Optional 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() { + HasDefaultNullability = false; + DefaultNullability = 0; + } + + friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.getDefaultNullability() == rhs.getDefaultNullability() && + lhs.HasDesignatedInits == rhs.HasDesignatedInits && + lhs.getSwiftImportAsNonGeneric() == + rhs.getSwiftImportAsNonGeneric() && + lhs.getSwiftObjCMembers() == rhs.getSwiftObjCMembers(); + } + + 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(lhs) |= rhs; + + // Merge nullability. + if (!lhs.getDefaultNullability()) { + if (auto nullable = rhs.getDefaultNullability()) { + lhs.setDefaultNullability(*nullable); + } + } + + if (!lhs.SwiftImportAsNonGenericSpecified && + rhs.SwiftImportAsNonGenericSpecified) { + lhs.SwiftImportAsNonGenericSpecified = true; + lhs.SwiftImportAsNonGeneric = rhs.SwiftImportAsNonGeneric; + } + + if (!lhs.SwiftObjCMembersSpecified && rhs.SwiftObjCMembersSpecified) { + lhs.SwiftObjCMembersSpecified = true; + lhs.SwiftObjCMembers = rhs.SwiftObjCMembers; + } + + 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; + + /// The C type of the variable, as a string. + std::string Type; + +public: + VariableInfo() + : CommonEntityInfo(), + NullabilityAudited(false), + Nullable(0) { } + + Optional getNullability() const { + if (NullabilityAudited) + return static_cast(Nullable); + + return None; + } + + void setNullabilityAudited(NullabilityKind kind) { + NullabilityAudited = true; + Nullable = static_cast(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(lhs) == rhs && + lhs.NullabilityAudited == rhs.NullabilityAudited && + lhs.Nullable == rhs.Nullable && + lhs.Type == rhs.Type; + } + + friend bool operator!=(const VariableInfo &lhs, const VariableInfo &rhs) { + return !(lhs == rhs); + } + + friend VariableInfo &operator|=(VariableInfo &lhs, + const VariableInfo &rhs) { + static_cast(lhs) |= rhs; + if (!lhs.NullabilityAudited && rhs.NullabilityAudited) + lhs.setNullabilityAudited(*rhs.getNullability()); + if (lhs.Type.empty() && !rhs.Type.empty()) + lhs.Type = rhs.Type; + return lhs; + } +}; + +/// Describes API notes data for an Objective-C property. +class ObjCPropertyInfo : public VariableInfo { + unsigned SwiftImportAsAccessorsSpecified : 1; + unsigned SwiftImportAsAccessors : 1; + +public: + ObjCPropertyInfo() + : VariableInfo(), SwiftImportAsAccessorsSpecified(false), + SwiftImportAsAccessors(false) {} + + /// Merge class-wide information into the given property. + friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs, + const ObjCContextInfo &rhs) { + static_cast(lhs) |= rhs; + + // Merge nullability. + if (!lhs.getNullability()) { + if (auto nullable = rhs.getDefaultNullability()) { + lhs.setNullabilityAudited(*nullable); + } + } + + return lhs; + } + + Optional getSwiftImportAsAccessors() const { + if (SwiftImportAsAccessorsSpecified) + return SwiftImportAsAccessors; + return None; + } + void setSwiftImportAsAccessors(Optional 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(rhs); + if (!lhs.SwiftImportAsAccessorsSpecified && + rhs.SwiftImportAsAccessorsSpecified) { + lhs.SwiftImportAsAccessorsSpecified = true; + lhs.SwiftImportAsAccessors = rhs.SwiftImportAsAccessors; + } + return lhs; + } + + friend bool operator==(const ObjCPropertyInfo &lhs, + const ObjCPropertyInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.getSwiftImportAsAccessors() == rhs.getSwiftImportAsAccessors(); + } +}; + +/// 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; + + /// A biased RetainCountConventionKind, where 0 means "unspecified". + /// + /// Only relevant for out-parameters. + unsigned RawRetainCountConvention : 3; + +public: + ParamInfo() : VariableInfo(), NoEscapeSpecified(false), NoEscape(false), + RawRetainCountConvention() { } + + Optional isNoEscape() const { + if (!NoEscapeSpecified) return None; + return NoEscape; + } + void setNoEscape(Optional noescape) { + if (noescape) { + NoEscapeSpecified = true; + NoEscape = *noescape; + } else { + NoEscapeSpecified = false; + NoEscape = false; + } + } + + Optional getRetainCountConvention() const { + if (!RawRetainCountConvention) + return None; + return static_cast(RawRetainCountConvention - 1); + } + void setRetainCountConvention(Optional convention){ + if (convention) + RawRetainCountConvention = static_cast(convention.getValue())+1; + else + RawRetainCountConvention = 0; + assert(getRetainCountConvention() == convention && "bitfield too small"); + } + + friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) { + static_cast(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(lhs) == rhs && + lhs.NoEscapeSpecified == rhs.NoEscapeSpecified && + lhs.NoEscape == rhs.NoEscape && + lhs.RawRetainCountConvention == rhs.RawRetainCountConvention; + } + + 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. +/// +/// 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 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; + + /// 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 + // 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 Params; + + FunctionInfo() + : CommonEntityInfo(), + NullabilityAudited(false), + NumAdjustedNullable(0), + RawRetainCountConvention() { } + + static unsigned getMaxNullabilityIndex() { + return ((sizeof(NullabilityPayload) * CHAR_BIT)/NullabilityKindSize); + } + + void addTypeInfo(unsigned index, NullabilityKind kind) { + assert(index <= getMaxNullabilityIndex()); + assert(static_cast(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(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(( NullabilityPayload + >> (index * NullabilityKindSize) ) + & NullabilityKindMask); + } + +public: + NullabilityKind getParamTypeInfo(unsigned index) const { + return getTypeInfo(index + 1); + } + + NullabilityKind getReturnTypeInfo() const { + return getTypeInfo(0); + } + + Optional getRetainCountConvention() const { + if (!RawRetainCountConvention) + return None; + return static_cast(RawRetainCountConvention - 1); + } + void setRetainCountConvention(Optional convention){ + if (convention) + RawRetainCountConvention = static_cast(convention.getValue())+1; + else + RawRetainCountConvention = 0; + assert(getRetainCountConvention() == convention && "bitfield too small"); + } + + friend bool operator==(const FunctionInfo &lhs, const FunctionInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.NullabilityAudited == rhs.NullabilityAudited && + lhs.NumAdjustedNullable == rhs.NumAdjustedNullable && + lhs.NullabilityPayload == rhs.NullabilityPayload && + lhs.ResultType == rhs.ResultType && + lhs.Params == rhs.Params && + lhs.RawRetainCountConvention == rhs.RawRetainCountConvention; + } + + 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 this is a required initializer. + unsigned Required : 1; + + ObjCMethodInfo() + : FunctionInfo(), + DesignatedInit(false), + Required(false) { } + + friend bool operator==(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.DesignatedInit == rhs.DesignatedInit && + 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() { } +}; + +/// Describes API notes data for an enumerator. +class EnumConstantInfo : public CommonEntityInfo { +public: + 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: + Optional EnumExtensibility; + + Optional isFlagEnum() const { + if (HasFlagEnum) + return IsFlagEnum; + return None; + } + void setFlagEnum(Optional 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(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(lhs) == rhs && + lhs.isFlagEnum() == rhs.isFlagEnum() && + lhs.EnumExtensibility == rhs.EnumExtensibility; + } +}; + +/// 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 SwiftWrapper; + + TypedefInfo() : CommonTypeInfo() { } + + friend TypedefInfo &operator|=(TypedefInfo &lhs, const TypedefInfo &rhs) { + lhs |= static_cast(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(lhs) == rhs && + lhs.SwiftWrapper == rhs.SwiftWrapper; + } +}; + +/// Descripts a series of options for a module +struct ModuleOptions { + bool SwiftInferImportAsMember = false; +}; + +} // end namespace api_notes +} // end namespace clang + +#endif // LLVM_CLANG_API_NOTES_TYPES_H diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 5e2f4031d96cc..7f69439ff2484 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1147,6 +1147,9 @@ class ASTContext : public RefCountedBase { /// 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 @@ -1983,6 +1986,16 @@ class ASTContext : public RefCountedBase { 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/Attr.h b/clang/include/clang/AST/Attr.h index 3c2d626f8cf47..3f7d4b2d75b6c 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -24,6 +24,7 @@ #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/VersionTuple.h" diff --git a/clang/include/clang/AST/AttrIterator.h b/clang/include/clang/AST/AttrIterator.h index 78ce9314a2bba..ad87e142a2167 100644 --- a/clang/include/clang/AST/AttrIterator.h +++ b/clang/include/clang/AST/AttrIterator.h @@ -95,6 +95,8 @@ class specific_attr_iterator { specific_attr_iterator Right) { return !(Left == Right); } + + Iterator getCurrent() const { return Current; } }; template diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 01c2f1809771b..adea10b33188b 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1590,6 +1590,9 @@ class DeclContext { /// True if this method is the getter or setter for an explicit property. uint64_t IsPropertyAccessor : 1; + /// True if this method is a synthesized property accessor stub. + uint64_t IsSynthesizedAccessorStub : 1; + /// Method has a definition. uint64_t IsDefined : 1; diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 8d85ac36d8611..8372747b1c1d4 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -172,6 +172,7 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext { Selector SelInfo, QualType T, TypeSourceInfo *ReturnTInfo, DeclContext *contextDecl, bool isInstance = true, bool isVariadic = false, bool isPropertyAccessor = false, + bool isSynthesizedAccessorStub = false, bool isImplicitlyDeclared = false, bool isDefined = false, ImplementationControl impControl = None, bool HasRelatedResultType = false); @@ -232,6 +233,7 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext { Selector SelInfo, QualType T, TypeSourceInfo *ReturnTInfo, DeclContext *contextDecl, bool isInstance = true, bool isVariadic = false, bool isPropertyAccessor = false, + bool isSynthesizedAccessorStub = false, bool isImplicitlyDeclared = false, bool isDefined = false, ImplementationControl impControl = None, bool HasRelatedResultType = false); @@ -408,7 +410,7 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext { /// \return the type for \c self and set \arg selfIsPseudoStrong and /// \arg selfIsConsumed accordingly. QualType getSelfType(ASTContext &Context, const ObjCInterfaceDecl *OID, - bool &selfIsPseudoStrong, bool &selfIsConsumed); + bool &selfIsPseudoStrong, bool &selfIsConsumed) const; ImplicitParamDecl * getSelfDecl() const { return SelfDecl; } void setSelfDecl(ImplicitParamDecl *SD) { SelfDecl = SD; } @@ -436,6 +438,14 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext { ObjCMethodDeclBits.IsPropertyAccessor = isAccessor; } + bool isSynthesizedAccessorStub() const { + return ObjCMethodDeclBits.IsSynthesizedAccessorStub; + } + + void setSynthesizedAccessorStub(bool isSynthesizedAccessorStub) { + ObjCMethodDeclBits.IsSynthesizedAccessorStub = isSynthesizedAccessorStub; + } + bool isDefined() const { return ObjCMethodDeclBits.IsDefined; } void setDefined(bool isDefined) { ObjCMethodDeclBits.IsDefined = isDefined; } @@ -466,6 +476,9 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext { ObjCMethodDeclBits.HasSkippedBody = Skipped; } + /// True if the method is tagged as objc_direct + bool isDirectMethod() const; + /// Returns the property associated with this method's selector. /// /// Note that even if this particular method is not marked as a property @@ -747,13 +760,14 @@ class ObjCPropertyDecl : public NamedDecl { /// property attribute rather than a type qualifier. OBJC_PR_nullability = 0x1000, OBJC_PR_null_resettable = 0x2000, - OBJC_PR_class = 0x4000 + OBJC_PR_class = 0x4000, + OBJC_PR_direct = 0x8000 // Adding a property should change NumPropertyAttrsBits }; enum { /// Number of bits fitting all the property attributes. - NumPropertyAttrsBits = 15 + NumPropertyAttrsBits = 16 }; enum SetterKind { Assign, Retain, Copy, Weak }; @@ -876,6 +890,7 @@ class ObjCPropertyDecl : public NamedDecl { bool isInstanceProperty() const { return !isClassProperty(); } bool isClassProperty() const { return PropertyAttributes & OBJC_PR_class; } + bool isDirectProperty() const { return PropertyAttributes & OBJC_PR_direct; } ObjCPropertyQueryKind getQueryKind() const { return isClassProperty() ? ObjCPropertyQueryKind::OBJC_PR_query_class : @@ -902,6 +917,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; } @@ -910,6 +930,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; } @@ -2728,17 +2753,25 @@ raw_ostream &operator<<(raw_ostream &OS, const ObjCImplementationDecl &ID); class ObjCCompatibleAliasDecl : public NamedDecl { /// 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 L, IdentifierInfo *Id, - ObjCInterfaceDecl* aliasedClass) - : NamedDecl(ObjCCompatibleAlias, DC, L, Id), AliasedClass(aliasedClass) {} + ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation NameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass, + SourceLocation AliasedClassLoc, SourceLocation AtLoc) + : NamedDecl(ObjCCompatibleAlias, DC, NameLoc, Id), + AliasedClass(AliasedClass), AliasedClassLoc(AliasedClassLoc), + AtLoc(AtLoc) {} void anchor() override; 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); @@ -2747,6 +2780,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; } }; @@ -2779,6 +2823,11 @@ class ObjCPropertyImplDecl : public Decl { /// Null for \@dynamic. Required for \@synthesize. ObjCIvarDecl *PropertyIvarDecl; + /// The getter's definition, which has an empty body if synthesized. + ObjCMethodDecl *GetterMethodDecl = nullptr; + /// The getter's definition, which has an empty body if synthesized. + ObjCMethodDecl *SetterMethodDecl = nullptr; + /// Null for \@dynamic. Non-null if property must be copy-constructed in /// getter. Expr *GetterCXXConstructor = nullptr; @@ -2845,6 +2894,12 @@ class ObjCPropertyImplDecl : public Decl { return IvarLoc.isValid() && IvarLoc != getLocation(); } + ObjCMethodDecl *getGetterMethodDecl() const { return GetterMethodDecl; } + void setGetterMethodDecl(ObjCMethodDecl *MD) { GetterMethodDecl = MD; } + + ObjCMethodDecl *getSetterMethodDecl() const { return SetterMethodDecl; } + void setSetterMethodDecl(ObjCMethodDecl *MD) { SetterMethodDecl = MD; } + Expr *getGetterCXXConstructor() const { return GetterCXXConstructor; } 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 +class DependentASTVisitor : public RecursiveASTVisitor { +private: + bool visitDependentReference( + const Type *T, const DeclarationName &Name, SourceLocation Loc, + llvm::function_ref Filter) { + if (!T) + return true; + const TemplateSpecializationType *TST = + T->getAs(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + if (!RD->hasDefinition()) + return true; + RD = RD->getDefinition(); + std::vector Symbols = + RD->lookupDependentName(Name, Filter); + // FIXME: Improve overload handling. + if (Symbols.size() != 1) + return true; + if (Loc.isInvalid()) + return true; + return RecursiveASTVisitor::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(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/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/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h index c6fae6f465ff6..c37836cbecff6 100644 --- a/clang/include/clang/AST/NestedNameSpecifier.h +++ b/clang/include/clang/AST/NestedNameSpecifier.h @@ -229,6 +229,23 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { void dump() const; void dump(llvm::raw_ostream &OS) const; void dump(llvm::raw_ostream &OS, const LangOptions &LO) 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); }; /// A C++ nested-name-specifier augmented with source location 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(Args)...); case QualType::PCK_ARCWeak: return asDerived().visitARCWeak(FT, std::forward(Args)...); + case QualType::PCK_PtrAuth: + return asDerived().visitPtrAuth(FT, std::forward(Args)...); case QualType::PCK_Struct: return asDerived().visitStruct(FT, std::forward(Args)...); case QualType::PCK_Trivial: diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 80eec6a5a8be3..310be3d0a51de 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -48,6 +48,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), @@ -59,8 +60,8 @@ struct PrintingPolicy { PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), ConstantsAsWritten(false), - SuppressImplicitBase(false), FullyQualifiedName(false), - PrintCanonicalTypes(false) {} + SuppressImplicitBase(false), UseStdFunctionForLambda(false), + FullyQualifiedName(false), PrintCanonicalTypes(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 @@ -91,6 +92,10 @@ struct PrintingPolicy { /// "const int" type specifier and instead only print the "*y". unsigned SuppressSpecifiers : 1; + /// \brief Whether we should supress the printing of the actual storage class + /// specifiers for the given declaration. + bool SupressStorageClassSpecifiers : 1; + /// Whether type printing should skip printing the tag keyword. /// /// This is used when printing the inner type of elaborated types, @@ -230,6 +235,9 @@ struct PrintingPolicy { /// When true, don't print the implicit 'self' or 'this' expressions. unsigned SuppressImplicitBase : 1; + /// \brief Whether we should use std::function<...> for lambda record types. + bool UseStdFunctionForLambda : 1; + /// When true, print the fully qualified name of function declarations. /// This is the opposite of SuppressScope and thus overrules it. unsigned FullyQualifiedName : 1; diff --git a/clang/include/clang/AST/StableHash.h b/clang/include/clang/AST/StableHash.h new file mode 100644 index 0000000000000..5c6f900a37289 --- /dev/null +++ b/clang/include/clang/AST/StableHash.h @@ -0,0 +1,46 @@ +//===--- 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 + +namespace llvm { +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 +/// 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); + +/// 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/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; #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/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 + 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/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 0d25e775e2afd..45eec56329937 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -101,6 +101,10 @@ def NonStaticNonConstCXXMethod [{!S->isStatic() && !S->isConst()}], "non-static non-const member functions">; +def ObjCClassMethod : SubsetSubjectisInstanceMethod()}], + "Objective-C class methods">; + def ObjCInstanceMethod : SubsetSubjectisInstanceMethod()}], "Objective-C instance methods">; @@ -229,6 +233,9 @@ class VariadicEnumArgument values, list Enums = enums; } +// Represents an attribute wrapped by another attribute. +class AttrArgument : Argument; + // This handles one spelling of an attribute. class Spelling { string Name = name; @@ -1747,6 +1754,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 : DeclOrTypeAttr { let Spellings = [Clang<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; @@ -1840,6 +1853,12 @@ def ObjCSubclassingRestricted : InheritableAttr { let Documentation = [ObjCSubclassingRestrictedDocs]; } +def ObjCCompleteDefinition : InheritableAttr { + let Spellings = [GNU<"objc_complete_definition">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [Undocumented]; +} + def ObjCExplicitProtocolImpl : InheritableAttr { let Spellings = [Clang<"objc_protocol_requires_explicit_implementation">]; let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>; @@ -1852,6 +1871,20 @@ def ObjCDesignatedInitializer : Attr { let Documentation = [Undocumented]; } +def ObjCDirect : Attr { + let Spellings = [Clang<"objc_direct">]; + let Subjects = SubjectList<[ObjCMethod], ErrorDiag>; + let LangOpts = [ObjC]; + let Documentation = [ObjCDirectDocs]; +} + +def ObjCDirectMembers : Attr { + let Spellings = [Clang<"objc_direct_members">]; + let Subjects = SubjectList<[ObjCImpl, ObjCCategory], ErrorDiag>; + let LangOpts = [ObjC]; + let Documentation = [ObjCDirectMembersDocs]; +} + def ObjCRuntimeName : Attr { let Spellings = [Clang<"objc_runtime_name">]; let Subjects = SubjectList<[ObjCInterface, ObjCProtocol], ErrorDiag>; @@ -1955,6 +1988,101 @@ def Regparm : TypeAttr { let ASTNode = 0; } +def SwiftBridge : Attr { + let Spellings = [GNU<"swift_bridge">]; + let Subjects = SubjectList<[Tag, TypedefName, ObjCInterface, ObjCProtocol], + ErrorDiag, "ExpectedType">; + let Args = [StringArgument<"SwiftType">]; + 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>; + let Documentation = [SwiftObjCMembersDocs]; +} + +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<[Function, ObjCMethod], 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 SwiftNewtype : InheritableAttr { + 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]; +} + +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. + let Spellings = []; + let SemaHandler = 0; + 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">, + BoolArgument<"IsReplacedByActive">]; + 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">, + BoolArgument<"IsReplacedByActive">]; + let SemaHandler = 0; + let Documentation = [Undocumented]; + let AdditionalMembers = [{ + attr::Kind getAttrKindToRemove() const { + return static_cast(getRawKind()); + } + }]; +} + def NoDeref : TypeAttr { let Spellings = [Clang<"noderef">]; let Documentation = [NoDerefDocs]; @@ -2292,6 +2420,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 3a9093124637e..e4123156d6afd 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1352,6 +1352,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 + + 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 ````. + +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. + +```` provides predefined qualifiers for various language +features that implicitly use pointer authentication. + }]; +} + def ExternalSourceSymbolDocs : Documentation { let Category = DocCatDecl; let Content = [{ @@ -3196,6 +3355,72 @@ where clause is one of the following: }]; } +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 = [{ +The ``ns_error_domain`` attribute indicates a global constant representing the error domain. + }]; +} + +def SwiftBridgeDocs : Documentation { + let Category = SwiftDocs; + let Content = [{ +The ``swift_bridge`` attribute indicates that the type to which the attribute appertains is bridged to the named Swift type. + }]; +} + +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 = [{ +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"; + 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. + +}]; +} + +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. +* ``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"; @@ -3897,6 +4122,104 @@ overheads associated with defining and calling such a method. }]; } +def ObjCDirectDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +The ``objc_direct`` attribute can be used to mark an Objective-C method as +being *direct*. A direct method is treated statically like an ordinary method, +but dynamically it behaves more like a C function. This lowers some of the costs +associated with the method but also sacrifices some of the ordinary capabilities +of Objective-C methods. + +A message send of a direct method calls the implementation directly, as if it +were a C function, rather than using ordinary Objective-C method dispatch. This +is substantially faster and potentially allows the implementation to be inlined, +but it also means the method cannot be overridden in subclasses or replaced +dynamically, as ordinary Objective-C methods can. + +Furthermore, a direct method is not listed in the class's method lists. This +substantially reduces the code-size overhead of the method but also means it +cannot be called dynamically using ordinary Objective-C method dispatch at all; +in particular, this means that it cannot override a superclass method or satisfy +a protocol requirement. + +Because a direct method cannot be overridden, it is an error to perform +a ``super`` message send of one. + +Although a message send of a direct method causes the method to be called +directly as if it were a C function, it still obeys Objective-C semantics in other +ways: + +- If the receiver is ``nil``, the message send does nothing and returns the zero value + for the return type. + +- A message send of a direct class method will cause the class to be initialized, + including calling the ``+initialize`` method if present. + +- The implicit ``_cmd`` parameter containing the method's selector is still defined. + In order to minimize code-size costs, the implementation will not emit a reference + to the selector if the parameter is unused within the method. + +Symbols for direct method implementations are implicitly given hidden +visibility, meaning that they can only be called within the same linkage unit. + +It is an error to do any of the following: + +- declare a direct method in a protocol, +- declare an override of a direct method with a method in a subclass, +- declare an override of a non-direct method with a direct method in a subclass, +- declare a method with different directness in different class interfaces, or +- implement a non-direct method (as declared in any class interface) with a direct method. + +If any of these rules would be violated if every method defined in an +``@implementation`` within a single linkage unit were declared in an +appropriate class interface, the program is ill-formed with no diagnostic +required. If a violation of this rule is not diagnosed, behavior remains +well-defined; this paragraph is simply reserving the right to diagnose such +conflicts in the future, not to treat them as undefined behavior. + +Additionally, Clang will warn about any ``@selector`` expression that +names a selector that is only known to be used for direct methods. + +For the purpose of these rules, a "class interface" includes a class's primary +``@interface`` block, its class extensions, its categories, its declared protocols, +and all the class interfaces of its superclasses. + +An Objective-C property can be declared with the ``direct`` property +attribute. If a direct property declaration causes an implicit declaration of +a getter or setter method (that is, if the given method is not explicitly +declared elsewhere), the method is declared to be direct. + +Some programmers may wish to make many methods direct at once. In order +to simplify this, the ``objc_direct_members`` attribute is provided; see its +documentation for more information. + }]; +} + +def ObjCDirectMembersDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +The ``objc_direct_members`` attribute can be placed on an Objective-C +``@interface`` or ``@implementation`` to mark that methods declared +therein should be considered direct by default. See the documentation +for ``objc_direct`` for more information about direct methods. + +When ``objc_direct_members`` is placed on an ``@interface`` block, every +method in the block is considered to be declared as direct. This includes any +implicit method declarations introduced by property declarations. If the method +redeclares a non-direct method, the declaration is ill-formed, exactly as if the +method was annotated with the ``objc_direct`` attribute. ``objc_direct_members`` +cannot be placed on the primary interface of a class, only on category or class +extension interfaces. + +When ``objc_direct_members`` is placed on an ``@implementation`` block, +methods defined in the block are considered to be declared as direct unless +they have been previously declared as non-direct in any interface of the class. +This includes the implicit method definitions introduced by synthesized +properties, including auto-synthesized properties. + }]; +} + def SelectAnyDocs : Documentation { let Category = DocCatDecl; let Content = [{ 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/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index f0d101534e57f..787532ef459fb 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -130,6 +130,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/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 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/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 9e494aa371cda..94ae011a0b20b 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -473,6 +473,9 @@ class DiagnosticsEngine : public RefCountedBase { /// Second string argument for the delayed diagnostic. std::string DelayedDiagArg2; + /// Third string argument for the delayed diagnostic. + std::string DelayedDiagArg3; + /// Optional flag value. /// /// Some flags accept values, for instance: -Wframe-larger-than= and @@ -874,8 +877,12 @@ class DiagnosticsEngine : public RefCountedBase { /// \param Arg2 A string argument that will be provided to the /// diagnostic. A copy of this string will be stored in the /// DiagnosticsEngine object itself. + /// + /// \param Arg3 A string argument that will be provided to the + /// diagnostic. A copy of this string will be stored in the + /// DiagnosticsEngine object itself. void SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1 = "", - StringRef Arg2 = ""); + StringRef Arg2 = "", StringRef Arg3 = ""); /// Clear out the current diagnostic. void Clear() { CurDiagID = std::numeric_limits::max(); } diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index 48ba8c0f469f8..20e4676bf6f75 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -149,4 +149,3 @@ include "DiagnosticParseKinds.td" include "DiagnosticRefactoringKinds.td" include "DiagnosticSemaKinds.td" include "DiagnosticSerializationKinds.td" - diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index d6281f157eea7..2b8b0a6aca087 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -303,6 +303,19 @@ 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">, InGroup>; +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>; +def warn_apinotes_private_case_system : NonportablePrivateAPINotesPath, + DefaultIgnore, InGroup>; + // C++ for OpenCL. def err_openclcxx_not_supported : Error< "'%0' is not supported in C++ for OpenCL">; 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/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index a798b498d4e9a..f2efe577c5ead 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -216,6 +216,10 @@ def err_modules_embed_file_not_found : def err_module_header_file_not_found : Error<"module header file '%0' not found">, DefaultFatal; +def remark_index_producing_module_file_data : Remark<"producing index data for " + "module file '%0'">, + InGroup; + 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 29d27ec681f51..a7bba469ac817 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -380,6 +380,7 @@ def ModuleBuild : DiagGroup<"module-build">; def ModuleImport : DiagGroup<"module-import">; 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">; @@ -503,6 +504,7 @@ 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 IntInBoolContext : DiagGroup<"int-in-bool-context">; def TautologicalTypeLimitCompare : DiagGroup<"tautological-type-limit-compare">; def TautologicalUnsignedZeroCompare : DiagGroup<"tautological-unsigned-zero-compare">; @@ -672,8 +674,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">; @@ -689,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/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 5b9391b5a4527..57f87f64ad112 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -29,7 +29,7 @@ namespace clang { enum { DIAG_SIZE_COMMON = 300, DIAG_SIZE_DRIVER = 200, - DIAG_SIZE_FRONTEND = 150, + DIAG_SIZE_FRONTEND = 151, // swift-clang has 1 extra diag DIAG_SIZE_SERIALIZATION = 120, DIAG_SIZE_LEX = 400, DIAG_SIZE_PARSE = 600, diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index b64cbc23f8100..deab09d5b2c87 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -752,7 +752,7 @@ def warn_quoted_include_in_framework_header : Warning< >, InGroup, DefaultIgnore; def warn_framework_include_private_from_public : Warning< "public framework header includes private framework header '%0'" - >, InGroup; + >, InGroup, DefaultIgnore; def warn_auto_module_import : Warning< "treating #%select{include|import|include_next|__include_macros}0 as an " diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 7c9f4da778a60..049d418c73cc8 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -270,7 +270,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; + InGroup, DefaultIgnore; def err_invalid_reference_qualifier_application : Error< "'%0' qualifier may not be applied to a reference">; @@ -1247,6 +1247,9 @@ def err_pragma_invalid_keyword : Error< def err_pragma_pipeline_invalid_keyword : Error< "invalid argument; expected '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/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td index 5446b32efbdd4..0c7da26de7aef 100644 --- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td +++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td @@ -30,4 +30,29 @@ def err_refactor_extract_prohibited_expression : Error<"the selected " } +// On github swift-clang only; to be upstreamed: + +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">; +} + +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/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1aff4688de5d6..15d91fb16c2a7 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -736,6 +736,56 @@ def warn_fortify_source_size_mismatch : Warning< "'%0' size argument is too large; destination buffer has size %1," " but size argument is %2">, InGroup; +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; +def warn_ptrauth_auth_null_pointer : + Warning<"authenticating a null pointer will almost certainly trap">, + InGroup; +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'">; + +// __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">, @@ -906,8 +956,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; def warn_readonly_property : Warning< "attribute 'readonly' of property %0 restricts attribute " "'readwrite' of property inherited from %1">, @@ -987,6 +1037,22 @@ def warn_objc_boxing_invalid_utf8_string : Warning< "string is ill-formed as UTF-8 and will become a null %0 when boxed">, InGroup; +def err_objc_direct_on_protocol : Error< + "'objc_direct' attribute cannot be applied to %select{methods|properties}0 " + "declared in an Objective-C protocol">; +def err_objc_direct_missing_on_decl : Error< + "direct method implementation was previously declared not direct">; +def err_objc_direct_on_override : Error< + "methods that %select{override superclass methods|implement protocol requirements}0 cannot be direct">; +def err_objc_override_direct_method : Error< + "cannot override a method that is declared direct by a superclass">; +def warn_objc_direct_ignored : Warning< + "%0 attribute isn't implemented by this Objective-C runtime">, + InGroup; +def warn_objc_direct_property_ignored : Warning< + "direct attribute on property %0 ignored (not implemented by this Objective-C runtime)">, + InGroup; + def warn_conflicting_overriding_ret_types : Warning< "conflicting return type in " "declaration of %0%diff{: $ vs $|}1,2">, @@ -1072,6 +1138,7 @@ def warn_accessor_property_type_mismatch : Warning< "type of property %0 does not match type of accessor %1">; def note_conv_function_declared_at : Note<"type conversion function declared here">; def note_method_declared_at : Note<"method %0 declared here">; +def note_direct_method_declared_at : Note<"direct method %0 declared here">; def note_property_attribute : Note<"property %0 is declared " "%select{deprecated|unavailable|partial}1 here">; def err_setter_type_void : Error<"type of setter must be void">; @@ -1303,10 +1370,18 @@ def warn_unimplemented_selector: Warning< InGroup, DefaultIgnore; def warn_unimplemented_protocol_method : Warning< "method %0 in protocol %1 not implemented">, InGroup; +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; +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">, InGroup, DefaultIgnore; +def err_direct_selector_expression: Error< + "@selector expression formed with direct selector %0">; def err_objc_kindof_nonobject : Error< "'__kindof' specifier cannot be applied to non-object type %0">; @@ -1320,6 +1395,12 @@ def err_objc_method_unsupported_param_ret_type : Error< def warn_messaging_unqualified_id : Warning< "messaging unqualified id">, DefaultIgnore, InGroup>; +def err_messaging_unqualified_id_with_direct_method : Error< + "messaging unqualified id with a method that is possibly direct">; +def err_messaging_super_with_direct_method : Error< + "messaging super with a direct method">; +def err_messaging_class_with_direct_method : Error< + "messaging a Class with a method that is possibly direct">; // C++ declarations def err_static_assert_expression_is_not_constant : Error< @@ -3545,6 +3626,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; +def warn_attribute_noescape_non_pointer : Warning< + "'noescape' attribute ignored on parameter of non-pointer type %0">, + InGroup; def note_declared_nonnull : Note< "declared %select{'returns_nonnull'|'nonnull'}0 here">; def warn_attribute_sentinel_named_arguments : Warning< @@ -3685,6 +3769,67 @@ 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 warn_attr_swift_name_decl_kind : Warning< + "%0 attribute cannot be applied to this declaration">, + InGroup; +def warn_attr_swift_name_function : Warning< + "parameter of %0 attribute must be a Swift function name string">, + InGroup; +def warn_attr_swift_name_function_no_prototype : Warning< + "%0 attribute can only be applied to function declarations with prototypes">, + InGroup; +def warn_attr_swift_name_context_name_invalid_identifier : Warning< + "%0 attribute has invalid identifier for context name">, + InGroup; +def warn_attr_swift_name_basename_invalid_identifier : Warning< + "%0 attribute has invalid identifier for base name">, + InGroup; +def warn_attr_swift_name_parameter_invalid_identifier : Warning< + "%0 attribute has invalid identifier for parameter name">, + InGroup; +def warn_attr_swift_name_missing_parameters : Warning< + "%0 attribute is missing parameter label clause">, + InGroup; +def warn_attr_swift_name_subscript_not_accessor : Warning< + "%0 attribute for 'subscript' must be a getter or setter">, + InGroup; +def warn_attr_swift_name_subscript_no_parameter : Warning< + "%0 attribute for 'subscript' must take at least one parameter">, + InGroup; +def warn_attr_swift_name_subscript_getter_newValue : Warning< + "%0 attribute for 'subscript' getter cannot take a 'newValue:' parameter">, + InGroup; +def warn_attr_swift_name_subscript_setter_no_newValue : Warning< + "%0 attribute for 'subscript' setter must take a 'newValue:' parameter">, + InGroup; +def warn_attr_swift_name_subscript_setter_multiple_newValues : Warning< + "%0 attribute for 'subscript' setter cannot take multiple 'newValue:' parameters">, + InGroup; +def warn_attr_swift_name_getter_parameters : Warning< + "%0 attribute for getter must not take any parameters besides 'self:'">, + InGroup; +def warn_attr_swift_name_setter_parameters : Warning< + "%0 attribute for setter must take one parameter for new value">, + InGroup; +def warn_attr_swift_name_multiple_selfs : Warning< + "%0 attribute cannot specify more than one 'self:' parameter">, + InGroup; +def warn_attr_swift_name_static_subscript : Warning< + "%0 attribute for 'subscript' must take a 'self:' parameter">, + InGroup; +def warn_attr_swift_name_num_params : Warning< + "too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">, + InGroup; +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">; +def warn_swift_newtype_attribute_non_typedef : Warning< + "'swift_newtype' attribute may be put on a typedef only; " + "attribute is ignored">, InGroup>; def warn_ignored_objc_externally_retained : Warning< "'objc_externally_retained' can only be applied to local variables " "%select{of retainable type|with strong ownership}0">, @@ -4870,7 +5015,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 " @@ -7286,6 +7431,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">; @@ -7590,6 +7748,8 @@ def ext_typecheck_cond_pointer_integer_mismatch : ExtWarn< "pointer/integer type mismatch in conditional expression" "%diff{ ($ and $)|}0,1">, InGroup>; +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">, @@ -8509,6 +8669,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; +def note_fill_in_missing_cases : Note<"add missing switch cases">; def warn_unannotated_fallthrough : Warning< "unannotated fall-through between switch labels">, @@ -8805,6 +8966,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 warn_nsconsumed_attribute_mismatch : Warning< err_nsconsumed_attribute_mismatch.Text>, InGroup; def warn_nsreturns_retained_attribute_mismatch : Warning< @@ -9105,6 +9274,13 @@ def warn_mig_server_routine_does_not_return_kern_return_t : Warning< InGroup; } // 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/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 757dbbeee3cc0..f499996470c3f 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -74,6 +74,8 @@ def note_module_file_imported_by : Note< "imported by %select{|module '%2' in }1'%0'">; def err_module_file_not_module : Error< "AST file '%0' was not built as a module">, DefaultFatal; +def err_module_file_missing_top_level_submodule : Error< + "module file '%0' is missing its top-level submodule">, DefaultFatal; def remark_module_import : Remark< "importing module '%0'%select{| into '%3'}2 from '%1'">, diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 28eb694ba9a89..0f7b9b9290946 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -56,6 +56,7 @@ FEATURE(attribute_availability_app_extension, true) FEATURE(attribute_availability_with_version_underscores, true) FEATURE(attribute_availability_tvos, true) FEATURE(attribute_availability_watchos, true) +FEATURE(attribute_availability_swift, true) FEATURE(attribute_availability_with_strict, true) FEATURE(attribute_availability_with_replacement, true) FEATURE(attribute_availability_in_templates, true) @@ -82,6 +83,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, @@ -89,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/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index 7aa1d074a5916..ccd95dddcc4bd 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -94,10 +94,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; @@ -108,7 +104,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; @@ -120,7 +116,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: @@ -333,18 +329,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/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index eba4f835d661f..8b6e57df8c82f 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -143,6 +143,12 @@ 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(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") @@ -166,6 +172,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__)") @@ -308,6 +315,9 @@ 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(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/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index 0f2549f099435..e0399b82a47c7 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -268,6 +268,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; + /// Describes the visibility of the various names within a /// particular module. enum NameVisibilityKind { diff --git a/clang/include/clang/Basic/ObjCRuntime.h b/clang/include/clang/Basic/ObjCRuntime.h index 5329b38c20722..1c4a69269deea 100644 --- a/clang/include/clang/Basic/ObjCRuntime.h +++ b/clang/include/clang/Basic/ObjCRuntime.h @@ -446,6 +446,20 @@ class ObjCRuntime { llvm_unreachable("bad kind"); } + /// Does this runtime supports direct dispatch + bool allowsDirectDispatch() const { + switch (getKind()) { + case FragileMacOSX: return false; + case MacOSX: return true; + case iOS: return true; + case WatchOS: return true; + case GCC: return false; + case GNUstep: return false; + case ObjFW: return false; + } + llvm_unreachable("bad kind"); + } + /// Try to parse an Objective-C runtime specification from the given /// string. /// diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h new file mode 100644 index 0000000000000..d712c17ee2bea --- /dev/null +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -0,0 +1,212 @@ +//===--- 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 +#include +#include +#include +#include "llvm/Support/ErrorHandling.h" + +namespace clang { + +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. + 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 : 3; + } Soft; + + struct { + unsigned Kind : NumKindBits; + unsigned AddressDiscriminated : 1; + unsigned Discrimination : 2; + unsigned Key : 2; + } ARM8_3; + }; + +public: + 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); + 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::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); + } +}; + + +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; + + /// 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; + + /// 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; + + /// The ABI for C++ member function pointers. + PointerAuthSchema CXXMemberFunctionPointers; +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Basic/SourceMgrAdapter.h b/clang/include/clang/Basic/SourceMgrAdapter.h new file mode 100644 index 0000000000000..dd7b83f1a5146 --- /dev/null +++ b/clang/include/clang/Basic/SourceMgrAdapter.h @@ -0,0 +1,85 @@ +//=== 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 +#include + +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, 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 &SourceMgrAdapter::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/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 9a3bb986930ef..a30c60cc98f99 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. @@ -1224,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 ae908bbdf3a8e..8b23447654a0b 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) @@ -531,6 +531,8 @@ ALIAS("__is_same_as", __is_same, 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/Basic/Version.h b/clang/include/clang/Basic/Version.h index 2881d8db954e0..60b5688e547f7 100644 --- a/clang/include/clang/Basic/Version.h +++ b/clang/include/clang/Basic/Version.h @@ -56,6 +56,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/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index 31f0cea572324..a8457ac349cd6 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; @@ -42,6 +43,7 @@ class CXXMethodDecl; class CodeGenOptions; class CoverageSourceInfo; class DiagnosticsEngine; +class GlobalDecl; class HeaderSearchOptions; class ObjCMethodDecl; class PreprocessorOptions; @@ -84,6 +86,26 @@ 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 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); + +/// 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..87cc00957196b 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -25,8 +25,11 @@ #include 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,17 @@ 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, + 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/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 1d550eb15ea8f..3471a2523d559 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -250,6 +250,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">, @@ -411,6 +415,8 @@ def cfguard : Flag<["-"], "cfguard">, 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">, @@ -548,6 +554,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/Driver/Job.h b/clang/include/clang/Driver/Job.h index 41d9722808526..97299fcd2d834 100644 --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -31,9 +31,11 @@ class Tool; 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 fa609281a6ccf..f1ae8ff11a434 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -329,6 +329,13 @@ def objcmt_whitelist_dir_path: Joined<["-"], "objcmt-whitelist-dir-path=">, Flag def : Joined<["-"], "objcmt-white-list-dir-path=">, Flags<[CC1Option]>, Alias; +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, Flags<[Unsupported]>; @@ -797,6 +804,21 @@ def forder_file_instrumentation : Flag<["-"], "forder-file-instrumentation">, Group, Flags<[CC1Option, CoreOption]>, HelpText<"Generate instrumented code to collect order file into default.profraw file (overridden by '=' form of option or LLVM_PROFILE_FILE env var)">; +def fapinotes : Flag<["-"], "fapinotes">, Group, + Flags<[CC1Option]>, HelpText<"Enable external API notes support">; +def fapinotes_modules : Flag<["-"], "fapinotes-modules">, Group, + Flags<[CC1Option]>, HelpText<"Enable module-based external API notes support">; +def fno_apinotes : Flag<["-"], "fno-apinotes">, Group, + Flags<[CC1Option]>, HelpText<"Disable external API notes support">; +def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group, + Flags<[CC1Option]>, HelpText<"Disable module-based external API notes support">; +def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, + Group, Flags<[DriverOption]>, MetaVarName<"">, + HelpText<"Does nothing; API notes are no longer cached separately from modules">; +def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, + Group, Flags<[CC1Option]>, MetaVarName<"">, + HelpText<"Specify the Swift version to use when filtering API notes">; + def faddrsig : Flag<["-"], "faddrsig">, Group, Flags<[CoreOption, CC1Option]>, HelpText<"Emit an address-significance table">; def fno_addrsig : Flag<["-"], "fno-addrsig">, Group, Flags<[CoreOption]>, @@ -1934,6 +1956,29 @@ def fstrict_return : Flag<["-"], "fstrict-return">, Group, def fno_strict_return : Flag<["-"], "fno-strict-return">, 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 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">, Group, Flags<[CC1Option]>, HelpText<"Treat editor placeholders as valid source code">; @@ -2045,6 +2090,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, Flags<[CC1Option]>, + HelpText<"Add directory to the API notes search path referenced by module name">, MetaVarName<"">; def idirafter : JoinedOrSeparate<["-"], "idirafter">, Group, Flags<[CC1Option]>, HelpText<"Add directory to AFTER include search path">; def iframework : JoinedOrSeparate<["-"], "iframework">, Group, Flags<[CC1Option]>, 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 Consumer); + +/// Responsible for the fix-its that perform the +/// "add missing protocol requirements" refactoring operation. +namespace fillInMissingProtocolStubs { + +class FillInMissingProtocolStubsImpl; +class FillInMissingProtocolStubs { + std::unique_ptr 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 Consumer); +}; + +void addMissingProtocolStubs( + ASTContext &Context, const ObjCContainerDecl *Container, + llvm::function_ref Consumer); + +} // end namespace fillInMissingProtocolStubs + +} // end namespace edit +} // end namespace clang + +#endif // LLVM_CLANG_EDIT_REFACTORING_FIXITS_H diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 0ed5c9beac272..9537584caa50c 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -179,6 +179,12 @@ class CompilerInstance : public ModuleLoader { /// The list of active output files. std::list OutputFiles; + /// \brief An optional callback function used to wrap all FrontendActions + /// produced to generate imported modules before they are executed. + std::function + (const FrontendOptions &opts, std::unique_ptr action)> + GenModuleActionWrapper; + /// Force an output buffer. std::unique_ptr OutputStream; @@ -299,6 +305,13 @@ class CompilerInstance : public ModuleLoader { return Invocation->getHeaderSearchOptsPtr(); } + APINotesOptions &getAPINotesOpts() { + return Invocation->getAPINotesOpts(); + } + const APINotesOptions &getAPINotesOpts() const { + return Invocation->getAPINotesOpts(); + } + LangOptions &getLangOpts() { return *Invocation->getLangOpts(); } @@ -804,6 +817,15 @@ class CompilerInstance : public ModuleLoader { bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override; + void setGenModuleActionWrapper(std::function + (const FrontendOptions &Opts, std::unique_ptr Action)> Wrapper) { + GenModuleActionWrapper = Wrapper; + }; + + std::function + (const FrontendOptions &Opts, std::unique_ptr Action)> + getGenModuleActionWrapper() const { return GenModuleActionWrapper; } + void addDependencyCollector(std::shared_ptr Listener) { DependencyCollectors.push_back(std::move(Listener)); } diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index f3253d5b40e3c..b55bcbb269d5a 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H #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" @@ -124,6 +125,9 @@ class CompilerInvocation : public CompilerInvocationBase { MigratorOptions MigratorOpts; + /// Options controlling API notes. + APINotesOptions APINotesOpts; + /// Options controlling IRgen and the backend. CodeGenOptions CodeGenOpts; @@ -182,7 +186,7 @@ class CompilerInvocation : public CompilerInvocationBase { /// 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 @@ -193,6 +197,9 @@ class CompilerInvocation : public CompilerInvocationBase { MigratorOptions &getMigratorOpts() { return MigratorOpts; } const MigratorOptions &getMigratorOpts() const { return MigratorOpts; } + APINotesOptions &getAPINotesOpts() { return APINotesOpts; } + const APINotesOptions &getAPINotesOpts() const { return APINotesOpts; } + CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; } const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } 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/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index 09d83adf579b8..96f32dd571958 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -365,6 +365,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 Inputs; @@ -442,7 +446,8 @@ class FrontendOptions { UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), ModulesEmbedAllFiles(false), - IncludeTimestamps(true), TimeTraceGranularity(500) {} + IncludeTimestamps(true), IndexIgnoreSystemSymbols(false), + IndexRecordCodegenName(false), TimeTraceGranularity(500) {} /// getInputKindForExtension - Return the appropriate input kind for a file /// extension. For example, "c" would return Language::C. diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index 0f9b17ee50893..78c4227d93ddf 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -145,6 +145,7 @@ class DependencyFileGenerator : public DependencyCollector { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; + bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; }; diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h new file mode 100644 index 0000000000000..8e438fd7dc861 --- /dev/null +++ b/clang/include/clang/Index/IndexDataStore.h @@ -0,0 +1,74 @@ +//===--- 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 "llvm/Support/Chrono.h" +#include +#include +#include + +namespace clang { +namespace index { + +class IndexDataStore { +public: + ~IndexDataStore(); + + static std::unique_ptr + create(StringRef IndexStorePath, std::string &Error); + + StringRef getFilePath() const; + bool foreachUnitName(bool sorted, + llvm::function_ref receiver); + + static unsigned getFormatVersion(); + + enum class UnitEventKind { + Removed, + Modified, + /// The directory got deleted. No more events will follow. + DirectoryDeleted, + Failure + }; + struct UnitEvent { + UnitEventKind Kind; + StringRef UnitName; + }; + struct UnitEventNotification { + bool IsInitial; + ArrayRef Events; + }; + typedef std::function UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler Handler); + /// \returns true if an error occurred. + bool startEventListening(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..4db55f68bbf17 --- /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. +indexstore_symbol_property_t getIndexStoreProperties(SymbolPropertySet Props); + +/// Map a SymbolRoleSet to its indexstore representation. +indexstore_symbol_role_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 + +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 Relations; + SymbolRoleSet Roles; + unsigned Line; + unsigned Column; +}; + +class IndexRecordReader { + IndexRecordReader(); + +public: + static std::unique_ptr + createWithRecordFilename(StringRef RecordFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr + createWithFilePath(StringRef FilePath, std::string &Error); + static std::unique_ptr + createWithBuffer(std::unique_ptr 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 Checker, + llvm::function_ref 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 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 DeclsFilter, + ArrayRef RelatedDeclsFilter, + llvm::function_ref Receiver); + bool foreachOccurrence( + llvm::function_ref Receiver); + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount, + llvm::function_ref 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 &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 Related); +}; + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXRECORDWRITER_H diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index 2e1e6005d68a6..da3fe03a2813e 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -54,6 +54,8 @@ enum class SymbolKind : uint8_t { Parameter, Using, + + CommentTag, }; enum class SymbolLanguage : uint8_t { @@ -72,6 +74,28 @@ enum class SymbolSubKind : uint8_t { AccessorSetter, UsingTypename, UsingValue, + + // Swift sub-kinds + + SwiftAccessorWillSet, + SwiftAccessorDidSet, + SwiftAccessorAddressor, + SwiftAccessorMutableAddressor, + SwiftAccessorRead, + SwiftAccessorModify, + + SwiftExtensionOfStruct, + SwiftExtensionOfClass, + SwiftExtensionOfEnum, + SwiftExtensionOfProtocol, + + SwiftPrefixOperator, + SwiftPostfixOperator, + SwiftInfixOperator, + + SwiftSubscript, + SwiftAssociatedType, + SwiftGenericTypeParam, }; typedef uint16_t SymbolPropertySet; diff --git a/clang/include/clang/Index/IndexUnitReader.h b/clang/include/clang/Index/IndexUnitReader.h new file mode 100644 index 0000000000000..4c40edcbe960f --- /dev/null +++ b/clang/include/clang/Index/IndexUnitReader.h @@ -0,0 +1,83 @@ +//===--- 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 + createWithUnitFilename(StringRef UnitFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr + createWithFilePath(StringRef FilePath, std::string &Error); + + static Optional> + 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; + }; + 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 Receiver); + + bool foreachInclude(llvm::function_ref 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 +#include + +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 &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 &Scratch)> GetInfoForModuleFn; + struct FileInclude { + int Index; + unsigned Line; + }; + struct FileEntryData { + const FileEntry *File; + bool IsSystem; + int ModuleIndex; + std::vector Includes; + }; + std::vector Files; + std::vector Modules; + llvm::DenseMap IndexByFile; + llvm::DenseMap IndexByModule; + llvm::DenseSet SeenASTFiles; + struct RecordOrUnitData { + std::string Name; + int FileIndex; + int ModuleIndex; + bool IsSystem; + }; + std::vector Records; + std::vector 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 &Str); + void getUnitPathForOutputFile(StringRef FilePath, SmallVectorImpl &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 isUnitUpToDateForOutputFile(StringRef FilePath, + Optional TimeCompareFilePath, + std::string &Error); + static void getUnitNameForAbsoluteOutputFile(StringRef FilePath, SmallVectorImpl &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 9ed2a018f1617..2dceec2c01438 100644 --- a/clang/include/clang/Index/IndexingAction.h +++ b/clang/include/clang/Index/IndexingAction.h @@ -16,14 +16,18 @@ #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/ArrayRef.h" #include +#include namespace clang { class ASTContext; class ASTConsumer; class ASTReader; class ASTUnit; + class CompilerInstance; class Decl; class FrontendAction; + class FrontendOptions; + class Module; namespace serialization { class ModuleFile; @@ -31,7 +35,20 @@ namespace serialization { namespace index { class IndexDataConsumer; + class IndexUnitWriter; +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; +}; /// Creates an ASTConsumer that indexes all symbols (macros and AST decls). std::unique_ptr createIndexingASTConsumer( std::shared_ptr DataConsumer, @@ -69,6 +86,18 @@ std::unique_ptr indexMacrosCallback(IndexDataConsumer &Consumer, void indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, IndexDataConsumer &DataConsumer, IndexingOptions Opts); +/// \param WrappedAction another frontend action to wrap over or null. +std::unique_ptr +createIndexDataRecordingAction(const FrontendOptions &FEOpts, + std::unique_ptr 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/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 97a222f4a703f..27e84f63bab8f 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -173,15 +173,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. @@ -531,6 +533,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); + /// 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/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 1e6b28d4aa3d8..6bd36cd83d979 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -59,6 +59,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. @@ -237,6 +245,9 @@ class ModuleMap { /// 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; + /// Whether files in this module can only include non-modular headers /// and headers from used modules. unsigned NoUndeclaredIncludes : 1; diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index 11607811dc8f7..dd4f4623938c2 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 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 &getSkippedRanges() { - ensureSkippedRangesLoaded(); return SkippedRanges; } diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 5add58fd59366..15d800a293ea1 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2605,6 +2605,14 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, ParsedAttr::Syntax Syntax); + void ParseSwiftNewtypeAttribute(IdentifierInfo &SwiftNewtype, + SourceLocation SwiftNewtypeLoc, + ParsedAttributes &attrs, + SourceLocation *endLoc, + IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, + ParsedAttr::Syntax Syntax); + void ParseAttributeWithTypeArg(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, @@ -2624,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); @@ -3079,11 +3089,25 @@ 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(); ExprResult ParseExpressionTrait(); + ExprResult ParseBuiltinPtrauthTypeDiscriminator(); + //===--------------------------------------------------------------------===// // Preprocessor code-completion pass-through void CodeCompleteDirective(bool InConditional) override; diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index b417f89c0e5b6..e3ead60bb43f6 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -833,7 +833,8 @@ class ObjCDeclSpec { DQ_PR_unsafe_unretained = 0x800, DQ_PR_nullability = 0x1000, DQ_PR_null_resettable = 0x2000, - DQ_PR_class = 0x4000 + DQ_PR_class = 0x4000, + DQ_PR_direct = 0x8000, }; ObjCDeclSpec() @@ -903,7 +904,7 @@ class ObjCDeclSpec { unsigned objcDeclQualifier : 7; // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind - unsigned PropertyAttributes : 15; + unsigned PropertyAttributes : 16; unsigned Nullability : 2; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 60f02f5bfbb48..2854ba287b609 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -29,6 +29,7 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/TypeLoc.h" +#include "clang/APINotes/APINotesManager.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/ExpressionTraits.h" #include "clang/Basic/Module.h" @@ -55,6 +56,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TinyPtrVector.h" #include +#include #include #include #include @@ -378,6 +380,7 @@ class Sema { ASTConsumer &Consumer; DiagnosticsEngine &Diags; SourceManager &SourceMgr; + api_notes::APINotesManager APINotes; /// Flag indicating whether or not to collect detailed statistics. bool CollectStats; @@ -713,6 +716,10 @@ class Sema { OpaqueParser = P; } + /// \brief Callback to the parser to parse a type expressed as a string. + std::function + ParseTypeFromStringCallback; + class DelayedDiagnostics; class DelayedDiagnosticsState { @@ -1678,6 +1685,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. init(foo:bar:baz:) or controllerForName(_:), + /// 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 + /// context.identifier name. + /// + /// \returns true if the name is a valid swift name for \p D, false otherwise. + bool DiagnoseSwiftName(Decl *D, StringRef Name, + SourceLocation ArgLoc, + const IdentifierInfo *AttrName); + private: /// Methods for marking which expressions involve dereferencing a pointer /// marked with the 'noderef' attribute. Expressions are checked bottom up as @@ -2114,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, @@ -2202,6 +2230,9 @@ class Sema { ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC, SourceLocation Loc, QualType T); + QualType adjustParameterTypeForObjCAutoRefCount(QualType T, + SourceLocation NameLoc, + TypeSourceInfo *TSInfo); ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc, SourceLocation NameLoc, IdentifierInfo *Name, QualType T, TypeSourceInfo *TSInfo, @@ -2819,6 +2850,8 @@ class Sema { const SpeculativeLoadHardeningAttr &AL); OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, const AttributeCommonInfo &CI); + SwiftNameAttr *mergeSwiftNameAttr(Decl *D, const AttributeCommonInfo &CI, + StringRef Name, bool Override); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const InternalLinkageAttr &AL); @@ -3719,6 +3752,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 @@ -3756,6 +3795,29 @@ class Sema { /// Valid types should not have multiple attributes with different CCs. const AttributedType *getCallingConvAttributedType(QualType T) const; + /// Check whether a nullability type specifier can be added to the given + /// type through some means not written in source (e.g. API notes). + /// + /// \param type The type to which the nullability specifier will be + /// added. On success, this type will be updated appropriately. + /// + /// \param nullability The nullability specifier to add. + /// + /// \param diagLoc The location to use for diagnostics. + /// + /// \param allowArrayTypes Whether to accept nullability specifiers on an + /// array type (e.g., because it will decay to a pointer). + /// + /// \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 checkImplicitNullabilityTypeSpecifier(QualType &type, + NullabilityKind nullability, + SourceLocation diagLoc, + bool allowArrayTypes, + bool overrideExisting); + /// Stmt attributes - this routine is the top level dispatcher. StmtResult ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributesView &Attrs, @@ -8901,6 +8963,15 @@ 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 CheckObjCMethodDirectOverrides(ObjCMethodDecl *method, + ObjCMethodDecl *overridden); + void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, ObjCInterfaceDecl *CurrentClass, ResultTypeCompatibilityKind RTC); @@ -11494,6 +11565,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/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 52504976692bd..85269fbab415a 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -197,25 +197,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. @@ -347,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, @@ -365,6 +343,12 @@ namespace serialization { /// Record code for the diagnostic options table. DIAGNOSTIC_OPTIONS, + /// 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, }; @@ -648,9 +632,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 7495c2b17aa27..787dd71245a21 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -751,13 +751,6 @@ class ASTReader /// added to the global preprocessing entity ID to produce a local ID. GlobalPreprocessedEntityMapType GlobalPreprocessedEntityMap; - using GlobalSkippedRangeMapType = - ContinuousRangeMap; - - /// 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. //@{ @@ -1325,6 +1318,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); @@ -1440,7 +1435,7 @@ class ASTReader /// do with non-routine failures (e.g., corrupted AST file). void Error(StringRef Msg) const; void Error(unsigned DiagID, StringRef Arg1 = StringRef(), - StringRef Arg2 = StringRef()) const; + StringRef Arg2 = StringRef(), StringRef Arg3 = StringRef()) const; void Error(unsigned DiagID, StringRef Arg1, StringRef Arg2, unsigned Select) const; void Error(llvm::Error &&Err) const; @@ -1721,9 +1716,6 @@ class ASTReader Optional 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 1979c53a7133c..bff0e2b9de5db 100644 --- a/clang/include/clang/Serialization/Module.h +++ b/clang/include/clang/Serialization/Module.h @@ -159,6 +159,9 @@ class ModuleFile { /// Whether the PCH has a corresponding object file. bool PCHHasObjectFile = false; + /// Whether the top-level module has been read from the AST file. + bool DidReadTopLevelSubmodule = false; + /// The file entry for the module file. const FileEntry *File = nullptr; @@ -335,12 +338,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/include/clang/Serialization/ModuleManager.h b/clang/include/clang/Serialization/ModuleManager.h index 5b3b22be759c7..5f20fd7d2eca6 100644 --- a/clang/include/clang/Serialization/ModuleManager.h +++ b/clang/include/clang/Serialization/ModuleManager.h @@ -255,9 +255,7 @@ class ModuleManager { std::string &ErrorStr); /// Remove the modules starting from First (to the end). - void removeModules(ModuleIterator First, - llvm::SmallPtrSetImpl &LoadedSuccessfully, - ModuleMap *modMap); + void removeModules(ModuleIterator First, ModuleMap *modMap); /// Add an in-memory buffer the list of known buffers void addInMemoryBuffer(StringRef FileName, diff --git a/clang/include/clang/Tooling/Refactor/IndexerQuery.h b/clang/include/clang/Tooling/Refactor/IndexerQuery.h new file mode 100644 index 0000000000000..8e379d6f38d03 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/IndexerQuery.h @@ -0,0 +1,312 @@ +//===--- 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 + +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; + + /// 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 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); } + + bool verify(ASTContext &Context) override; + + 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 +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) {} + + virtual ~DeclPredicateNode() { } + + static std::unique_ptr + create(const DeclPredicate &Predicate); + static std::unique_ptr + 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 Child; + +public: + DeclPredicateNotPredicate(std::unique_ptr 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 + +enum class QueryBoolResult { + Unknown, + Yes, + No, +}; + +// FIXME: Check that 'T' is either a PersistentDeclRef<> or a Decl *. +template 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 &&Other) = default; + Indexed &operator=(Indexed &&Other) = default; + Indexed(const Indexed &Other) = default; + Indexed &operator=(const Indexed &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; + + std::vector Input; + std::unique_ptr Predicate; + +protected: + std::vector>> Output; + +public: + DeclarationsQuery(std::vector Input, + std::unique_ptr Predicate) + : IndexerQuery(BaseUIDString, nullptr), Input(std::move(Input)), + Predicate(std::move(Predicate)) { + assert(!this->Input.empty() && "empty declarations list!"); + } + + ArrayRef getInputs() const { return Input; } + + void invalidateTUSpecificState() override { Input.clear(); } + + bool verify(ASTContext &Context) override; + + void setOutput(std::vector>> 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 +class ManyToManyDeclarationsQuery final + : public std::enable_if::value, + DeclarationsQuery>::type { +public: + ManyToManyDeclarationsQuery( + ArrayRef Input, + std::unique_ptr Predicate) + : DeclarationsQuery(std::vector(Input.begin(), Input.end()), + std::move(Predicate)) {} + + std::vector>> getOutput() const { + std::vector>> Results; + for (const auto &Ref : DeclarationsQuery::Output) + Results.push_back(Indexed>( + PersistentDeclRef(Ref.Decl.USR), Ref.IsNotDefined)); + 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 +std::unique_ptr> +filter(ArrayRef Declarations, + BoolDeclPredicate (*Fn)(const DeclEntity &), + typename std::enable_if::value>::type * = + nullptr) { + return std::make_unique>( + 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 + +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 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..f5c2f668e0141 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringActions.def @@ -0,0 +1,62 @@ +//===--- 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_SUB_ACTION(Expression, Extract, "Extract Expression", + "extract-expression") + +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 +#include + +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 Replacements; + std::vector> + AssociatedSymbols; + std::unique_ptr Continuation; + + RefactoringResult( + std::vector Replacements, + std::unique_ptr Continuation = nullptr) + : Replacements(std::move(Replacements)), + Continuation(std::move(Continuation)) {} + + RefactoringResult(std::unique_ptr 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 + 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 + runInExternalASTUnit(ASTContext &Context) = 0; +}; + +// TODO: Remove in favour of diagnostics. +class RefactoringOperationError + : public llvm::ErrorInfo { +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 getRefactoringCandidates() { return {}; } + + virtual std::vector getAvailableSubActions() { + return {}; + } + + virtual llvm::Expected + 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 RefactoringOp; + std::unique_ptr 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 +#include + +namespace clang { +namespace tooling { + +namespace detail { + +struct PersistentDeclRefBase {}; + +} // end namespace detail + +/// Declaration references are persisted across translation units by using +/// USRs. +template +struct PersistentDeclRef : std::enable_if::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 create(const Decl *D) { + // FIXME: Move the getUSRForDecl method somewhere else. + return PersistentDeclRef(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..0368122cbf050 --- /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 OldRefactoringOption { + virtual ~OldRefactoringOption() = 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> Options; + +public: + RefactoringOptionSet() {} + template RefactoringOptionSet(const T &Option) { add(Option); } + + RefactoringOptionSet(RefactoringOptionSet &&) = default; + RefactoringOptionSet &operator=(RefactoringOptionSet &&) = default; + + RefactoringOptionSet(const RefactoringOptionSet &) = delete; + RefactoringOptionSet &operator=(const RefactoringOptionSet &) = delete; + + template 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 const T *get() const { + auto It = Options.find(StringRef(T::Name)); + if (It == Options.end()) + return nullptr; + return static_cast(It->getValue().get()); + } + + template const T &get(const T &Default) const { + const auto *Ptr = get(); + return Ptr ? *Ptr : Default; + } + + void print(llvm::raw_ostream &OS) const; + + static llvm::Expected 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..60db7cc09b151 --- /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_OLD_REFACTORING_OPTIONS_H +#define LLVM_CLANG_TOOLING_REFACTOR_OLD_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 : OldRefactoringOption { +protected: + bool Value = false; + void serializeImpl(const SerializationContext &Context, const char *Name); + +public: + operator bool() const { return Value; } +}; + +template 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 { + static constexpr const char *Name = "rename.avoid.textual.matches"; +}; + +} // end namespace option +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_OLD_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..86dd2fbe8a32d --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h @@ -0,0 +1,86 @@ +//===--- 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 + +namespace clang { +namespace tooling { + +/// \brief Represent a symbol that can be used for an additional refactoring +/// action that associated. +class RefactoringResultAssociatedSymbol { + OldSymbolName Name; + +public: + RefactoringResultAssociatedSymbol(OldSymbolName Name) + : Name(std::move(Name)) {} + + const OldSymbolName &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 Offsets; + bool IsDeclaration; + + AssociatedSymbolLocation(ArrayRef Offsets, + bool IsDeclaration = false) + : Offsets(Offsets.begin(), Offsets.end()), + IsDeclaration(IsDeclaration) {} + }; + llvm::SmallDenseMap + 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..81442222a0f3a --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h @@ -0,0 +1,110 @@ +//===--- 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" +#include "llvm/Support/Mutex.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 { + OldSymbolName Name; + std::vector 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 IndexedOccurrences, + bool IsObjCSelector, + bool SearchForStringLiteralOccurrences = false) + : Name(std::move(Name)), + IndexedOccurrences(std::move(IndexedOccurrences)), + IsObjCSelector(IsObjCSelector), + SearchForStringLiteralOccurrences(SearchForStringLiteralOccurrences) {} + 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 OldSymbolOccurrence &Occurrence, + SourceManager &SM, + 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 Symbols; + IndexedFileOccurrenceConsumer &Consumer; + IndexedFileRenamerLock &Lock; + const RefactoringOptionSet *Options; + +public: + IndexedFileOccurrenceProducer(ArrayRef Symbols, + IndexedFileOccurrenceConsumer &Consumer, + IndexedFileRenamerLock &Lock, + 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..cf795334a5fa9 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h @@ -0,0 +1,146 @@ +//===--- 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: + 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 + /// symbol. May not be the most canonical declaration. + const NamedDecl *FoundDecl; + /// An optional Objective-C selector. + llvm::Optional 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 OldSymbolOccurrence { + /// The source locations that correspond to the occurence of the symbol. + SmallVector Locations; + +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, + + /// \brief This is a textual occurrence of a symbol in a string literal. + MatchingStringLiteral + }; + + 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; + + OldSymbolOccurrence() + : Kind(MatchingSymbol), IsMacroExpansion(false), SymbolIndex(0) {} + + OldSymbolOccurrence(OccurrenceKind Kind, bool IsMacroExpansion, + unsigned SymbolIndex, ArrayRef Locations) + : Locations(Locations.begin(), Locations.end()), Kind(Kind), + IsMacroExpansion(IsMacroExpansion), SymbolIndex(SymbolIndex) { + assert(!Locations.empty() && "Renamed occurence without locations!"); + } + + OldSymbolOccurrence(OldSymbolOccurrence &&) = default; + OldSymbolOccurrence &operator=(OldSymbolOccurrence &&) = default; + + ArrayRef 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 { + 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 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 OldSymbolOccurrence &LHS, const OldSymbolOccurrence &RHS); + +/// \brief Equal-to operator between the two renamed symbol occurrences. +bool operator==(const OldSymbolOccurrence &LHS, const OldSymbolOccurrence &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..9a2883e64751e --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenamingOperation.h @@ -0,0 +1,44 @@ +//===--- 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 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(OldSymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl &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..fdb9f340b6e51 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/SymbolName.h @@ -0,0 +1,74 @@ +//===--- 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 +#include + +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 OldSymbolName { +public: + OldSymbolName() {} + + /// \brief Creates a \c SymbolName by decomposing the given \p Name using + /// language specific logic. + OldSymbolName(StringRef Name, const LangOptions &LangOpts); + OldSymbolName(StringRef Name, bool IsObjectiveCSelector); + explicit OldSymbolName(ArrayRef Name); + + OldSymbolName(OldSymbolName &&) = default; + OldSymbolName &operator=(OldSymbolName &&) = default; + + OldSymbolName(const OldSymbolName &) = default; + OldSymbolName &operator=(const OldSymbolName &) = 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 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: + std::vector Strings; +}; + +raw_ostream &operator<<(raw_ostream &OS, const OldSymbolName &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..d6f8e495e6037 --- /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 +#include + +namespace clang { +namespace tooling { +namespace rename { + +// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree! +std::vector +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 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 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 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 +#include + +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::VisitNestedNameSpecifier instead. +class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback { +public: + explicit NestedNameSpecifierLocFinder(ASTContext &Context) + : Context(Context) {} + + ArrayRef 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"); + Locations.push_back(*NNS); + } + + ASTContext &Context; + std::vector 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 2cbe865bce879..595499fd64f4c 100644 --- a/clang/include/clang/module.modulemap +++ b/clang/include/clang/module.modulemap @@ -148,6 +148,9 @@ module Clang_Tooling { // 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" } module Clang_ToolingCore { diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h new file mode 100644 index 0000000000000..6765ffa9e827f --- /dev/null +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -0,0 +1,515 @@ +//===--- 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" + +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); +} + +template +static inline Ret functionPtrFromFunctionRef(void *ctx, Params ...params) { + auto fn = (llvm::function_ref *)ctx; + return (*fn)(std::forward(params)...); +} + +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 receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_occurrence_relations_apply(obj, ^bool(indexstore_symbol_relation_t sym_rel) { + return receiver(sym_rel); + }); +#else + return indexstore_occurrence_relations_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + std::pair getLineCol() { + unsigned line, col; + indexstore_occurrence_get_line_col(obj, &line, &col); + return std::make_pair(line, col); + } +}; + +class IndexStore; +typedef std::shared_ptr 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(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 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 indexstore_store_units_apply_f(obj, sorted, &receiver, functionPtrFromFunctionRef); +#endif + } + + class UnitEvent { + indexstore_unit_event_t obj; + public: + UnitEvent(indexstore_unit_event_t obj) : obj(obj) {} + + enum class Kind { + 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_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; + } + + StringRef getUnitName() const { + return stringFromIndexStoreStringRef(indexstore_unit_event_get_unit_name(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 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)); + }); +#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; + 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 &nameBuf) { + llvm::SmallString<256> buf = outputPath; + 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); + } + + 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 filter, + llvm::function_ref 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 indexstore_record_reader_search_symbols_f(obj, &filter, functionPtrFromFunctionRef, + &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachSymbol(bool noCache, llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_symbols_apply(obj, noCache, ^bool(indexstore_symbol_t sym) { + return receiver(sym); + }); +#else + return indexstore_record_reader_symbols_apply_f(obj, noCache, &receiver, functionPtrFromFunctionRef); +#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 symbolsFilter, + ArrayRef relatedSymbolsFilter, + llvm::function_ref receiver) { + llvm::SmallVector c_symbolsFilter; + c_symbolsFilter.reserve(symbolsFilter.size()); + for (IndexRecordSymbol sym : symbolsFilter) { + c_symbolsFilter.push_back(sym.obj); + } + llvm::SmallVector c_relatedSymbolsFilter; + c_relatedSymbolsFilter.reserve(relatedSymbolsFilter.size()); + 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(), + c_relatedSymbolsFilter.size(), + ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + 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 + } + + bool foreachOccurrence( + llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_apply(obj, ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return indexstore_record_reader_occurrences_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineEnd, + llvm::function_ref 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 indexstore_record_reader_occurrences_in_line_range_apply_f(obj, + lineStart, + lineEnd, + &receiver, functionPtrFromFunctionRef); +#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)); } + +}; + +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 receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_dependencies_apply(obj, ^bool(indexstore_unit_dependency_t dep) { + return receiver(dep); + }); +#else + return indexstore_unit_reader_dependencies_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachInclude(llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_includes_apply(obj, ^bool(indexstore_unit_include_t inc) { + return receiver(inc); + }); +#else + return indexstore_unit_reader_includes_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } +}; + +} // namespace indexstore + +#endif diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h new file mode 100644 index 0000000000000..5f190635bd29d --- /dev/null +++ b/clang/include/indexstore/indexstore.h @@ -0,0 +1,580 @@ +/*===-- 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 +#include +#include +#include + +/** + * \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 11 + +#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 +# ifdef _WIN32 +# ifdef IndexStore_EXPORTS +# define INDEXSTORE_PUBLIC __declspec(dllexport) +# else +# define INDEXSTORE_PUBLIC __declspec(dllimport) +# endif +# else +# define INDEXSTORE_PUBLIC +# endif +#endif + +#ifndef __has_feature +# 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 + +#if __has_attribute(flag_enum) +# define INDEXSTORE_FLAG_ENUM_ATTR __attribute__((flag_enum)) +#else +# define INDEXSTORE_FLAG_ENUM_ATTR +#endif + +#if __has_attribute(enum_extensibility) +# define INDEXSTORE_OPEN_ENUM_ATTR __attribute__((enum_extensibility(open))) +#else +# define INDEXSTORE_OPEN_ENUM_ATTR +#endif + +#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 +#endif +#endif + +#ifndef INDEXSTORE_OPTIONS +# define INDEXSTORE_OPTIONS(_type, _name) _type _name; enum INDEXSTORE_OPTIONS_ATTRS +#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, + 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, + 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; + +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_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 +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); + +#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 + +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 + /// 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_USING = 26, + + 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_USINGTYPENAME = 5, + INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE = 6, + + 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_SWIFTACCESSORREAD = 1014, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY = 1015, +} indexstore_symbol_subkind_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, + 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_PROTOCOL_INTERFACE = 1 << 8, +}; + +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 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, + 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, + INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE = 1 << 20, + + // 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_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 indexstore_symbol_property_t +indexstore_symbol_get_properties(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_role_t +indexstore_symbol_get_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_role_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 indexstore_symbol_role_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, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_relation_t symbol_rel)); +#endif + +INDEXSTORE_PUBLIC bool +indexstore_occurrence_relations_apply_f(indexstore_occurrence_t, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)); + +INDEXSTORE_PUBLIC indexstore_symbol_role_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, + 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 +/// for more queries. +INDEXSTORE_PUBLIC bool +indexstore_record_reader_symbols_apply(indexstore_record_reader_t, + bool nocache, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_apply(indexstore_record_reader_t, + 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, + 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 +/// 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, + 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, + INDEXSTORE_NOESCAPE bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), + void *receiver_ctx, + 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, + 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, + 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, + 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, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)); + +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 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, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_unit_dependency_t)); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_includes_apply(indexstore_unit_reader_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, + 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, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_unit_include_t)); + +INDEXSTORE_END_DECLS + +#endif diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h new file mode 100644 index 0000000000000..4cd2d9b904679 --- /dev/null +++ b/clang/lib/APINotes/APINotesFormat.h @@ -0,0 +1,309 @@ +//===--- 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/PointerEmbeddedInt.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitcode/RecordLayout.h" +#include "llvm/Bitstream/BitCodes.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 = 24; // EnumExtensibility+FlagEnum + +using IdentifierID = PointerEmbeddedInt; +using IdentifierIDField = BCVBR<16>; + +using SelectorID = PointerEmbeddedInt; +using SelectorIDField = BCVBR<16>; + +using StoredContextID = PointerEmbeddedInt; + +/// 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 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 + /// (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, + + /// 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, + + /// The enum constant data block, which maps enumerator names to + /// information about the enumerators. + ENUM_CONSTANT_BLOCK_ID, +}; + +namespace control_block { + // These IDs must \em not be renumbered or reordered without incrementing + // VERSION_MAJOR. + enum { + METADATA = 1, + MODULE_NAME = 2, + MODULE_OPTIONS = 3, + SOURCE_FILE = 4, + }; + + 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 + >; + + using ModuleOptionsLayout = BCRecordLayout< + MODULE_OPTIONS, + BCFixed<1> // SwiftInferImportAsMember + >; + + using SourceFileLayout = BCRecordLayout< + SOURCE_FILE, + BCVBR<16>, // file size + BCVBR<16> // creation time + >; +} + +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_ID_DATA = 1, + OBJC_CONTEXT_INFO_DATA = 2, + }; + + using ObjCContextIDLayout = BCRecordLayout< + OBJC_CONTEXT_ID_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + 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. + >; +} + +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 + >; +} + +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 + >; +}; + +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; + llvm::SmallVector Identifiers; +}; + +} // end namespace api_notes +} // end namespace clang + +namespace llvm { + template<> + struct DenseMapInfo { + typedef DenseMapInfo 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(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..11b087931fef1 --- /dev/null +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -0,0 +1,454 @@ +//===--- 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/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" +#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/ADT/SmallPtrSet.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include + +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"); + +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) { } + +APINotesManager::~APINotesManager() { + // Free the API notes readers. + for (const auto &entry : Readers) { + if (auto reader = entry.second.dyn_cast()) { + delete reader; + } + } + + delete CurrentModuleReaders[0]; + delete CurrentModuleReaders[1]; +} + +std::unique_ptr +APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { + PrettyStackTraceDoubleString trace("Loading API notes from ", + apiNotesFile->getName()); + + // Open the source file. + 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. + // 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 apiNotesBuffer; + std::unique_ptr compiledBuffer; + { + 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(sourceBuffer->getBuffer(), + SourceMgr.getFileEntryForID(sourceFileID), + OS, + srcMgrAdapter.getDiagHandler(), + srcMgrAdapter.getDiagContext())) + return nullptr; + + // Make a copy of the compiled form into the buffer. + compiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(apiNotesBuffer.data(), apiNotesBuffer.size())); + } + + // 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?"); + return reader; +} + +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 FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directory, + StringRef basename, + bool wantPublic) { + FileManager &fileMgr = SourceMgr.getFileManager(); + + llvm::SmallString<128> path; + path += directory->getName(); + + StringRef basenameSuffix = ""; + if (!wantPublic) basenameSuffix = "_private"; + + // Look for the source API notes file. + llvm::sys::path::append(path, + llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION); + auto file = fileMgr.getFile(path, /*Open*/true); + return file ? *file : nullptr; +} + +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. + auto 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. + auto 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; +} + +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; +} + +/// \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, + ArrayRef searchPaths) { + assert(!CurrentModuleReaders[0] && + "Already loaded API notes for the current module?"); + + FileManager &fileMgr = SourceMgr.getFileManager(); + auto moduleName = module->getTopLevelModuleName(); + + // First, look relative to the module itself. + if (lookInModule) { + 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; + + if (!wantPublic) + checkPrivateAPINotesName(SourceMgr.getDiagnostics(), file, module); + + // 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 "Headers" or "PrivateHeaders" + // subdirectory. + // + // Public modules: + // - Headers/Foo.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) + llvm::SmallString<128> path; + path += module->Directory->getName(); + + 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); + } + + 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 (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 && hasPrivateSubmodules(module)) + 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 = findAPINotesFile(*searchDir, moduleName)) { + CurrentModuleReaders[0] = loadAPINotes(file).release(); + return !getCurrentModuleReaders().empty(); + } + } + } + + // Didn't find any API notes. + return false; +} + +llvm::SmallVector APINotesManager::findAPINotes(SourceLocation Loc) { + llvm::SmallVector 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 Results; + + // If we don't have source location information, we're done. + 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 Results; + const FileEntry *File = SourceMgr.getFileEntryForID(ID); + if (!File) return Results; + + // 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, + llvm::SmallPtrSet> DirsVisited; + 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()) { + DirsVisited.insert(Dir); + Dir = OtherDir; + continue; + } + + // We have the answer. + if (auto Reader = Known->second.dyn_cast()) + Results.push_back(Reader); + 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. + if (auto Reader = Readers[Dir].dyn_cast()) + Results.push_back(Reader); + 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 (auto APINotesFile = FileMgr.getFile(APINotesPath)) { + if (!loadAPINotes(Dir, *APINotesFile)) { + ++NumHeaderAPINotes; + if (auto Reader = Readers[Dir].dyn_cast()) + Results.push_back(Reader); + 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 { + auto DirEntry = FileMgr.getDirectory(ParentPath); + Dir = DirEntry ? *DirEntry : nullptr; + } + } 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 Results; +} diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp new file mode 100644 index 0000000000000..328995d42c91c --- /dev/null +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -0,0 +1,2001 @@ +//===--- 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/Bitstream/BitstreamReader.h" +#include "llvm/Support/DJB.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 { + /// Deserialize a version tuple. + VersionTuple readVersionTuple(const uint8_t *&data) { + uint8_t numVersions = (*data++) & 0x03; + + unsigned major = endian::readNext(data); + if (numVersions == 0) + return VersionTuple(major); + + unsigned minor = endian::readNext(data); + if (numVersions == 1) + return VersionTuple(major, minor); + + unsigned subminor = endian::readNext(data); + if (numVersions == 2) + return VersionTuple(major, minor, subminor); + + unsigned build = endian::readNext(data); + return VersionTuple(major, minor, subminor, build); + } + + /// An on-disk hash table whose data is versioned based on the Swift version. + template + class VersionedTableInfo { + public: + using internal_key_type = KeyType; + using external_key_type = KeyType; + using data_type = SmallVector, 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(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + unsigned numElements = endian::readNext(data); + data_type result; + result.reserve(numElements); + for (unsigned i = 0; i != numElements; ++i) { + auto version = readVersionTuple(data); + auto dataBefore = data; (void)dataBefore; + 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++; + info.Unavailable = (unavailableBits >> 1) & 0x01; + info.UnavailableInSwift = unavailableBits & 0x01; + if ((unavailableBits >> 2) & 0x01) + info.setSwiftPrivate(static_cast((unavailableBits >> 3) & 0x01)); + + unsigned msgLength = endian::readNext(data); + info.UnavailableMsg + = std::string(reinterpret_cast(data), + reinterpret_cast(data) + msgLength); + data += msgLength; + + unsigned swiftNameLength + = endian::readNext(data); + info.SwiftName + = std::string(reinterpret_cast(data), + reinterpret_cast(data) + swiftNameLength); + data += swiftNameLength; + } + + /// Read serialized CommonTypeInfo. + void readCommonTypeInfo(const uint8_t *&data, CommonTypeInfo &info) { + readCommonEntityInfo(data, info); + + unsigned swiftBridgeLength = + endian::readNext(data); + if (swiftBridgeLength > 0) { + info.setSwiftBridge( + std::string(reinterpret_cast(data), swiftBridgeLength-1)); + data += swiftBridgeLength-1; + } + + unsigned errorDomainLength = + endian::readNext(data); + if (errorDomainLength > 0) { + info.setNSErrorDomain( + std::string(reinterpret_cast(data), errorDomainLength-1)); + data += errorDomainLength-1; + } + } + + /// 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::djbHash(key); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + return StringRef(reinterpret_cast(data), length); + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + return endian::readNext(data); + } + }; + + /// Used to deserialize the on-disk Objective-C class table. + class ObjCContextIDTableInfo { + public: + // identifier ID, is-protocol + using internal_key_type = std::pair; + using external_key_type = internal_key_type; + using data_type = unsigned; + 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(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID + = endian::readNext(data); + auto isProtocol = endian::readNext(data); + return { nameID, isProtocol }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + return endian::readNext(data); + } + }; + + /// Used to deserialize the on-disk Objective-C property table. + class ObjCContextInfoTableInfo + : public VersionedTableInfo + { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + return endian::readNext(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(payload&0x03)); + payload >>= 3; + + if (payload & (1 << 1)) + info.setSwiftObjCMembers(payload & 1); + payload >>= 2; + + if (payload & (1 << 1)) + info.setSwiftImportAsNonGeneric(payload & 1); + + return info; + } + }; + + /// Read serialized VariableInfo. + void readVariableInfo(const uint8_t *&data, VariableInfo &info) { + readCommonEntityInfo(data, info); + if (*data++) { + info.setNullabilityAudited(static_cast(*data)); + } + ++data; + + auto typeLen + = endian::readNext(data); + info.setType(std::string(data, data + typeLen)); + data += typeLen; + } + + /// Used to deserialize the on-disk Objective-C property table. + class ObjCPropertyTableInfo + : public VersionedTableInfo, + ObjCPropertyInfo> + { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto classID = endian::readNext(data); + auto nameID = endian::readNext(data); + char isInstance = endian::readNext(data); + return std::make_tuple(classID, nameID, isInstance); + } + + static ObjCPropertyInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + ObjCPropertyInfo info; + readVariableInfo(data, info); + uint8_t flags = *data++; + if (flags & (1 << 0)) + info.setSwiftImportAsAccessors(flags & (1 << 1)); + return info; + } + }; + + /// Read serialized ParamInfo. + void readParamInfo(const uint8_t *&data, ParamInfo &info) { + readVariableInfo(data, info); + + uint8_t payload = endian::readNext(data); + if (auto rawConvention = payload & 0x7) { + auto convention = static_cast(rawConvention-1); + info.setRetainCountConvention(convention); + } + payload >>= 3; + 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); + + uint8_t payload = endian::readNext(data); + if (auto rawConvention = payload & 0x7) { + auto convention = static_cast(rawConvention-1); + info.setRetainCountConvention(convention); + } + payload >>= 3; + info.NullabilityAudited = payload & 0x1; + payload >>= 1; assert(payload == 0 && "Bad API notes"); + + info.NumAdjustedNullable + = endian::readNext(data); + info.NullabilityPayload + = endian::readNext(data); + + unsigned numParams = endian::readNext(data); + while (numParams > 0) { + ParamInfo pi; + readParamInfo(data, pi); + info.Params.push_back(pi); + --numParams; + } + + unsigned resultTypeLen + = endian::readNext(data); + info.ResultType = std::string(data, data + resultTypeLen); + data += resultTypeLen; + } + + /// Used to deserialize the on-disk Objective-C method table. + class ObjCMethodTableInfo + : public VersionedTableInfo, + ObjCMethodInfo> { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto classID = endian::readNext(data); + auto selectorID = endian::readNext(data); + auto isInstance = endian::readNext(data); + return internal_key_type{ classID, selectorID, isInstance }; + } + + 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; + + readFunctionInfo(data, info); + 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::getHashValue(key); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return llvm::DenseMapInfo::isEqual(lhs, rhs); + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + internal_key_type key; + key.NumPieces = endian::readNext(data); + unsigned numIdents = (length - sizeof(uint16_t)) / sizeof(uint32_t); + for (unsigned i = 0; i != numIdents; ++i) { + key.Identifiers.push_back( + endian::readNext(data)); + } + return key; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + return endian::readNext(data); + } + }; + + /// Used to deserialize the on-disk global variable table. + class GlobalVariableTableInfo + : public VersionedTableInfo { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static GlobalVariableInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + GlobalVariableInfo info; + readVariableInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk global function table. + class GlobalFunctionTableInfo + : public VersionedTableInfo { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static GlobalFunctionInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + GlobalFunctionInfo info; + readFunctionInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk enumerator table. + class EnumConstantTableInfo + : public VersionedTableInfo { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static EnumConstantInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + EnumConstantInfo info; + readCommonEntityInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk tag table. + class TagTableInfo + : public VersionedTableInfo { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + 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((payload & 0x3) - 1); + } + + readCommonTypeInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk typedef table. + class TypedefTableInfo + : public VersionedTableInfo { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static TypedefInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + TypedefInfo info; + + uint8_t payload = *data++; + if (payload > 0) { + info.SwiftWrapper = static_cast((payload & 0x3) - 1); + } + + readCommonTypeInfo(data, info); + return info; + } + }; +} // end anonymous namespace + +class APINotesReader::Implementation { +public: + /// The input buffer for the API notes data. + llvm::MemoryBuffer *InputBuffer; + + /// Whether we own the input buffer. + bool OwnsInputBuffer; + + /// The Swift version to use for filtering. + VersionTuple SwiftVersion; + + /// 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> SourceFileSizeAndModTime; + + /// Various options and attributes for the module + ModuleOptions ModuleOpts; + + using SerializedIdentifierTable = + llvm::OnDiskIterableChainedHashTable; + + /// The identifier table. + std::unique_ptr IdentifierTable; + + using SerializedObjCContextIDTable = + llvm::OnDiskIterableChainedHashTable; + + /// The Objective-C context ID table. + std::unique_ptr ObjCContextIDTable; + + using SerializedObjCContextInfoTable = + llvm::OnDiskIterableChainedHashTable; + + /// The Objective-C context info table. + std::unique_ptr ObjCContextInfoTable; + + using SerializedObjCPropertyTable = + llvm::OnDiskIterableChainedHashTable; + + /// The Objective-C property table. + std::unique_ptr ObjCPropertyTable; + + using SerializedObjCMethodTable = + llvm::OnDiskIterableChainedHashTable; + + /// The Objective-C method table. + std::unique_ptr ObjCMethodTable; + + using SerializedObjCSelectorTable = + llvm::OnDiskIterableChainedHashTable; + + /// The Objective-C selector table. + std::unique_ptr ObjCSelectorTable; + + using SerializedGlobalVariableTable = + llvm::OnDiskIterableChainedHashTable; + + /// The global variable table. + std::unique_ptr GlobalVariableTable; + + using SerializedGlobalFunctionTable = + llvm::OnDiskIterableChainedHashTable; + + /// The global function table. + std::unique_ptr GlobalFunctionTable; + + using SerializedEnumConstantTable = + llvm::OnDiskIterableChainedHashTable; + + /// The enumerator table. + std::unique_ptr EnumConstantTable; + + using SerializedTagTable = + llvm::OnDiskIterableChainedHashTable; + + /// The tag table. + std::unique_ptr TagTable; + + using SerializedTypedefTable = + llvm::OnDiskIterableChainedHashTable; + + /// The typedef table. + std::unique_ptr TypedefTable; + + /// Retrieve the identifier ID for the given string, or an empty + /// optional if the string is unknown. + Optional getIdentifier(StringRef str); + + /// Retrieve the selector ID for the given selector, or an empty + /// optional if the string is unknown. + Optional getSelector(ObjCSelectorRef selector); + + bool readControlBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readIdentifierBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readObjCContextBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readObjCPropertyBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readObjCMethodBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readObjCSelectorBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readGlobalVariableBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readEnumConstantBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readTagBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readTypedefBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); +}; + +Optional 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 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 &scratch) { + if (cursor.EnterSubBlock(CONTROL_BLOCK_ID)) + return true; + + bool sawMetadata = false; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + 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; + + case control_block::MODULE_OPTIONS: + 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. + break; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return !sawMetadata; +} + +bool APINotesReader::Implementation::readIdentifierBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (IdentifierTable) + return true; + + uint32_t tableOffset; + identifier_block::IdentifierDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCContextBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (ObjCContextIDTable) + return true; + + uint32_t tableOffset; + objc_context_block::ObjCContextIDLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + 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(blobData.data()); + + ObjCContextInfoTable.reset( + SerializedObjCContextInfoTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCPropertyBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (ObjCPropertyTable) + return true; + + uint32_t tableOffset; + objc_property_block::ObjCPropertyDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCMethodBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (ObjCMethodTable) + return true; + + uint32_t tableOffset; + objc_method_block::ObjCMethodDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readObjCSelectorBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (ObjCSelectorTable) + return true; + + uint32_t tableOffset; + objc_selector_block::ObjCSelectorDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readGlobalVariableBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (GlobalVariableTable) + return true; + + uint32_t tableOffset; + global_variable_block::GlobalVariableDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readGlobalFunctionBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (GlobalFunctionTable) + return true; + + uint32_t tableOffset; + global_function_block::GlobalFunctionDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readEnumConstantBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (EnumConstantTable) + return true; + + uint32_t tableOffset; + enum_constant_block::EnumConstantDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readTagBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(TAG_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (TagTable) + return true; + + uint32_t tableOffset; + tag_block::TagDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +bool APINotesReader::Implementation::readTypedefBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) + return true; + + llvm::Expected 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; + + 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; + + 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; + llvm::Expected 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. + if (TypedefTable) + return true; + + uint32_t tableOffset; + typedef_block::TypedefDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(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; + } + + maybeNext = cursor.advance(); + if (!maybeNext) { + // FIXME this drops the error on the floor. + consumeError(maybeNext.takeError()); + return false; + } + next = maybeNext.get(); + } + + return false; +} + +APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, + bool ownsInputBuffer, + VersionTuple swiftVersion, + bool &failed) + : Impl(*new Implementation) +{ + failed = false; + + // Initialize the input buffer. + Impl.InputBuffer = inputBuffer; + Impl.OwnsInputBuffer = ownsInputBuffer; + Impl.SwiftVersion = swiftVersion; + llvm::BitstreamCursor cursor(*Impl.InputBuffer); + + // Validate signature. + for (auto byte : API_NOTES_SIGNATURE) { + if (cursor.AtEndOfStream()) { + failed = true; + return; + } + if (Expected 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; + } + } + + // Look at all of the blocks. + bool hasValidControlBlock = false; + SmallVector scratch; + while (!cursor.AtEndOfStream()) { + llvm::Expected 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; + + 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; + + 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; + 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. + if (cursor.SkipBlock()) { + failed = true; + return; + } + break; + } + } + + if (!cursor.AtEndOfStream()) { + failed = true; + return; + } +} + +APINotesReader::~APINotesReader() { + if (Impl.OwnsInputBuffer) + delete Impl.InputBuffer; + + delete &Impl; +} + +std::unique_ptr +APINotesReader::get(std::unique_ptr inputBuffer, + VersionTuple swiftVersion) { + bool failed = false; + std::unique_ptr + reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true, + swiftVersion, failed)); + if (failed) + return nullptr; + + return reader; +} + +std::unique_ptr +APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer, + VersionTuple swiftVersion) { + bool failed = false; + std::unique_ptr + reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false, + swiftVersion, failed)); + if (failed) + return nullptr; + + return reader; +} + +StringRef APINotesReader::getModuleName() const { + return Impl.ModuleName; +} + +Optional> +APINotesReader::getSourceFileSizeAndModTime() const { + return Impl.SourceFileSizeAndModTime; +} + +ModuleOptions APINotesReader::getModuleOptions() const { + return Impl.ModuleOpts; +} + +template +APINotesReader::VersionedInfo::VersionedInfo( + VersionTuple version, + SmallVector, 1> results) + : Results(std::move(results)) { + + assert(!Results.empty()); + assert(std::is_sorted(Results.begin(), Results.end(), + [](const std::pair &left, + const std::pair &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) { + // 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. 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 { + if (!Impl.ObjCContextIDTable) + return None; + + Optional classID = Impl.getIdentifier(name); + if (!classID) + return None; + + auto knownID = Impl.ObjCContextIDTable->find({*classID, '\0'}); + if (knownID == Impl.ObjCContextIDTable->end()) + return None; + + return ContextID(*knownID); +} + +auto APINotesReader::lookupObjCClassInfo(StringRef name) + -> VersionedInfo { + if (!Impl.ObjCContextInfoTable) + return None; + + Optional contextID = lookupObjCClassID(name); + if (!contextID) + return None; + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) + return None; + + return { Impl.SwiftVersion, *knownInfo }; +} + +auto APINotesReader::lookupObjCProtocolID(StringRef name) + -> Optional { + if (!Impl.ObjCContextIDTable) + return None; + + Optional 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) + -> VersionedInfo { + if (!Impl.ObjCContextInfoTable) + return None; + + Optional contextID = lookupObjCProtocolID(name); + if (!contextID) + return None; + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) + return None; + + return { Impl.SwiftVersion, *knownInfo }; +} + + +auto APINotesReader::lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance) + -> VersionedInfo { + if (!Impl.ObjCPropertyTable) + return None; + + Optional propertyID = Impl.getIdentifier(name); + if (!propertyID) + return None; + + auto known = Impl.ObjCPropertyTable->find(std::make_tuple(contextID.Value, + *propertyID, + (char)isInstance)); + if (known == Impl.ObjCPropertyTable->end()) + return None; + + return { Impl.SwiftVersion, *known }; +} + +auto APINotesReader::lookupObjCMethod( + ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod) + -> VersionedInfo { + if (!Impl.ObjCMethodTable) + return None; + + Optional 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 { Impl.SwiftVersion, *known }; +} + +auto APINotesReader::lookupGlobalVariable( + StringRef name) + -> VersionedInfo { + if (!Impl.GlobalVariableTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.GlobalVariableTable->find(*nameID); + if (known == Impl.GlobalVariableTable->end()) + return None; + + return { Impl.SwiftVersion, *known }; +} + +auto APINotesReader::lookupGlobalFunction(StringRef name) + -> VersionedInfo { + if (!Impl.GlobalFunctionTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.GlobalFunctionTable->find(*nameID); + if (known == Impl.GlobalFunctionTable->end()) + return None; + + return { Impl.SwiftVersion, *known }; +} + +auto APINotesReader::lookupEnumConstant(StringRef name) + -> VersionedInfo { + if (!Impl.EnumConstantTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.EnumConstantTable->find(*nameID); + if (known == Impl.EnumConstantTable->end()) + return None; + + return { Impl.SwiftVersion, *known }; +} + +auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo { + if (!Impl.TagTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TagTable->find(*nameID); + if (known == Impl.TagTable->end()) + return None; + + return { Impl.SwiftVersion, *known }; +} + +auto APINotesReader::lookupTypedef(StringRef name) + -> VersionedInfo { + if (!Impl.TypedefTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TypedefTable->find(*nameID); + if (known == Impl.TypedefTable->end()) + return None; + + return { Impl.SwiftVersion, *known }; +} diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp new file mode 100644 index 0000000000000..18c55b59259ad --- /dev/null +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -0,0 +1,1340 @@ +//===--- 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 "clang/Basic/FileManager.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/DJB.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/OnDiskHashTable.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/DataTypes.h" +#include +#include +using namespace clang; +using namespace api_notes; +using namespace llvm::support; + +namespace { + template using VersionedSmallVector = + SmallVector, 1>; +} + +class APINotesWriter::Implementation { + /// Mapping from strings to identifier IDs. + llvm::StringMap IdentifierIDs; + + /// Mapping from selectors to selector ID. + llvm::DenseMap SelectorIDs; + + /// Scratch space for bitstream writing. + SmallVector ScratchRecord; + +public: + /// 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). + /// + /// 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>> + ObjCContexts; + + /// Mapping from context IDs to the identifier ID holding the name. + llvm::DenseMap ObjCContextNames; + + /// Information about Objective-C properties. + /// + /// Indexed by the context ID, property name, and whether this is an + /// instance property. + llvm::DenseMap, + llvm::SmallVector, + 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, + llvm::SmallVector, 1>> + ObjCMethods; + + /// Information about global variables. + /// + /// Indexed by the identifier ID. + llvm::DenseMap, + 1>> + GlobalVariables; + + /// Information about global functions. + /// + /// Indexed by the identifier ID. + llvm::DenseMap, + 1>> + GlobalFunctions; + + /// Information about enumerators. + /// + /// Indexed by the identifier ID. + llvm::DenseMap, + 1>> + EnumConstants; + + /// Information about tags. + /// + /// Indexed by the identifier ID. + llvm::DenseMap, 1>> + Tags; + + /// Information about typedefs. + /// + /// Indexed by the identifier ID. + llvm::DenseMap, 1>> + Typedefs; + + /// 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); + void writeEnumConstantBlock(llvm::BitstreamWriter &writer); + void writeTagBlock(llvm::BitstreamWriter &writer); + void writeTypedefBlock(llvm::BitstreamWriter &writer); +}; + +/// Record the name of a block. +static void emitBlockID(llvm::BitstreamWriter &out, unsigned ID, + StringRef name, + SmallVectorImpl &nameBuffer) { + SmallVector 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 &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 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_ID_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); + + if (SwiftInferImportAsMember) { + control_block::ModuleOptionsLayout moduleOptions(writer); + moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember); + } + + if (SourceFile) { + control_block::SourceFileLayout sourceFile(writer); + sourceFile.emit(ScratchRecord, SourceFile->getSize(), + SourceFile->getModificationTime()); + } +} + +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::djbHash(key); + } + + std::pair 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 writer(out, little); + writer.write(keyLength); + writer.write(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) { + endian::Writer writer(out, little); + writer.write(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 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::write(blobStream, 0, little); + 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 5 + info.UnavailableMsg.size() + info.SwiftName.size(); + } + + /// Emit a serialized representation of the common entity information. + static void emitCommonEntityInfo(raw_ostream &out, + const CommonEntityInfo &info) { + endian::Writer writer(out, little); + 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(payload); + + writer.write(info.UnavailableMsg.size()); + out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); + writer.write(info.SwiftName.size()); + 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() ? info.getSwiftBridge()->size() : 0) + + 2 + (info.getNSErrorDomain() ? info.getNSErrorDomain()->size() : 0) + + 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 writer(out, little); + if (auto swiftBridge = info.getSwiftBridge()) { + writer.write(swiftBridge->size() + 1); + out.write(swiftBridge->c_str(), swiftBridge->size()); + } else { + writer.write(0); + } + if (auto nsErrorDomain = info.getNSErrorDomain()) { + writer.write(nsErrorDomain->size() + 1); + out.write(nsErrorDomain->c_str(), info.getNSErrorDomain()->size()); + } else { + writer.write(0); + } + } + + /// Used to serialize the on-disk Objective-C context table. + class ObjCContextIDTableInfo { + public: + using key_type = std::pair; // identifier ID, is-protocol + using key_type_ref = key_type; + using data_type = unsigned; + 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(llvm::hash_value(key)); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(uint32_t) + 1; + uint32_t dataLength = sizeof(uint32_t); + endian::Writer writer(out, little); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(key.first); + writer.write(key.second); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + endian::Writer writer(out, little); + writer.write(data); + } + }; +} // end anonymous namespace + +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; + } + + /// Emit a serialized representation of a version tuple. + void emitVersionTuple(raw_ostream &out, const VersionTuple &version) { + endian::Writer writer(out, little); + + // 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; + writer.write(descriptor); + + // Write the components. + writer.write(version.getMajor()); + if (auto minor = version.getMinor()) + writer.write(*minor); + if (auto subminor = version.getSubminor()) + writer.write(*subminor); + if (auto build = version.getBuild()) + writer.write(*build); + } + + /// Localized helper to make a type dependent, thwarting template argument + /// deduction. + template + struct MakeDependent { + typedef T Type; + }; + + /// Determine the size of an array of versioned information, + template + unsigned getVersionedInfoSize( + const SmallVectorImpl> &infoArray, + llvm::function_ref::Type&)> + getInfoSize) { + unsigned result = sizeof(uint16_t); // # of elements + for (const auto &element : infoArray) { + result += getVersionTupleSize(element.first); + result += getInfoSize(element.second); + } + + return result; + } + + /// Emit versioned information. + template + void emitVersionedInfo( + raw_ostream &out, + SmallVectorImpl> &infoArray, + llvm::function_ref::Type& info)> + emitInfo) { + std::sort(infoArray.begin(), infoArray.end(), + [](const std::pair &left, + const std::pair &right) -> bool { + assert(left.first != right.first && "two entries for the same version"); + return left.first < right.first; + }); + endian::Writer writer(out, little); + writer.write(infoArray.size()); + for (const auto &element : infoArray) { + emitVersionTuple(out, element.first); + emitInfo(out, element.second); + } + } + + /// Retrieve the serialized size of the given VariableInfo, for use in + /// on-disk hash tables. + unsigned getVariableInfoSize(const VariableInfo &info) { + return 2 + getCommonEntityInfoSize(info) + 2 + info.getType().size(); + } + + /// Emit a serialized representation of the variable information. + 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(*nullable); + } else { + // Nothing to do. + } + + out.write(reinterpret_cast(bytes), 2); + + endian::Writer writer(out, little); + writer.write(info.getType().size()); + out.write(info.getType().data(), info.getType().size()); + } + + /// On-dish hash table info key base for handling versioned data. + template + class VersionedTableInfo { + Derived &asDerived() { + return *static_cast(this); + } + + const Derived &asDerived() const { + return *static_cast(this); + } + + public: + using key_type = KeyType; + using key_type_ref = key_type; + using data_type = + SmallVector, 1>; + using data_type_ref = 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 EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = asDerived().getKeyLength(key); + uint32_t dataLength = getVersionedInfoSize(data, + [this](const UnversionedDataType &unversionedInfo) { + return asDerived().getUnversionedInfoSize(unversionedInfo); + }); + + endian::Writer writer(out, little); + writer.write(keyLength); + writer.write(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 { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(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 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(*nullable); + } + payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0); + out << payload; + } + }; + + /// Used to serialize the on-disk Objective-C property table. + class ObjCPropertyTableInfo + : public VersionedTableInfo, + 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 writer(out, little); + writer.write(std::get<0>(key)); + writer.write(std::get<1>(key)); + writer.write(std::get<2>(key)); + } + + unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) { + return getVariableInfoSize(info) + 1; + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) { + emitVariableInfo(out, info); + uint8_t flags = 0; + if (Optional value = info.getSwiftImportAsAccessors()) { + flags |= 1 << 0; + flags |= value.getValue() << 1; + } + out << flags; + } + }; +} // 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 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::write(blobStream, 0, little); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextIDLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } + + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator + 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::write(blobStream, 0, little); + 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); + + if (ObjCProperties.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator 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::write(blobStream, 0, little); + tableOffset = generator.Emit(blobStream); + } + + objc_property_block::ObjCPropertyDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +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 writer(out, little); + + uint8_t payload = 0; + if (auto noescape = info.isNoEscape()) { + payload |= 0x01; + if (*noescape) + payload |= 0x02; + } + payload <<= 3; + if (auto retainCountConvention = info.getRetainCountConvention()) { + payload |= static_cast(retainCountConvention.getValue()) + 1; + } + writer.write(payload); + } + + /// Retrieve the serialized size of the given FunctionInfo, for use in + /// on-disk hash tables. + static unsigned getFunctionInfoSize(const FunctionInfo &info) { + 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. + static void emitFunctionInfo(raw_ostream &out, const FunctionInfo &info) { + emitCommonEntityInfo(out, info); + + endian::Writer writer(out, little); + + uint8_t payload = 0; + payload |= info.NullabilityAudited; + payload <<= 3; + if (auto retainCountConvention = info.getRetainCountConvention()) { + payload |= static_cast(retainCountConvention.getValue()) + 1; + } + writer.write(payload); + + writer.write(info.NumAdjustedNullable); + writer.write(info.NullabilityPayload); + + // Parameters. + writer.write(info.Params.size()); + for (const auto &pi : info.Params) + emitParamInfo(out, pi); + + // Result type. + writer.write(info.ResultType.size()); + out.write(info.ResultType.data(), info.ResultType.size()); + } + + /// Used to serialize the on-disk Objective-C method table. + class ObjCMethodTableInfo + : public VersionedTableInfo, + ObjCMethodInfo> { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t) + 1; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(std::get<0>(key)); + writer.write(std::get<1>(key)); + writer.write(std::get<2>(key)); + } + + unsigned getUnversionedInfoSize(const ObjCMethodInfo &info) { + return 1 + getFunctionInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) { + uint8_t payload = 0; + payload = (payload << 1) | info.DesignatedInit; + payload = (payload << 1) | info.Required; + endian::Writer writer(out, little); + writer.write(payload); + + emitFunctionInfo(out, info); + } + }; +} // 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 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::write(blobStream, 0, little); + 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::getHashValue(key); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(uint16_t) + + sizeof(uint32_t) * key.Identifiers.size(); + uint32_t dataLength = sizeof(uint32_t); + endian::Writer writer(out, little); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(key.NumPieces); + for (auto piece : key.Identifiers) { + writer.write(piece); + } + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + endian::Writer writer(out, little); + writer.write(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 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::write(blobStream, 0, little); + 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 VersionedTableInfo { + public: + unsigned getKeyLength(key_type_ref key) { + return sizeof(uint32_t); + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(key); + } + + unsigned getUnversionedInfoSize(const GlobalVariableInfo &info) { + return getVariableInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalVariableInfo &info) { + emitVariableInfo(out, info); + } + }; +} // 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 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::write(blobStream, 0, little); + 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 VersionedTableInfo { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(key); + } + + unsigned getUnversionedInfoSize(const GlobalFunctionInfo &info) { + return getFunctionInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalFunctionInfo &info) { + emitFunctionInfo(out, info); + } + }; +} // 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 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::write(blobStream, 0, little); + tableOffset = generator.Emit(blobStream); + } + + global_function_block::GlobalFunctionDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Used to serialize the on-disk global enum constant. + class EnumConstantTableInfo + : public VersionedTableInfo { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(key); + } + + unsigned getUnversionedInfoSize(const EnumConstantInfo &info) { + return getCommonEntityInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const EnumConstantInfo &info) { + emitCommonEntityInfo(out, info); + } + }; +} // 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 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::write(blobStream, 0, little); + tableOffset = generator.Emit(blobStream); + } + + enum_constant_block::EnumConstantDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + template + class CommonTypeTableInfo + : public VersionedTableInfo { + public: + using key_type_ref = typename CommonTypeTableInfo::key_type_ref; + + unsigned getKeyLength(key_type_ref) { + return sizeof(IdentifierID); + } + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out, little); + writer.write(key); + } + + 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 { + public: + unsigned getUnversionedInfoSize(const TagInfo &info) { + return 1 + getCommonTypeInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const TagInfo &info) { + endian::Writer writer(out, little); + + uint8_t payload = 0; + if (auto enumExtensibility = info.EnumExtensibility) { + payload |= static_cast(enumExtensibility.getValue()) + 1; + assert((payload < (1 << 2)) && "must fit in two bits"); + } + + payload <<= 2; + if (Optional value = info.isFlagEnum()) { + payload |= 1 << 0; + payload |= value.getValue() << 1; + } + + writer.write(payload); + + emitCommonTypeInfo(out, info); + } + }; + +} // 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 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::write(blobStream, 0, little); + 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 CommonTypeTableInfo { + + public: + unsigned getUnversionedInfoSize(const TypedefInfo &info) { + return 1 + getCommonTypeInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const TypedefInfo &info) { + endian::Writer writer(out, little); + + uint8_t payload = 0; + if (auto swiftWrapper = info.SwiftWrapper) { + payload |= static_cast(*swiftWrapper) + 1; + } + + writer.write(payload); + + emitCommonTypeInfo(out, info); + } + + }; +} // 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 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::write(blobStream, 0, little); + 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 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); + writeEnumConstantBlock(writer); + writeTagBlock(writer); + writeTypedefBlock(writer); + } + + // Write the buffer to the stream. + os.write(buffer.data(), buffer.size()); + os.flush(); +} + +APINotesWriter::APINotesWriter(StringRef moduleName, const FileEntry *sourceFile) + : Impl(*new Implementation) +{ + Impl.ModuleName = moduleName; + Impl.SourceFile = sourceFile; +} + +APINotesWriter::~APINotesWriter() { + delete &Impl; +} + + +void APINotesWriter::writeToStream(raw_ostream &os) { + Impl.writeToStream(os); +} + +ContextID APINotesWriter::addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { + IdentifierID nameID = Impl.getIdentifier(name); + + std::pair key(nameID, isClass ? 0 : 1); + auto known = Impl.ObjCContexts.find(key); + if (known == Impl.ObjCContexts.end()) { + unsigned nextID = Impl.ObjCContexts.size() + 1; + + VersionedSmallVector emptyVersionedInfo; + known = Impl.ObjCContexts.insert( + std::make_pair(key, std::make_pair(nextID, emptyVersionedInfo))) + .first; + + Impl.ObjCContextNames[nextID] = nameID; + } + + // 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, + VersionTuple swiftVersion) { + IdentifierID nameID = Impl.getIdentifier(name); + 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, + VersionTuple swiftVersion) { + SelectorID selectorID = Impl.getSelector(selector); + auto key = std::tuple{ + contextID.Value, selectorID, isInstanceMethod}; + 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})); + 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, + VersionTuple swiftVersion) { + IdentifierID variableID = Impl.getIdentifier(name); + Impl.GlobalVariables[variableID].push_back({swiftVersion, info}); +} + +void APINotesWriter::addGlobalFunction(llvm::StringRef name, + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { + IdentifierID nameID = Impl.getIdentifier(name); + Impl.GlobalFunctions[nameID].push_back({swiftVersion, info}); +} + +void APINotesWriter::addEnumConstant(llvm::StringRef name, + const EnumConstantInfo &info, + VersionTuple swiftVersion) { + IdentifierID enumConstantID = Impl.getIdentifier(name); + Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info}); +} + +void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info, + VersionTuple swiftVersion) { + IdentifierID tagID = Impl.getIdentifier(name); + Impl.Tags[tagID].push_back({swiftVersion, info}); +} + +void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion) { + IdentifierID typedefID = Impl.getIdentifier(name); + Impl.Typedefs[typedefID].push_back({swiftVersion, info}); +} + +void APINotesWriter::addModuleOptions(ModuleOptions opts) { + Impl.SwiftInferImportAsMember = opts.SwiftInferImportAsMember; +} + diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp new file mode 100644 index 0000000000000..baa77a578749f --- /dev/null +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -0,0 +1,1104 @@ +//===--- 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/VersionTuple.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include + +/* + + 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. + +--- + Name: AppKit # The name of the framework + + Availability: OSX # Optional: Specifies which platform the API is + # available on. [OSX / iOS / none/ + # available / nonswift] + + 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 + ... + Enumerators: # List of enumerators + ... + 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: + + - 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: "" + + 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, + NonSwift, + }; + + enum class MethodKind { + Class, + 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 + }; + + /// 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; + StringRef Msg; + AvailabilityItem() : Mode(APIAvailability::Available), Msg("") {} + }; + + static llvm::Optional AbsentNullability = llvm::None; + static llvm::Optional DefaultNullability = + NullabilityKind::NonNull; + typedef std::vector NullabilitySeq; + + struct Param { + unsigned Position; + Optional NoEscape = false; + Optional Nullability; + Optional RetainCountConvention; + StringRef Type; + }; + typedef std::vector ParamsSeq; + + struct Method { + StringRef Selector; + MethodKind Kind; + ParamsSeq Params; + NullabilitySeq Nullability; + Optional NullabilityOfRet; + Optional RetainCountConvention; + AvailabilityItem Availability; + Optional SwiftPrivate; + StringRef SwiftName; + FactoryAsInitKind FactoryAsInit = FactoryAsInitKind::Infer; + bool DesignatedInit = false; + bool Required = false; + StringRef ResultType; + }; + typedef std::vector MethodsSeq; + + struct Property { + StringRef Name; + llvm::Optional Kind; + llvm::Optional Nullability; + AvailabilityItem Availability; + Optional SwiftPrivate; + StringRef SwiftName; + Optional SwiftImportAsAccessors; + StringRef Type; + }; + typedef std::vector PropertiesSeq; + + struct Class { + StringRef Name; + bool AuditedForNullability = false; + AvailabilityItem Availability; + Optional SwiftPrivate; + StringRef SwiftName; + Optional SwiftBridge; + Optional NSErrorDomain; + Optional SwiftImportAsNonGeneric; + Optional SwiftObjCMembers; + MethodsSeq Methods; + PropertiesSeq Properties; + }; + typedef std::vector ClassesSeq; + + struct Function { + StringRef Name; + ParamsSeq Params; + NullabilitySeq Nullability; + Optional NullabilityOfRet; + Optional RetainCountConvention; + AvailabilityItem Availability; + Optional SwiftPrivate; + StringRef SwiftName; + StringRef Type; + StringRef ResultType; + }; + typedef std::vector FunctionsSeq; + + struct GlobalVariable { + StringRef Name; + llvm::Optional Nullability; + AvailabilityItem Availability; + Optional SwiftPrivate; + StringRef SwiftName; + StringRef Type; + }; + typedef std::vector GlobalVariablesSeq; + + struct EnumConstant { + StringRef Name; + AvailabilityItem Availability; + Optional SwiftPrivate; + StringRef SwiftName; + }; + typedef std::vector EnumConstantsSeq; + + struct Tag { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + Optional SwiftPrivate; + Optional SwiftBridge; + Optional NSErrorDomain; + Optional EnumExtensibility; + Optional FlagEnum; + Optional EnumConvenienceKind; + }; + typedef std::vector TagsSeq; + + struct Typedef { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + Optional SwiftPrivate; + Optional SwiftBridge; + Optional NSErrorDomain; + Optional SwiftWrapper; + }; + typedef std::vector TypedefsSeq; + + struct TopLevelItems { + ClassesSeq Classes; + ClassesSeq Protocols; + FunctionsSeq Functions; + GlobalVariablesSeq Globals; + EnumConstantsSeq EnumConstants; + TagsSeq Tags; + TypedefsSeq Typedefs; + }; + + struct Versioned { + VersionTuple Version; + TopLevelItems Items; + }; + + typedef std::vector VersionedSeq; + + struct Module { + StringRef Name; + AvailabilityItem Availability; + TopLevelItems TopLevel; + VersionedSeq SwiftVersions; + + llvm::Optional SwiftInferImportAsMember = {llvm::None}; + + 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(Param) +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) +LLVM_YAML_IS_SEQUENCE_VECTOR(Versioned) + +namespace llvm { + namespace yaml { + + template <> + struct ScalarEnumerationTraits { + 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 { + 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); + } + }; + + template <> + struct ScalarEnumerationTraits { + static void enumeration(IO &io, MethodKind &value) { + io.enumCase(value, "Class", MethodKind::Class); + io.enumCase(value, "Instance", MethodKind::Instance); + } + }; + + template <> + struct ScalarEnumerationTraits { + 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, "nonswift", APIAvailability::NonSwift); + io.enumCase(value, "available", APIAvailability::Available); + } + }; + + template<> + struct ScalarEnumerationTraits { + 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 ScalarEnumerationTraits { + 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 ScalarEnumerationTraits { + 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 ScalarEnumerationTraits { + 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 { + 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"; + + return StringRef(); + } + + static QuotingType mustQuote(StringRef) { return QuotingType::None; } + }; + + template <> + struct MappingTraits { + static void mapping(IO &io, Param& p) { + 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("")); + } + }; + + template <> + struct MappingTraits { + 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); + io.mapOptional("AvailabilityMsg", p.Availability.Msg); + io.mapOptional("SwiftPrivate", p.SwiftPrivate); + io.mapOptional("SwiftName", p.SwiftName); + io.mapOptional("SwiftImportAsAccessors", p.SwiftImportAsAccessors); + io.mapOptional("Type", p.Type, StringRef("")); + } + }; + + template <> + struct MappingTraits { + 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("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("")); + } + }; + + template <> + struct MappingTraits { + 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("SwiftPrivate", c.SwiftPrivate); + io.mapOptional("SwiftName", c.SwiftName); + 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); + } + }; + + template <> + struct MappingTraits { + 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("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("")); + } + }; + + template <> + struct MappingTraits { + 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); + io.mapOptional("SwiftPrivate", v.SwiftPrivate); + io.mapOptional("SwiftName", v.SwiftName); + io.mapOptional("Type", v.Type, StringRef("")); + } + }; + + template <> + struct MappingTraits { + 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("SwiftPrivate", v.SwiftPrivate); + io.mapOptional("SwiftName", v.SwiftName); + } + }; + + template <> + struct MappingTraits { + 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("SwiftPrivate", t.SwiftPrivate); + 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); + io.mapOptional("EnumKind", t.EnumConvenienceKind); + } + }; + + template <> + struct MappingTraits { + 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("SwiftPrivate", t.SwiftPrivate); + io.mapOptional("SwiftName", t.SwiftName); + io.mapOptional("SwiftBridge", t.SwiftBridge); + io.mapOptional("NSErrorDomain", t.NSErrorDomain); + io.mapOptional("SwiftWrapper", t.SwiftWrapper); + } + }; + + 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 { + static void mapping(IO &io, Versioned& v) { + io.mapRequired("Version", v.Version); + mapTopLevelItems(io, v.Items); + } + }; + + template <> + struct MappingTraits { + 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("SwiftInferImportAsMember", m.SwiftInferImportAsMember); + + mapTopLevelItems(io, m.TopLevel); + + io.mapOptional("SwiftVersions", m.SwiftVersions); + } + }; + } +} + +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(yin.error()); +} + +namespace { + using namespace api_notes; + + class YAMLConverter { + const Module &TheModule; + const FileEntry *SourceFile; + APINotesWriter *Writer; + 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, + const FileEntry *sourceFile, + llvm::raw_ostream &os, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt) : + TheModule(module), SourceFile(sourceFile), Writer(0), OS(os), + DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt), + ErrorOccured(false) {} + + bool convertAvailability(const AvailabilityItem &in, + CommonEntityInfo &outInfo, + llvm::StringRef apiName) { + // Populate the unavailability information. + outInfo.Unavailable = (in.Mode == APIAvailability::None); + outInfo.UnavailableInSwift = (in.Mode == APIAvailability::NonSwift); + if (outInfo.Unavailable || outInfo.UnavailableInSwift) { + outInfo.UnavailableMsg = in.Msg; + } else { + if (!in.Msg.empty()) { + emitError("availability message for available API '" + + apiName + "' will not be used"); + } + } + 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); + pi.setType(p.Type); + pi.setRetainCountConvention(p.RetainCountConvention); + while (outInfo.Params.size() <= p.Position) { + outInfo.Params.push_back(ParamInfo()); + } + outInfo.Params[p.Position] |= pi; + } + } + + void convertNullability(const NullabilitySeq &nullability, + Optional 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; + } + } + + /// Convert the common parts of an entity from YAML. + template + bool convertCommon(const T& common, CommonEntityInfo &info, + StringRef apiName) { + convertAvailability(common.Availability, info, apiName); + info.setSwiftPrivate(common.SwiftPrivate); + info.SwiftName = common.SwiftName; + return false; + } + + /// Convert the common parts of a type entity from YAML. + template + bool convertCommonType(const T& common, CommonTypeInfo &info, + StringRef apiName) { + if (convertCommon(common, info, apiName)) + return true; + + info.setSwiftBridge(common.SwiftBridge); + info.setNSErrorDomain(common.NSErrorDomain); + return false; + } + + // Translate from Method into ObjCMethodInfo and write it out. + void convertMethod(const Method &meth, + ContextID classID, StringRef className, + VersionTuple swiftVersion) { + ObjCMethodInfo mInfo; + + if (convertCommon(meth, mInfo, meth.Selector)) + return; + + // Check if the selector ends with ':' to determine if it takes arguments. + bool takesArguments = meth.Selector.endswith(":"); + + // Split the selector into pieces. + llvm::SmallVector 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) { + emitError("'FactoryAsInit' is no longer valid; " + "use 'SwiftName' instead"); + } + mInfo.ResultType = meth.ResultType; + + // Translate parameter information. + convertParams(meth.Params, mInfo); + + // Translate nullability info. + convertNullability(meth.Nullability, meth.NullabilityOfRet, + mInfo, meth.Selector); + + mInfo.setRetainCountConvention(meth.RetainCountConvention); + + // Write it. + Writer->addObjCMethod(classID, selectorRef, + meth.Kind == MethodKind::Instance, + mInfo, swiftVersion); + } + + void convertContext(const Class &cl, bool isClass, + VersionTuple swiftVersion) { + // Write the class. + ObjCContextInfo cInfo; + + if (convertCommonType(cl, cInfo, cl.Name)) + return; + + if (cl.AuditedForNullability) + 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); + + // Write all methods. + llvm::StringMap> 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, swiftVersion); + } + + // Write all properties. + llvm::StringSet<> knownInstanceProperties; + llvm::StringSet<> knownClassProperties; + for (const auto &prop : cl.Properties) { + // Check for duplicate property definitions. + 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; + } + + // Translate from Property into ObjCPropertyInfo. + ObjCPropertyInfo pInfo; + convertAvailability(prop.Availability, pInfo, prop.Name); + pInfo.setSwiftPrivate(prop.SwiftPrivate); + pInfo.SwiftName = prop.SwiftName; + if (prop.Nullability) + 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, + swiftVersion); + } else { + // Add both instance and class properties with this name. + Writer->addObjCProperty(clID, prop.Name, true, pInfo, swiftVersion); + Writer->addObjCProperty(clID, prop.Name, false, pInfo, swiftVersion); + } + } + } + + void convertTopLevelItems(const TopLevelItems &items, + VersionTuple swiftVersion) { + // Write all classes. + llvm::StringSet<> knownClasses; + 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, swiftVersion); + } + + // Write all protocols. + llvm::StringSet<> knownProtocols; + 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, swiftVersion); + } + + // Write all global variables. + llvm::StringSet<> knownGlobals; + for (const auto &global : items.Globals) { + // Check for duplicate global variables. + if (!knownGlobals.insert(global.Name).second) { + emitError("multiple definitions of global variable '" + + global.Name + "'"); + continue; + } + + GlobalVariableInfo info; + convertAvailability(global.Availability, info, global.Name); + info.setSwiftPrivate(global.SwiftPrivate); + info.SwiftName = global.SwiftName; + if (global.Nullability) + info.setNullabilityAudited(*global.Nullability); + info.setType(global.Type); + Writer->addGlobalVariable(global.Name, info, swiftVersion); + } + + // Write all global functions. + llvm::StringSet<> knownFunctions; + for (const auto &function : items.Functions) { + // Check for duplicate global functions. + if (!knownFunctions.insert(function.Name).second) { + emitError("multiple definitions of global function '" + + function.Name + "'"); + continue; + } + + GlobalFunctionInfo info; + convertAvailability(function.Availability, info, function.Name); + info.setSwiftPrivate(function.SwiftPrivate); + info.SwiftName = function.SwiftName; + convertParams(function.Params, info); + convertNullability(function.Nullability, + function.NullabilityOfRet, + info, function.Name); + info.ResultType = function.ResultType; + info.setRetainCountConvention(function.RetainCountConvention); + Writer->addGlobalFunction(function.Name, info, swiftVersion); + } + + // Write all enumerators. + llvm::StringSet<> knownEnumConstants; + for (const auto &enumConstant : items.EnumConstants) { + // Check for duplicate enumerators + if (!knownEnumConstants.insert(enumConstant.Name).second) { + emitError("multiple definitions of enumerator '" + + enumConstant.Name + "'"); + continue; + } + + EnumConstantInfo info; + convertAvailability(enumConstant.Availability, info, enumConstant.Name); + info.setSwiftPrivate(enumConstant.SwiftPrivate); + info.SwiftName = enumConstant.SwiftName; + Writer->addEnumConstant(enumConstant.Name, info, swiftVersion); + } + + // Write all tags. + llvm::StringSet<> knownTags; + for (const auto &t : items.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; + + 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); + } + + // Write all typedefs. + llvm::StringSet<> knownTypedefs; + for (const auto &t : items.Typedefs) { + // Check for duplicate typedef definitions. + if (!knownTypedefs.insert(t.Name).second) { + emitError("multiple definitions of typedef '" + t.Name + "'"); + continue; + } + + TypedefInfo typedefInfo; + if (convertCommonType(t, typedefInfo, t.Name)) + continue; + typedefInfo.SwiftWrapper = t.SwiftWrapper; + + Writer->addTypedef(t.Name, typedefInfo, swiftVersion); + } + } + + bool convertModule() { + // 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; + opts.SwiftInferImportAsMember = true; + Writer->addModuleOptions(opts); + } + + // Convert the versioned information. + for (const auto &versioned : TheModule.SwiftVersions) { + convertTopLevelItems(versioned.Items, versioned.Version); + } + + if (!ErrorOccured) + Writer->writeToStream(OS); + + return ErrorOccured; + } + }; +} + +static bool compile(const Module &module, + const FileEntry *sourceFile, + llvm::raw_ostream &os, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt){ + using namespace api_notes; + + YAMLConverter c(module, sourceFile, 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, + const FileEntry *sourceFile, + llvm::raw_ostream &os, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt) { + Module module; + + if (!diagHandler) { + diagHandler = &printDiagnostic; + } + + if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt)) + return true; + + return compile(module, sourceFile, os, diagHandler, diagHandlerCtxt); +} 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/Types.cpp b/clang/lib/APINotes/Types.cpp new file mode 100644 index 0000000000000..4bbb4a86851cd --- /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 << " " << 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/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index cda51ec755a8d..b03bfe41da2c9 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 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); @@ -4647,10 +4658,6 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, bool allowOnPointerType) const { hasError = false; - if (const auto *objT = dyn_cast(type.getTypePtr())) { - return getObjCTypeParamType(objT->getDecl(), protocols); - } - // Apply protocol qualifiers to ObjCObjectPointerType. if (allowOnPointerType) { if (const auto *objPtr = @@ -6197,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()) @@ -8873,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/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 9477e414cf554..46b978074f3df 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3963,10 +3963,10 @@ ExpectedDecl ASTNodeImporter::VisitObjCMethodDecl(ObjCMethodDecl *D) { ObjCMethodDecl *ToMethod; if (GetImportedOrCreateDecl( - ToMethod, D, Importer.getToContext(), Loc, - ToEndLoc, Name.getObjCSelector(), ToReturnType, - ToReturnTypeSourceInfo, DC, D->isInstanceMethod(), D->isVariadic(), - D->isPropertyAccessor(), D->isImplicit(), D->isDefined(), + ToMethod, D, Importer.getToContext(), Loc, ToEndLoc, + Name.getObjCSelector(), ToReturnType, ToReturnTypeSourceInfo, DC, + D->isInstanceMethod(), D->isVariadic(), D->isPropertyAccessor(), + D->isSynthesizedAccessorStub(), D->isImplicit(), D->isDefined(), D->getImplementationControl(), D->hasRelatedResultType())) return ToMethod; 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/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 58b50de944ed0..fcea17722f829 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/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index bf748fbab8e96..7e770f0894062 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -775,14 +775,12 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod( // ObjCMethodDecl //===----------------------------------------------------------------------===// -ObjCMethodDecl::ObjCMethodDecl(SourceLocation beginLoc, SourceLocation endLoc, - Selector SelInfo, QualType T, - TypeSourceInfo *ReturnTInfo, - DeclContext *contextDecl, bool isInstance, - bool isVariadic, bool isPropertyAccessor, - bool isImplicitlyDeclared, bool isDefined, - ImplementationControl impControl, - bool HasRelatedResultType) +ObjCMethodDecl::ObjCMethodDecl( + SourceLocation beginLoc, SourceLocation endLoc, Selector SelInfo, + QualType T, TypeSourceInfo *ReturnTInfo, DeclContext *contextDecl, + bool isInstance, bool isVariadic, bool isPropertyAccessor, + bool isSynthesizedAccessorStub, bool isImplicitlyDeclared, bool isDefined, + ImplementationControl impControl, bool HasRelatedResultType) : NamedDecl(ObjCMethod, contextDecl, beginLoc, SelInfo), DeclContext(ObjCMethod), MethodDeclType(T), ReturnTInfo(ReturnTInfo), DeclEndLoc(endLoc) { @@ -793,6 +791,7 @@ ObjCMethodDecl::ObjCMethodDecl(SourceLocation beginLoc, SourceLocation endLoc, setInstanceMethod(isInstance); setVariadic(isVariadic); setPropertyAccessor(isPropertyAccessor); + setSynthesizedAccessorStub(isSynthesizedAccessorStub); setDefined(isDefined); setIsRedeclaration(false); setHasRedeclaration(false); @@ -810,12 +809,13 @@ ObjCMethodDecl *ObjCMethodDecl::Create( ASTContext &C, SourceLocation beginLoc, SourceLocation endLoc, Selector SelInfo, QualType T, TypeSourceInfo *ReturnTInfo, DeclContext *contextDecl, bool isInstance, bool isVariadic, - bool isPropertyAccessor, bool isImplicitlyDeclared, bool isDefined, - ImplementationControl impControl, bool HasRelatedResultType) { + bool isPropertyAccessor, bool isSynthesizedAccessorStub, + bool isImplicitlyDeclared, bool isDefined, ImplementationControl impControl, + bool HasRelatedResultType) { return new (C, contextDecl) ObjCMethodDecl( beginLoc, endLoc, SelInfo, T, ReturnTInfo, contextDecl, isInstance, - isVariadic, isPropertyAccessor, isImplicitlyDeclared, isDefined, - impControl, HasRelatedResultType); + isVariadic, isPropertyAccessor, isSynthesizedAccessorStub, + isImplicitlyDeclared, isDefined, impControl, HasRelatedResultType); } ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) { @@ -823,6 +823,10 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) { Selector(), QualType(), nullptr, nullptr); } +bool ObjCMethodDecl::isDirectMethod() const { + return hasAttr(); +} + bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const { return getMethodFamily() == OMF_init && hasAttr(); @@ -1077,7 +1081,7 @@ ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const { QualType ObjCMethodDecl::getSelfType(ASTContext &Context, const ObjCInterfaceDecl *OID, bool &selfIsPseudoStrong, - bool &selfIsConsumed) { + bool &selfIsConsumed) const { QualType selfTy; selfIsPseudoStrong = false; selfIsConsumed = false; @@ -1306,6 +1310,11 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { if (isPropertyAccessor()) { const auto *Container = cast(getParent()); + // For accessor stubs, go back to the interface. + if (auto *ImplDecl = dyn_cast(Container)) + if (isSynthesizedAccessorStub()) + Container = ImplDecl->getClassInterface(); + bool IsGetter = (NumArgs == 0); bool IsInstance = isInstanceMethod(); @@ -1358,6 +1367,15 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { } } + assert(isSynthesizedAccessorStub() && "expected an accessor stub"); + for (const auto *Cat : ClassDecl->known_categories()) { + if (Cat == Container) + continue; + + if (const auto *Found = findMatchingProperty(Cat)) + return Found; + } + llvm_unreachable("Marked as a property accessor but no property found!"); } @@ -1389,12 +1407,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, @@ -2195,18 +2209,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 608b0b44072b7..20825ad784840 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -598,13 +598,15 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXConversionDecl *ConversionDecl = dyn_cast(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(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 "; @@ -1347,6 +1349,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()) { @@ -1469,6 +1474,11 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) { first = false; } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_direct) { + Out << (first ? "" : ", ") << "direct"; + first = false; + } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic) { Out << (first ? "" : ", ") << "nonatomic"; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index f4ca2284224b3..ed14548d0575b 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" @@ -1856,6 +1857,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... @@ -1897,7 +1911,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { case Expr::ObjCBoxedExprClass: return cast(E)->isExpressibleAsConstantInitializer(); case Expr::CallExprClass: - return IsStringLiteralCall(cast(E)); + return isGlobalCallLValue(cast(E)); // For GCC compatibility, &&label has static storage duration. case Expr::AddrLabelExprClass: return true; @@ -8205,6 +8219,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: @@ -10634,6 +10650,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(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: { @@ -11927,6 +11950,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..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"); + // ::= [r] [V] [K] # restrict (C99), volatile, const if (Quals.hasRestrict()) Out << 'r'; @@ -3995,6 +3998,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..db2dcc5868274 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1013,6 +1013,7 @@ void JSONNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { attributeOnlyIfTrue("unsafe_unretained", Attrs & ObjCPropertyDecl::OBJC_PR_unsafe_unretained); attributeOnlyIfTrue("class", Attrs & ObjCPropertyDecl::OBJC_PR_class); + attributeOnlyIfTrue("direct", Attrs & ObjCPropertyDecl::OBJC_PR_direct); attributeOnlyIfTrue("nullability", Attrs & ObjCPropertyDecl::OBJC_PR_nullability); attributeOnlyIfTrue("null_resettable", @@ -1233,6 +1234,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/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp index 09d85102585bd..e13092ebbdb40 100644 --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -713,3 +713,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 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(Parent)) { + if (!Namespace->getIdentifier()) + continue; + + Result = NestedNameSpecifier::Create(Context, Result, Namespace); + } else if (const TagDecl *TD = dyn_cast(Parent)) + Result = NestedNameSpecifier::Create( + Context, Result, false, Context.getTypeDeclType(TD).getTypePtr()); + } + return Result; +} 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 +#include + +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 +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(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; +} diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 0f92d4c367e90..ff7b4fe46e4c9 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1284,6 +1284,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 63a6510324f75..2893a8f705e1e 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; @@ -1886,6 +1889,8 @@ void TextNodeDumper::VisitObjCCompatibleAliasDecl( const ObjCCompatibleAliasDecl *D) { dumpName(D); dumpDeclRef(D->getClassInterface()); + OS << " "; + dumpLocation(D->getClassInterfaceLoc()); } void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { @@ -1921,6 +1926,8 @@ void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { OS << " unsafe_unretained"; if (Attrs & ObjCPropertyDecl::OBJC_PR_class) OS << " class"; + if (Attrs & ObjCPropertyDecl::OBJC_PR_direct) + OS << " direct"; if (Attrs & ObjCPropertyDecl::OBJC_PR_getter) dumpDeclRef(D->getGetterMethodDecl(), "getter"); if (Attrs & ObjCPropertyDecl::OBJC_PR_setter) diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 4fed5b410b172..29cf68ae06df0 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1190,54 +1190,49 @@ struct SubstObjCTypeArgsVisitor ObjCSubstitutionContext context) : BaseType(ctx), TypeArgs(typeArgs), SubstContext(context) {} - QualType VisitObjCTypeParamType(const ObjCTypeParamType *OTPTy) { + QualType VisitTypedefType(const TypedefType *typedefTy) { // Replace an Objective-C type parameter reference with the corresponding // type argument. - ObjCTypeParamDecl *typeParam = OTPTy->getDecl(); - // If we have type arguments, use them. - if (!TypeArgs.empty()) { - QualType argType = TypeArgs[typeParam->getIndex()]; - if (OTPTy->qual_empty()) + if (auto *typeParam = dyn_cast(typedefTy->getDecl())) { + // If we have type arguments, use them. + if (!TypeArgs.empty()) { + // FIXME: Introduce SubstObjCTypeParamType ? + QualType argType = TypeArgs[typeParam->getIndex()]; return argType; + } - // Apply protocol lists if exists. - bool hasError; - SmallVector protocolsVec; - protocolsVec.append(OTPTy->qual_begin(), OTPTy->qual_end()); - ArrayRef protocolsToApply = protocolsVec; - return Ctx.applyObjCProtocolQualifiers( - argType, protocolsToApply, hasError, true/*allowOnPointerType*/); - } - - switch (SubstContext) { - case ObjCSubstitutionContext::Ordinary: - case ObjCSubstitutionContext::Parameter: - case ObjCSubstitutionContext::Superclass: - // Substitute the bound. - return typeParam->getUnderlyingType(); - - case ObjCSubstitutionContext::Result: - case ObjCSubstitutionContext::Property: { - // Substitute the __kindof form of the underlying type. - const auto *objPtr = - typeParam->getUnderlyingType()->castAs(); - - // __kindof types, id, and Class don't need an additional - // __kindof. - if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + switch (SubstContext) { + case ObjCSubstitutionContext::Ordinary: + case ObjCSubstitutionContext::Parameter: + case ObjCSubstitutionContext::Superclass: + // Substitute the bound. return typeParam->getUnderlyingType(); - // Add __kindof. - const auto *obj = objPtr->getObjectType(); - QualType resultTy = Ctx.getObjCObjectType( - obj->getBaseType(), obj->getTypeArgsAsWritten(), obj->getProtocols(), - /*isKindOf=*/true); - - // Rebuild object pointer type. - return Ctx.getObjCObjectPointerType(resultTy); - } + case ObjCSubstitutionContext::Result: + case ObjCSubstitutionContext::Property: { + // Substitute the __kindof form of the underlying type. + const auto *objPtr = typeParam->getUnderlyingType() + ->castAs(); + + // __kindof types, id, and Class don't need an additional + // __kindof. + if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + return typeParam->getUnderlyingType(); + + // Add __kindof. + const auto *obj = objPtr->getObjectType(); + QualType resultTy = Ctx.getObjCObjectType(obj->getBaseType(), + obj->getTypeArgsAsWritten(), + obj->getProtocols(), + /*isKindOf=*/true); + + // Rebuild object pointer type. + return Ctx.getObjCObjectPointerType(resultTy); + } + } + llvm_unreachable("Unexpected ObjCSubstitutionContext!"); } - llvm_unreachable("Unexpected ObjCSubstitutionContext!"); + return BaseType::VisitTypedefType(typedefTy); } QualType VisitFunctionType(const FunctionType *funcType) { @@ -2370,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 1065b99e0ce3d..1869d13e8200f 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -103,6 +103,7 @@ namespace { unsigned Indentation; bool HasEmptyPlaceHolder = false; bool InsideCCAttribute = false; + bool IgnoreFunctionProtoTypeConstQual = false; public: explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0) @@ -818,8 +819,11 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, printFunctionAfter(Info, OS); - if (!T->getMethodQuals().empty()) - OS << " " << T->getMethodQuals().getAsString(); + Qualifiers quals = T->getMethodQuals(); + if (IgnoreFunctionProtoTypeConstQual) + quals.removeConst(); + if (!quals.empty()) + OS << " " << quals.getAsString(); switch (T->getRefQualifier()) { case RQ_None: @@ -1165,6 +1169,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(D) && + cast(D)->isLambda()) { + OS << "std::function<"; + QualType T = cast(D)->getLambdaCallOperator()->getType(); + SaveAndRestore 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) @@ -1513,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"); @@ -1740,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)); @@ -1769,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; } @@ -1856,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/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(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(GD.getDecl()); + computeVTableRelatedInformation(MD->getParent()); + const auto *OriginalMD = findOriginalMethodInMap(MD); + + if (const auto *DD = dyn_cast(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 CreateVTableLayout(const ItaniumVTableBuilder &Builder) { SmallVector diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp index 43f9e715b3de2..be065ed9330fa 100644 --- a/clang/lib/Analysis/BodyFarm.cpp +++ b/clang/lib/Analysis/BodyFarm.cpp @@ -830,6 +830,16 @@ Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) { if (D->param_size() != 0) return nullptr; + // If the property was defined in an extension, search the extensions for + // overrides. + const ObjCInterfaceDecl *OID = D->getClassInterface(); + if (dyn_cast(D->getParent()) != OID) + for (auto *Ext : OID->known_extensions()) { + auto *OMD = Ext->getInstanceMethod(D->getSelector()); + if (OMD && !OMD->isImplicit()) + return nullptr; + } + Val = createObjCPropertyGetter(C, Prop); return Val.getValue(); diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index be739c70468e5..cea043ec72da2 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -60,6 +60,7 @@ add_clang_library(clangBasic Sanitizers.cpp SourceLocation.cpp SourceManager.cpp + SourceMgrAdapter.cpp Stack.cpp TargetInfo.cpp Targets.cpp diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index c82f74413ec14..f686b6953e303 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -145,19 +145,20 @@ void DiagnosticsEngine::Reset() { } void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1, - StringRef Arg2) { + StringRef Arg2, StringRef Arg3) { if (DelayedDiagID) return; DelayedDiagID = DiagID; DelayedDiagArg1 = Arg1.str(); DelayedDiagArg2 = Arg2.str(); + DelayedDiagArg3 = Arg3.str(); } void DiagnosticsEngine::ReportDelayed() { unsigned ID = DelayedDiagID; DelayedDiagID = 0; - Report(ID) << DelayedDiagArg1 << DelayedDiagArg2; + Report(ID) << DelayedDiagArg1 << DelayedDiagArg2 << DelayedDiagArg3; } void DiagnosticsEngine::DiagStateMap::appendFirst(DiagState *State) { diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 541431dbbe7d9..eb18b66ca5422 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -36,7 +36,8 @@ using namespace clang; Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, bool IsFramework, bool IsExplicit, unsigned VisibilityID) - : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent), + : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent), Directory(), + Umbrella(), ASTFile(nullptr), VisibilityID(VisibilityID), IsMissingRequirement(false), HasIncompatibleModuleFile(false), IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit), @@ -44,6 +45,11 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, 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()) @@ -431,6 +437,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/Basic/SourceMgrAdapter.cpp b/clang/lib/Basic/SourceMgrAdapter.cpp new file mode 100644 index 0000000000000..c80112e0dbb15 --- /dev/null +++ b/clang/lib/Basic/SourceMgrAdapter.cpp @@ -0,0 +1,140 @@ +//=== 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(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::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_Remark: + llvm_unreachable("remarks not implemented"); + + 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/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 3a21a19e1f19a..63303263ced40 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; @@ -754,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 c86cc63e3d84c..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" @@ -91,6 +92,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; } @@ -506,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) {} @@ -650,6 +659,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/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/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/Basic/Version.cpp b/clang/lib/Basic/Version.cpp index d6564582e7726..b64086cc946f2 100644 --- a/clang/lib/Basic/Version.cpp +++ b/clang/lib/Basic/Version.cpp @@ -147,4 +147,6 @@ std::string getClangFullCPPVersion() { return OS.str(); } +unsigned getClangMajorVersionNumber() { return CLANG_VERSION_MAJOR; } + } // end namespace clang diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index 0c03f5972b093..adcfee8a16649 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) @@ -20,6 +21,7 @@ add_subdirectory(FrontendTool) add_subdirectory(Tooling) add_subdirectory(DirectoryWatcher) add_subdirectory(Index) +add_subdirectory(IndexDataStore) if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) endif() diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index c15dc0be0b15c..1e97e5d6bc800 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); @@ -586,6 +591,7 @@ void EmitAssemblyHelper::CreatePasses(legacy::PassManager &MPM, // enabled when loop unrolling is enabled. PMBuilder.LoopsInterleaved = CodeGenOpts.UnrollLoops; PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions; + PMBuilder.SplitColdCode = CodeGenOpts.SplitColdCode; PMBuilder.PrepareForThinLTO = CodeGenOpts.PrepareForThinLTO; PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO; PMBuilder.RerollLoops = CodeGenOpts.RerollLoops; @@ -685,6 +691,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) @@ -1152,6 +1165,9 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( // configure the pipeline. PassBuilder::OptimizationLevel Level = mapToLevel(CodeGenOpts); + // -f[no-]split-cold-code + PB.setEnableHotColdSplitting(CodeGenOpts.SplitColdCode); + PB.registerPipelineStartEPCallback([](ModulePassManager &MPM) { MPM.addPass(createModuleToFunctionPassAdaptor( EntryExitInstrumenterPass(/*PostInlining=*/false))); diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index f90d9439af257..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, @@ -123,7 +124,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 +223,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(copyHelper->getOperand(0))->hasInternalLinkage() || cast(disposeHelper->getOperand(0)) @@ -623,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; @@ -966,7 +974,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 +1017,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()->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 +1044,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 +1285,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 +1304,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, if (!isa(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 +1312,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 +1335,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 +1444,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() + ->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)); @@ -1702,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) @@ -1722,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()) @@ -1847,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(); @@ -1962,6 +2021,7 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, } break; } + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: case BlockCaptureEntityKind::None: break; } @@ -2066,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. @@ -2409,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 { @@ -2628,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 || @@ -2826,8 +2926,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 +3018,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/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index cc2cbb907076f..59785e997f5e6 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 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()); @@ -4117,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(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/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/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index e832e4c283345..23245dcd6c339 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1789,6 +1789,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); @@ -3638,7 +3646,8 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, } if (HasAggregateEvalKind && isa(E) && - cast(E)->getCastKind() == CK_LValueToRValue) { + cast(E)->getCastKind() == CK_LValueToRValue && + !type.isNonTrivialToPrimitiveCopy()) { LValue L = EmitLValue(cast(E)->getSubExpr()); assert(L.isSimple()); args.addUncopiedAggregate(L, type); @@ -3806,7 +3815,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()); @@ -3881,7 +3891,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); @@ -4379,6 +4392,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, SmallVector BundleList = getBundlesForFunclet(CalleePtr); + // Add the pointer-authentication bundle. + EmitPointerAuthOperandBundle(ConcreteCallee.getPointerAuthInfo(), BundleList); + // Emit the actual call/invoke instruction. llvm::CallBase *CI; if (!InvokeDest) { @@ -4489,7 +4505,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/CGCall.h b/clang/lib/CodeGen/CGCall.h index 34558be5adb1b..6cdd5fb1a0728 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -62,6 +62,36 @@ 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 +103,10 @@ class CGCallee { Last = Virtual }; + struct OrdinaryInfoStorage { + CGCalleeInfo AbstractInfo; + CGPointerAuthInfo PointerAuthInfo; + }; struct BuiltinInfoStorage { const FunctionDecl *Decl; unsigned ID; @@ -89,7 +123,7 @@ class CGCallee { SpecialKind KindOrFunctionPointer; union { - CGCalleeInfo AbstractInfo; + OrdinaryInfoStorage OrdinaryInfo; BuiltinInfoStorage BuiltinInfo; PseudoDestructorInfoStorage PseudoDestructorInfo; VirtualInfoStorage VirtualInfo; @@ -108,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()); @@ -132,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, @@ -177,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()); @@ -187,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; diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 04ef912b18bd4..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; } @@ -2494,6 +2497,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 +2603,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(EmitPointerAuthAuth(PointerAuth, VTable)); + } + if (CGM.getCodeGenOpts().OptimizationLevel > 0 && CGM.getCodeGenOpts().StrictVTablePointers) CGM.DecorateInstructionWithInvariantGroup(VTable, RD); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index c0c8fd3c8f16f..7e699ff3497ab 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -293,13 +293,6 @@ StringRef CGDebugInfo::getObjCMethodName(const ObjCMethodDecl *OMD) { } } else if (const auto *OCD = dyn_cast(DC)) { OS << OCD->getClassInterface()->getName() << '(' << OCD->getName() << ')'; - } else if (isa(DC)) { - // We can extract the type of the class from the self pointer. - if (ImplicitParamDecl *SelfDecl = OMD->getSelfDecl()) { - QualType ClassTy = - cast(SelfDecl->getType())->getPointeeType(); - ClassTy.print(OS, PrintingPolicy(LangOptions())); - } } OS << ' ' << OMD->getSelector().getAsString() << ']'; @@ -849,6 +842,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 +876,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 +999,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. @@ -2600,8 +2601,8 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty, SourceLocation Loc = PD->getLocation(); llvm::DIFile *PUnit = getOrCreateFile(Loc); unsigned PLine = getLineNumber(Loc); - ObjCMethodDecl *Getter = PD->getGetterMethodDecl(); - ObjCMethodDecl *Setter = PD->getSetterMethodDecl(); + ObjCMethodDecl *Getter = PImpD->getGetterMethodDecl(); + ObjCMethodDecl *Setter = PImpD->getSetterMethodDecl(); PropertyNode = DBuilder.createObjCProperty( PD->getName(), PUnit, PLine, hasDefaultGetterName(PD, Getter) @@ -3490,6 +3491,39 @@ llvm::DISubprogram *CGDebugInfo::getFunctionDeclaration(const Decl *D) { return nullptr; } +llvm::DISubprogram *CGDebugInfo::getObjCMethodDeclaration( + const Decl *D, llvm::DISubroutineType *FnType, unsigned LineNo, + llvm::DINode::DIFlags Flags, llvm::DISubprogram::DISPFlags SPFlags) { + if (!D || DebugKind <= codegenoptions::DebugLineTablesOnly) + return nullptr; + + const auto *OMD = dyn_cast(D); + if (!OMD) + return nullptr; + + if (CGM.getCodeGenOpts().DwarfVersion < 5 && !OMD->isDirectMethod()) + return nullptr; + + // Starting with DWARF V5 method declarations are emitted as children of + // the interface type. + auto *ID = dyn_cast_or_null(D->getDeclContext()); + if (!ID) + ID = OMD->getClassInterface(); + if (!ID) + return nullptr; + QualType QTy(ID->getTypeForDecl(), 0); + auto It = TypeCache.find(QTy.getAsOpaquePtr()); + if (It == TypeCache.end()) + return nullptr; + auto *InterfaceType = cast(It->second); + llvm::DISubprogram *FD = DBuilder.createFunction( + InterfaceType, getObjCMethodName(OMD), StringRef(), + InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags); + DBuilder.finalizeSubprogram(FD); + ObjCMethodCache[ID].push_back({FD, OMD->isDirectMethod()}); + return FD; +} + // getOrCreateFunctionType - Construct type. If it is a c++ method, include // implicit parameter "this". llvm::DISubroutineType *CGDebugInfo::getOrCreateFunctionType(const Decl *D, @@ -3632,6 +3666,12 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc, unsigned LineNo = getLineNumber(Loc); unsigned ScopeLine = getLineNumber(ScopeLoc); + llvm::DISubroutineType *DIFnType = getOrCreateFunctionType(D, FnType, Unit); + llvm::DISubprogram *Decl = nullptr; + if (D) + Decl = isa(D) + ? getObjCMethodDeclaration(D, DIFnType, LineNo, Flags, SPFlags) + : getFunctionDeclaration(D); // FIXME: The function declaration we're constructing here is mostly reusing // declarations from CXXMethodDecl and not constructing new ones for arbitrary @@ -3639,9 +3679,8 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc, // all subprograms instead of the actual context since subprogram definitions // are emitted as CU level entities by the backend. llvm::DISubprogram *SP = DBuilder.createFunction( - FDContext, Name, LinkageName, Unit, LineNo, - getOrCreateFunctionType(D, FnType, Unit), ScopeLine, FlagsForDef, - SPFlagsForDef, TParamsArray.get(), getFunctionDeclaration(D)); + FDContext, Name, LinkageName, Unit, LineNo, DIFnType, ScopeLine, + FlagsForDef, SPFlagsForDef, TParamsArray.get(), Decl); Fn->setSubprogram(SP); // We might get here with a VarDecl in the case we're generating // code for the initialization of globals. Do not record these decls @@ -3658,26 +3697,6 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc, if (FD->hasBody() && !FD->param_empty()) SPDefCache[FD].reset(SP); - if (CGM.getCodeGenOpts().DwarfVersion >= 5) { - // Starting with DWARF V5 method declarations are emitted as children of - // the interface type. - if (const auto *OMD = dyn_cast_or_null(D)) { - const ObjCInterfaceDecl *ID = OMD->getClassInterface(); - QualType QTy(ID->getTypeForDecl(), 0); - auto It = TypeCache.find(QTy.getAsOpaquePtr()); - if (It != TypeCache.end()) { - llvm::DICompositeType *InterfaceDecl = - cast(It->second); - llvm::DISubprogram *FD = DBuilder.createFunction( - InterfaceDecl, Name, LinkageName, Unit, LineNo, - getOrCreateFunctionType(D, FnType, Unit), ScopeLine, Flags, SPFlags, - TParamsArray.get()); - DBuilder.finalizeSubprogram(FD); - ObjCMethodCache[ID].push_back(FD); - } - } - } - // Push the function onto the lexical block stack. LexicalBlockStack.emplace_back(SP); @@ -3748,7 +3767,9 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc, void CGDebugInfo::EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke, QualType CalleeType, const FunctionDecl *CalleeDecl) { - if (!CallOrInvoke || getCallSiteRelatedAttrs() == llvm::DINode::FlagZero) + auto &CGOpts = CGM.getCodeGenOpts(); + if (!CGOpts.EnableDebugEntryValues || !CGM.getLangOpts().Optimize || + !CallOrInvoke) return; auto *Func = CallOrInvoke->getCalledFunction(); @@ -4724,27 +4745,28 @@ void CGDebugInfo::finalize() { DBuilder.replaceTemporary(llvm::TempDIType(E.Decl), Ty); } - if (CGM.getCodeGenOpts().DwarfVersion >= 5) { - // Add methods to interface. - for (const auto &P : ObjCMethodCache) { - if (P.second.empty()) - continue; + // Add methods to interface. + for (const auto &P : ObjCMethodCache) { + if (P.second.empty()) + continue; - QualType QTy(P.first->getTypeForDecl(), 0); - auto It = TypeCache.find(QTy.getAsOpaquePtr()); - assert(It != TypeCache.end()); + QualType QTy(P.first->getTypeForDecl(), 0); + auto It = TypeCache.find(QTy.getAsOpaquePtr()); + assert(It != TypeCache.end()); - llvm::DICompositeType *InterfaceDecl = - cast(It->second); + llvm::DICompositeType *InterfaceDecl = + cast(It->second); - SmallVector EltTys; - auto CurrenetElts = InterfaceDecl->getElements(); - EltTys.append(CurrenetElts.begin(), CurrenetElts.end()); - for (auto &MD : P.second) - EltTys.push_back(MD); - llvm::DINodeArray Elements = DBuilder.getOrCreateArray(EltTys); - DBuilder.replaceArrays(InterfaceDecl, Elements); - } + auto CurElts = InterfaceDecl->getElements(); + SmallVector EltTys(CurElts.begin(), CurElts.end()); + + // For DWARF v4 or earlier, only add objc_direct methods. + for (auto &SubprogramDirect : P.second) + if (CGM.getCodeGenOpts().DwarfVersion >= 5 || SubprogramDirect.getInt()) + EltTys.push_back(SubprogramDirect.getPointer()); + + llvm::DINodeArray Elements = DBuilder.getOrCreateArray(EltTys); + DBuilder.replaceArrays(InterfaceDecl, Elements); } for (const auto &P : ReplaceMap) { @@ -4822,10 +4844,10 @@ llvm::DINode::DIFlags CGDebugInfo::getCallSiteRelatedAttrs() const { bool SupportsDWARFv4Ext = CGM.getCodeGenOpts().DwarfVersion == 4 && (CGM.getCodeGenOpts().getDebuggerTuning() == llvm::DebuggerKind::LLDB || - CGM.getCodeGenOpts().getDebuggerTuning() == llvm::DebuggerKind::GDB); + (CGM.getCodeGenOpts().EnableDebugEntryValues && + CGM.getCodeGenOpts().getDebuggerTuning() == llvm::DebuggerKind::GDB)); - if (!SupportsDWARFv4Ext && CGM.getCodeGenOpts().DwarfVersion < 5 && - !CGM.getCodeGenOpts().EnableDebugEntryValues) + if (!SupportsDWARFv4Ext && CGM.getCodeGenOpts().DwarfVersion < 5) return llvm::DINode::FlagZero; return llvm::DINode::FlagAllCallsDescribed; diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 5e26af4a4f752..fa7b213413c6e 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -114,7 +114,10 @@ class CGDebugInfo { llvm::SmallVector ObjCInterfaceCache; /// Cache of forward declarations for methods belonging to the interface. - llvm::DenseMap> + /// The extra bit on the DISubprogram specifies whether a method is + /// "objc_direct". + llvm::DenseMap>> ObjCMethodCache; /// Cache of references to clang modules and precompiled headers. @@ -613,6 +616,17 @@ class CGDebugInfo { /// declaration for the given method definition. llvm::DISubprogram *getFunctionDeclaration(const Decl *D); + /// \return debug info descriptor to the describe method declaration + /// for the given method definition. + /// \param FnType For Objective-C methods, their type. + /// \param LineNo The declaration's line number. + /// \param Flags The DIFlags for the method declaration. + /// \param SPFlags The subprogram-spcific flags for the method declaration. + llvm::DISubprogram * + getObjCMethodDeclaration(const Decl *D, llvm::DISubroutineType *FnType, + unsigned LineNo, llvm::DINode::DIFlags Flags, + llvm::DISubprogram::DISPFlags SPFlags); + /// \return debug info descriptor to describe in-class static data /// member declaration for the given out-of-class definition. If D /// is an out-of-class definition of a static data member of a 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(D)); EmitNullabilityCheck(lvalue, value, init->getExprLoc()); 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/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 99406715cbf8c..4e5c100e91677 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) { @@ -2393,14 +2409,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()) { - 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()) { @@ -2408,10 +2424,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 +2434,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); @@ -3873,7 +3888,8 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { bool IsBaseCXXThis = IsWrappedCXXThis(BaseExpr); if (IsBaseCXXThis) SkippedChecks.set(SanitizerKind::Alignment, true); - if (IsBaseCXXThis || isa(BaseExpr)) + if (IsBaseCXXThis || isa(BaseExpr) || + isa(Addr.getPointer())) SkippedChecks.set(SanitizerKind::Null, true); EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr.getPointer(), PtrTy, /*Alignment=*/CharUnits::Zero(), SkippedChecks); @@ -4549,10 +4565,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()->getPointeeType(); + CGCalleeInfo calleeInfo(functionType->getAs()); + CGPointerAuthInfo pointerAuth(key, discriminator); + CGCallee callee(calleeInfo, calleePtr, pointerAuth); + return callee; +} + CGCallee CodeGenFunction::EmitCallee(const Expr *E) { E = E->IgnoreParens(); @@ -4563,6 +4639,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()) { + auto result = EmitOrigPointerRValue(E); + + QualType functionType = ptrType->getPointeeType(); + assert(functionType->isFunctionType()); + + GlobalDecl GD; + if (const auto *VD = + dyn_cast_or_null(E->getReferencedDeclOfCallee())) { + GD = GlobalDecl(VD); + } + CGCalleeInfo calleeInfo(functionType->getAs(), GD); + CGCallee callee(calleeInfo, result.first, result.second); + return callee; + } + } + // Resolve direct calls. } else if (auto DRE = dyn_cast(E)) { if (auto FD = dyn_cast(DRE->getDecl())) { @@ -4581,6 +4678,36 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { // Treat pseudo-destructor calls differently. } else if (auto PDE = dyn_cast(E)) { return CGCallee::forPseudoDestructor(PDE); + + // Peephole specific builtin calls. + } else if (auto CE = dyn_cast(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. @@ -4594,14 +4721,14 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { calleePtr = EmitLValue(E).getPointer(); } assert(functionType->isFunctionType()); - GlobalDecl GD; if (const auto *VD = dyn_cast_or_null(E->getReferencedDeclOfCallee())) GD = GlobalDecl(VD); CGCalleeInfo calleeInfo(functionType->getAs(), GD); - CGCallee callee(calleeInfo, calleePtr); + CGPointerAuthInfo pointerAuth = CGM.getFunctionPointerAuthInfo(functionType); + CGCallee callee(calleeInfo, calleePtr, pointerAuth); return callee; } @@ -4624,6 +4751,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 96e8c9c0d0e61..ad62a597f30a6 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(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(); } @@ -1725,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()) {} @@ -1772,6 +1804,14 @@ class ConstantLValueEmitter : public ConstStmtVisitor + emitPointerAuthDiscriminator(const Expr *E); + llvm::Constant *tryEmitConstantSignedPointer(llvm::Constant *ptr, + PointerAuthQualifier auth); + bool hasNonZeroOffset() const { return !Value.getLValueOffset().isZero(); } @@ -1830,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(destTy)) @@ -1867,8 +1915,15 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { if (D->hasAttr()) return CGM.GetWeakRefReference(D).getPointer(); - if (auto FD = dyn_cast(D)) - return CGM.GetAddrOfFunction(FD); + if (auto FD = dyn_cast(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); + } if (auto VD = dyn_cast(D)) { // We can never refer to a variable with local storage. @@ -1958,6 +2013,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 +2029,99 @@ 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)); + 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(); + if (auto decl = dyn_cast_or_null(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 +ConstantLValueEmitter::emitPointerAuthDiscriminator(const Expr *E) { + E = E->IgnoreParens(); + + if (auto call = dyn_cast(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/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index c1391d46f60cb..3f499f9bf662d 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(E)) { + if (UO->getOpcode() == UO_Deref) { + return CGF.isPointerKnownNonNull(UO->getSubExpr()); + } + } + + if (auto DRE = dyn_cast(E)) { + return isDeclRefKnownNonNull(CGF, DRE->getDecl()); + } else if (auto ME = dyn_cast(E)) { + if (isa(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(E)) + return true; + + if (auto UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_AddrOf) { + return isLValueKnownNonNull(*this, UO->getSubExpr()); + } + } + + if (auto CE = dyn_cast(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(); @@ -2849,7 +2901,8 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) && CGF.getLangOpts().getSignedOverflowBehavior() != LangOptions::SOB_Trapping) { - llvm::AtomicRMWInst::BinOp aop = llvm::AtomicRMWInst::BAD_BINOP; + llvm::AtomicRMWInst::BinOp AtomicOp = llvm::AtomicRMWInst::BAD_BINOP; + llvm::Instruction::BinaryOps Op; switch (OpInfo.Opcode) { // We don't have atomicrmw operands for *, %, /, <<, >> case BO_MulAssign: case BO_DivAssign: @@ -2858,30 +2911,40 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( case BO_ShrAssign: break; case BO_AddAssign: - aop = llvm::AtomicRMWInst::Add; + AtomicOp = llvm::AtomicRMWInst::Add; + Op = llvm::Instruction::Add; break; case BO_SubAssign: - aop = llvm::AtomicRMWInst::Sub; + AtomicOp = llvm::AtomicRMWInst::Sub; + Op = llvm::Instruction::Sub; break; case BO_AndAssign: - aop = llvm::AtomicRMWInst::And; + AtomicOp = llvm::AtomicRMWInst::And; + Op = llvm::Instruction::And; break; case BO_XorAssign: - aop = llvm::AtomicRMWInst::Xor; + AtomicOp = llvm::AtomicRMWInst::Xor; + Op = llvm::Instruction::Xor; break; case BO_OrAssign: - aop = llvm::AtomicRMWInst::Or; + AtomicOp = llvm::AtomicRMWInst::Or; + Op = llvm::Instruction::Or; break; default: llvm_unreachable("Invalid compound assignment type"); } - if (aop != llvm::AtomicRMWInst::BAD_BINOP) { - llvm::Value *amt = CGF.EmitToMemory( + if (AtomicOp != llvm::AtomicRMWInst::BAD_BINOP) { + llvm::Value *Amt = CGF.EmitToMemory( EmitScalarConversion(OpInfo.RHS, E->getRHS()->getType(), LHSTy, E->getExprLoc()), LHSTy); - Builder.CreateAtomicRMW(aop, LHSLV.getPointer(), amt, + Value *OldVal = Builder.CreateAtomicRMW( + AtomicOp, LHSLV.getPointer(), Amt, llvm::AtomicOrdering::SequentiallyConsistent); + + // Since operation is atomic, the result type is guaranteed to be the + // same as the input in LLVM terms. + Result = Builder.CreateBinOp(Op, OldVal, Amt); return LHSLV; } } @@ -3906,6 +3969,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, 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, RValue SrcVal = this->CGF->EmitLoadOfLValue(SrcLV, SourceLocation()); this->CGF->EmitStoreThroughLValue(SrcVal, DstLV); } + + void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStackOffset, + std::array 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/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index 1fa72678081af..570c4fbf4c177 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -430,6 +430,20 @@ tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType, return None; } +CodeGen::RValue CGObjCRuntime::GeneratePossiblySpecializedMessageSend( + CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, + Selector Sel, llvm::Value *Receiver, const CallArgList &Args, + const ObjCInterfaceDecl *OID, const ObjCMethodDecl *Method, + bool isClassMessage) { + if (Optional SpecializedResult = + tryGenerateSpecializedMessageSend(CGF, ResultType, Receiver, Args, + Sel, Method, isClassMessage)) { + return RValue::get(SpecializedResult.getValue()); + } + return GenerateMessageSend(CGF, Return, ResultType, Sel, Receiver, Args, OID, + Method); +} + /// Instead of '[[MyClass alloc] init]', try to generate /// 'objc_alloc_init(MyClass)'. This provides a code size improvement on the /// caller side, as well as the optimized objc_alloc. @@ -611,16 +625,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, method); } else { // Call runtime methods directly if we can. - if (Optional SpecializedResult = - tryGenerateSpecializedMessageSend(*this, ResultType, Receiver, Args, - E->getSelector(), method, - isClassMessage)) { - result = RValue::get(SpecializedResult.getValue()); - } else { - result = Runtime.GenerateMessageSend(*this, Return, ResultType, - E->getSelector(), Receiver, Args, - OID, method); - } + result = Runtime.GeneratePossiblySpecializedMessageSend( + *this, Return, ResultType, E->getSelector(), Receiver, Args, OID, + method, isClassMessage); } // For delegate init calls in ARC, implicitly store the result of @@ -683,7 +690,13 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD, llvm::Function *Fn = CGM.getObjCRuntime().GenerateMethod(OMD, CD); const CGFunctionInfo &FI = CGM.getTypes().arrangeObjCMethodDeclaration(OMD); - CGM.SetInternalFunctionAttributes(OMD, Fn, FI); + if (OMD->isDirectMethod()) { + Fn->setVisibility(llvm::Function::HiddenVisibility); + CGM.SetLLVMFunctionAttributes(OMD, FI, Fn); + CGM.SetLLVMFunctionAttributesForDefinition(OMD, Fn); + } else { + CGM.SetInternalFunctionAttributes(OMD, Fn, FI); + } args.push_back(OMD->getSelfDecl()); args.push_back(OMD->getCmdDecl()); @@ -696,6 +709,14 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD, StartFunction(OMD, OMD->getReturnType(), Fn, FI, args, OMD->getLocation(), StartLoc); + if (OMD->isDirectMethod()) { + // This function is a direct call, it has to implement a nil check + // on entry. + // + // TODO: possibly have several entry points to elide the check + CGM.getObjCRuntime().GenerateDirectMethodPrologue(*this, Fn, OMD, CD); + } + // In ARC, certain methods get an extra cleanup. if (CGM.getLangOpts().ObjCAutoRefCount && OMD->isInstanceMethod() && @@ -954,8 +975,7 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, const ObjCPropertyImplDecl *PID) { llvm::Constant *AtomicHelperFn = CodeGenFunction(CGM).GenerateObjCAtomicGetterCopyHelperFunction(PID); - const ObjCPropertyDecl *PD = PID->getPropertyDecl(); - ObjCMethodDecl *OMD = PD->getGetterMethodDecl(); + ObjCMethodDecl *OMD = PID->getGetterMethodDecl(); assert(OMD && "Invalid call to generate getter (empty method)"); StartObjCMethod(OMD, IMP->getClassInterface()); @@ -1041,7 +1061,7 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyDecl *prop = propImpl->getPropertyDecl(); QualType propType = prop->getType(); - ObjCMethodDecl *getterMethod = prop->getGetterMethodDecl(); + ObjCMethodDecl *getterMethod = propImpl->getGetterMethodDecl(); ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl(); @@ -1311,9 +1331,8 @@ void CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyImplDecl *propImpl, llvm::Constant *AtomicHelperFn) { - const ObjCPropertyDecl *prop = propImpl->getPropertyDecl(); ObjCIvarDecl *ivar = propImpl->getPropertyIvarDecl(); - ObjCMethodDecl *setterMethod = prop->getSetterMethodDecl(); + ObjCMethodDecl *setterMethod = propImpl->getSetterMethodDecl(); // Just use the setter expression if Sema gave us one and it's // non-trivial. @@ -1490,8 +1509,7 @@ void CodeGenFunction::GenerateObjCSetter(ObjCImplementationDecl *IMP, const ObjCPropertyImplDecl *PID) { llvm::Constant *AtomicHelperFn = CodeGenFunction(CGM).GenerateObjCAtomicSetterCopyHelperFunction(PID); - const ObjCPropertyDecl *PD = PID->getPropertyDecl(); - ObjCMethodDecl *OMD = PD->getSetterMethodDecl(); + ObjCMethodDecl *OMD = PID->getSetterMethodDecl(); assert(OMD && "Invalid call to generate setter (empty method)"); StartObjCMethod(OMD, IMP->getClassInterface()); @@ -3559,7 +3577,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 +3683,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/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index d2c089d0360e1..70314d34ed536 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -606,6 +606,9 @@ class CGObjCGNU : public CGObjCRuntime { llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) override; + void GenerateDirectMethodPrologue(CodeGenFunction &CGF, llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) override; void GenerateCategory(const ObjCCategoryImplDecl *CMD) override; void GenerateClass(const ObjCImplementationDecl *ClassDecl) override; void RegisterAlias(const ObjCCompatibleAliasDecl *OAD) override; @@ -1880,13 +1883,12 @@ class CGObjCGNUstep2 : public CGObjCGNUstep { for (auto *propImpl : OID->property_impls()) if (propImpl->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) { - ObjCPropertyDecl *prop = propImpl->getPropertyDecl(); - auto addIfExists = [&](const ObjCMethodDecl* OMD) { - if (OMD) + auto addIfExists = [&](const ObjCMethodDecl *OMD) { + if (OMD && OMD->hasBody()) InstanceMethods.push_back(OMD); }; - addIfExists(prop->getGetterMethodDecl()); - addIfExists(prop->getSetterMethodDecl()); + addIfExists(propImpl->getGetterMethodDecl()); + addIfExists(propImpl->getSetterMethodDecl()); } if (InstanceMethods.size() == 0) @@ -2600,7 +2602,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 +2723,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); @@ -3494,13 +3498,12 @@ void CGObjCGNU::GenerateClass(const ObjCImplementationDecl *OID) { for (auto *propertyImpl : OID->property_impls()) if (propertyImpl->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) { - ObjCPropertyDecl *property = propertyImpl->getPropertyDecl(); auto addPropertyMethod = [&](const ObjCMethodDecl *accessor) { if (accessor) InstanceMethods.push_back(accessor); }; - addPropertyMethod(property->getGetterMethodDecl()); - addPropertyMethod(property->getSetterMethodDecl()); + addPropertyMethod(propertyImpl->getGetterMethodDecl()); + addPropertyMethod(propertyImpl->getSetterMethodDecl()); } llvm::Constant *Properties = GeneratePropertyList(OID, ClassDecl); @@ -3873,6 +3876,13 @@ llvm::Function *CGObjCGNU::GenerateMethod(const ObjCMethodDecl *OMD, return Method; } +void CGObjCGNU::GenerateDirectMethodPrologue(CodeGenFunction &CGF, + llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) { + // GNU runtime doesn't support direct calls at this time +} + llvm::FunctionCallee CGObjCGNU::GetPropertyGetFunction() { return GetPropertyFn; } diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 8e28b2f05c167..39525a57e59b5 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -874,6 +874,10 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { /// this translation unit. llvm::DenseMap MethodDefinitions; + /// DirectMethodDefinitions - map of direct methods which have been defined in + /// this translation unit. + llvm::DenseMap DirectMethodDefinitions; + /// PropertyNames - uniqued method variable names. llvm::DenseMap PropertyNames; @@ -1065,7 +1069,7 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { CodeGen::RValue EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, - llvm::Value *Sel, + Selector Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, @@ -1092,6 +1096,13 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD=nullptr) override; + llvm::Function *GenerateDirectMethod(const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD); + + void GenerateDirectMethodPrologue(CodeGenFunction &CGF, llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) override; + void GenerateProtocol(const ObjCProtocolDecl *PD) override; /// GetOrEmitProtocol - Get the protocol object for the given @@ -1573,9 +1584,13 @@ class CGObjCNonFragileABIMac : public CGObjCCommonMac { // base of the ivar access is a parameter to an Objective C method. // However, because the parameters are not available in the current // interface, we cannot perform this check. + // + // Note that for direct methods, because objc_msgSend is skipped, + // and that the method may be inlined, this optimization actually + // can't be performed. if (const ObjCMethodDecl *MD = dyn_cast_or_null(CGF.CurFuncDecl)) - if (MD->isInstanceMethod()) + if (MD->isInstanceMethod() && !MD->isDirectMethod()) if (const ObjCInterfaceDecl *ID = MD->getClassInterface()) return IV->getContainingInterface()->isSuperClassOf(ID); return false; @@ -2103,10 +2118,9 @@ CGObjCMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, CGM.getTypes().ConvertType(CGF.getContext().getObjCClassType()); Target = CGF.Builder.CreateBitCast(Target, ClassTy); CGF.Builder.CreateStore(Target, CGF.Builder.CreateStructGEP(ObjCSuper, 1)); - return EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), - ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy, - true, CallArgs, Method, Class, ObjCTypes); + return EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(), + ObjCTypes.SuperPtrCTy, true, CallArgs, Method, Class, + ObjCTypes); } /// Generate code for a message send expression. @@ -2118,10 +2132,9 @@ CodeGen::RValue CGObjCMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, const CallArgList &CallArgs, const ObjCInterfaceDecl *Class, const ObjCMethodDecl *Method) { - return EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), - Receiver, CGF.getContext().getObjCIdType(), - false, CallArgs, Method, Class, ObjCTypes); + return EmitMessageSend(CGF, Return, ResultType, Sel, Receiver, + CGF.getContext().getObjCIdType(), false, CallArgs, + Method, Class, ObjCTypes); } static bool isWeakLinkedClass(const ObjCInterfaceDecl *ID) { @@ -2137,7 +2150,7 @@ CodeGen::RValue CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, - llvm::Value *Sel, + Selector Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, @@ -2145,11 +2158,24 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, const ObjCMethodDecl *Method, const ObjCInterfaceDecl *ClassReceiver, const ObjCCommonTypesHelper &ObjCTypes) { + CodeGenTypes &Types = CGM.getTypes(); + auto selTy = CGF.getContext().getObjCSelType(); + llvm::Value *SelValue; + + if (Method && Method->isDirectMethod()) { + // Direct methods will synthesize the proper `_cmd` internally, + // so just don't bother with setting the `_cmd` argument. + assert(!IsSuper); + SelValue = llvm::UndefValue::get(Types.ConvertType(selTy)); + } else { + SelValue = GetSelector(CGF, Sel); + } + CallArgList ActualArgs; if (!IsSuper) Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy); ActualArgs.add(RValue::get(Arg0), Arg0Ty); - ActualArgs.add(RValue::get(Sel), CGF.getContext().getObjCSelType()); + ActualArgs.add(RValue::get(SelValue), selTy); ActualArgs.addFrom(CallArgs); // If we're calling a method, use the formal signature. @@ -2190,7 +2216,9 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, bool RequiresNullCheck = false; llvm::FunctionCallee Fn = nullptr; - if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) { + if (Method && Method->isDirectMethod()) { + Fn = GenerateDirectMethod(Method, Method->getClassInterface()); + } else if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) { if (ReceiverCanBeNull) RequiresNullCheck = true; Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper) : ObjCTypes.getSendStretFn(IsSuper); @@ -2961,7 +2989,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, @@ -3297,6 +3325,8 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, values.addInt(ObjCTypes.IntTy, Properties.size()); auto propertiesArray = values.beginArray(ObjCTypes.PropertyTy); for (auto PD : Properties) { + if (PD->isDirectProperty()) + continue; auto property = propertiesArray.beginStruct(ObjCTypes.PropertyTy); property.add(GetPropertyName(PD->getIdentifier())); property.add(GetPropertyTypeString(PD, Container)); @@ -3372,7 +3402,8 @@ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { }; SmallVector Methods[NumMethodLists]; for (const auto *MD : OCD->methods()) { - Methods[unsigned(MD->isClassMethod())].push_back(MD); + if (!MD->isDirectMethod()) + Methods[unsigned(MD->isClassMethod())].push_back(MD); } Values.add(GetClassName(OCD->getName())); @@ -3554,17 +3585,18 @@ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) { }; SmallVector Methods[NumMethodLists]; for (const auto *MD : ID->methods()) { - Methods[unsigned(MD->isClassMethod())].push_back(MD); + if (!MD->isDirectMethod()) + Methods[unsigned(MD->isClassMethod())].push_back(MD); } for (const auto *PID : ID->property_impls()) { if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) { - ObjCPropertyDecl *PD = PID->getPropertyDecl(); - - if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) + if (PID->getPropertyDecl()->isDirectProperty()) + continue; + if (ObjCMethodDecl *MD = PID->getGetterMethodDecl()) if (GetMethodDefinition(MD)) Methods[InstanceMethods].push_back(MD); - if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) + if (ObjCMethodDecl *MD = PID->getSetterMethodDecl()) if (GetMethodDefinition(MD)) Methods[InstanceMethods].push_back(MD); } @@ -3959,7 +3991,8 @@ llvm::Constant *CGObjCMac::emitMethodList(Twine name, MethodListType MLT, values.addInt(ObjCTypes.IntTy, methods.size()); auto methodArray = values.beginArray(ObjCTypes.MethodTy); for (auto MD : methods) { - emitMethodConstant(methodArray, MD); + if (!MD->isDirectMethod()) + emitMethodConstant(methodArray, MD); } methodArray.finishAndAddTo(values); @@ -3970,6 +4003,34 @@ llvm::Constant *CGObjCMac::emitMethodList(Twine name, MethodListType MLT, llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) { + llvm::Function *Method; + + if (OMD->isDirectMethod()) { + Method = GenerateDirectMethod(OMD, CD); + } else { + SmallString<256> Name; + GetNameForMethod(OMD, CD, Name); + + CodeGenTypes &Types = CGM.getTypes(); + llvm::FunctionType *MethodTy = + Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD)); + Method = + llvm::Function::Create(MethodTy, llvm::GlobalValue::InternalLinkage, + Name.str(), &CGM.getModule()); + } + + MethodDefinitions.insert(std::make_pair(OMD, Method)); + + return Method; +} + +llvm::Function * +CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) { + auto I = DirectMethodDefinitions.find(OMD->getCanonicalDecl()); + if (I != DirectMethodDefinitions.end()) + return I->second; + SmallString<256> Name; GetNameForMethod(OMD, CD, Name); @@ -3977,15 +4038,98 @@ llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD, llvm::FunctionType *MethodTy = Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD)); llvm::Function *Method = - llvm::Function::Create(MethodTy, - llvm::GlobalValue::InternalLinkage, - Name.str(), - &CGM.getModule()); - MethodDefinitions.insert(std::make_pair(OMD, Method)); + llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage, + Name.str(), &CGM.getModule()); + DirectMethodDefinitions.insert(std::make_pair(OMD->getCanonicalDecl(), Method)); return Method; } +void CGObjCCommonMac::GenerateDirectMethodPrologue( + CodeGenFunction &CGF, llvm::Function *Fn, const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) { + auto &Builder = CGF.Builder; + bool ReceiverCanBeNull = true; + auto selfAddr = CGF.GetAddrOfLocalVar(OMD->getSelfDecl()); + auto selfValue = Builder.CreateLoad(selfAddr); + + // Generate: + // + // /* for class methods only to force class lazy initialization */ + // self = [self self]; + // + // /* unless the receiver is never NULL */ + // if (self == nil) { + // return (ReturnType){ }; + // } + // + // _cmd = @selector(...) + // ... + + if (OMD->isClassMethod()) { + const ObjCInterfaceDecl *OID = cast(CD); + assert(OID && + "GenerateDirectMethod() should be called with the Class Interface"); + Selector SelfSel = GetNullarySelector("self", CGM.getContext()); + auto ResultType = CGF.getContext().getObjCIdType(); + RValue result; + CallArgList Args; + + // TODO: If this method is inlined, the caller might know that `self` is + // already initialized; for example, it might be an ordinary Objective-C + // method which always receives an initialized `self`, or it might have just + // forced initialization on its own. + // + // We should find a way to eliminate this unnecessary initialization in such + // cases in LLVM. + result = GeneratePossiblySpecializedMessageSend( + CGF, ReturnValueSlot(), ResultType, SelfSel, selfValue, Args, OID, + nullptr, true); + Builder.CreateStore(result.getScalarVal(), selfAddr); + + // Nullable `Class` expressions cannot be messaged with a direct method + // so the only reason why the receive can be null would be because + // of weak linking. + ReceiverCanBeNull = isWeakLinkedClass(OID); + } + + if (ReceiverCanBeNull) { + llvm::BasicBlock *SelfIsNilBlock = + CGF.createBasicBlock("objc_direct_method.self_is_nil"); + llvm::BasicBlock *ContBlock = + CGF.createBasicBlock("objc_direct_method.cont"); + + // if (self == nil) { + auto selfTy = cast(selfValue->getType()); + auto Zero = llvm::ConstantPointerNull::get(selfTy); + + llvm::MDBuilder MDHelper(CGM.getLLVMContext()); + Builder.CreateCondBr(Builder.CreateICmpEQ(selfValue, Zero), SelfIsNilBlock, + ContBlock, MDHelper.createBranchWeights(1, 1 << 20)); + + CGF.EmitBlock(SelfIsNilBlock); + + // return (ReturnType){ }; + auto retTy = OMD->getReturnType(); + Builder.SetInsertPoint(SelfIsNilBlock); + if (!retTy->isVoidType()) { + CGF.EmitNullInitialization(CGF.ReturnValue, retTy); + } + CGF.EmitBranchThroughCleanup(CGF.ReturnBlock); + // } + + // rest of the body + CGF.EmitBlock(ContBlock); + Builder.SetInsertPoint(ContBlock); + } + + // only synthesize _cmd if it's referenced + if (OMD->getCmdDecl()->isUsed()) { + Builder.CreateStore(GetSelector(CGF, OMD), + CGF.GetAddrOfLocalVar(OMD->getCmdDecl())); + } +} + llvm::GlobalVariable *CGObjCCommonMac::CreateMetadataVar(Twine Name, ConstantStructBuilder &Init, StringRef Section, @@ -6228,23 +6372,12 @@ llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer( SmallVector methods; if (flags & NonFragileABI_Class_Meta) { for (const auto *MD : ID->class_methods()) - methods.push_back(MD); + if (!MD->isDirectMethod()) + methods.push_back(MD); } else { for (const auto *MD : ID->instance_methods()) - methods.push_back(MD); - - for (const auto *PID : ID->property_impls()) { - if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize){ - ObjCPropertyDecl *PD = PID->getPropertyDecl(); - - if (auto MD = PD->getGetterMethodDecl()) - if (GetMethodDefinition(MD)) - methods.push_back(MD); - if (auto MD = PD->getSetterMethodDecl()) - if (GetMethodDefinition(MD)) - methods.push_back(MD); - } - } + if (!MD->isDirectMethod()) + methods.push_back(MD); } values.add(emitMethodList(ID->getObjCRuntimeNameAsString(), @@ -6565,6 +6698,8 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { SmallVector instanceMethods; SmallVector classMethods; for (const auto *MD : OCD->methods()) { + if (MD->isDirectMethod()) + continue; if (MD->isInstanceMethod()) { instanceMethods.push_back(MD); } else { @@ -6639,7 +6774,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); @@ -6707,9 +6849,8 @@ CGObjCNonFragileABIMac::emitMethodList(Twine name, MethodListType kind, // method_count values.addInt(ObjCTypes.IntTy, methods.size()); auto methodArray = values.beginArray(ObjCTypes.MethodTy); - for (auto MD : methods) { + for (auto MD : methods) emitMethodConstant(methodArray, MD, forProtocol); - } methodArray.finishAndAddTo(values); llvm::GlobalVariable *GV = finishAndCreateGlobal(values, prefix + name, CGM); @@ -6910,9 +7051,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); @@ -7213,7 +7353,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, @@ -7234,8 +7375,7 @@ CGObjCNonFragileABIMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, ? EmitVTableMessageSend(CGF, Return, ResultType, Sel, Receiver, CGF.getContext().getObjCIdType(), false, CallArgs, Method) - : EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), + : EmitMessageSend(CGF, Return, ResultType, Sel, Receiver, CGF.getContext().getObjCIdType(), false, CallArgs, Method, Class, ObjCTypes); } @@ -7280,7 +7420,12 @@ CGObjCNonFragileABIMac::GetClassGlobal(StringRef Name, } assert(GV->getLinkage() == L); - return GV; + + if (IsForDefinition || + GV->getValueType() == ObjCTypes.ClassnfABITy) + return GV; + + return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ClassnfABIPtrTy); } llvm::Constant * @@ -7413,7 +7558,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(ClassGV) || cast(ClassGV)->hasExternalWeakLinkage()); @@ -7466,8 +7612,7 @@ CGObjCNonFragileABIMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, ? EmitVTableMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy, true, CallArgs, Method) - : EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), + : EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy, true, CallArgs, Method, Class, ObjCTypes); } @@ -7719,11 +7864,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/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h index 471816cb5988e..f0b3525cfde25 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.h +++ b/clang/lib/CodeGen/CGObjCRuntime.h @@ -169,6 +169,21 @@ class CGObjCRuntime { const ObjCInterfaceDecl *Class = nullptr, const ObjCMethodDecl *Method = nullptr) = 0; + /// Generate an Objective-C message send operation. + /// + /// This variant allows for the call to be substituted with an optimized + /// variant. + CodeGen::RValue + GeneratePossiblySpecializedMessageSend(CodeGenFunction &CGF, + ReturnValueSlot Return, + QualType ResultType, + Selector Sel, + llvm::Value *Receiver, + const CallArgList& Args, + const ObjCInterfaceDecl *OID, + const ObjCMethodDecl *Method, + bool isClassMessage); + /// Generate an Objective-C message send operation to the super /// class initiated in a method for Class and with the given Self /// object. @@ -205,6 +220,12 @@ class CGObjCRuntime { virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) = 0; + /// Generates prologue for direct Objective-C Methods. + virtual void GenerateDirectMethodPrologue(CodeGenFunction &CGF, + llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) = 0; + /// Return the runtime function for getting properties. virtual llvm::FunctionCallee GetPropertyGetFunction() = 0; diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp new file mode 100644 index 0000000000000..ab870df65b78b --- /dev/null +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -0,0 +1,644 @@ +//===--- 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 + +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); +} + +/// Compute an ABI-stable hash of the given string. +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); +} + +CGPointerAuthInfo +CodeGenModule::getMemberFunctionPointerAuthInfo(QualType functionType) { + assert(functionType->getAs() && + "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, + QualType type) { + assert(type->isPointerType()); + + // Function pointers use the function-pointer schema by default. + if (auto ptrTy = type->getAs()) { + 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 std::pair +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 +CodeGenFunction::EmitOrigPointerRValue(const Expr *E) { + assert(E->getType()->isPointerType()); + + E = E->IgnoreParens(); + if (auto load = dyn_cast(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(const_cast(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(E)) { + if (auto FD = dyn_cast(DRE->getDecl())) { + return { CGM.getRawFunctionPointer(FD), CGPointerAuthInfo() }; + } + } else if (auto ME = dyn_cast(E)) { + if (auto FD = dyn_cast(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(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(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; +} + +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 +/// representation is pretty complicated (an llvm::ValueMap) and we don't +/// want to have to include that information in CodeGenModule.h. +template +static CacheTy &getOrCreateCache(void *&abstractStorage) { + auto cache = static_cast(abstractStorage); + if (cache) return *cache; + + abstractStorage = cache = new CacheTy(); + return *cache; +} + +template +static void destroyCache(void *&abstractStorage) { + delete static_cast(abstractStorage); + abstractStorage = nullptr; +} + +namespace { +struct PointerAuthConstantEntry { + unsigned Key; + llvm::Constant *OtherDiscriminator; + llvm::GlobalVariable *Global; +}; + +using PointerAuthConstantEntries = + std::vector; +using ByConstantCacheTy = + llvm::ValueMap; +using ByDeclCacheTy = + llvm::DenseMap; +} + +/// 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(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(stripped)) + name = origGlobal->getName(); + else if (const auto *ce = dyn_cast(stripped)) + if (ce->getOpcode() == llvm::Instruction::GetElementPtr) + name = cast(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(otherDiscriminator))) { + + // Get or create the cache. + auto &cache = + getOrCreateCache(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()); +} + +/// 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, + llvm::Constant *storageAddress, + llvm::Constant *otherDiscriminator) { + return CGM.getConstantSignedPointer(pointer, key, storageAddress, + 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) { + 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(ConstantSignedPointersByConstant); + destroyCache(ConstantSignedPointersByDecl); + destroyCache(SignedThunkPointers); +} + +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(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(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); +} + +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(SignedThunkPointers); + entry = &cache[FD->getCanonicalDecl()]; + if (*entry) + return llvm::ConstantExpr::getBitCast(*entry, pointer->getType()); + } + + pointer = getConstantSignedPointer( + pointer, pointerAuth.getKey(), nullptr, + cast_or_null(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(FD)->getParent()->getTypeForDecl()); + return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), functionType, + FD); +} 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/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/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 89dd06948baab..574c9c1f85248 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -635,8 +635,7 @@ static llvm::Constant *getPrologueSignature(CodeGenModule &CGM, return CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM); } -void CodeGenFunction::StartFunction(GlobalDecl GD, - QualType RetTy, +void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, llvm::Function *Fn, const CGFunctionInfo &FnInfo, const FunctionArgList &Args, @@ -2404,3 +2403,93 @@ llvm::DebugLoc CodeGenFunction::SourceLocToDebugLoc(SourceLocation Location) { return llvm::DebugLoc(); } + +void CodeGenFunction::EmitPointerAuthOperandBundle( + const CGPointerAuthInfo &pointerAuth, + SmallVectorImpl &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 5c3d1764fad73..6873d536adbcb 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3600,7 +3600,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) { @@ -3650,6 +3651,51 @@ 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 &bundles); + + 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 + 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, @@ -3947,7 +3993,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/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index be8f389e1809c..6e35caccd824e 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 @@ -1514,16 +1516,15 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, !CodeGenOpts.DisableO0ImplyOptNone && CodeGenOpts.OptimizationLevel == 0; // We can't add optnone in the following cases, it won't pass the verifier. ShouldAddOptNone &= !D->hasAttr(); - ShouldAddOptNone &= !F->hasFnAttribute(llvm::Attribute::AlwaysInline); ShouldAddOptNone &= !D->hasAttr(); - if (ShouldAddOptNone || D->hasAttr()) { + // Add optnone, but do so only if the function isn't always_inline. + if ((ShouldAddOptNone || D->hasAttr()) && + !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { B.addAttribute(llvm::Attribute::OptimizeNone); // OptimizeNone implies noinline; we should not be inlining such functions. B.addAttribute(llvm::Attribute::NoInline); - assert(!F->hasFnAttribute(llvm::Attribute::AlwaysInline) && - "OptimizeNone and AlwaysInline on same function!"); // We still need to handle naked functions even though optnone subsumes // much of their semantics. @@ -1539,7 +1540,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, B.addAttribute(llvm::Attribute::NoInline); } else if (D->hasAttr()) { B.addAttribute(llvm::Attribute::NoDuplicate); - } else if (D->hasAttr()) { + } else if (D->hasAttr() && !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { + // Add noinline if the function isn't always_inline. B.addAttribute(llvm::Attribute::NoInline); } else if (D->hasAttr() && !F->hasFnAttribute(llvm::Attribute::NoInline)) { @@ -5113,11 +5115,12 @@ void CodeGenModule::EmitObjCPropertyImplementations(const // we want, that just indicates if the decl came from a // property. What we want to know is if the method is defined in // this implementation. - if (!D->getInstanceMethod(PD->getGetterName())) + auto *Getter = PID->getGetterMethodDecl(); + if (!Getter || Getter->isSynthesizedAccessorStub()) CodeGenFunction(*this).GenerateObjCGetter( - const_cast(D), PID); - if (!PD->isReadOnly() && - !D->getInstanceMethod(PD->getSetterName())) + const_cast(D), PID); + auto *Setter = PID->getSetterMethodDecl(); + if (!PD->isReadOnly() && (!Setter || Setter->isSynthesizedAccessorStub())) CodeGenFunction(*this).GenerateObjCSetter( const_cast(D), PID); } @@ -5154,12 +5157,13 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { if (needsDestructMethod(D)) { IdentifierInfo *II = &getContext().Idents.get(".cxx_destruct"); Selector cxxSelector = getContext().Selectors.getSelector(0, &II); - ObjCMethodDecl *DTORMethod = - ObjCMethodDecl::Create(getContext(), D->getLocation(), D->getLocation(), - cxxSelector, getContext().VoidTy, nullptr, D, - /*isInstance=*/true, /*isVariadic=*/false, - /*isPropertyAccessor=*/true, /*isImplicitlyDeclared=*/true, - /*isDefined=*/false, ObjCMethodDecl::Required); + ObjCMethodDecl *DTORMethod = ObjCMethodDecl::Create( + getContext(), D->getLocation(), D->getLocation(), cxxSelector, + getContext().VoidTy, nullptr, D, + /*isInstance=*/true, /*isVariadic=*/false, + /*isPropertyAccessor=*/true, /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, ObjCMethodDecl::Required); D->addInstanceMethod(DTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, DTORMethod, false); D->setHasDestructors(true); @@ -5174,17 +5178,13 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { IdentifierInfo *II = &getContext().Idents.get(".cxx_construct"); Selector cxxSelector = getContext().Selectors.getSelector(0, &II); // The constructor returns 'self'. - ObjCMethodDecl *CTORMethod = ObjCMethodDecl::Create(getContext(), - D->getLocation(), - D->getLocation(), - cxxSelector, - getContext().getObjCIdType(), - nullptr, D, /*isInstance=*/true, - /*isVariadic=*/false, - /*isPropertyAccessor=*/true, - /*isImplicitlyDeclared=*/true, - /*isDefined=*/false, - ObjCMethodDecl::Required); + ObjCMethodDecl *CTORMethod = ObjCMethodDecl::Create( + getContext(), D->getLocation(), D->getLocation(), cxxSelector, + getContext().getObjCIdType(), nullptr, D, /*isInstance=*/true, + /*isVariadic=*/false, + /*isPropertyAccessor=*/true, /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, ObjCMethodDecl::Required); D->addInstanceMethod(CTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, CTORMethod, true); D->setHasNonZeroConstructors(true); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index f5014c05b0672..d6a22458ccf66 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; @@ -405,6 +406,11 @@ class CodeGenModule : public CodeGenTypeCache { /// Global annotations. std::vector Annotations; + /// Signed constant pointers. + void *ConstantSignedPointersByDecl = nullptr; + void *SignedThunkPointers = nullptr; + void *ConstantSignedPointersByConstant = nullptr; + /// Map used to get unique annotation strings. llvm::StringMap AnnotationStrings; @@ -547,6 +553,8 @@ class CodeGenModule : public CodeGenTypeCache { MetadataTypeMap VirtualMetadataIdMap; MetadataTypeMap GeneralizedMetadataIdMap; + llvm::DenseMap PtrAuthDiscriminatorHashes; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, @@ -845,6 +853,51 @@ 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); + + 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); + + 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); @@ -1514,6 +1567,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/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 8f9b16470b642..e82d718881a37 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 Info = + llvm::GlobalPtrAuthInfo::analyze(Ptr); + + if (!Info || !isa(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(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(src)) + return EmitMemberPointerConversion(E, cast(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(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(src)) - return EmitMemberPointerConversion(E, cast(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(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, @@ -1267,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); } } @@ -1729,12 +1854,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 +1921,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 +1950,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 +2075,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); @@ -2391,6 +2545,14 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, if (llvm::Function *fn = dyn_cast(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(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 @@ -2398,8 +2560,7 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, // function. addr = llvm::Constant::getNullValue(CGF.Int8PtrTy); - llvm::Value *args[] = {llvm::ConstantExpr::getBitCast( - cast(dtor.getCallee()), dtorTy), + llvm::Value *args[] = {llvm::ConstantExpr::getBitCast(dtorCallee, dtorTy), llvm::ConstantExpr::getBitCast(addr, AddrInt8PtrTy), handle}; CGF.EmitNounwindRuntimeCall(atexit, args); @@ -2752,6 +2913,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( + 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(); + 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(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. @@ -3268,6 +3495,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); } @@ -4372,6 +4603,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(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/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 7ec3950bc6f9c..7b250f257959e 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1899,7 +1899,7 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, VFunc = Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); } - CGCallee Callee(GD, VFunc); + CGCallee Callee(GD, VFunc, /*unsigned*/ CGPointerAuthInfo()); return Callee; } @@ -3385,7 +3385,7 @@ CGCallee MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer( FunctionPointer = Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo()); - CGCallee Callee(FPT, FunctionPointer); + CGCallee Callee(FPT, FunctionPointer, /*unsigned*/ CGPointerAuthInfo()); return Callee; } diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 3fb38a79051cd..08ad38db33c92 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1382,7 +1382,9 @@ void Driver::generateCompilationDiagnostics( } // 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)); llvm::SmallString<128> Script(CrashInfo.Filename); llvm::sys::path::replace_extension(Script, "sh"); diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 0a95e49694f95..bdba4edb6aea4 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -72,6 +72,8 @@ static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, .Default(false); if (IsInclude) return !HaveCrashVFS; + if (StringRef(Flag).startswith("-index-store-path")) + return true; // The remaining flags are treated as a single argument. @@ -93,6 +95,8 @@ static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, return !HaveCrashVFS; if (FlagRef.startswith("-fmodules-cache-path=")) return true; + if (FlagRef.startswith("-fapinotes-cache-path=")) + return true; SkipNum = 0; return false; @@ -225,6 +229,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]; @@ -288,6 +293,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/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index a014c611ee228..5cbc2e858cb58 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -631,6 +631,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 3a5fe6ddeaed5..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 @@ -139,10 +150,15 @@ 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" || + MtuneLowerCase == "lightning") { Features.push_back("+zcm"); Features.push_back("+zcz"); } + return true; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 6f3f7bfe61f4d..d838d8c5f7a8f 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1018,6 +1018,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"); bool HasTarget = false; for (const Arg *A : Args.filtered(options::OPT_MT, options::OPT_MQ)) { @@ -4266,6 +4267,26 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, RenderARCMigrateToolOptions(D, Args, CmdArgs); + 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(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. // @@ -4746,6 +4767,19 @@ 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) || + 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"); + + Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version); + } + // -fblocks=0 is default. if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks, TC.IsBlocksDefault()) || @@ -5062,6 +5096,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 d550eea946709..d7063855b84a8 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -57,7 +57,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) @@ -72,8 +72,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) { @@ -444,6 +449,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)) { @@ -832,8 +841,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: @@ -921,6 +933,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. @@ -1639,7 +1682,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; @@ -2627,6 +2670,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/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/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..4265b0d1d9f6d --- /dev/null +++ b/clang/lib/Edit/FillInMissingProtocolStubs.cpp @@ -0,0 +1,466 @@ +//===--- 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 + +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::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; + +/// 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 InstanceMethods; + llvm::DenseMap 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; + 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))); + } + } + + void markImplementedMethods(const ObjCContainerDecl *Container) { + assert(isa(Container) && "Not an implementation container"); + markMethodsFrom(Container, MethodInfo::IsImplemented); + if (const auto *ID = dyn_cast(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(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(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(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 + getMissingRequiredMethods(const ObjCContainerDecl *Container) { + MethodInfo::MethodPresenceKind Kind = isa(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + std::vector 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(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + llvm::SmallVector 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]->getEndLoc(); + if (Loc.isMacroID()) + Loc = SM.getExpansionRange(Loc).getEnd(); + for (const ObjCMethodDecl *M : + makeArrayRef(MethodsFromProtocolInContainer).drop_front()) { + SourceLocation EndLoc = M->getEndLoc(); + if (EndLoc.isMacroID()) + EndLoc = SM.getExpansionRange(EndLoc).getEnd(); + 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::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(Container)) + return Impl->getClassInterface(); + if (const auto *CategoryImpl = dyn_cast(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(ContainerProtocolSource)) { + if (!I->hasDefinition()) + return false; + Protocols = gatherSuitableClassProtocols(I, Container, API); + if (Protocols.empty()) + return false; + } else if (const auto *I = + dyn_cast(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(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 = std::make_unique(); + 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 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(Container) + ? Container->getEndLoc() + : getLocationOfPrecedingToken(Container->getEndLoc(), + Context.getSourceManager(), + Context.getLangOpts()); + if (InsertionLoc.isInvalid()) + InsertionLoc = Container->getEndLoc(); + + 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(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 Consumer) { + ::perform(Impl->Methods, Impl->Container, Context, Consumer); +} + +void fillInMissingProtocolStubs::addMissingProtocolStubs( + ASTContext &Context, const ObjCContainerDecl *Container, + llvm::function_ref 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..4bb31fe6bc8f6 --- /dev/null +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -0,0 +1,192 @@ +//===--- 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 + +using namespace clang; + +namespace { + +struct CaseInfo { + const SwitchCase *Case, *NextCase; + unsigned Index; +}; +typedef std::unordered_map 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 &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 (!Case->getSubStmt()) + continue; + if (isa(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 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(Case)) { + DefaultCase = Case; + continue; + } + const auto *CS = cast(Case); + if (const auto *LHS = CS->getLHS()) { + Expr::EvalResult Result; + if (!LHS->EvaluateAsInt(Result, Context)) + continue; + // Only allow constant that fix into 64 bits. + if (Result.Val.getInt().getMinSignedBits() > 64) + continue; + 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. + 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 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->getBeginLoc(); + else + InsertionLoc = Switch->getBody()->getEndLoc(); + } + Consumer(FixItHint::CreateInsertion( + Context.getSourceManager().getSpellingLoc(InsertionLoc), OS.str())); + }; + + // Determine which enum cases are uncovered. + + llvm::SmallVector, 8> EnumCases; + llvm::SmallVector 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 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->getBeginLoc(); + + 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->getBeginLoc() + : /*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/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/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index f5e291b7fe17e..88e30d19b7419 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -1775,6 +1775,9 @@ ASTUnit *ASTUnit::LoadFromCommandLine( if (ModuleFormat) CI->getHeaderSearchOpts().ModuleFormat = ModuleFormat.getValue(); + if (ForSerialization) + CI->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true; + // Create the AST unit. std::unique_ptr AST; AST.reset(new ASTUnit(false)); diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index 0e23b92e2dea9..ea72ef883cc37 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -52,6 +52,7 @@ add_clang_library(clangFrontend ${optional_deps} LINK_LIBS + clangAPINotes clangAST clangBasic clangDriver diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index a0663217453a0..0d40650675807 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -7,6 +7,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" @@ -28,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/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" @@ -473,7 +475,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(); } @@ -623,6 +625,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()); @@ -1101,8 +1124,10 @@ compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, 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 an in-memory module cache, @@ -1127,6 +1152,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. @@ -1144,8 +1173,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 Action( + new GenerateModuleFromModuleMapAction); + if (wrapGenModuleAction) { + Action = wrapGenModuleAction(FrontendOpts, std::move(Action)); + } + Instance.ExecuteAction(*Action); }, DesiredStackSize); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 46a7e39770a6f..826841a5724da 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -664,8 +664,94 @@ 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 (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; + using Discrimination = PointerAuthSchema::Discrimination; + // If you change anything here, be sure to update . + 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.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.CXXMemberFunctionPointers = + PointerAuthSchema(Key::ASIA, false, Discrimination::Type); + Opts.ThunkCXXVirtualMemberPointers = false; + } + + 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; @@ -1371,6 +1457,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)) { @@ -1411,6 +1499,15 @@ 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. + // + // 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); + Opts.PassPlugins = Args.getAllArgValues(OPT_fpass_plugin_EQ); Opts.SymbolPartition = Args.getLastArgValue(OPT_fsymbol_partition_EQ); @@ -1424,6 +1521,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); @@ -1953,6 +2051,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(Language::Unknown); if (const Arg *A = Args.getLastArg(OPT_x)) { StringRef XValue = A->getValue(); @@ -2182,6 +2284,18 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, Opts.AddVFSOverlayFile(A->getValue()); } +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()); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, const llvm::Triple &T, PreprocessorOptions &PPOpts, @@ -2781,6 +2895,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.CPlusPlusModules; 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); @@ -2888,6 +3003,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, // is enabled. 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); Opts.Cmse = Args.hasArg(OPT_mcmse); // Armv8-M Security Extensions @@ -3108,6 +3225,13 @@ 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) || @@ -3477,10 +3601,10 @@ 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); + llvm::Triple T(Res.getTargetOpts().Triple); if (DashX.getFormat() == InputKind::Precompiled || DashX.getLanguage() == Language::LLVM_IR) { @@ -3516,6 +3640,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. @@ -3541,6 +3669,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. if (T.isSPIR()) Res.getDiagnosticOpts().Warnings.push_back("spir-compat"); @@ -3554,7 +3686,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; @@ -3639,7 +3780,19 @@ 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)); + } + + // 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); } // When compiling with -gmodules, also hash -fdebug-prefix-map as it @@ -3655,6 +3808,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/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index 4bb0167bd5976..7fe8b6a17bc4e 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)) @@ -196,7 +227,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(std::make_unique( + *this, PP.getSourceManager(), PP.getDiagnostics())); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + std::make_unique(*this, SkipUnusedModuleMaps)); } bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule, diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index b3b7976f8f3e3..d017eaf3eb626 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -709,6 +709,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 (auto PCHDir = FileMgr.getDirectory(PCHInclude)) { std::error_code EC; @@ -737,6 +738,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/Frontend/Rewrite/RewriteModernObjC.cpp b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp index 45495065ada64..831f95e8c6be4 100644 --- a/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp +++ b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -908,9 +908,9 @@ RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) { static bool mustSynthesizeSetterGetterMethod(ObjCImplementationDecl *IMP, ObjCPropertyDecl *PD, bool getter) { - return getter ? !IMP->getInstanceMethod(PD->getGetterName()) - : !IMP->getInstanceMethod(PD->getSetterName()); - + auto *OMD = IMP->getInstanceMethod(getter ? PD->getGetterName() + : PD->getSetterName()); + return !OMD || OMD->isSynthesizedAccessorStub(); } void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, @@ -952,7 +952,7 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, "id objc_getProperty(id, SEL, long, bool);\n"; } RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getGetterMethodDecl(), Getr); + PID->getGetterMethodDecl(), Getr); Getr += "{ "; // Synthesize an explicit cast to gain access to the ivar. // See objc-act.c:objc_synthesize_new_getter() for details. @@ -960,7 +960,7 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) Getr += "typedef "; const FunctionType *FPRetType = nullptr; - RewriteTypeIntoString(PD->getGetterMethodDecl()->getReturnType(), Getr, + RewriteTypeIntoString(PID->getGetterMethodDecl()->getReturnType(), Getr, FPRetType); Getr += " _TYPE"; if (FPRetType) { @@ -1012,7 +1012,7 @@ void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, } RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getSetterMethodDecl(), Setr); + PID->getSetterMethodDecl(), Setr); Setr += "{ "; // Synthesize an explicit cast to initialize the ivar. // See objc-act.c:objc_synthesize_new_setter() for details. @@ -1346,6 +1346,8 @@ void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) { InsertText(CID->getBeginLoc(), "// "); for (auto *OMD : IMD ? IMD->instance_methods() : CID->instance_methods()) { + if (!OMD->getBody()) + continue; std::string ResultStr; RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); SourceLocation LocStart = OMD->getBeginLoc(); @@ -1357,6 +1359,8 @@ void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) { } for (auto *OMD : IMD ? IMD->class_methods() : CID->class_methods()) { + if (!OMD->getBody()) + continue; std::string ResultStr; RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); SourceLocation LocStart = OMD->getBeginLoc(); @@ -7031,12 +7035,12 @@ void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (ObjCMethodDecl *Getter = Prop->getGetterMethodDecl()) if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/)) InstanceMethods.push_back(Getter); if (PD->isReadOnly()) continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (ObjCMethodDecl *Setter = Prop->getSetterMethodDecl()) if (mustSynthesizeSetterGetterMethod(IDecl, PD, false /*setter*/)) InstanceMethods.push_back(Setter); } @@ -7281,11 +7285,11 @@ void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (ObjCMethodDecl *Getter = Prop->getGetterMethodDecl()) InstanceMethods.push_back(Getter); if (PD->isReadOnly()) continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (ObjCMethodDecl *Setter = Prop->getSetterMethodDecl()) InstanceMethods.push_back(Setter); } diff --git a/clang/lib/Frontend/Rewrite/RewriteObjC.cpp b/clang/lib/Frontend/Rewrite/RewriteObjC.cpp index 6a22da178fbc9..0cb7592b99820 100644 --- a/clang/lib/Frontend/Rewrite/RewriteObjC.cpp +++ b/clang/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -786,8 +786,9 @@ void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, if (!OID) return; + unsigned Attributes = PD->getPropertyAttributes(); - if (!PD->getGetterMethodDecl()->isDefined()) { + if (PID->getGetterMethodDecl() && !PID->getGetterMethodDecl()->isDefined()) { bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_copy)); @@ -799,7 +800,7 @@ void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, "id objc_getProperty(id, SEL, long, bool);\n"; } RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getGetterMethodDecl(), Getr); + PID->getGetterMethodDecl(), Getr); Getr += "{ "; // Synthesize an explicit cast to gain access to the ivar. // See objc-act.c:objc_synthesize_new_getter() for details. @@ -807,7 +808,7 @@ void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) Getr += "typedef "; const FunctionType *FPRetType = nullptr; - RewriteTypeIntoString(PD->getGetterMethodDecl()->getReturnType(), Getr, + RewriteTypeIntoString(PID->getGetterMethodDecl()->getReturnType(), Getr, FPRetType); Getr += " _TYPE"; if (FPRetType) { @@ -843,7 +844,8 @@ void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, InsertText(onePastSemiLoc, Getr); } - if (PD->isReadOnly() || PD->getSetterMethodDecl()->isDefined()) + if (PD->isReadOnly() || !PID->getSetterMethodDecl() || + PID->getSetterMethodDecl()->isDefined()) return; // Generate the 'setter' function. @@ -858,7 +860,7 @@ void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, } RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getSetterMethodDecl(), Setr); + PID->getSetterMethodDecl(), Setr); Setr += "{ "; // Synthesize an explicit cast to initialize the ivar. // See objc-act.c:objc_synthesize_new_setter() for details. @@ -1168,6 +1170,8 @@ void RewriteObjC::RewriteImplementationDecl(Decl *OID) { InsertText(IMD ? IMD->getBeginLoc() : CID->getBeginLoc(), "// "); for (auto *OMD : IMD ? IMD->instance_methods() : CID->instance_methods()) { + if (!OMD->getBody()) + continue; std::string ResultStr; RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); SourceLocation LocStart = OMD->getBeginLoc(); @@ -1179,6 +1183,8 @@ void RewriteObjC::RewriteImplementationDecl(Decl *OID) { } for (auto *OMD : IMD ? IMD->class_methods() : CID->class_methods()) { + if (!OMD->getBody()) + continue; std::string ResultStr; RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); SourceLocation LocStart = OMD->getBeginLoc(); @@ -5355,12 +5361,12 @@ void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDe ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (ObjCMethodDecl *Getter = Prop->getGetterMethodDecl()) if (!Getter->isDefined()) InstanceMethods.push_back(Getter); if (PD->isReadOnly()) continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (ObjCMethodDecl *Setter = Prop->getSetterMethodDecl()) if (!Setter->isDefined()) InstanceMethods.push_back(Setter); } @@ -5633,11 +5639,11 @@ void RewriteObjCFragileABI::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *ID ObjCPropertyDecl *PD = Prop->getPropertyDecl(); if (!PD) continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (ObjCMethodDecl *Getter = Prop->getGetterMethodDecl()) InstanceMethods.push_back(Getter); if (PD->isReadOnly()) continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (ObjCMethodDecl *Setter = Prop->getSetterMethodDecl()) InstanceMethods.push_back(Setter); } RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), 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 9bf70b793d9b8..889da9478fa59 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -22,6 +22,7 @@ #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/Utils.h" #include "clang/FrontendTool/Utils.h" +#include "clang/Index/IndexingAction.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "llvm/Option/OptTable.h" @@ -173,6 +174,11 @@ CreateFrontendAction(CompilerInstance &CI) { } #endif + if (!FEOpts.IndexStorePath.empty()) { + Act = index::createIndexDataRecordingAction(FEOpts, std::move(Act)); + CI.setGenModuleActionWrapper(&index::createIndexDataRecordingAction); + } + // 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/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..2df030d000ab1 --- /dev/null +++ b/clang/lib/Headers/ptrauth.h @@ -0,0 +1,356 @@ +/*===---- 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 + +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) + +/* 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 +#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) + +#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/Index/BitstreamVisitor.h b/clang/lib/Index/BitstreamVisitor.h new file mode 100644 index 0000000000000..481327eafba10 --- /dev/null +++ b/clang/lib/Index/BitstreamVisitor.h @@ -0,0 +1,191 @@ +//===--- 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/Bitstream/BitstreamReader.h" +#include "clang/Basic/LLVM.h" +#include "clang/Serialization/ASTReader.h" +#include + +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() { + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + llvm::report_fatal_error("SavedStreamPosition failed jumping: " + + toString(std::move(Err))); + } + +private: + llvm::BitstreamCursor &Cursor; + uint64_t Offset; +}; + +enum class StreamVisit { + Continue, + Skip, + Abort +}; + +template +class BitstreamVisitor { + SmallVector BlockStack; + +protected: + llvm::BitstreamCursor &Stream; + Optional 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) { + Expected 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: + 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) { + Expected> MaybeBlockInfo = Stream.ReadBlockInfoBlock(); + if (!MaybeBlockInfo) { + Error = toString(MaybeBlockInfo.takeError()); + return false; + } + BlockInfo = MaybeBlockInfo.get(); + if (!BlockInfo) { + Error = "malformed BlockInfoBlock"; + return false; + } + Stream.setBlockInfo(&*BlockInfo); + break; + } + + StreamVisit Ret = static_cast(this)->visitBlock(Entry.ID); + switch (Ret) { + case StreamVisit::Continue: + if (Stream.EnterSubBlock(Entry.ID)) { + Error = "malformed block record"; + return false; + } + if (llvm::Error Err = readBlockAbbrevs(Stream)) { + Error = toString(std::move(Err)); + return false; + } + 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; + Expected 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(this)->visitRecord(BlockID, RecID, Record, Blob); + switch (Ret) { + case StreamVisit::Continue: + break; + + case StreamVisit::Skip: + if (Expected Skipped = Stream.skipRecord(Entry.ID)) { + Error = toString(Skipped.takeError()); + return false; + } + break; + + case StreamVisit::Abort: + return false; + } + break; + } + } + } + } + + static llvm::Error readBlockAbbrevs(llvm::BitstreamCursor &Cursor) { + while (true) { + uint64_t Offset = Cursor.GetCurrentBitNo(); + Expected 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) { + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + return Err; + return llvm::Error::success(); + } + if (llvm::Error Err = Cursor.ReadAbbrevRecord()) + return Err; + } + } +}; + +} // 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 e4ea7ea1660c9..18ba9ec6113d9 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -1,17 +1,25 @@ set(LLVM_LINK_COMPONENTS + BitReader Core Support ) add_clang_library(clangIndex + ClangIndexRecordWriter.cpp CommentToXML.cpp FileIndexRecord.cpp IndexBody.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..a8c8bd953c40c --- /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(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) + ASTNameGen.reset(new ASTNameGenerator(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 LineNo = SM.getLineNumber(FID, Offset); + unsigned ColNo = SM.getColumnNumber(FID, Offset); + return std::make_pair(LineNo, ColNo); + }; + + for (auto &Occur : IdxRecord.getDeclOccurrencesSortedByOffset()) { + unsigned Line, Col; + std::tie(Line, Col) = getLineCol(Occur.Offset); + SmallVector 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 &Scratch) { + const Decl *D = static_cast(OD); + auto Info = getSymbolInfo(D); + + writer::Symbol Sym; + Sym.SymInfo = Info; + + auto *ND = dyn_cast(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 (ASTNameGen && ND) { + llvm::raw_svector_ostream OS(Scratch); + ASTNameGen->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..81ab6a662745e --- /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/AST/Mangle.h" +#include "clang/Index/IndexRecordWriter.h" +#include "clang/Index/IndexingAction.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 ASTNameGen; + llvm::BumpPtrAllocator Allocator; + llvm::DenseMap USRByDecl; + IndexRecordHasher Hasher; + +public: + ClangIndexRecordWriter(ASTContext &Ctx, RecordingOptions Opts); + ~ClangIndexRecordWriter(); + + ASTContext &getASTContext() { return Ctx; } + ASTNameGenerator *getASTNameGen() { return ASTNameGen.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/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp new file mode 100644 index 0000000000000..f27ffa6a54e10 --- /dev/null +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -0,0 +1,522 @@ +//===--- 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/ADT/SmallString.h" +#include "llvm/Bitstream/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 &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 &PathBuf) { + sys::path::append(PathBuf, UnitName); +} + +void store::appendUnitSubDir(SmallVectorImpl &StorePathBuf) { + return appendSubDir("units", StorePathBuf); +} + +void store::appendRecordSubDir(SmallVectorImpl &StorePathBuf) { + return appendSubDir("records", StorePathBuf); +} + +void store::appendInteriorRecordPath(StringRef RecordName, + SmallVectorImpl &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(); + 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_USING: + return SymbolKind::Using; + 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_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: + return SymbolSubKind::SwiftAccessorDidSet; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR: + 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: + 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) { + 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; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface; + + return SymbolProperties; +} + +/// Map an indexstore representation to a SymbolRoleSet, handling unknown +/// values. +SymbolRoleSet index::getSymbolRoles(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; + if (Roles & INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::NameReference; + + return SymbolRoles; +} + +/// 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::Using: + return INDEXSTORE_SYMBOL_KIND_USING; + 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::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: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET; + case SymbolSubKind::SwiftAccessorAddressor: + 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: + 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. +indexstore_symbol_property_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; + case SymbolProperty::ProtocolInterface: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE; + break; + } + }); + return static_cast(storeProp); +} + +/// Map a SymbolRoleSet to its indexstore representation. +indexstore_symbol_role_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::Undefinition: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_UNDEFINITION; + 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; + case SymbolRole::NameReference: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE; + break; + } + }); + return static_cast(storeRoles); +} diff --git a/clang/lib/Index/IndexDataStoreUtils.h b/clang/lib/Index/IndexDataStoreUtils.h new file mode 100644 index 0000000000000..3618ae8137822 --- /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/Bitstream/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 &StorePathBuf); +void appendInteriorUnitPath(StringRef UnitName, + SmallVectorImpl &PathBuf); +void appendRecordSubDir(SmallVectorImpl &StorePathBuf); +void appendInteriorRecordPath(StringRef RecordName, + SmallVectorImpl &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 RecordData; +typedef SmallVectorImpl 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/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp index 5bbbb0d32bf45..985098fc61654 100644 --- a/clang/lib/Index/IndexDecl.cpp +++ b/clang/lib/Index/IndexDecl.cpp @@ -42,15 +42,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; } - /// Returns true if the given method has been defined explicitly by the - /// user. - static bool hasUserDefined(const ObjCMethodDecl *D, - const ObjCImplDecl *Container) { - const ObjCMethodDecl *MD = Container->getMethod(D->getSelector(), - D->isInstanceMethod()); - return MD && !MD->isImplicit() && MD->isThisDeclarationADefinition(); - } - void handleTemplateArgumentLoc(const TemplateArgumentLoc &TALoc, const NamedDecl *Parent, const DeclContext *DC) { @@ -78,6 +69,17 @@ class IndexingDeclVisitor : public ConstDeclVisitor { } } + /// Returns true if the given method has been defined explicitly by the + /// user. + static bool hasUserDefined(const ObjCMethodDecl *D, + const ObjCImplDecl *Container) { + const ObjCMethodDecl *MD = Container->getMethod(D->getSelector(), + D->isInstanceMethod()); + return MD && !MD->isImplicit() && MD->isThisDeclarationADefinition() && + !MD->isSynthesizedAccessorStub(); + } + + void handleDeclarator(const DeclaratorDecl *D, const NamedDecl *Parent = nullptr, bool isIBType = false) { @@ -534,13 +536,11 @@ class IndexingDeclVisitor : public ConstDeclVisitor { SymbolRoleSet AccessorMethodRoles = SymbolRoleSet(SymbolRole::Dynamic) | SymbolRoleSet(SymbolRole::Implicit); if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { - if (MD->isPropertyAccessor() && - !hasUserDefined(MD, Container)) + if (MD->isPropertyAccessor() && !hasUserDefined(MD, Container)) IndexCtx.handleDecl(MD, Loc, AccessorMethodRoles, {}, Container); } if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { - if (MD->isPropertyAccessor() && - !hasUserDefined(MD, Container)) + if (MD->isPropertyAccessor() && !hasUserDefined(MD, Container)) IndexCtx.handleDecl(MD, Loc, AccessorMethodRoles, {}, Container); } if (ObjCIvarDecl *IvarD = D->getPropertyIvarDecl()) { diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp new file mode 100644 index 0000000000000..ee22ec8bcf3c3 --- /dev/null +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -0,0 +1,481 @@ +//===--- 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 { + 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()) + || D->isExternC()) + return Hash; + + for (auto param : D->parameters()) { + COMBINE_HASH(Hasher.hash(param->getType())); + } + 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(); + const Decl *D = cast(DC)->getCanonicalDecl(); + if (auto *ND = dyn_cast(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 &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.getDeclOccurrencesSortedByOffset()) { + 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(D) || isa(D)) { + return tryCache(D, D); + } else if (auto *NS = dyn_cast(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(T)) { + return COMBINE_HASH(BT->getKind()); + } + if (const PointerType *PT = dyn_cast(T)) { + COMBINE_HASH('*'); + CT = asCanon(PT->getPointeeType()); + continue; + } + if (const ReferenceType *RT = dyn_cast(T)) { + COMBINE_HASH('&'); + CT = asCanon(RT->getPointeeType()); + continue; + } + if (const BlockPointerType *BT = dyn_cast(T)) { + COMBINE_HASH('B'); + CT = asCanon(BT->getPointeeType()); + continue; + } + if (const ObjCObjectPointerType *OPT = dyn_cast(T)) { + COMBINE_HASH('*'); + CT = asCanon(OPT->getPointeeType()); + continue; + } + if (const TagType *TT = dyn_cast(T)) { + return COMBINE_HASH('$', hash(TT->getDecl()->getCanonicalDecl())); + } + if (const ObjCInterfaceType *OIT = dyn_cast(T)) { + return COMBINE_HASH('$', hash(OIT->getDecl()->getCanonicalDecl())); + } + if (const ObjCObjectType *OIT = dyn_cast(T)) { + for (auto *Prot : OIT->getProtocols()) + COMBINE_HASH(hash(Prot)); + CT = asCanon(OIT->getBaseType()); + continue; + } + if (const TemplateTypeParmType *TTP = dyn_cast(T)) { + return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex()); + } + if (const InjectedClassNameType *InjT = dyn_cast(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 +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(Template)) { + return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex()); + } + + return COMBINE_HASH(Hasher.hash(Template->getCanonicalDecl())); + } + + // 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... + LLVM_FALLTHROUGH; + 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(T)) { + return COMBINE_HASH('P', hash(asCanon(Expansion->getPattern()))); + } + if (const RValueReferenceType *RT = dyn_cast(T)) { + return COMBINE_HASH('%', hash(asCanon(RT->getPointeeType()))); + } + if (const FunctionProtoType *FT = dyn_cast(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(T)) { + return COMBINE_HASH('<', hash(asCanon(CT->getElementType()))); + } + if (const TemplateSpecializationType *Spec + = dyn_cast(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(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())); + break; + 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 class CanQual; + typedef CanQual CanQualType; + +namespace index { + class FileIndexRecord; + +class IndexRecordHasher { + ASTContext &Ctx; + llvm::DenseMap 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 + 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..65592eda3c9e2 --- /dev/null +++ b/clang/lib/Index/IndexRecordReader.cpp @@ -0,0 +1,436 @@ +//===--- 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/Bitstream/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 Buffer; + llvm::BitstreamCursor DeclCursor; + llvm::BitstreamCursor OccurCursor; + ArrayRef DeclOffsets; + const IndexRecordDecl **Decls; + + void setDeclOffsets(ArrayRef Offs) { + DeclOffsets = Offs; + Decls = Allocator.Allocate(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(); + 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 Checker, + llvm::function_ref 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(); + *D = LocalD; + Decls[I] = D; + Receiver(D); + } + if (!Ret.ContinueSearch) + return false; + } + return true; + } + + void readDecl(unsigned Index, IndexRecordDecl &RecD) { + RecordData Record; + StringRef Blob; + if (llvm::Error Err = DeclCursor.JumpToBit(DeclOffsets[Index])) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + } + Expected MaybeCode = DeclCursor.ReadCode(); + if (!MaybeCode) { + // FIXME this drops the error on the floor. + consumeError(MaybeCode.takeError()); + } + unsigned Code = MaybeCode.get(); + Expected 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; + + 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 DeclsFilter, + ArrayRef RelatedDeclsFilter, + IndexRecordOccurrence &RecOccur) { + + auto isDeclIDContained = [](unsigned DeclID, + ArrayRef 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 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 DeclsFilter, + ArrayRef RelatedDeclsFilter, + function_ref Receiver) { + class OccurBitVisitor : public BitstreamVisitor { + IndexRecordReader::Implementation &Reader; + ArrayRef DeclsFilter; + ArrayRef RelatedDeclsFilter; + function_ref Receiver; + + public: + OccurBitVisitor(llvm::BitstreamCursor &Stream, + IndexRecordReader::Implementation &Reader, + ArrayRef DeclsFilter, + ArrayRef RelatedDeclsFilter, + function_ref 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 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 { + 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; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.DeclCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + + case REC_DECLOCCURRENCES_BLOCK_ID: + Reader.OccurCursor = Stream; + if (Reader.OccurCursor.EnterSubBlock(ID)) { + *Error = "malformed block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.OccurCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + 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((const 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::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::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::createWithBuffer(std::unique_ptr Buffer, + std::string &Error) { + + std::unique_ptr Reader; + Reader.reset(new IndexRecordReader()); + auto &Impl = Reader->Impl; + Impl.Buffer = std::move(Buffer); + llvm::BitstreamCursor Stream(*Impl.Buffer); + + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return nullptr; + } + + // Sniff for the signature. + for (unsigned char C : {'I', 'D', 'X', 'R'}) { + if (Expected 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; + } + + 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 Checker, + llvm::function_ref Receiver) { + return Impl.searchDecls(std::move(Checker), std::move(Receiver)); +} + +bool IndexRecordReader::foreachDecl(bool NoCache, + function_ref Receiver) { + return Impl.foreachDecl(NoCache, std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrence( + ArrayRef DeclsFilter, + ArrayRef RelatedDeclsFilter, + function_ref Receiver) { + return Impl.foreachOccurrence(DeclsFilter, RelatedDeclsFilter, + std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrence( + llvm::function_ref Receiver) { + return foreachOccurrence(None, None, std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrenceInLineRange(unsigned lineStart, + unsigned lineCount, + llvm::function_ref 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..95ed25df9b9dd --- /dev/null +++ b/clang/lib/Index/IndexRecordWriter.cpp @@ -0,0 +1,376 @@ +//===--- 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/Bitstream/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, 4> Related; +}; + +struct RecordState { + std::string RecordPath; + SmallString<512> Buffer; + BitstreamWriter Stream; + + DenseMap IndexForDecl; + std::vector Decls; + std::vector 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(); + 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 +static StringRef data(const std::vector &v) { + if (v.empty()) + return StringRef(); + return StringRef(reinterpret_cast(&v[0]), sizeof(T) * v.size()); +} + +template static StringRef data(const SmallVectorImpl &v) { + return StringRef(reinterpret_cast(v.data()), + sizeof(T) * v.size()); +} + +static void writeDecls(BitstreamWriter &Stream, ArrayRef Decls, + ArrayRef Occurrences, + writer::SymbolWriterCallback GetSymbolForDecl) { + SmallVector DeclOffsets; + DeclOffsets.reserve(Decls.size()); + + //===--------------------------------------------------------------------===// + // DECLS_BLOCK_ID + //===--------------------------------------------------------------------===// + + Stream.EnterSubblock(REC_DECLS_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + 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; + bool enableValidation = getenv("CLANG_INDEX_VALIDATION_CHECKS") != nullptr; +#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 + 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 + + 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(); + 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(); + 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(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(); + + 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())) { + 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 Related) { + assert(Record && "called addOccurrence without calling beginRecord"); + auto &State = *static_cast(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/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index 5165567ff75eb..49264cb2a1619 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -516,6 +516,7 @@ StringRef index::getSymbolKindString(SymbolKind K) { case SymbolKind::ConversionFunction: return "conversion-func"; case SymbolKind::Parameter: return "param"; case SymbolKind::Using: return "using"; + case SymbolKind::CommentTag: return "comment-tag"; } llvm_unreachable("invalid symbol kind"); } @@ -529,6 +530,22 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) { case SymbolSubKind::AccessorSetter: return "acc-set"; case SymbolSubKind::UsingTypename: return "using-typename"; case SymbolSubKind::UsingValue: return "using-value"; + 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::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"; + 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"); } diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp new file mode 100644 index 0000000000000..30e474d6d6165 --- /dev/null +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -0,0 +1,531 @@ +//===--- 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/Bitstream/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/Process.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +namespace { + +typedef function_ref DependencyReceiver; +typedef function_ref IncludeReceiver; + +class IndexUnitReaderImpl { + sys::TimePoint<> ModTime; + std::unique_ptr 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 Paths; + StringRef PathsBuffer; + + struct ModuleInfo { + unsigned NameOffset; + unsigned NameSize; + }; + std::vector Modules; + StringRef ModuleNamesBuffer; + + bool init(std::unique_ptr 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 &Path, int PathIndex); + + StringRef getModuleName(int ModuleIndex); +}; + +class IndexUnitBitstreamVisitor : public BitstreamVisitor { + 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; + } + 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; + if (Reader.IncludeCursor.EnterSubBlock(ID)) { + *Error = "malformed unit includes block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.IncludeCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + 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 + BlockVisitorCallback; + +class IndexUnitBlockBitstreamVisitor : public BitstreamVisitor { + 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 Buf, + sys::TimePoint<> ModTime, std::string &Error) { + this->ModTime = ModTime; + this->MemBuf = std::move(Buf); + llvm::BitstreamCursor Stream(*MemBuf); + + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return true; + } + + // Sniff for the signature. + for (unsigned char C : {'I', 'D', 'X', 'U'}) { + if (Expected 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; + } + + 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; + I++; // Reserved field. + I++; // Reserved field. + 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}); + }); + + 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 &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::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::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() { + llvm::sys::Process::SafelyCloseFileDescriptor(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(sys::fs::convertFDToNativeFile(FD), + FilePath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrOrBuf) { + raw_string_ostream(Error) << "Failed opening '" << FilePath << "': " + << ErrOrBuf.getError().message(); + return nullptr; + } + + std::unique_ptr Impl(new IndexUnitReaderImpl()); + bool Err = Impl->init(std::move(*ErrOrBuf), FileStat.getLastModificationTime(), + Error); + if (Err) + return nullptr; + + std::unique_ptr Reader; + Reader.reset(new IndexUnitReader(Impl.release())); + return Reader; +} + +Optional> +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(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..0cebb5187e7d8 --- /dev/null +++ b/clang/lib/Index/IndexUnitWriter.cpp @@ -0,0 +1,635 @@ +//===--- 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/Bitstream/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 Dirs; + std::vector FileBitPaths; + DenseMap FileToIndex; + +public: + PathStorage(StringRef workDir, StringRef sysrootPath) { + WorkDir = workDir; + if (sysrootPath == "/") + sysrootPath = StringRef(); + SysrootPath = sysrootPath; + } + + StringRef getPathsBuffer() const { return PathsBuf.str(); } + + ArrayRef 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 &Str) { + SmallString<256> AbsPath(FilePath); + FileMgr.makeAbsolutePath(AbsPath); + return getUnitNameForAbsoluteOutputFile(AbsPath, Str); +} + +void IndexUnitWriter::getUnitPathForOutputFile(StringRef FilePath, + SmallVectorImpl &Str) { + Str.append(UnitsPath.begin(), UnitsPath.end()); + Str.push_back('/'); + return getUnitNameForOutputFile(FilePath, Str); +} + +Optional IndexUnitWriter::isUnitUpToDateForOutputFile(StringRef FilePath, + Optional 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 &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(); + 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(); + + 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); + 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(); + 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 FileUsedForRecordOrUnit; + FileUsedForRecordOrUnit.resize(Files.size()); + + Stream.EnterSubblock(UNIT_DEPENDENCIES_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + 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) + // 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)); + + 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); + } + Record.push_back(0); // Reserved. + Record.push_back(0); // Reserved. + 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(0); // Reserved. + Record.push_back(0); // Reserved. + 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(); + 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(); + 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(); + 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(); + 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(); + 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 6d6133e89d864..8275859aafeed 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -7,11 +7,17 @@ //===----------------------------------------------------------------------===// #include "clang/Index/IndexingAction.h" +#include "ClangIndexRecordWriter.h" +#include "FileIndexRecord.h" +#include "IndexDataStoreUtils.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/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" @@ -139,7 +145,7 @@ index::createIndexingAction(std::shared_ptr DataConsumer, } static bool topLevelDeclVisitor(void *context, const Decl *D) { - IndexingContext &IndexCtx = *static_cast(context); + IndexingContext &IndexCtx = *static_cast(context); return IndexCtx.indexTopLevelDecl(D); } @@ -209,3 +215,684 @@ void index::indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, } DataConsumer.finish(); } + +//===----------------------------------------------------------------------===// +// Index Data Recording +//===----------------------------------------------------------------------===// + +namespace { + +class IndexDataRecorder : public IndexDataConsumer { + IndexingContext *IndexCtx = nullptr; + const Preprocessor *PP = nullptr; + typedef llvm::DenseMap> + 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 Relations, + 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) + 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 &Includes; + SourceManager &SourceMgr; + +public: + IncludePPCallbacks(IndexingContext &indexCtx, RecordingOptions recordOpts, + std::vector &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 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, + SrcMgr::CharacteristicKind FileType) override { + if (HashLoc.isFileID() && File && File->isValid()) + addInclude(HashLoc, File); + } +}; + +class IndexDependencyProvider { +public: + virtual ~IndexDependencyProvider() {} + + virtual void visitFileDependencies( + const CompilerInstance &CI, + llvm::function_ref visitor) = 0; + virtual void + visitIncludes(llvm::function_ref + visitor) = 0; + virtual void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref + visitor) = 0; +}; + +class SourceFilesIndexDependencyCollector : public DependencyCollector, + public IndexDependencyProvider { + IndexingContext &IndexCtx; + RecordingOptions RecordOpts; + llvm::SetVector Entries; + llvm::BitVector IsSystemByUID; + std::vector 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(std::make_unique( + 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 visitor) + override { + for (auto *FE : getEntries()) { + visitor(FE, isSystemFile(FE)); + } + } + + void + visitIncludes(llvm::function_ref + visitor) override { + for (auto &Include : Includes) { + visitor(Include.Source, Include.Line, Include.Target); + } + } + + void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref + 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 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 (llvm::ErrorOr 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 IndexRecordASTConsumer : public ASTConsumer { + std::shared_ptr PP; + std::shared_ptr IndexCtx; + +public: + IndexRecordASTConsumer(std::shared_ptr PP, + std::shared_ptr 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; + IndexDataRecorder Recorder; + std::shared_ptr IndexCtx; + SourceFilesIndexDependencyCollector DepCollector; + + IndexRecordActionBase(IndexingOptions IndexOpts, RecordingOptions recordOpts) + : RecordOpts(std::move(recordOpts)), + IndexCtx(new IndexingContext(IndexOpts, Recorder)), + DepCollector(*IndexCtx, RecordOpts) {} + + std::unique_ptr + createIndexASTConsumer(CompilerInstance &CI) { + IndexCtx->setSysrootPath(CI.getHeaderSearchOpts().Sysroot); + Recorder.init(IndexCtx.get(), CI); + + Preprocessor &PP = CI.getPreprocessor(); + DepCollector.setSourceManager(&CI.getSourceManager()); + DepCollector.setSysrootPath(IndexCtx->getSysrootPath()); + DepCollector.attachToPreprocessor(PP); + + return std::make_unique(CI.getPreprocessorPtr(), + 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 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 WrappedAction, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts) + : WrapperFrontendAction(std::move(WrappedAction)), + IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} + +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (!OtherConsumer) + return nullptr; + + CreatedASTConsumer = true; + std::vector> Consumers; + Consumers.push_back(std::move(OtherConsumer)); + Consumers.push_back(createIndexASTConsumer(CI)); + return std::make_unique(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 &Scratch) -> writer::ModuleInfo { + assert(mod); + writer::ModuleInfo info; + std::string fullName = + static_cast(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 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 + 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 + 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 +createIndexDataRecordingAction(IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + std::unique_ptr WrappedAction) { + if (WrappedAction) + return std::make_unique( + std::move(WrappedAction), std::move(IndexOpts), std::move(RecordOpts)); + return std::make_unique(std::move(IndexOpts), + std::move(RecordOpts)); +} + +static std::pair +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 index::createIndexDataRecordingAction( + const FrontendOptions &FEOpts, + std::unique_ptr 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 e29856007149e..ee81704a5fc73 100644 --- a/clang/lib/Index/IndexingContext.cpp +++ b/clang/lib/Index/IndexingContext.cpp @@ -119,12 +119,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; @@ -193,6 +188,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 = @@ -355,7 +400,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, const Expr *OrigE, const Decl *OrigD, const DeclContext *ContainerDC) { - if (D->isImplicit() && !isa(D)) + if (D->isImplicit() && !(isa(D) || isa(D))) return true; if (!isa(D) || shouldSkipNamelessDecl(cast(D))) return true; @@ -365,12 +410,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 3136878c080c6..a52bbc017518c 100644 --- a/clang/lib/Index/IndexingContext.h +++ b/clang/lib/Index/IndexingContext.h @@ -11,10 +11,12 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Index/IndexSymbol.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/MacroInfo.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" namespace clang { class ASTContext; @@ -30,7 +32,7 @@ namespace clang { class Stmt; class Expr; class TypeLoc; - class SourceLocation; + class DirectoryEntry; namespace index { class IndexDataConsumer; @@ -39,6 +41,13 @@ class IndexingContext { IndexingOptions IndexOpts; IndexDataConsumer &DataConsumer; ASTContext *Ctx = nullptr; + std::string SysrootPath; + + // Records whether a directory entry is system or not. + llvm::DenseMap 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 LastFileCheck; public: IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer) @@ -48,6 +57,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/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/IndexDataStore/IndexDataStore.cpp b/clang/lib/IndexDataStore/IndexDataStore.cpp new file mode 100644 index 0000000000000..96054ab48fb59 --- /dev/null +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -0,0 +1,229 @@ +//===--- 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 "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "../lib/Index/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; + +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 TheUnitEventHandlerData; + std::unique_ptr DirWatcher; + +public: + explicit IndexDataStoreImpl(StringRef indexStorePath) + : FilePath(indexStorePath) { + TheUnitEventHandlerData = std::make_shared(); + } + + StringRef getFilePath() const { return FilePath; } + bool foreachUnitName(bool sorted, + llvm::function_ref receiver); + void setUnitEventHandler(IndexDataStore::UnitEventHandler Handler); + bool startEventListening(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 receiver) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + std::vector 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) { + std::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(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 Events, bool isInitial) { + SmallVector UnitEvents; + UnitEvents.reserve(Events.size()); + for (const DirectoryWatcher::Event &evt : Events) { + IndexDataStore::UnitEventKind K; + StringRef UnitName = sys::path::filename(evt.Filename); + switch (evt.Kind) { + case DirectoryWatcher::Event::EventKind::Removed: + K = IndexDataStore::UnitEventKind::Removed; break; + case DirectoryWatcher::Event::EventKind::Modified: + K = IndexDataStore::UnitEventKind::Modified; break; + 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}); + } + + if (auto handler = localUnitEventHandlerData->getHandler()) { + IndexDataStore::UnitEventNotification EventNote{isInitial, UnitEvents}; + handler(EventNote); + } + }; + + llvm::Expected> ExpectedDirWatcher = + DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, waitInitialSync); + if (!ExpectedDirWatcher) { + Error = llvm::toString(ExpectedDirWatcher.takeError()); + return true; + } + + DirWatcher = std::move(ExpectedDirWatcher.get()); + 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::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( + new IndexDataStore(new IndexDataStoreImpl(IndexStorePath))); +} + +#define IMPL static_cast(Impl) + +IndexDataStore::~IndexDataStore() { + delete IMPL; +} + +StringRef IndexDataStore::getFilePath() const { + return IMPL->getFilePath(); +} + +bool IndexDataStore::foreachUnitName(bool sorted, + llvm::function_ref 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(bool waitInitialSync, std::string &Error) { + return IMPL->startEventListening(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/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp index f0c5900c8ce45..2efef52bc03eb 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -1286,7 +1286,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/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 902b173979156..847e6111ff6d9 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -1298,6 +1298,35 @@ SourceLocation Lexer::findLocationAfterToken( 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 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/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index fe20a35070362..3056b0c28594d 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -769,8 +769,17 @@ ModuleMap::isHeaderUnavailableInModule(const FileEntry *Header, Module *ModuleMap::findModule(StringRef Name) const { llvm::StringMap::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; } @@ -1713,6 +1722,9 @@ namespace { /// The 'exhaustive' attribute. AT_exhaustive, + // \brief The 'swift_infer_import_as_member' attribute. + AT_swift_infer_import_as_member, + /// The 'no_undeclared_includes' attribute. AT_no_undeclared_includes }; @@ -2873,6 +2885,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { .Case("extern_c", AT_extern_c) .Case("no_undeclared_includes", AT_no_undeclared_includes) .Case("system", AT_system) + .Case("swift_infer_import_as_member", AT_swift_infer_import_as_member) .Default(AT_unknown); switch (Attribute) { case AT_unknown: @@ -2888,6 +2901,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/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 3b7eaee3c914f..6ca0865c063af 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -617,9 +617,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 115256db48095..8a46129e927a2 100644 --- a/clang/lib/Lex/PreprocessingRecord.cpp +++ b/clang/lib/Lex/PreprocessingRecord.cpp @@ -324,23 +324,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; @@ -430,7 +413,6 @@ void PreprocessingRecord::Defined(const Token &MacroNameTok, void PreprocessingRecord::SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) { - assert(Range.isValid()); SkippedRanges.emplace_back(Range.getBegin(), EndifLoc); } @@ -511,6 +493,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/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index aa314da8e5b40..4c10850e1e1cb 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -44,6 +44,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef( 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/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index c41eb74a9cf37..d86c48edbf005 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -23,6 +23,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" @@ -298,6 +299,38 @@ IdentifierLoc *Parser::ParseIdentifierLoc() { return IL; } +void Parser::ParseSwiftNewtypeAttribute( + IdentifierInfo &SwiftNewtype, SourceLocation SwiftNewtypeLoc, + ParsedAttributes &attrs, SourceLocation *endLoc, IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, ParsedAttr::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, @@ -456,6 +489,10 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, Syntax); return; + } else if (AttrKind == ParsedAttr::AT_SwiftNewtype) { + ParseSwiftNewtypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, + ScopeName, ScopeLoc, Syntax); + return; } else if (attributeIsTypeArgAttr(*AttrName)) { ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, Syntax); @@ -981,7 +1018,7 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// /// version-arg: /// 'introduced' '=' version -/// 'deprecated' '=' version +/// 'deprecated' ['=' version] /// 'obsoleted' = version /// 'unavailable' /// opt-replacement: @@ -2870,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 @@ -3473,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: @@ -4925,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: @@ -5122,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: @@ -5361,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()); @@ -7116,3 +7199,69 @@ 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 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, + /*IsReinject=*/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/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index c6ffbfc968d07..42bde56146247 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2818,8 +2818,10 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // initialize it. ThisDecl = VT->getTemplatedDecl(); - if (ThisDecl) + 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/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/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 42d6221a7333a..5e21b7542676b 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -208,6 +208,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) { @@ -822,6 +824,7 @@ static void diagnoseRedundantPropertyNullability(Parser &P, /// property-attribute: /// getter '=' identifier /// setter '=' identifier ':' +/// direct /// readonly /// readwrite /// assign @@ -954,6 +957,8 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable); } else if (II->isStr("class")) { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_class); + } else if (II->isStr("direct")) { + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_direct); } else { Diag(AttrName, diag::err_objc_expected_property_attr) << II; SkipUntil(tok::r_paren, StopAtSemi); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 2645f27e656f7..143440359014d 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -67,6 +67,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) { @@ -414,6 +419,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/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 89c3f6c47b497..51b197c9e4be4 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -28,6 +28,7 @@ add_clang_library(clangSema Sema.cpp SemaAccess.cpp SemaAttr.cpp + SemaAPINotes.cpp SemaCXXScopeSpec.cpp SemaCast.cpp SemaChecking.cpp @@ -74,4 +75,5 @@ add_clang_library(clangSema clangBasic clangEdit clangLex + clangAPINotes ) diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp index 333f4d70986a0..c1bc8f79a309e 100644 --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -385,7 +385,7 @@ void IdentifierResolver::updatingIdentifier(IdentifierInfo &II) { PP.getExternalSource()->updateOutOfDateIdentifier(II); if (II.isFromAST()) - II.setFETokenInfoChangedSinceDeserialization(); + II.setChangedSinceDeserialization(); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index bedea21679503..f77c668a1be95 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -132,7 +132,13 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, : ExternalSource(nullptr), isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), - SourceMgr(PP.getSourceManager()), CollectStats(false), + SourceMgr(PP.getSourceManager()), + + // Don't you dare clang-format this. + // APINotes is a never-ending source of conflicts. Don't make it worse. + APINotes(SourceMgr, LangOpts), + + CollectStats(false), CodeCompleter(CodeCompleter), CurContext(nullptr), OriginalLexicalContext(nullptr), MSStructPragmaOn(false), MSPointerToMemberRepresentationMethod( diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp new file mode 100644 index 0000000000000..f2bfbd736b1b6 --- /dev/null +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -0,0 +1,972 @@ +//===--- 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; + +namespace { + enum IsActive_t : bool { + IsNotActive, + IsActive + }; + enum IsReplacement_t : bool { + IsNotReplacement, + IsReplacement + }; + + struct VersionedInfoMetadata { + /// An empty version refers to unversioned metadata. + VersionTuple Version; + unsigned IsActive: 1; + unsigned IsReplacement: 1; + + VersionedInfoMetadata(VersionTuple version, IsActive_t active, + IsReplacement_t replacement) + : Version(version), IsActive(active == IsActive_t::IsActive), + IsReplacement(replacement == IsReplacement_t::IsReplacement) {} + }; +} // end anonymous namespace + +/// 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, + VersionedInfoMetadata metadata) { + if (!metadata.IsActive) + return; + + QualType type; + + // Nullability for a function/method appertains to the retain type. + if (auto function = dyn_cast(decl)) { + type = function->getReturnType(); + } else if (auto method = dyn_cast(decl)) { + type = method->getReturnType(); + } else if (auto value = dyn_cast(decl)) { + type = value->getType(); + } else if (auto property = dyn_cast(decl)) { + type = property->getType(); + } else { + return; + } + + // Check the nullability specifier on this type. + QualType origType = type; + S.checkImplicitNullabilityTypeSpecifier(type, nullability, + decl->getLocation(), + isa(decl), + /*overrideExisting=*/true); + if (type.getTypePtr() == origType.getTypePtr()) + return; + + if (auto function = dyn_cast(decl)) { + const FunctionType *fnType = function->getType()->castAs(); + if (const FunctionProtoType *proto = dyn_cast(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(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(decl)) { + value->setType(type); + + // Make it a context-sensitive keyword if we can. + if (auto parm = dyn_cast(decl)) { + if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) { + parm->setObjCDeclQualifier( + Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() | + Decl::OBJC_TQ_CSNullability)); + } + } + } else if (auto property = dyn_cast(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"); + } +} + +/// 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(mem), string.size()); +} + +static AttributeCommonInfo getDummyAttrInfo() { + return AttributeCommonInfo(SourceRange(), + AttributeCommonInfo::UnknownAttribute, + AttributeCommonInfo::AS_GNU, + /*Spelling*/0); +} + +namespace { + template + struct AttrKindFor {}; + +#define ATTR(X) \ + template <> struct AttrKindFor { \ + 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. + template + void handleAPINotedAttribute( + Sema &S, Decl *D, bool shouldAddAttribute, + VersionedInfoMetadata metadata, + llvm::function_ref createAttr, + llvm::function_ref getExistingAttr) { + if (metadata.IsActive) { + auto existing = getExistingAttr(D); + 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); + D->addAttr(versioned); + } + + // If we're supposed to add a new attribute, do so. + if (shouldAddAttribute) { + if (auto attr = createAttr()) { + D->addAttr(attr); + } + } + + } else { + if (shouldAddAttribute) { + if (auto attr = createAttr()) { + 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::value, + /*IsReplacedByActive*/metadata.IsReplacement); + D->addAttr(versioned); + } + } + } + + template + void handleAPINotedAttribute( + Sema &S, Decl *D, bool shouldAddAttribute, + VersionedInfoMetadata metadata, + llvm::function_ref createAttr) { + handleAPINotedAttribute(S, D, shouldAddAttribute, metadata, createAttr, + [](const Decl *decl) { + return llvm::find_if(decl->attrs(), [](const Attr *next) { + return isa(next); + }); + }); + } +} + +template +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(S, D, shouldAddAttribute, metadata, [&] { + return new (S.Context) A(S.Context, getDummyAttrInfo()); + }, [](const Decl *D) -> Decl::attr_iterator { + return llvm::find_if(D->attrs(), [](const Attr *next) -> bool { + return isa(next) || + isa(next) || + isa(next) || + isa(next) || + isa(next); + }); + }); +} + +static void handleAPINotedRetainCountConvention( + Sema &S, Decl *D, VersionedInfoMetadata metadata, + Optional convention) { + if (!convention) + return; + switch (convention.getValue()) { + case api_notes::RetainCountConventionKind::None: + if (isa(D)) { + handleAPINotedRetainCountAttribute( + S, D, /*shouldAddAttribute*/true, metadata); + } else { + handleAPINotedRetainCountAttribute(S, D, /*shouldAddAttribute*/false, + metadata); + } + break; + case api_notes::RetainCountConventionKind::CFReturnsRetained: + handleAPINotedRetainCountAttribute( + S, D, /*shouldAddAttribute*/true, metadata); + break; + case api_notes::RetainCountConventionKind::CFReturnsNotRetained: + handleAPINotedRetainCountAttribute( + S, D, /*shouldAddAttribute*/true, metadata); + break; + case api_notes::RetainCountConventionKind::NSReturnsRetained: + handleAPINotedRetainCountAttribute( + S, D, /*shouldAddAttribute*/true, metadata); + break; + case api_notes::RetainCountConventionKind::NSReturnsNotRetained: + handleAPINotedRetainCountAttribute( + S, D, /*shouldAddAttribute*/true, metadata); + break; + } +} + +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonEntityInfo &info, + VersionedInfoMetadata metadata) { + // Availability + if (info.Unavailable) { + handleAPINotedAttribute(S, D, true, metadata, + [&] { + return new (S.Context) UnavailableAttr(S.Context, getDummyAttrInfo(), + CopyString(S.Context, + info.UnavailableMsg)); + }); + } + + if (info.UnavailableInSwift) { + handleAPINotedAttribute(S, D, true, metadata, [&] { + return new (S.Context) AvailabilityAttr(S.Context, getDummyAttrInfo(), + &S.Context.Idents.get("swift"), + VersionTuple(), VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + CopyString(S.Context, + info.UnavailableMsg), + /*Strict=*/false, + /*Replacement=*/StringRef(), + /*Priority=*/Sema::AP_Explicit); + }, + [](const Decl *decl) { + return llvm::find_if(decl->attrs(), [](const Attr *next) -> bool { + auto *AA = dyn_cast(next); + if (!AA) + return false; + const IdentifierInfo *platform = AA->getPlatform(); + if (!platform) + return false; + return platform->isStr("swift"); + }); + }); + } + + // swift_private + if (auto swiftPrivate = info.isSwiftPrivate()) { + handleAPINotedAttribute(S, D, *swiftPrivate, metadata, + [&] { + return new (S.Context) SwiftPrivateAttr(S.Context, getDummyAttrInfo()); + }); + } + + // swift_name + if (!info.SwiftName.empty()) { + handleAPINotedAttribute(S, D, true, metadata, + [&]() -> SwiftNameAttr * { + auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); + + if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(), + &APINoteName)) { + return nullptr; + } + + return new (S.Context) SwiftNameAttr(S.Context, getDummyAttrInfo(), + CopyString(S.Context, + info.SwiftName)); + }); + } +} + +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonTypeInfo &info, + VersionedInfoMetadata metadata) { + // swift_bridge + if (auto swiftBridge = info.getSwiftBridge()) { + handleAPINotedAttribute(S, D, !swiftBridge->empty(), + metadata, [&] { + return new (S.Context) SwiftBridgeAttr(S.Context, getDummyAttrInfo(), + CopyString(S.Context, + *swiftBridge)); + }); + } + + // ns_error_domain + if (auto nsErrorDomain = info.getNSErrorDomain()) { + handleAPINotedAttribute(S, D, !nsErrorDomain->empty(), + metadata, [&] { + return new (S.Context) NSErrorDomainAttr( + S.Context, getDummyAttrInfo(), &S.Context.Idents.get(*nsErrorDomain)); + }); + } + + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// 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, + VersionedInfoMetadata metadata) { + // Type override. + if (metadata.IsActive && !info.getType().empty() && + S.ParseTypeFromStringCallback) { + auto parsedType = S.ParseTypeFromStringCallback(info.getType(), + "", + D->getLocation()); + if (parsedType.isUsable()) { + QualType type = Sema::GetTypeFromParser(parsedType.get()); + auto typeInfo = + S.Context.getTrivialTypeSourceInfo(type, D->getLocation()); + + if (auto var = dyn_cast(D)) { + // Make adjustments to parameter types. + if (isa(var)) { + type = S.adjustParameterTypeForObjCAutoRefCount(type, + D->getLocation(), + typeInfo); + 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(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, metadata); + } + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// Process API notes for a parameter. +static void ProcessAPINotes(Sema &S, ParmVarDecl *D, + const api_notes::ParamInfo &info, + VersionedInfoMetadata metadata) { + // noescape + if (auto noescape = info.isNoEscape()) { + handleAPINotedAttribute(S, D, *noescape, metadata, [&] { + return new (S.Context) NoEscapeAttr(S.Context, getDummyAttrInfo()); + }); + } + + // Retain count convention + handleAPINotedRetainCountConvention(S, D, metadata, + info.getRetainCountConvention()); + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// Process API notes for a global variable. +static void ProcessAPINotes(Sema &S, VarDecl *D, + const api_notes::GlobalVariableInfo &info, + VersionedInfoMetadata metadata) { + // Handle common entity information. + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// Process API notes for an Objective-C property. +static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, + const api_notes::ObjCPropertyInfo &info, + VersionedInfoMetadata metadata) { + // Handle common entity information. + ProcessAPINotes(S, D, static_cast(info), + metadata); + if (auto asAccessors = info.getSwiftImportAsAccessors()) { + handleAPINotedAttribute(S, D, + *asAccessors, + metadata, [&] { + return new (S.Context) SwiftImportPropertyAsAccessorsAttr( + S.Context, getDummyAttrInfo()); + }); + } +} + +namespace { + typedef llvm::PointerUnion FunctionOrMethod; +} + +/// Process API notes for a function or method. +static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, + const api_notes::FunctionInfo &info, + VersionedInfoMetadata metadata) { + // Find the declaration itself. + FunctionDecl *FD = AnyFunc.dyn_cast(); + Decl *D = FD; + ObjCMethodDecl *MD = 0; + if (!D) { + MD = AnyFunc.get(); + D = MD; + } + + // Nullability of return type. + if (info.NullabilityAudited) { + applyNullability(S, D, info.getReturnTypeInfo(), metadata); + } + + // Parameters. + unsigned NumParams; + if (FD) + 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], metadata); + } + + // Nullability. + if (info.NullabilityAudited) + applyNullability(S, Param, info.getParamTypeInfo(I), metadata); + + if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) + anyTypeChanged = true; + } + + // Result type override. + QualType overriddenResultType; + if (metadata.IsActive && !info.ResultType.empty() && + S.ParseTypeFromStringCallback) { + auto parsedType = S.ParseTypeFromStringCallback(info.ResultType, + "", + 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()) { + if (overriddenResultType.isNull()) + overriddenResultType = fnProtoType->getReturnType(); + + SmallVector 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(); + FD->setType( + S.Context.getFunctionNoProtoType(overriddenResultType, + fnNoProtoType->getExtInfo())); + } + } + + // Retain count convention + handleAPINotedRetainCountConvention(S, D, metadata, + info.getRetainCountConvention()); + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// Process API notes for a global function. +static void ProcessAPINotes(Sema &S, FunctionDecl *D, + const api_notes::GlobalFunctionInfo &info, + VersionedInfoMetadata metadata) { + + // Handle common function information. + ProcessAPINotes(S, FunctionOrMethod(D), + static_cast(info), metadata); +} + +/// Process API notes for an enumerator. +static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, + const api_notes::EnumConstantInfo &info, + VersionedInfoMetadata metadata) { + + // Handle common information. + ProcessAPINotes(S, D, + static_cast(info), + metadata); +} + +/// Process API notes for an Objective-C method. +static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, + const api_notes::ObjCMethodInfo &info, + VersionedInfoMetadata metadata) { + // Designated initializers. + if (info.DesignatedInit) { + handleAPINotedAttribute(S, D, true, metadata, + [&] { + if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { + IFace->setHasDesignatedInitializers(); + } + return new (S.Context) ObjCDesignatedInitializerAttr(S.Context, + getDummyAttrInfo()); + }); + } + + // Handle common function information. + ProcessAPINotes(S, FunctionOrMethod(D), + static_cast(info), metadata); +} + +/// Process API notes for a tag. +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(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 new (S.Context) EnumExtensibilityAttr(S.Context, + getDummyAttrInfo(), + kind); + }); + } + + if (auto flagEnum = info.isFlagEnum()) { + handleAPINotedAttribute(S, D, flagEnum.getValue(), metadata, + [&] { + return new (S.Context) FlagEnumAttr(S.Context, getDummyAttrInfo()); + }); + } + + // Handle common type information. + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// Process API notes for a typedef. +static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, + const api_notes::TypedefInfo &info, + VersionedInfoMetadata metadata) { + // swift_wrapper + using SwiftWrapperKind = api_notes::SwiftWrapperKind; + + if (auto swiftWrapper = info.SwiftWrapper) { + handleAPINotedAttribute(S, D, + *swiftWrapper != SwiftWrapperKind::None, metadata, + [&] { + 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; + } + AttributeCommonInfo syntaxInfo{SourceRange(), + AttributeCommonInfo::AT_SwiftNewtype, + AttributeCommonInfo::AS_GNU, + SwiftNewtypeAttr::GNU_swift_wrapper}; + return new (S.Context) SwiftNewtypeAttr(S.Context, syntaxInfo, kind); + }); + } + + // Handle common type information. + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// Process API notes for an Objective-C class or protocol. +static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, + const api_notes::ObjCContextInfo &info, + VersionedInfoMetadata metadata) { + + // Handle common type information. + ProcessAPINotes(S, D, static_cast(info), + metadata); +} + +/// Process API notes for an Objective-C class. +static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, + const api_notes::ObjCContextInfo &info, + VersionedInfoMetadata metadata) { + if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) { + handleAPINotedAttribute(S, D, *asNonGeneric, + metadata, [&] { + return new (S.Context) SwiftImportAsNonGenericAttr(S.Context, + getDummyAttrInfo()); + }); + } + + if (auto objcMembers = info.getSwiftObjCMembers()) { + handleAPINotedAttribute(S, D, *objcMembers, + metadata, [&] { + return new (S.Context) SwiftObjCMembersAttr(S.Context, + getDummyAttrInfo()); + }); + } + + // Handle information common to Objective-C classes and protocols. + ProcessAPINotes(S, static_cast(D), info, + 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 +static void maybeAttachUnversionedSwiftName( + Sema &S, Decl *D, + const api_notes::APINotesReader::VersionedInfo Info) { + if (D->hasAttr()) + 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(SelectedVersion, IsNotActive, + IsReplacement); + handleAPINotedAttribute(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. +template +static void ProcessVersionedAPINotes( + Sema &S, SpecificDecl *D, + const api_notes::APINotesReader::VersionedInfo Info) { + + maybeAttachUnversionedSwiftName(S, D, 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]; + auto Active = (i == Selected) ? IsActive : IsNotActive; + auto Replacement = IsNotReplacement; + if (Active == IsNotActive && Version.empty()) { + Replacement = IsReplacement; + Version = Info[Selected].first; + } + ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active, + Replacement)); + } +} + +/// Process API notes that are associated with this declaration, mapping them +/// to attributes as appropriate. +void Sema::ProcessAPINotes(Decl *D) { + if (!D) + return; + + // Globals. + if (D->getDeclContext()->isFileContext()) { + // Global variables. + if (auto VD = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupGlobalVariable(VD->getName()); + ProcessVersionedAPINotes(*this, VD, Info); + } + + return; + } + + // Global functions. + if (auto FD = dyn_cast(D)) { + if (FD->getDeclName().isIdentifier()) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupGlobalFunction(FD->getName()); + ProcessVersionedAPINotes(*this, FD, Info); + } + } + + return; + } + + // Objective-C classes. + if (auto Class = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupObjCClassInfo(Class->getName()); + ProcessVersionedAPINotes(*this, Class, Info); + } + + return; + } + + // Objective-C protocols. + if (auto Protocol = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName()); + ProcessVersionedAPINotes(*this, Protocol, Info); + } + + return; + } + + // Tags + if (auto Tag = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupTag(Tag->getName()); + ProcessVersionedAPINotes(*this, Tag, Info); + } + + return; + } + + // Typedefs + if (auto Typedef = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupTypedef(Typedef->getName()); + ProcessVersionedAPINotes(*this, Typedef, Info); + } + + return; + } + + return; + } + + // Enumerators. + if (D->getDeclContext()->getRedeclContext()->isFileContext()) { + if (auto EnumConstant = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupEnumConstant(EnumConstant->getName()); + ProcessVersionedAPINotes(*this, EnumConstant, Info); + } + + return; + } + } + + if (auto ObjCContainer = dyn_cast(D->getDeclContext())) { + // Location function that looks up an Objective-C context. + auto GetContext = [&](api_notes::APINotesReader *Reader) + -> Optional { + if (auto Protocol = dyn_cast(ObjCContainer)) { + if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName())) + return *Found; + + return None; + } + + if (auto Impl = dyn_cast(ObjCContainer)) { + if (auto Cat = Impl->getCategoryDecl()) + ObjCContainer = Cat; + else + return None; + } + + if (auto Category = dyn_cast(ObjCContainer)) { + if (Category->getClassInterface()) + ObjCContainer = Category->getClassInterface(); + else + return None; + } + + if (auto Impl = dyn_cast(ObjCContainer)) { + if (Impl->getClassInterface()) + ObjCContainer = Impl->getClassInterface(); + else + return None; + } + + if (auto Class = dyn_cast(ObjCContainer)) { + if (auto Found = Reader->lookupObjCClassID(Class->getName())) + return *Found; + + return None; + + } + + return None; + }; + + // Objective-C methods. + if (auto Method = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetContext(Reader)) { + // Map the selector. + Selector Sel = Method->getSelector(); + SmallVector 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; + + auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, + Method->isInstanceMethod()); + ProcessVersionedAPINotes(*this, Method, Info); + } + } + } + + // Objective-C properties. + if (auto Property = dyn_cast(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetContext(Reader)) { + bool isInstanceProperty = + (Property->getPropertyAttributesAsWritten() & + ObjCPropertyDecl::OBJC_PR_class) == 0; + auto Info = Reader->lookupObjCProperty(*Context, Property->getName(), + isInstanceProperty); + ProcessVersionedAPINotes(*this, Property, Info); + } + } + + return; + } + + return; + } +} diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 741cf63876068..78deab1c5421e 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(SubExpr)) SubExpr = BindExpr->getSubExpr(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8322a9bf14775..cc4e1062cba10 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 +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(); + if (!baseDecl || baseDecl->hasAttr()) + 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(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(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(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(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: @@ -9459,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()->getDecl()->fields()) visit(FD->getType(), FD->getLocation()); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index e4c4264d9dc2e..261539edfbfd1 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -648,49 +648,6 @@ ResultBuilder::ShadowMapEntry::end() const { return iterator(DeclOrVector.get()->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 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 auto *Namespace = dyn_cast(Parent)) { - if (!Namespace->getIdentifier()) - continue; - - Result = NestedNameSpecifier::Create(Context, Result, Namespace); - } else if (const auto *TD = dyn_cast(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]). @@ -801,8 +758,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; } @@ -3973,7 +3930,7 @@ static void MaybeAddOverrideCalls(Sema &S, DeclContext *InContext, // If we need a nested-name-specifier, add one now. if (!InContext) { - NestedNameSpecifier *NNS = getRequiredQualification( + NestedNameSpecifier *NNS = NestedNameSpecifier::getRequiredQualification( S.Context, CurContext, Overridden->getDeclContext()); if (NNS) { std::string Str; @@ -4244,7 +4201,8 @@ static void AddEnumerators(ResultBuilder &Results, ASTContext &Context, // 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); } Results.EnterNewScope(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index aba7049b0a516..5aa77dcfb843f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -867,6 +867,9 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); LookupParsedName(Result, S, &SS, !CurMethod); + if (SS.isInvalid()) + return NameClassification::Error(); + // For unqualified lookup in a class template in MSVC mode, look into // dependent base classes where the primary class template is known. if (Result.empty() && SS.isEmpty() && getLangOpts().MSVCCompat) { @@ -879,7 +882,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, // synthesized instance variables), if we're in an Objective-C method. // FIXME: This lookup really, really needs to be folded in to the normal // unqualified lookup mechanism. - if (!SS.isSet() && CurMethod && !isResultTypeOrTemplate(Result, NextToken)) { + if (SS.isEmpty() && CurMethod && !isResultTypeOrTemplate(Result, NextToken)) { DeclResult Ivar = LookupIvarInObjCMethod(Result, S, Name); if (Ivar.isInvalid()) return NameClassification::Error(); @@ -899,7 +902,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, case LookupResult::NotFound: // If an unqualified-id is followed by a '(', then we have a function // call. - if (!SS.isSet() && NextToken.is(tok::l_paren)) { + if (SS.isEmpty() && NextToken.is(tok::l_paren)) { // In C++, this is an ADL-only call. // FIXME: Reference? if (getLangOpts().CPlusPlus) @@ -921,7 +924,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, return NameClassification::NonType(D); } - if (getLangOpts().CPlusPlus2a && !SS.isSet() && NextToken.is(tok::less)) { + if (getLangOpts().CPlusPlus2a && SS.isEmpty() && NextToken.is(tok::less)) { // In C++20 onwards, this could be an ADL-only call to a function // template, and we're required to assume that this is a template name. // @@ -1063,7 +1066,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, hasAnyAcceptableTemplateNames( Result, /*AllowFunctionTemplates=*/true, /*AllowDependent=*/false, - /*AllowNonTemplateFunctions*/ !SS.isSet() && + /*AllowNonTemplateFunctions*/ SS.isEmpty() && getLangOpts().CPlusPlus2a))) { // C++ [temp.names]p3: // After name lookup (3.4) finds that a name is a template-name or that @@ -1092,7 +1095,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, IsFunctionTemplate = isa(TD); IsVarTemplate = isa(TD); - if (SS.isSet() && !SS.isInvalid()) + if (SS.isNotEmpty()) Template = Context.getQualifiedTemplateName(SS.getScopeRep(), /*TemplateKeyword=*/false, TD); @@ -2563,6 +2566,9 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.mergeMinSizeAttr(D, *MA); else if (const auto *OA = dyn_cast(Attr)) NewAttr = S.mergeOptimizeNoneAttr(D, *OA); + else if (const auto *SNA = dyn_cast(Attr)) + NewAttr = S.mergeSwiftNameAttr(D, *SNA, SNA->getName(), + AMK == Sema::AMK_Override); else if (const auto *InternalLinkageA = dyn_cast(Attr)) NewAttr = S.mergeInternalLinkageAttr(D, *InternalLinkageA); else if (const auto *CommonA = dyn_cast(Attr)) @@ -2575,6 +2581,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, (AMK == Sema::AMK_Override || AMK == Sema::AMK_ProtocolImplementation)) NewAttr = nullptr; + else if (isa(Attr) && AMK == Sema::AMK_Override) + NewAttr = nullptr; else if (const auto *UA = dyn_cast(Attr)) NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid()); else if (const auto *SLHA = dyn_cast(Attr)) @@ -11528,6 +11536,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) {} @@ -13164,10 +13178,9 @@ 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 NameLoc, + TypeSourceInfo *TSInfo) { // In ARC, infer a lifetime qualifier for appropriate parameter types. if (getLangOpts().ObjCAutoRefCount && T.getObjCLifetime() == Qualifiers::OCL_None && @@ -13195,6 +13208,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, TSInfo); + ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name, Context.getAdjustedParameterType(T), TSInfo, SC, nullptr); @@ -13231,6 +13254,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 @@ -14104,7 +14133,9 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, // Always attach attributes to the underlying decl. if (TemplateDecl *TD = dyn_cast(D)) D = TD->getTemplatedDecl(); + ProcessDeclAttributeList(S, D, Attrs); + ProcessAPINotes(D); if (CXXMethodDecl *Method = dyn_cast_or_null(D)) if (Method->isStatic()) @@ -16662,8 +16693,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()) @@ -16869,6 +16904,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, CDecl->setIvarRBraceLoc(RBrac); } } + ProcessAPINotes(Record); } /// Determine whether the given integral value is representable within @@ -17179,6 +17215,8 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, ProcessDeclAttributeList(S, New, Attrs); AddPragmaAttributes(S, New); + ProcessAPINotes(New); + // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); PushOnScopeChains(New, S); @@ -17374,6 +17412,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange, QualType EnumType = Context.getTypeDeclType(Enum); ProcessDeclAttributeList(S, Enum, Attrs); + 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 7f68d20149166..3c9459aca3fe4 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2600,6 +2600,29 @@ static void handleVisibilityAttr(Sema &S, Decl *D, const ParsedAttr &AL, D->addAttr(newAttr); } +static void handleObjCDirectAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // objc_direct cannot be set on methods declared in the context of a protocol + if (isa(D->getDeclContext())) { + S.Diag(AL.getLoc(), diag::err_objc_direct_on_protocol) << false; + return; + } + + if (S.getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + handleSimpleAttribute(S, D, AL); + } else { + S.Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL; + } +} + +static void handleObjCDirectMembersAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + if (S.getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + handleSimpleAttribute(S, D, AL); + } else { + S.Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL; + } +} + static void handleObjCMethodFamilyAttr(Sema &S, Decl *D, const ParsedAttr &AL) { const auto *M = cast(D); if (!AL.isArgIdent(0)) { @@ -4212,6 +4235,25 @@ OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, return ::new (Context) OptimizeNoneAttr(Context, CI); } +SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, const AttributeCommonInfo &CI, + StringRef Name, bool Override) { + if (SwiftNameAttr *Inline = D->getAttr()) { + if (Override) { + // FIXME: Warn about an incompatible override. + return nullptr; + } + + if (Inline->getName() != Name && !Inline->isImplicit()) { + Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; + Diag(CI.getLoc(), diag::note_conflicting_attribute); + } + + D->dropAttr(); + } + + return ::new (Context) SwiftNameAttr(Context, CI, Name); +} + SpeculativeLoadHardeningAttr *Sema::mergeSpeculativeLoadHardeningAttr( Decl *D, const SpeculativeLoadHardeningAttr &AL) { if (checkAttrMutualExclusion(*this, D, AL)) @@ -5188,6 +5230,39 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) ObjCRequiresSuperAttr(S.Context, Attrs)); } +static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &Attr) { + if (!isa(D)) { + S.Diag(D->getBeginLoc(), 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)->getBeginLoc(); + + 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()) { + S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl) + << identLoc->Ident; + return; + } + + D->addAttr(::new (S.Context) + NSErrorDomainAttr(S.Context, Attr, identLoc->Ident)); +} + static void handleObjCBridgeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { IdentifierLoc *Parm = AL.isArgIdent(0) ? AL.getArgAsIdent(0) : nullptr; @@ -5349,6 +5424,414 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) ObjCPreciseLifetimeAttr(S.Context, AL)); } +static Optional +validateSwiftFunctionName(StringRef Name, + unsigned &SwiftParamCount, + bool &IsSingleParamInit) { + SwiftParamCount = 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 diag::warn_attr_swift_name_function; + + 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 diag::warn_attr_swift_name_context_name_invalid_identifier; + } else { + IsMember = true; + } + + if (!isValidIdentifier(BaseName) || BaseName == "_") + 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::warn_attr_swift_name_subscript_not_accessor; + + if (Parameters.empty()) + 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::warn_attr_swift_name_subscript_no_parameter; + if (isSetter) + return diag::warn_attr_swift_name_setter_parameters; + + return None; + } + + if (Parameters.back() != ':') + return diag::warn_attr_swift_name_function; + + Optional SelfLocation; + Optional NewValueLocation; + unsigned NewValueCount = 0; + StringRef NextParam; + do { + std::tie(NextParam, Parameters) = Parameters.split(':'); + + if (!isValidIdentifier(NextParam)) + 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::warn_attr_swift_name_multiple_selfs; + + // The "self" location is the current parameter. + SelfLocation = SwiftParamCount; + } + + // "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 = SwiftParamCount; + } + ++SwiftParamCount; + } while (!Parameters.empty()); + + // Only instance subscripts are currently supported. + if (IsSubscript && !SelfLocation) + return diag::warn_attr_swift_name_static_subscript; + + IsSingleParamInit = + (SwiftParamCount == 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; + unsigned ParamDiag; + + if (isSetter) { + NumExpectedParams = 1; + ParamDiag = diag::warn_attr_swift_name_setter_parameters; + } else { + NumExpectedParams = 0; + ParamDiag = diag::warn_attr_swift_name_getter_parameters; + } + + // Instance methods have one parameter for "self". + if (SelfLocation) ++NumExpectedParams; + + // Subscripts may have additional parameters beyond the expected params for + // the index. + if (IsSubscript) { + 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::warn_attr_swift_name_subscript_setter_no_newValue; + // There can only be one. + if (NewValueCount > 1) + return diag::warn_attr_swift_name_subscript_setter_multiple_newValues; + } else { + // Subscript getters should have no 'newValue:' parameter. + if (NewValueLocation) + return diag::warn_attr_swift_name_subscript_getter_newValue; + } + } else { + // Property accessors must have exactly the number of expected params. + if (SwiftParamCount != NumExpectedParams) + return ParamDiag; + } + } + + return None; +} + +/// 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. init(foo:bar:baz:) or controllerForName(_:), +/// 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 +/// context.identifier 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, + const IdentifierInfo *AttrName) { + if (isa(D) || isa(D)) { + ArrayRef Params; + unsigned ParamCount; + + if (const auto *Method = dyn_cast(D)) { + ParamCount = Method->getSelector().getNumArgs(); + Params = Method->parameters().slice(0, ParamCount); + } else { + const auto *Function = cast(D); + ParamCount = Function->getNumParams(); + Params = Function->parameters(); + + if (!Function->hasWrittenPrototype()) { + Diag(ArgLoc, diag::warn_attr_swift_name_function_no_prototype) + << AttrName; + return false; + } + } + + unsigned SwiftParamCount; + bool IsSingleParamInit; + if (auto diagID = validateSwiftFunctionName(Name, SwiftParamCount, + IsSingleParamInit)) { + Diag(ArgLoc, *diagID) << AttrName; + return false; + } + + 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) { + Diag(ArgLoc, diag::warn_attr_swift_name_num_params) + << (SwiftParamCount > ParamCount) << AttrName + << ParamCount << SwiftParamCount; + return false; + } + + } else if (isa(D) || isa(D) || + isa(D) || isa(D) || + isa(D) || isa(D) || isa(D) || + isa(D) || isa(D)) { + StringRef ContextName, BaseName; + std::tie(ContextName, BaseName) = Name.split('.'); + if (BaseName.empty()) { + BaseName = ContextName; + ContextName = StringRef(); + } else if (!isValidIdentifier(ContextName)) { + Diag(ArgLoc, diag::warn_attr_swift_name_context_name_invalid_identifier) + << AttrName; + return false; + } + + if (!isValidIdentifier(BaseName)) { + Diag(ArgLoc, diag::warn_attr_swift_name_basename_invalid_identifier) + << AttrName; + return false; + } + + } else { + Diag(ArgLoc, diag::warn_attr_swift_name_decl_kind) << AttrName; + return false; + } + return true; +} + +static void handleSwiftName(Sema &S, Decl *D, const ParsedAttr &Attr) { + StringRef Name; + SourceLocation ArgLoc; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc)) + return; + + if (!S.DiagnoseSwiftName(D, Name, ArgLoc, Attr.getAttrName())) + return; + + D->addAttr(::new (S.Context) SwiftNameAttr(S.Context, Attr, Name)); +} + +static bool isErrorParameter(Sema &S, QualType paramType) { + if (auto ptr = paramType->getAs()) { + auto outerPointee = ptr->getPointeeType(); + + // NSError**. + if (auto objcPtr = outerPointee->getAs()) { + if (auto iface = objcPtr->getInterfaceDecl()) + if (iface->getIdentifier() == S.getNSErrorIdent()) + return true; + } + + // CFErrorRef*. + if (auto cPtr = outerPointee->getAs()) { + auto innerPointee = cPtr->getPointeeType(); + if (auto recordType = innerPointee->getAs()) { + if (S.isCFError(recordType->getDecl())) + return true; + } + } + } + + return false; +} + +static void handleSwiftError(Sema &S, Decl *D, const ParsedAttr &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.getAttrName() << 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.getAttrName() << isa(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.getAttrName() << conventionStr + << isa(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.getAttrName() << conventionStr + << isa(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(S.Context, attr, convention)); +} + +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; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + return; + + // Don't duplicate annotations that are already set. + if (D->hasAttr()) { + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getAttrName(); + return; + } + + D->addAttr(::new (S.Context) SwiftBridgeAttr(S.Context, Attr, Str)); +} + +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) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) + << Attr.getAttrName() << 1; + Attr.setInvalid(); + return; + } + if (!Attr.isArgIdent(0)) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) + << Attr.getAttrName() << 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.getAttrName() << II; + Attr.setInvalid(); + return; + } + + if (!isa(D)) { + S.Diag(Attr.getLoc(), diag::warn_swift_newtype_attribute_non_typedef); + return; + } + + D->addAttr(::new (S.Context) SwiftNewtypeAttr(S.Context, Attr, Kind)); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -6817,6 +7300,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_ObjCBoxable: handleObjCBoxable(S, D, AL); break; + case ParsedAttr::AT_NSErrorDomain: + handleNSErrorDomain(S, D, AL); + break; case ParsedAttr::AT_CFAuditedTransfer: handleSimpleAttributeWithExclusions(S, D, AL); @@ -6909,6 +7395,13 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_ObjCRootClass: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ObjCDirect: + handleObjCDirectAttr(S, D, AL); + break; + case ParsedAttr::AT_ObjCDirectMembers: + handleObjCDirectMembersAttr(S, D, AL); + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCNonLazyClass: handleSimpleAttribute(S, D, AL); break; @@ -6918,6 +7411,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_ObjCClassStub: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ObjCCompleteDefinition: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCExplicitProtocolImpl: handleObjCSuppresProtocolAttr(S, D, AL); break; @@ -7210,6 +7706,28 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_RenderScriptKernel: handleSimpleAttribute(S, D, AL); break; + // Swift attributes. + case ParsedAttr::AT_SwiftPrivate: + handleSimpleAttribute(S, D, AL); + break; + case ParsedAttr::AT_SwiftName: + handleSwiftName(S, D, AL); + break; + case ParsedAttr::AT_SwiftError: + handleSwiftError(S, D, AL); + break; + case ParsedAttr::AT_SwiftBridge: + handleSwiftBridgeAttr(S, D, AL); + break; + case ParsedAttr::AT_SwiftBridgedTypedef: + handleSimpleAttribute(S, D, AL); + break; + case ParsedAttr::AT_SwiftObjCMembers: + handleSimpleAttribute(S, D, AL); + break; + case ParsedAttr::AT_SwiftNewtype: + handleSwiftNewtypeAttr(S, D, AL); + break; // XRay attributes. case ParsedAttr::AT_XRayInstrument: handleSimpleAttribute(S, D, AL); @@ -7497,6 +8015,9 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { // Apply additional attributes specified by '#pragma clang attribute'. AddPragmaAttributes(S, D); + + // 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 e3f57e3472688..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(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()) if (!RT->isDependentType() && !cast(RT->getDecl())->canPassInRegisters()) { @@ -9382,6 +9420,7 @@ Decl *Sema::ActOnStartNamespaceDef( ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList); AddPragmaAttributes(DeclRegionScope, Namespc); + ProcessAPINotes(Namespc); // FIXME: Should we be merging attributes? if (const VisibilityAttr *Attr = Namespc->getAttr()) @@ -9906,6 +9945,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, if (UDir) ProcessDeclAttributeList(S, UDir, AttrList); + ProcessAPINotes(UDir); return UDir; } @@ -11012,6 +11052,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS, ProcessDeclAttributeList(S, NewTD, AttrList); AddPragmaAttributes(S, NewTD); + ProcessAPINotes(NewTD); CheckTypedefForVariablyModifiedType(S, NewTD); Invalid |= NewTD->isInvalidDecl(); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index db594bbd21ddc..c336ec7071ea1 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -19,6 +19,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" @@ -1055,6 +1056,9 @@ Decl *Sema::ActOnStartClassInterface( ObjCInterfaceDecl *IDecl = ObjCInterfaceDecl::Create(Context, CurContext, AtInterfaceLoc, ClassName, typeParamList, PrevIDecl, ClassLoc); + ProcessDeclAttributeList(TUScope, IDecl, AttrList); + ProcessAPINotes(IDecl); + if (PrevIDecl) { // Class already seen. Was it a definition? if (ObjCInterfaceDecl *Def = PrevIDecl->getDefinition()) { @@ -1065,7 +1069,6 @@ Decl *Sema::ActOnStartClassInterface( } } - ProcessDeclAttributeList(TUScope, IDecl, AttrList); AddPragmaAttributes(TUScope, IDecl); PushOnScopeChains(IDecl, TUScope); @@ -1169,7 +1172,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); @@ -1254,6 +1258,7 @@ Decl *Sema::ActOnStartProtocolInterface( ProcessDeclAttributeList(TUScope, PDecl, AttrList); AddPragmaAttributes(TUScope, PDecl); + ProcessAPINotes(PDecl); // Merge attributes from previous declarations. if (PrevDecl) @@ -2202,13 +2207,16 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, Diag(IVI->getLocation(), diag::err_inconsistent_ivar_count); } +static bool shouldWarnUndefinedMethod(const ObjCMethodDecl *M) { + // No point warning no definition of method which is 'unavailable'. + return M->getAvailability() != AR_Unavailable; +} + static void WarnUndefinedMethod(Sema &S, SourceLocation ImpLoc, - ObjCMethodDecl *method, - bool &IncompleteImpl, + ObjCMethodDecl *method, bool &IncompleteImpl, unsigned DiagID, NamedDecl *NeededFor = nullptr) { - // No point warning no definition of method which is 'unavailable'. - if (method->getAvailability() == AR_Unavailable) + if (!shouldWarnUndefinedMethod(method)) return; // FIXME: For now ignore 'IncompleteImpl'. @@ -2669,14 +2677,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 *MissingRequirements) { ObjCCategoryDecl *C = dyn_cast(CDecl); ObjCInterfaceDecl *IDecl = C ? C->getClassInterface() : dyn_cast(CDecl); @@ -2736,6 +2742,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()) { @@ -2765,8 +2772,13 @@ 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) { + if (!HasMissingRequirements) + HasMissingRequirements = shouldWarnUndefinedMethod(method); + } else { + WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, + PDecl); + } } } } @@ -2788,14 +2800,23 @@ 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) { + if (!HasMissingRequirements) + HasMissingRequirements = shouldWarnUndefinedMethod(method); + } 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 @@ -2828,6 +2849,9 @@ void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap, "Expected to find the method through lookup as well"); // ImpMethodDecl may be null as in a @dynamic property. if (ImpMethodDecl) { + // Skip property accessor function stubs. + if (ImpMethodDecl->isSynthesizedAccessorStub()) + continue; if (!WarnCategoryMethodImpl) WarnConflictingTypedMethods(ImpMethodDecl, I, isa(CDecl)); @@ -2854,6 +2878,9 @@ void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap, "Expected to find the method through lookup as well"); // ImpMethodDecl may be null as in a @dynamic property. if (ImpMethodDecl) { + // Skip property accessor function stubs. + if (ImpMethodDecl->isSynthesizedAccessorStub()) + continue; if (!WarnCategoryMethodImpl) WarnConflictingTypedMethods(ImpMethodDecl, I, isa(CDecl)); @@ -3007,23 +3034,49 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, LazyProtocolNameSet ExplicitImplProtocols; + bool UseEditorDiagnostics = !getDiagnostics() + .getDiagnosticOptions() + .DiagnosticSerializationFile.empty() || + getLangOpts().AllowEditorPlaceholders; + llvm::SmallPtrSet MissingRequirements; if (ObjCInterfaceDecl *I = dyn_cast (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(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(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 @@ -3233,6 +3286,9 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, if (left->isHidden() || right->isHidden()) return false; + if (left->isDirectMethod() != right->isDirectMethod()) + return false; + if (getLangOpts().ObjCAutoRefCount && (left->hasAttr() != right->hasAttr() || @@ -3424,6 +3480,9 @@ static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen, if (!chosen->isInstanceMethod()) return false; + if (chosen->isDirectMethod() != other->isDirectMethod()) + return false; + Selector sel = chosen->getSelector(); if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length") return false; @@ -3903,6 +3962,25 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, || isa(ClassDecl); bool checkIdenticalMethods = isa(ClassDecl); + // Make synthesized accessor stub functions visible. + // ActOnPropertyImplDecl() creates them as not visible in case + // they are overridden by an explicit method that is encountered + // later. + if (auto *OID = dyn_cast(CurContext)) { + for (auto PropImpl : OID->property_impls()) { + if (auto *Getter = PropImpl->getGetterMethodDecl()) + if (Getter->isSynthesizedAccessorStub()) { + OID->makeDeclVisibleInContext(Getter); + OID->addDecl(Getter); + } + if (auto *Setter = PropImpl->getSetterMethodDecl()) + if (Setter->isSynthesizedAccessorStub()) { + OID->makeDeclVisibleInContext(Setter); + OID->addDecl(Setter); + } + } + } + // FIXME: Remove these and use the ObjCContainerDecl/DeclContext. llvm::DenseMap InsMap; llvm::DenseMap ClsMap; @@ -4001,8 +4079,8 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, continue; for (const auto *Ext : IDecl->visible_extensions()) { - if (ObjCMethodDecl *GetterMethod - = Ext->getInstanceMethod(Property->getGetterName())) + if (ObjCMethodDecl *GetterMethod = + Ext->getInstanceMethod(Property->getGetterName())) GetterMethod->setPropertyAccessor(true); if (!Property->isReadOnly()) if (ObjCMethodDecl *SetterMethod @@ -4024,7 +4102,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef 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()) { SourceLocation DeclLoc(IDecl->getLocation()); SourceLocation SuperClassLoc(getLocForEndOfToken(DeclLoc)); Diag(DeclLoc, diag::warn_objc_root_class_missing) @@ -4314,6 +4392,18 @@ class OverrideSearch { }; } // end anonymous namespace +void Sema::CheckObjCMethodDirectOverrides(ObjCMethodDecl *method, + ObjCMethodDecl *overridden) { + if (const auto *attr = overridden->getAttr()) { + Diag(method->getLocation(), diag::err_objc_override_direct_method); + Diag(attr->getLocation(), diag::note_previous_declaration); + } else if (const auto *attr = method->getAttr()) { + Diag(attr->getLocation(), diag::err_objc_direct_on_override) + << isa(overridden->getDeclContext()); + Diag(overridden->getLocation(), diag::note_previous_declaration); + } +} + void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, ObjCInterfaceDecl *CurrentClass, ResultTypeCompatibilityKind RTC) { @@ -4332,8 +4422,8 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, if (isa(overridden->getDeclContext()) || CurrentClass != overridden->getClassInterface() || overridden->isOverriding()) { + CheckObjCMethodDirectOverrides(ObjCMethod, overridden); hasOverriddenMethodsInBaseOrProtocol = true; - } else if (isa(ObjCMethod->getDeclContext())) { // OverrideSearch will return as "overridden" the same method in the // interface. For hasOverriddenMethodsInBaseOrProtocol, we need to @@ -4357,6 +4447,7 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, for (ObjCMethodDecl *SuperOverridden : overrides) { if (isa(SuperOverridden->getDeclContext()) || CurrentClass != SuperOverridden->getClassInterface()) { + CheckObjCMethodDirectOverrides(ObjCMethod, SuperOverridden); hasOverriddenMethodsInBaseOrProtocol = true; overridden->setOverriding(true); break; @@ -4464,6 +4555,12 @@ static void mergeInterfaceMethodToImpl(Sema &S, method->getLocation())); } + if (!method->isDirectMethod()) + if (const auto *attr = prevMethod->getAttr()) { + method->addAttr( + ObjCDirectAttr::CreateImplicit(S.Context, attr->getLocation())); + } + // Merge nullability of the result type. QualType newReturnType = mergeTypeNullabilityForRedecl( @@ -4551,6 +4648,7 @@ Decl *Sema::ActOnMethodDeclaration( Diag(MethodLoc, diag::err_missing_method_context); return nullptr; } + Decl *ClassDecl = cast(CurContext); QualType resultDeclType; @@ -4574,7 +4672,7 @@ Decl *Sema::ActOnMethodDeclaration( ObjCMethodDecl *ObjCMethod = ObjCMethodDecl::Create( Context, MethodLoc, EndLoc, Sel, resultDeclType, ReturnTInfo, CurContext, MethodType == tok::minus, isVariadic, - /*isPropertyAccessor=*/false, + /*isPropertyAccessor=*/false, /*isSynthesizedAccessorStub=*/false, /*isImplicitlyDeclared=*/false, /*isDefined=*/false, MethodDeclKind == tok::objc_optional ? ObjCMethodDecl::Optional : ObjCMethodDecl::Required, @@ -4624,6 +4722,7 @@ Decl *Sema::ActOnMethodDeclaration( // Apply the attributes to the parameter. ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs); AddPragmaAttributes(TUScope, Param); + ProcessAPINotes(Param); if (Param->hasAttr()) { Diag(Param->getLocation(), diag::err_block_on_nonlocal); @@ -4654,6 +4753,7 @@ Decl *Sema::ActOnMethodDeclaration( ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList); AddPragmaAttributes(TUScope, ObjCMethod); + ProcessAPINotes(ObjCMethod); // Add the method now. const ObjCMethodDecl *PrevMethod = nullptr; @@ -4666,12 +4766,39 @@ Decl *Sema::ActOnMethodDeclaration( ImpDecl->addClassMethod(ObjCMethod); } + // If this method overrides a previous @synthesize declaration, + // register it with the property. Linear search through all + // properties here, because the autosynthesized stub hasn't been + // made visible yet, so it can be overriden by a later + // user-specified implementation. + for (ObjCPropertyImplDecl *PropertyImpl : ImpDecl->property_impls()) { + if (auto *Setter = PropertyImpl->getSetterMethodDecl()) + if (Setter->getSelector() == Sel && + Setter->isInstanceMethod() == ObjCMethod->isInstanceMethod()) { + assert(Setter->isSynthesizedAccessorStub() && "autosynth stub expected"); + PropertyImpl->setSetterMethodDecl(ObjCMethod); + } + if (auto *Getter = PropertyImpl->getGetterMethodDecl()) + if (Getter->getSelector() == Sel && + Getter->isInstanceMethod() == ObjCMethod->isInstanceMethod()) { + assert(Getter->isSynthesizedAccessorStub() && "autosynth stub expected"); + PropertyImpl->setGetterMethodDecl(ObjCMethod); + break; + } + } + // Merge information from the @interface declaration into the // @implementation. if (ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface()) { if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(), ObjCMethod->isInstanceMethod())) { mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD); + if (const auto *attr = ObjCMethod->getAttr()) { + if (!IMD->isDirectMethod()) { + Diag(attr->getLocation(), diag::err_objc_direct_missing_on_decl); + Diag(IMD->getLocation(), diag::note_previous_declaration); + } + } // Warn about defining -dealloc in a category. if (isa(ImpDecl) && IMD->isOverriding() && @@ -4679,6 +4806,9 @@ Decl *Sema::ActOnMethodDeclaration( Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category) << ObjCMethod->getDeclName(); } + } else if (ImpDecl->hasAttr()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); } // Warn if a method declared in a protocol to which a category or @@ -4698,6 +4828,11 @@ Decl *Sema::ActOnMethodDeclaration( } } } else { + if (!ObjCMethod->isDirectMethod() && + ClassDecl->hasAttr()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); + } cast(ClassDecl)->addDecl(ObjCMethod); } @@ -4783,6 +4918,9 @@ Decl *Sema::ActOnMethodDeclaration( } } + // Insert the invisible arguments, self and _cmd! + ObjCMethod->createImplicitParams(Context, ObjCMethod->getClassInterface()); + ActOnDocumentableDecl(ObjCMethod); return ObjCMethod; @@ -5063,6 +5201,9 @@ void Sema::DiagnoseUnusedBackingIvarInAccessor(Scope *S, if (!IV) continue; + if (CurMethod->isSynthesizedAccessorStub()) + continue; + UnusedBackingIvarChecker Checker(*this, CurMethod, IV); Checker.TraverseStmt(CurMethod->getBody()); if (Checker.AccessedIvar) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index e41cd5b6653a2..8bfb89d9ac63e 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)) @@ -6975,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 @@ -7937,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; @@ -8646,6 +8673,16 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (RHS.isInvalid()) return Incompatible; } + + Expr *PRE = RHS.get()->IgnoreParenCasts(); + if (Diagnose && isa(PRE)) { + ObjCProtocolDecl *PDecl = cast(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); @@ -14697,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/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index e18621e42a6b1..3f968ac04ed60 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -288,6 +288,7 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, S.NSNumberPointer, ReturnTInfo, S.NSNumberDecl, /*isInstance=*/false, /*isVariadic=*/false, /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, /*isImplicitlyDeclared=*/true, /*isDefined=*/false, ObjCMethodDecl::Required, /*HasRelatedResultType=*/false); @@ -563,6 +564,7 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { NSStringPointer, ReturnTInfo, NSStringDecl, /*isInstance=*/false, /*isVariadic=*/false, /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, /*isImplicitlyDeclared=*/true, /*isDefined=*/false, ObjCMethodDecl::Required, /*HasRelatedResultType=*/false); @@ -671,20 +673,15 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { // Debugger needs to work even if NSValue hasn't been defined. TypeSourceInfo *ReturnTInfo = nullptr; ObjCMethodDecl *M = ObjCMethodDecl::Create( - Context, - SourceLocation(), - SourceLocation(), - ValueWithBytesObjCType, - NSValuePointer, - ReturnTInfo, - NSValueDecl, - /*isInstance=*/false, - /*isVariadic=*/false, - /*isPropertyAccessor=*/false, - /*isImplicitlyDeclared=*/true, - /*isDefined=*/false, - ObjCMethodDecl::Required, - /*HasRelatedResultType=*/false); + Context, SourceLocation(), SourceLocation(), ValueWithBytesObjCType, + NSValuePointer, ReturnTInfo, NSValueDecl, + /*isInstance=*/false, + /*isVariadic=*/false, + /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); SmallVector Params; @@ -815,7 +812,7 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { Context, SourceLocation(), SourceLocation(), Sel, IdT, ReturnTInfo, Context.getTranslationUnitDecl(), false /*Instance*/, false /*isVariadic*/, - /*isPropertyAccessor=*/false, + /*isPropertyAccessor=*/false, /*isSynthesizedAccessorStub=*/false, /*isImplicitlyDeclared=*/true, /*isDefined=*/false, ObjCMethodDecl::Required, false); SmallVector Params; @@ -916,16 +913,14 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, NSAPI::NSDict_dictionaryWithObjectsForKeysCount); ObjCMethodDecl *Method = NSDictionaryDecl->lookupClassMethod(Sel); if (!Method && getLangOpts().DebuggerObjCLiteral) { - Method = ObjCMethodDecl::Create(Context, - SourceLocation(), SourceLocation(), Sel, - IdT, - nullptr /*TypeSourceInfo */, - Context.getTranslationUnitDecl(), - false /*Instance*/, false/*isVariadic*/, - /*isPropertyAccessor=*/false, - /*isImplicitlyDeclared=*/true, /*isDefined=*/false, - ObjCMethodDecl::Required, - false); + Method = ObjCMethodDecl::Create( + Context, SourceLocation(), SourceLocation(), Sel, IdT, + nullptr /*TypeSourceInfo */, Context.getTranslationUnitDecl(), + false /*Instance*/, false /*isVariadic*/, + /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, false); SmallVector Params; ParmVarDecl *objects = ParmVarDecl::Create(Context, Method, SourceLocation(), @@ -1174,6 +1169,35 @@ static void DiagnoseMismatchedSelectors(Sema &S, SourceLocation AtLoc, } } +static void HelperToDiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc, + Selector Sel, + ObjCMethodList &MethList, + bool &onlyDirect) { + ObjCMethodList *M = &MethList; + for (M = M->getNext(); M; M = M->getNext()) { + ObjCMethodDecl *Method = M->getMethod(); + if (Method->getSelector() != Sel) + continue; + if (!Method->isDirectMethod()) + onlyDirect = false; + } +} + +static void DiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc, + Selector Sel, bool &onlyDirect) { + for (Sema::GlobalMethodPool::iterator b = S.MethodPool.begin(), + e = S.MethodPool.end(); b != e; b++) { + // first, instance methods + ObjCMethodList &InstMethList = b->second.first; + HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, InstMethList, + onlyDirect); + + // second, class methods + ObjCMethodList &ClsMethList = b->second.second; + HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, ClsMethList, onlyDirect); + } +} + ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, SourceLocation AtLoc, SourceLocation SelLoc, @@ -1196,9 +1220,18 @@ ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, } else Diag(SelLoc, diag::warn_undeclared_selector) << Sel; - } else + } else { + bool onlyDirect = Method->isDirectMethod(); + DiagnoseDirectSelectorsExpr(*this, AtLoc, Sel, onlyDirect); DiagnoseMismatchedSelectors(*this, AtLoc, Method, LParenLoc, RParenLoc, WarnMultipleSelectors); + if (onlyDirect) { + Diag(AtLoc, diag::err_direct_selector_expression) + << Method->getSelector(); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + } if (Method && Method->getImplementationControl() != ObjCMethodDecl::Optional && @@ -1246,12 +1279,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()) @@ -2771,9 +2800,6 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, } } - if (ReceiverType->isObjCIdType() && !isImplicit) - Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id); - // There's a somewhat weird interaction here where we assume that we // won't actually have a method unless we also don't need to do some // of the more detailed type-checking on the receiver. @@ -2975,6 +3001,30 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, (Method && Method->getMethodFamily() == OMF_init) ? getEnclosingFunction() : nullptr; + if (Method && Method->isDirectMethod()) { + if (ReceiverType->isObjCIdType() && !isImplicit) { + Diag(Receiver->getExprLoc(), + diag::err_messaging_unqualified_id_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + + if (ReceiverType->isObjCClassType() && !isImplicit) { + Diag(Receiver->getExprLoc(), + diag::err_messaging_class_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + + if (SuperLoc.isValid()) { + Diag(SuperLoc, diag::err_messaging_super_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + } else if (ReceiverType->isObjCIdType() && !isImplicit) { + Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id); + } + if (DIFunctionScopeInfo && DIFunctionScopeInfo->ObjCIsDesignatedInit && (SuperLoc.isValid() || isSelfExpr(Receiver))) { diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index ac810745d2f5f..ec06b76e74ca3 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -306,6 +306,8 @@ makePropertyAttributesAsWritten(unsigned Attributes) { attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_atomic; if (Attributes & ObjCDeclSpec::DQ_PR_class) attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_class; + if (Attributes & ObjCDeclSpec::DQ_PR_direct) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_direct; return (ObjCPropertyDecl::PropertyAttributeKind)attributesAsWritten; } @@ -643,8 +645,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, GetterNameLoc); @@ -652,6 +652,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); @@ -705,9 +707,21 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, if (Attributes & ObjCDeclSpec::DQ_PR_null_resettable) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_null_resettable); - if (Attributes & ObjCDeclSpec::DQ_PR_class) + if (Attributes & ObjCDeclSpec::DQ_PR_class) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_class); + if ((Attributes & ObjCDeclSpec::DQ_PR_direct) || + CDecl->hasAttr()) { + if (isa(CDecl)) { + Diag(PDecl->getLocation(), diag::err_objc_direct_on_protocol) << true; + } else if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) { + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_direct); + } else { + Diag(PDecl->getLocation(), diag::warn_objc_direct_property_ignored) + << PDecl->getDeclName(); + } + } + return PDecl; } @@ -1037,6 +1051,31 @@ static bool hasWrittenStorageAttribute(ObjCPropertyDecl *Prop, return false; } +/// Create a synthesized property accessor stub inside the \@implementation. +static ObjCMethodDecl * +RedeclarePropertyAccessor(ASTContext &Context, ObjCImplementationDecl *Impl, + ObjCMethodDecl *AccessorDecl, SourceLocation AtLoc, + SourceLocation PropertyLoc) { + ObjCMethodDecl *Decl = AccessorDecl; + ObjCMethodDecl *ImplDecl = ObjCMethodDecl::Create( + Context, AtLoc, PropertyLoc, Decl->getSelector(), Decl->getReturnType(), + Decl->getReturnTypeSourceInfo(), Impl, Decl->isInstanceMethod(), + Decl->isVariadic(), Decl->isPropertyAccessor(), /* isSynthesized*/ true, + Decl->isImplicit(), Decl->isDefined(), Decl->getImplementationControl(), + Decl->hasRelatedResultType()); + ImplDecl->getMethodFamily(); + if (Decl->hasAttrs()) + ImplDecl->setAttrs(Decl->getAttrs()); + ImplDecl->setSelfDecl(Decl->getSelfDecl()); + ImplDecl->setCmdDecl(Decl->getCmdDecl()); + SmallVector SelLocs; + Decl->getSelectorLocs(SelLocs); + ImplDecl->setMethodParams(Context, Decl->parameters(), SelLocs); + ImplDecl->setLexicalDeclContext(Impl); + ImplDecl->setDefined(false); + return ImplDecl; +} + /// ActOnPropertyImplDecl - This routine performs semantic checks and /// builds the AST node for a property implementation declaration; declared /// as \@synthesize or \@dynamic. @@ -1404,6 +1443,18 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (ObjCMethodDecl *getterMethod = property->getGetterMethodDecl()) { getterMethod->createImplicitParams(Context, IDecl); + + // Redeclare the getter within the implementation as DeclContext. + if (Synthesize) { + // If the method hasn't been overridden, create a synthesized implementation. + ObjCMethodDecl *OMD = ClassImpDecl->getMethod( + getterMethod->getSelector(), getterMethod->isInstanceMethod()); + if (!OMD) + OMD = RedeclarePropertyAccessor(Context, IC, getterMethod, AtLoc, + PropertyLoc); + PIDecl->setGetterMethodDecl(OMD); + } + if (getLangOpts().CPlusPlus && Synthesize && !CompleteTypeErr && Ivar->getType()->isRecordType()) { // For Objective-C++, need to synthesize the AST for the IVAR object to be @@ -1456,8 +1507,20 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, break; } } + if (ObjCMethodDecl *setterMethod = property->getSetterMethodDecl()) { setterMethod->createImplicitParams(Context, IDecl); + + // Redeclare the setter within the implementation as DeclContext. + if (Synthesize) { + ObjCMethodDecl *OMD = ClassImpDecl->getMethod( + setterMethod->getSelector(), setterMethod->isInstanceMethod()); + if (!OMD) + OMD = RedeclarePropertyAccessor(Context, IC, setterMethod, + AtLoc, PropertyLoc); + PIDecl->setSetterMethodDecl(OMD); + } + if (getLangOpts().CPlusPlus && Synthesize && !CompleteTypeErr && Ivar->getType()->isRecordType()) { // FIXME. Eventually we want to do this for Objective-C as well. @@ -1852,10 +1915,12 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl *IMPDecl, if (IMPDecl->FindPropertyImplDecl( Prop->getIdentifier(), Prop->getQueryKind())) continue; - if (IMPDecl->getInstanceMethod(Prop->getGetterName())) { + ObjCMethodDecl *ImpMethod = IMPDecl->getInstanceMethod(Prop->getGetterName()); + if (ImpMethod && !ImpMethod->getBody()) { if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_readonly) continue; - if (IMPDecl->getInstanceMethod(Prop->getSetterName())) + ImpMethod = IMPDecl->getInstanceMethod(Prop->getSetterName()); + if (ImpMethod && !ImpMethod->getBody()) continue; } if (ObjCPropertyImplDecl *PID = @@ -2083,7 +2148,6 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, void Sema::diagnoseNullResettableSynthesizedSetters(const ObjCImplDecl *impDecl) { for (const auto *propertyImpl : impDecl->property_impls()) { const auto *property = propertyImpl->getPropertyDecl(); - // Warn about null_resettable properties with synthesized setters, // because the setter won't properly handle nil. if (propertyImpl->getPropertyImplementation() @@ -2092,16 +2156,16 @@ void Sema::diagnoseNullResettableSynthesizedSetters(const ObjCImplDecl *impDecl) ObjCPropertyDecl::OBJC_PR_null_resettable) && property->getGetterMethodDecl() && property->getSetterMethodDecl()) { - auto *getterMethod = property->getGetterMethodDecl(); - auto *setterMethod = property->getSetterMethodDecl(); - if (!impDecl->getInstanceMethod(setterMethod->getSelector()) && - !impDecl->getInstanceMethod(getterMethod->getSelector())) { + auto *getterImpl = propertyImpl->getGetterMethodDecl(); + auto *setterImpl = propertyImpl->getSetterMethodDecl(); + if ((!getterImpl || getterImpl->isSynthesizedAccessorStub()) && + (!setterImpl || setterImpl->isSynthesizedAccessorStub())) { SourceLocation loc = propertyImpl->getLocation(); if (loc.isInvalid()) loc = impDecl->getBeginLoc(); Diag(loc, diag::warn_null_resettable_setter) - << setterMethod->getSelector() << property->getDeclName(); + << setterImpl->getSelector() << property->getDeclName(); } } } @@ -2138,6 +2202,10 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, SetterMethod = Property->isClassProperty() ? IMPDecl->getClassMethod(Property->getSetterName()) : IMPDecl->getInstanceMethod(Property->getSetterName()); + if (GetterMethod && GetterMethod->isSynthesizedAccessorStub()) + GetterMethod = nullptr; + if (SetterMethod && SetterMethod->isSynthesizedAccessorStub()) + SetterMethod = nullptr; LookedUpGetterSetter = true; if (GetterMethod) { Diag(GetterMethod->getLocation(), @@ -2161,15 +2229,13 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, Property->getIdentifier(), Property->getQueryKind())) { if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; - if (!LookedUpGetterSetter) { - GetterMethod = Property->isClassProperty() ? - IMPDecl->getClassMethod(Property->getGetterName()) : - IMPDecl->getInstanceMethod(Property->getGetterName()); - SetterMethod = Property->isClassProperty() ? - IMPDecl->getClassMethod(Property->getSetterName()) : - IMPDecl->getInstanceMethod(Property->getSetterName()); - } - if ((GetterMethod && !SetterMethod) || (!GetterMethod && SetterMethod)) { + GetterMethod = PIDecl->getGetterMethodDecl(); + SetterMethod = PIDecl->getSetterMethodDecl(); + if (GetterMethod && GetterMethod->isSynthesizedAccessorStub()) + GetterMethod = nullptr; + if (SetterMethod && SetterMethod->isSynthesizedAccessorStub()) + SetterMethod = nullptr; + if ((bool)GetterMethod ^ (bool)SetterMethod) { SourceLocation MethodLoc = (GetterMethod ? GetterMethod->getLocation() : SetterMethod->getLocation()); @@ -2210,8 +2276,10 @@ void Sema::DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D for (const auto *PID : D->property_impls()) { const ObjCPropertyDecl *PD = PID->getPropertyDecl(); if (PD && !PD->hasAttr() && - !PD->isClassProperty() && - !D->getInstanceMethod(PD->getGetterName())) { + !PD->isClassProperty()) { + ObjCMethodDecl *IM = PID->getGetterMethodDecl(); + if (IM && !IM->isSynthesizedAccessorStub()) + continue; ObjCMethodDecl *method = PD->getGetterMethodDecl(); if (!method) continue; @@ -2396,20 +2464,21 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { } } - GetterMethod = ObjCMethodDecl::Create(Context, Loc, Loc, - property->getGetterName(), - resultTy, nullptr, CD, - !IsClassProperty, /*isVariadic=*/false, - /*isPropertyAccessor=*/true, - /*isImplicitlyDeclared=*/true, /*isDefined=*/false, - (property->getPropertyImplementation() == - ObjCPropertyDecl::Optional) ? - ObjCMethodDecl::Optional : - ObjCMethodDecl::Required); + GetterMethod = ObjCMethodDecl::Create( + Context, Loc, Loc, property->getGetterName(), resultTy, nullptr, CD, + !IsClassProperty, /*isVariadic=*/false, + /*isPropertyAccessor=*/true, /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + (property->getPropertyImplementation() == ObjCPropertyDecl::Optional) + ? ObjCMethodDecl::Optional + : ObjCMethodDecl::Required); CD->addDecl(GetterMethod); AddPropertyAttrs(*this, GetterMethod, property); + if (property->isDirectProperty()) + GetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc)); + if (property->hasAttr()) GetterMethod->addAttr(NSReturnsNotRetainedAttr::CreateImplicit(Context, Loc)); @@ -2423,12 +2492,17 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { Context, SA->getName(), Loc, AttributeCommonInfo::AS_GNU, SectionAttr::GNU_section)); + ProcessAPINotes(GetterMethod); + if (getLangOpts().ObjCAutoRefCount) CheckARCMethodDecl(GetterMethod); } else // A user declared getter will be synthesize when @synthesize of // the property with the same name is seen in the @implementation GetterMethod->setPropertyAccessor(true); + + GetterMethod->createImplicitParams(Context, + GetterMethod->getClassInterface()); property->setGetterMethodDecl(GetterMethod); // Skip setter if property is read-only. @@ -2447,6 +2521,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { nullptr, CD, !IsClassProperty, /*isVariadic=*/false, /*isPropertyAccessor=*/true, + /*isSynthesizedAccessorStub=*/false, /*isImplicitlyDeclared=*/true, /*isDefined=*/false, (property->getPropertyImplementation() == @@ -2483,11 +2558,17 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { AddPropertyAttrs(*this, SetterMethod, property); + if (property->isDirectProperty()) + SetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc)); + CD->addDecl(SetterMethod); if (const SectionAttr *SA = property->getAttr()) SetterMethod->addAttr(SectionAttr::CreateImplicit( Context, SA->getName(), Loc, AttributeCommonInfo::AS_GNU, SectionAttr::GNU_section)); + + 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) @@ -2496,6 +2577,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { // A user declared setter will be synthesize when @synthesize of // the property with the same name is seen in the @implementation SetterMethod->setPropertyAccessor(true); + + SetterMethod->createImplicitParams(Context, + SetterMethod->getClassInterface()); property->setSetterMethodDecl(SetterMethod); } // Add any synthesized methods to the global pool. This allows us to diff --git a/clang/lib/Sema/SemaPseudoObject.cpp b/clang/lib/Sema/SemaPseudoObject.cpp index 06bcd8d00ded7..602806968ced2 100644 --- a/clang/lib/Sema/SemaPseudoObject.cpp +++ b/clang/lib/Sema/SemaPseudoObject.cpp @@ -1190,16 +1190,15 @@ bool ObjCSubscriptOpBuilder::findAtIndexGetter() { true /*instance*/); if (!AtIndexGetter && S.getLangOpts().DebuggerObjCLiteral) { - AtIndexGetter = ObjCMethodDecl::Create(S.Context, SourceLocation(), - SourceLocation(), AtIndexGetterSelector, - S.Context.getObjCIdType() /*ReturnType*/, - nullptr /*TypeSourceInfo */, - S.Context.getTranslationUnitDecl(), - true /*Instance*/, false/*isVariadic*/, - /*isPropertyAccessor=*/false, - /*isImplicitlyDeclared=*/true, /*isDefined=*/false, - ObjCMethodDecl::Required, - false); + AtIndexGetter = ObjCMethodDecl::Create( + S.Context, SourceLocation(), SourceLocation(), AtIndexGetterSelector, + S.Context.getObjCIdType() /*ReturnType*/, nullptr /*TypeSourceInfo */, + S.Context.getTranslationUnitDecl(), true /*Instance*/, + false /*isVariadic*/, + /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, false); ParmVarDecl *Argument = ParmVarDecl::Create(S.Context, AtIndexGetter, SourceLocation(), SourceLocation(), arrayRef ? &S.Context.Idents.get("index") @@ -1303,6 +1302,7 @@ bool ObjCSubscriptOpBuilder::findAtIndexSetter() { ReturnType, ReturnTInfo, S.Context.getTranslationUnitDecl(), true /*Instance*/, false /*isVariadic*/, /*isPropertyAccessor=*/false, + /*isSynthesizedAccessorStub=*/false, /*isImplicitlyDeclared=*/true, /*isDefined=*/false, ObjCMethodDecl::Required, false); SmallVector Params; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 6c680f29da4f9..a1ab7e19c8fb5 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -10,13 +10,12 @@ // //===----------------------------------------------------------------------===// -#include "clang/Sema/Ownership.h" -#include "clang/Sema/SemaInternal.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTLambda.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" @@ -27,11 +26,14 @@ #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/Ownership.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" @@ -1229,14 +1231,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/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index a85fb6c1dc833..83734c8b882a9 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1665,6 +1665,7 @@ DeclResult Sema::CheckClassTemplate( NewClass->startDefinition(); ProcessDeclAttributeList(S, NewClass, Attr); + ProcessAPINotes(NewClass); if (PrevClassTemplate) mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); @@ -7978,6 +7979,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization( } ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. @@ -9142,6 +9144,7 @@ DeclResult Sema::ActOnExplicitInstantiation( bool PreviouslyDLLExported = Specialization->hasAttr(); 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 @@ -9546,6 +9549,9 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, Prev->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); // Merge attributes. ProcessDeclAttributeList(S, Prev, D.getDeclSpec().getAttributes()); + if (PrevTemplate) { + ProcessAPINotes(Prev); + } if (TSK == TSK_ExplicitInstantiationDefinition) InstantiateVariableDefinition(D.getIdentifierLoc(), Prev); } @@ -9721,6 +9727,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, } ProcessDeclAttributeList(S, Specialization, D.getDeclSpec().getAttributes()); + ProcessAPINotes(Specialization); // In MSVC mode, dllimported explicit instantiation definitions are treated as // instantiation declarations. diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 91945e23515ff..ff66a51b5baf5 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1192,20 +1192,6 @@ TypeResult Sema::actOnObjCTypeArgsAndProtocolQualifiers( ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } - if (auto OTPTL = ResultTL.getAs()) { - // 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(); // Type argument information. @@ -2470,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, @@ -2558,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: @@ -3709,32 +3705,9 @@ classifyPointerDeclarator(Sema &S, QualType type, Declarator &declarator, if (auto recordType = type->getAs()) { 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. CFErrorRef used to be declared with "objc_bridge" but is - // now declared with "objc_bridge_mutable", so look for either one of - // the two attributes. - if (recordDecl->getTagKind() == TTK_Struct && numNormalPointers > 0) { - IdentifierInfo *bridgedType = nullptr; - if (auto bridgeAttr = recordDecl->getAttr()) - bridgedType = bridgeAttr->getBridgedType(); - else if (auto bridgeAttr = - recordDecl->getAttr()) - bridgedType = bridgeAttr->getBridgedType(); - - if (bridgedType == 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; @@ -3758,6 +3731,33 @@ classifyPointerDeclarator(Sema &S, QualType type, Declarator &declarator, } } +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. CFErrorRef used to be declared with "objc_bridge" but + // is now declared with "objc_bridge_mutable", so look for either one of the + // two attributes. + if (recordDecl->getTagKind() == TTK_Struct) { + IdentifierInfo *bridgedType = nullptr; + if (auto bridgeAttr = recordDecl->getAttr()) + bridgedType = bridgeAttr->getBridgedType(); + else if (auto bridgeAttr = + recordDecl->getAttr()) + bridgedType = bridgeAttr->getBridgedType(); + + if (bridgedType == 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 @@ -4633,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. @@ -6514,6 +6519,25 @@ 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(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()); +} + /// Map a nullability attribute kind to a nullability kind. static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) { switch (kind) { @@ -6531,30 +6555,18 @@ static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) { } } -/// Applies a nullability type specifier to the given type, if possible. -/// -/// \param state The type processing state. -/// -/// \param type The type to which the nullability specifier will be -/// added. On success, this type will be updated appropriately. -/// -/// \param attr The attribute as written on the type. -/// -/// \param allowOnArrayType Whether to accept nullability specifiers on an -/// array type (e.g., because it will decay to a pointer). -/// -/// \returns true if a problem has been diagnosed, false on success. -static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, +static bool checkNullabilityTypeSpecifier(Sema &S, + TypeProcessingState *state, + ParsedAttr *parsedAttr, QualType &type, - ParsedAttr &attr, - bool allowOnArrayType) { - Sema &S = state.getSema(); - - NullabilityKind nullability = mapNullabilityAttrKind(attr.getKind()); - SourceLocation nullabilityLoc = attr.getLoc(); - bool isContextSensitive = attr.isContextSensitiveKeywordAttribute(); - - recordNullabilitySeen(S, nullabilityLoc); + NullabilityKind nullability, + SourceLocation nullabilityLoc, + bool isContextSensitive, + bool allowOnArrayType, + bool overrideExisting) { + bool implicit = (state == nullptr); + if (!implicit) + recordNullabilitySeen(S, nullabilityLoc); // Check for existing nullability attributes on the type. QualType desugared = type; @@ -6563,6 +6575,9 @@ static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, if (auto existingNullability = attributed->getImmediateNullability()) { // Duplicated nullability. if (nullability == *existingNullability) { + if (implicit) + break; + S.Diag(nullabilityLoc, diag::warn_nullability_duplicate) << DiagNullabilityKind(nullability, isContextSensitive) << FixItHint::CreateRemoval(nullabilityLoc); @@ -6570,11 +6585,16 @@ static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, break; } - // Conflicting nullability. - S.Diag(nullabilityLoc, diag::err_nullability_conflicting) - << DiagNullabilityKind(nullability, isContextSensitive) - << DiagNullabilityKind(*existingNullability, false); - return true; + if (!overrideExisting) { + // Conflicting nullability. + S.Diag(nullabilityLoc, diag::err_nullability_conflicting) + << DiagNullabilityKind(nullability, isContextSensitive) + << DiagNullabilityKind(*existingNullability, false); + return true; + } + + // Rebuild the attributed type, dropping the existing nullability. + type = rebuildAttributedTypeWithoutNullability(S.Context, type); } desugared = attributed->getModifiedType(); @@ -6585,7 +6605,7 @@ static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, // have nullability specifiers on them, which means we cannot // provide a useful Fix-It. if (auto existingNullability = desugared->getNullability(S.Context)) { - if (nullability != *existingNullability) { + if (nullability != *existingNullability && !implicit) { S.Diag(nullabilityLoc, diag::err_nullability_conflicting) << DiagNullabilityKind(nullability, isContextSensitive) << DiagNullabilityKind(*existingNullability, false); @@ -6610,15 +6630,16 @@ static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, // If this definitely isn't a pointer type, reject the specifier. if (!desugared->canHaveNullability() && !(allowOnArrayType && desugared->isArrayType())) { - S.Diag(nullabilityLoc, diag::err_nullability_nonpointer) - << DiagNullabilityKind(nullability, isContextSensitive) << type; + if (!implicit) { + S.Diag(nullabilityLoc, diag::err_nullability_nonpointer) + << DiagNullabilityKind(nullability, isContextSensitive) << type; + } return true; } // 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. const Type *pointeeType; if (desugared->isArrayType()) pointeeType = desugared->getArrayElementTypeNoTypeQual(); @@ -6641,11 +6662,42 @@ static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, } // Form the attributed type. - type = state.getAttributedType( - createNullabilityAttr(S.Context, attr, nullability), type, type); + if (state) { + assert(parsedAttr); + Attr *A = createNullabilityAttr(S.Context, *parsedAttr, nullability); + type = state->getAttributedType(A, type, type); + } else { + attr::Kind attrKind = AttributedType::getNullabilityAttrKind(nullability); + type = S.Context.getAttributedType(attrKind, type, type); + } return false; } +static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, + QualType &type, + ParsedAttr &attr, + bool allowOnArrayType) { + NullabilityKind nullability = mapNullabilityAttrKind(attr.getKind()); + SourceLocation nullabilityLoc = attr.getLoc(); + bool isContextSensitive = attr.isContextSensitiveKeywordAttribute(); + + return checkNullabilityTypeSpecifier(state.getSema(), &state, &attr, type, + nullability, nullabilityLoc, + isContextSensitive, allowOnArrayType, + /*overrideExisting*/false); +} + +bool Sema::checkImplicitNullabilityTypeSpecifier(QualType &type, + NullabilityKind nullability, + SourceLocation diagLoc, + bool allowArrayTypes, + bool overrideExisting) { + return checkNullabilityTypeSpecifier(*this, nullptr, nullptr, type, + nullability, diagLoc, + /*isContextSensitive*/false, + allowArrayTypes, overrideExisting); +} + /// Check the application of the Objective-C '__kindof' qualifier to /// the given type. static bool checkObjCKindOfType(TypeProcessingState &state, QualType &type, @@ -6659,7 +6711,6 @@ static bool checkObjCKindOfType(TypeProcessingState &state, QualType &type, return false; } - // Find out if it's an Objective-C object or object pointer type; const ObjCObjectPointerType *ptrType = type->getAs(); const ObjCObjectType *objType = ptrType ? ptrType->getObjectType() : type->getAs(); @@ -7307,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) { @@ -7616,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/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 2d3884ebe0211..dab750d3ce987 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1239,12 +1239,12 @@ void ASTReader::Error(StringRef Msg) const { } } -void ASTReader::Error(unsigned DiagID, - StringRef Arg1, StringRef Arg2) const { +void ASTReader::Error(unsigned DiagID, StringRef Arg1, StringRef Arg2, + StringRef Arg3) const { if (Diags.isDiagnosticInFlight()) - Diags.SetDelayedDiagnostic(DiagID, Arg1, Arg2); + Diags.SetDelayedDiagnostic(DiagID, Arg1, Arg2, Arg3); else - Diag(DiagID) << Arg1 << Arg2; + Diag(DiagID) << Arg1 << Arg2 << Arg3; } void ASTReader::Error(unsigned DiagID, StringRef Arg1, StringRef Arg2, @@ -2528,14 +2528,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 && @@ -3408,8 +3400,10 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { break; case SOURCE_MANAGER_LINE_TABLE: - if (ParseLineTable(F, Record)) + if (ParseLineTable(F, Record)) { + Error("malformed SOURCE_MANAGER_LINE_TABLE in AST file"); return Failure; + } break; case SOURCE_LOCATION_PRELOADS: { @@ -3507,24 +3501,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"); @@ -4174,6 +4150,20 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, PreviousGeneration = incrementGeneration(*ContextObj); unsigned NumModules = ModuleMgr.size(); + auto removeModulesAndReturn = [&](ASTReadResult ReadResult) { + assert(ReadResult && "expected to return error"); + ModuleMgr.removeModules(ModuleMgr.begin() + NumModules, + PP.getLangOpts().Modules + ? &PP.getHeaderSearchInfo().getModuleMap() + : nullptr); + + // If we find that any modules are unusable, the global index is going + // to be out-of-date. Just remove it. + GlobalIndex.reset(); + ModuleMgr.setGlobalIndex(nullptr); + return ReadResult; + }; + SmallVector Loaded; switch (ASTReadResult ReadResult = ReadASTCore(FileName, Type, ImportLoc, @@ -4184,42 +4174,33 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, case OutOfDate: case VersionMismatch: case ConfigurationMismatch: - case HadErrors: { - llvm::SmallPtrSet LoadedSet; - for (const ImportedModule &IM : Loaded) - LoadedSet.insert(IM.Mod); - - ModuleMgr.removeModules(ModuleMgr.begin() + NumModules, LoadedSet, - PP.getLangOpts().Modules - ? &PP.getHeaderSearchInfo().getModuleMap() - : nullptr); - - // If we find that any modules are unusable, the global index is going - // to be out-of-date. Just remove it. - GlobalIndex.reset(); - ModuleMgr.setGlobalIndex(nullptr); - return ReadResult; - } + case HadErrors: + return removeModulesAndReturn(ReadResult); case Success: break; } // Here comes stuff that we only do once the entire chain is loaded. - // Load the AST blocks of all of the modules that we loaded. - for (SmallVectorImpl::iterator M = Loaded.begin(), - MEnd = Loaded.end(); - M != MEnd; ++M) { - ModuleFile &F = *M->Mod; + // Load the AST blocks of all of the modules that we loaded. We can still + // hit errors parsing the ASTs at this point. + for (ImportedModule &M : Loaded) { + ModuleFile &F = *M.Mod; // Read the AST block. if (ASTReadResult Result = ReadASTBlock(F, ClientLoadCapabilities)) - return Result; + return removeModulesAndReturn(Result); + + // The AST block should always have a definition for the main module. + if (F.isModule() && !F.DidReadTopLevelSubmodule) { + Error(diag::err_module_file_missing_top_level_submodule, F.FileName); + return removeModulesAndReturn(Failure); + } // Read the extension blocks. while (!SkipCursorToBlock(F.Stream, EXTENSION_BLOCK_ID)) { if (ASTReadResult Result = ReadExtensionBlock(F)) - return Result; + return removeModulesAndReturn(Result); } // Once read, set the ModuleFile bit base offset and update the size in @@ -4227,6 +4208,11 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, F.GlobalBitOffset = TotalModulesSizeInBits; TotalModulesSizeInBits += F.SizeInBits; GlobalBitOffsetsMap.insert(std::make_pair(F.GlobalBitOffset, &F)); + } + + // Preload source locations and interesting indentifiers. + for (ImportedModule &M : Loaded) { + ModuleFile &F = *M.Mod; // Preload SLocEntries. for (unsigned I = 0, N = F.PreloadSLocEntries.size(); I != N; ++I) { @@ -4269,10 +4255,8 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, // Setup the import locations and notify the module manager that we've // committed to these module files. - for (SmallVectorImpl::iterator M = Loaded.begin(), - MEnd = Loaded.end(); - M != MEnd; ++M) { - ModuleFile &F = *M->Mod; + for (ImportedModule &M : Loaded) { + ModuleFile &F = *M.Mod; ModuleMgr.moduleFileAccepted(&F); @@ -4280,10 +4264,10 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, F.DirectImportLoc = ImportLoc; // FIXME: We assume that locations from PCH / preamble do not need // any translation. - if (!M->ImportedBy) - F.ImportLoc = M->ImportLoc; + if (!M.ImportedBy) + F.ImportLoc = M.ImportLoc; else - F.ImportLoc = TranslateSourceLocation(*M->ImportedBy, M->ImportLoc); + F.ImportLoc = TranslateSourceLocation(*M.ImportedBy, M.ImportLoc); } if (!PP.getLangOpts().CPlusPlus || @@ -4607,6 +4591,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 ModuleCache, we're stuck // with it; we can only load a single version of each module. @@ -4698,6 +4687,21 @@ 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 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; @@ -4773,8 +4777,10 @@ ASTReader::ASTReadResult ASTReader::ReadExtensionBlock(ModuleFile &F) { switch (MaybeRecCode.get()) { case EXTENSION_METADATA: { ModuleFileExtensionMetadata Metadata; - if (parseModuleFileExtensionMetadata(Record, Blob, Metadata)) + if (parseModuleFileExtensionMetadata(Record, Blob, Metadata)) { + Error("malformed EXTENSION_METADATA in AST file"); return Failure; + } // Find a module file extension with this block name. auto Known = ModuleFileExtensions.find(Metadata.BlockName); @@ -5431,7 +5437,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; } @@ -5441,6 +5450,11 @@ 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++]; @@ -5476,16 +5490,14 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { // Don't emit module relocation error if we have -fno-validate-pch if (!PP.getPreprocessorOpts().DisablePCHValidation && CurFile != F.File) { - if (!Diags.isDiagnosticInFlight()) { - Diag(diag::err_module_file_conflict) - << CurrentModule->getTopLevelModuleName() - << CurFile->getName() - << F.File->getName(); - } + Error(diag::err_module_file_conflict, + CurrentModule->getTopLevelModuleName(), CurFile->getName(), + F.File->getName()); return Failure; } } + F.DidReadTopLevelSubmodule = true; CurrentModule->setASTFile(F.File); CurrentModule->PresumedModuleMapFile = F.ModuleMapPath; } @@ -5500,6 +5512,11 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { 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); @@ -5764,6 +5781,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) { @@ -5783,20 +5821,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, @@ -5862,20 +5888,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 PPInfo = getModulePreprocessedEntity(Index); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9aa8c77c62319..5c2158b6bc7a9 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1013,12 +1013,13 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { // definitions rarely show up in headers. Reader.PendingBodies[MD] = GetCurrentCursorOffset(); HasPendingBody = true; - MD->setSelfDecl(ReadDeclAs()); - MD->setCmdDecl(ReadDeclAs()); } + MD->setSelfDecl(ReadDeclAs()); + MD->setCmdDecl(ReadDeclAs()); MD->setInstanceMethod(Record.readInt()); MD->setVariadic(Record.readInt()); MD->setPropertyAccessor(Record.readInt()); + MD->setSynthesizedAccessorStub(Record.readInt()); MD->setDefined(Record.readInt()); MD->setOverriding(Record.readInt()); MD->setHasSkippedBody(Record.readInt()); @@ -1258,6 +1259,8 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) { VisitNamedDecl(CAD); CAD->setClassInterface(ReadDeclAs()); + CAD->setClassInterfaceLoc(ReadSourceLocation()); + CAD->setAtLoc(ReadSourceLocation()); } void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { @@ -1313,6 +1316,8 @@ void ASTDeclReader::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { D->setPropertyDecl(ReadDeclAs()); D->PropertyIvarDecl = ReadDeclAs(); D->IvarLoc = ReadSourceLocation(); + D->setGetterMethodDecl(ReadDeclAs()); + D->setSetterMethodDecl(ReadDeclAs()); D->setGetterCXXConstructor(Record.readExpr()); D->setSetterCXXAssignment(Record.readExpr()); } @@ -2670,6 +2675,8 @@ class AttrReader { Expr *readExpr() { return Reader->ReadExpr(*F); } + Attr *readAttr() { return Reader->ReadAttr(*F, Record, Idx); } + std::string readString() { return Reader->ReadString(Record, Idx); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 28affedbbb30f..133220e77e640 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1094,7 +1094,6 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(OPTIONS_BLOCK); RECORD(LANGUAGE_OPTIONS); RECORD(TARGET_OPTIONS); - RECORD(FILE_SYSTEM_OPTIONS); RECORD(HEADER_SEARCH_OPTIONS); RECORD(PREPROCESSOR_OPTIONS); @@ -1122,7 +1121,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); @@ -1330,6 +1328,8 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(UNHASHED_CONTROL_BLOCK); RECORD(SIGNATURE); RECORD(DIAGNOSTIC_OPTIONS); + RECORD(HEADER_SEARCH_PATHS); + RECORD(FILE_SYSTEM_OPTIONS); RECORD(DIAG_PRAGMA_MAPPINGS); #undef RECORD @@ -1449,6 +1449,36 @@ 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(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); + + // 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); @@ -1641,36 +1671,12 @@ 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 = 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(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); @@ -2791,26 +2797,6 @@ void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec) { Stream.EmitRecordWithBlob(PPEOffsetAbbrev, Record, bytes(PreprocessedEntityOffsets)); } - - // Write the skipped region table for the preprocessing record. - ArrayRef SkippedRanges = PPRec.getSkippedRanges(); - if (SkippedRanges.size() > 0) { - std::vector SerializedSkippedRanges; - SerializedSkippedRanges.reserve(SkippedRanges.size()); - for (auto const& Range : SkippedRanges) - SerializedSkippedRanges.emplace_back(Range); - - using namespace llvm; - auto Abbrev = std::make_shared(); - 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) { @@ -2863,6 +2849,11 @@ 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 @@ -2969,6 +2960,12 @@ 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, @@ -3648,8 +3645,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()); } @@ -3802,10 +3797,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/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 039b57f88e736..305598348d267 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -662,17 +662,17 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) { VisitNamedDecl(D); // FIXME: convert to LazyStmtPtr? // Unlike C/C++, method bodies will never be in header files. - bool HasBodyStuff = D->getBody() != nullptr || - D->getSelfDecl() != nullptr || D->getCmdDecl() != nullptr; + bool HasBodyStuff = D->getBody() != nullptr; Record.push_back(HasBodyStuff); if (HasBodyStuff) { Record.AddStmt(D->getBody()); - Record.AddDeclRef(D->getSelfDecl()); - Record.AddDeclRef(D->getCmdDecl()); } + Record.AddDeclRef(D->getSelfDecl()); + Record.AddDeclRef(D->getCmdDecl()); Record.push_back(D->isInstanceMethod()); Record.push_back(D->isVariadic()); Record.push_back(D->isPropertyAccessor()); + Record.push_back(D->isSynthesizedAccessorStub()); Record.push_back(D->isDefined()); Record.push_back(D->isOverriding()); Record.push_back(D->hasSkippedBody()); @@ -827,6 +827,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; } @@ -884,6 +886,8 @@ void ASTDeclWriter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { Record.AddDeclRef(D->getPropertyDecl()); Record.AddDeclRef(D->getPropertyIvarDecl()); Record.AddSourceLocation(D->getPropertyIvarDeclLoc()); + Record.AddDeclRef(D->getGetterMethodDecl()); + Record.AddDeclRef(D->getSetterMethodDecl()); Record.AddStmt(D->getGetterCXXConstructor()); Record.AddStmt(D->getSetterCXXAssignment()); Code = serialization::DECL_OBJC_PROPERTY_IMPL; diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index 4b9f20fca4f80..7de88a1d4b6d4 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) { @@ -219,10 +223,7 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, return NewlyLoaded; } -void ModuleManager::removeModules( - ModuleIterator First, - llvm::SmallPtrSetImpl &LoadedSuccessfully, - ModuleMap *modMap) { +void ModuleManager::removeModules(ModuleIterator First, ModuleMap *modMap) { auto Last = end(); if (First == Last) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp index 0058f3d3881fc..0c46447e19853 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -144,6 +144,8 @@ void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, continue; const Stmt *Body = M->getBody(); + if (M->isSynthesizedAccessorStub()) + continue; assert(Body); MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this, diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index cce3449b8873f..45f1d7f839695 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -624,7 +624,7 @@ static bool isObjCTypeParamDependent(QualType Type) { : public RecursiveASTVisitor { public: IsObjCTypeParamDependentTypeVisitor() : Result(false) {} - bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { + bool VisitTypedefType(const TypedefType *Type) { if (isa(Type->getDecl())) { Result = true; return false; diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index d95f809bec1aa..e7408b805fa85 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -1080,7 +1080,7 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const { const ObjCPropertyDecl *ObjCMethodCall::getAccessedProperty() const { // Look for properties accessed with property syntax (foo.bar = ...) - if ( getMessageKind() == OCM_PropertyAccess) { + if (getMessageKind() == OCM_PropertyAccess) { const PseudoObjectExpr *POE = getContainingPseudoObjectExpr(); assert(POE && "Property access without PseudoObjectExpr?"); diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 8236907ea773b..fea8100c3b3bc 100644 --- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -722,13 +722,6 @@ std::string AnalysisConsumer::getFunctionName(const Decl *D) { } else if (const auto *OCD = dyn_cast(DC)) { OS << OCD->getClassInterface()->getName() << '(' << OCD->getName() << ')'; - } else if (isa(DC)) { - // We can extract the type of the class from the self pointer. - if (ImplicitParamDecl *SelfDecl = OMD->getSelfDecl()) { - QualType ClassTy = - cast(SelfDecl->getType())->getPointeeType(); - ClassTy.print(OS, PrintingPolicy(LangOptions())); - } } OS << ' ' << OMD->getSelector().getAsString() << ']'; diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt index 05061f0a10a80..8f96ad4d93409 100644 --- a/clang/lib/Tooling/CMakeLists.txt +++ b/clang/lib/Tooling/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS add_subdirectory(Core) add_subdirectory(Inclusions) +add_subdirectory(Refactor) add_subdirectory(Refactoring) add_subdirectory(ASTDiff) add_subdirectory(Syntax) @@ -41,4 +42,6 @@ add_clang_library(clangTooling clangRewrite clangSerialization clangToolingCore + clangToolingRefactor + clangToolingRefactoring ) 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(); const bool Result = Compiler.ExecuteAction(*Action); 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) { diff --git a/clang/lib/Tooling/Refactor/ASTSlice.cpp b/clang/lib/Tooling/Refactor/ASTSlice.cpp new file mode 100644 index 0000000000000..de6de11485c82 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTSlice.cpp @@ -0,0 +1,636 @@ +//===--- 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 + +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 { +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(D) && !D->isImplicit()) + collectDeclIfInRange(D); + // TODO: Handle Lambda/Blocks. + if (!isa(D) && !isa(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(S)) + return RecursiveASTVisitor::TraverseStmt(S); + llvm::SaveAndRestore Parent(ParentStmt, CurrentStmt); + llvm::SaveAndRestore 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(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 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->getBeginLoc(), EndLoc)) + break; + const SourceLocation FileLoc = + 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 + // 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(CurrDecl)) { + EndLoc = CurrDecl->getEndLoc(); + 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 +findIfStmtStart(const IfStmt *If, unsigned Index, + ArrayRef NodeTree) { + if (Index >= NodeTree.size()) + return {If, Index}; // We've reached the top of the tree, return. + const auto *ParentIf = + dyn_cast_or_null(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 +canonicalizeSelectedExpr(const Stmt *S, unsigned Index, + ArrayRef 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; + auto Next = std::make_pair(Parent, Index + 1); + // The entire pseudo expression is selected when just its syntactic + // form is selected. + if (isa(S)) { + if (const auto *POE = dyn_cast_or_null(Parent)) { + if (POE->getSyntacticForm() == S) + return Next; + } + } + + // Look through the implicit casts in the parents. + unsigned ParentIndex = Index + 1; + for (; ParentIndex <= NodeTree.size() && isa(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(S) && isa(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(S) && isa(Parent)) + return Next; + // The entire call should be selected when just the callee is selected. + if (const auto *DRE = dyn_cast(S)) { + if (const auto *Call = dyn_cast(Parent)) { + if (Call->getCalleeDecl() == DRE->getDecl()) + return Next; + } + } + return Same; +} + +Optional ASTSlice::nearestSelectedStmt( + llvm::function_ref 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(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(S), Node.index(), NodeTree); + return SelectedStmt(*this, If.first, If.second); + } + default: + break; + } + + return SelectedStmt(*this, S, Node.index()); + } + return None; +} + +Optional +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::innermostSelectedDecl( + llvm::function_ref 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::innermostSelectedDecl(ArrayRef 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 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->getBeginLoc(), 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->getBeginLoc(), 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 +findCompoundStatementSourceConstruct(const CompoundStmt *CS, + ArrayRef 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(S)) + return {CS, 0}; + if (const auto *If = dyn_cast(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 +findSwitchSourceConstruct(const SwitchCase *Case, + ArrayRef NodeTree) { + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Stmt *S = Node.value().getStmtOrNull(); + if (!S) + continue; + if (isa(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::getInnermostCompletelySelectedDecl() { + assert(SelectionRange.isValid() && "No selection range!"); + if (CachedSelectedInnermostDecl) + return *CachedSelectedInnermostDecl; + computeSelectionRangeOverlapKinds(NodeTree, SelectionRange, + Context.getSourceManager()); + Optional 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->getBeginLoc(), Case->getColonLoc()); + if (areRangesOverlapping(Range, SelectionRange, SM)) + return true; + } + return false; +} + +Optional 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(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(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(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(Construct.first) && + isCaseSelected(cast(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->getBeginLoc(), + 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 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(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 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(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 StmtOrDecl; + const Stmt *ParentStmt; + const Decl *ParentDecl; + SourceRange Range; + SelectionRangeOverlapKind SelectionRangeOverlap = UnknownOverlap; + + const Stmt *getStmtOrNull() const { + return StmtOrDecl.dyn_cast(); + } + + const Decl *getDeclOrNull() const { + return StmtOrDecl.dyn_cast(); + } + + 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 + nearestSelectedStmt(llvm::function_ref 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 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 + innermostSelectedDecl(llvm::function_ref 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 innermostSelectedDecl(ArrayRef Classes, + unsigned Options = 0); + + /// Returns the set of statements that overlap with the selection range. + Optional 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 computeSelectedStmtSet(); + + /// Returns the innermost declaration that contains both the start and the + /// end of the selection range. + Optional getInnermostCompletelySelectedDecl(); + + /// The lowest element is the top of the hierarchy + SmallVector NodeTree; + ASTContext &Context; + SourceLocation SelectionLocation; + SourceRange SelectionRange; + Optional> CachedSelectedStmtSet; + Optional> 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..50b446165cf86 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp @@ -0,0 +1,70 @@ +//===-- 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 { + llvm::StringMap &USRs; + unsigned NumFound = 0; + +public: + USRToDeclConverter(llvm::StringMap &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) { + if (USR.empty()) + return nullptr; + 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(); + llvm::ErrorOr 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..8871e1da091a4 --- /dev/null +++ b/clang/lib/Tooling/Refactor/CMakeLists.txt @@ -0,0 +1,49 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangToolingRefactor + ASTSlice.cpp + ASTStateSerialization.cpp + Extract.cpp + ExtractRepeatedExpressionIntoVariable.cpp + ExtractionUtils.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 + clangFrontend + clangIndex + clangLex + clangToolingCore + clangRewrite + ) + +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + set_source_files_properties(Extract.cpp PROPERTIES COMPILE_FLAGS /bigobj) +endif() diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp new file mode 100644 index 0000000000000..717ed17fd1185 --- /dev/null +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -0,0 +1,2015 @@ +//===--- 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 "ExtractionUtils.h" +#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 + +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(*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; } +}; + +enum class ExtractionKind { Function, Method, Expression }; + +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 Candidates, + Optional ExtractedStmtRange, + Optional FirstCandidateInfo, + ExtractionKind Kind) + : S(S), ParentStmt(ParentStmt), + FunctionLikeParentDecl(FunctionLikeParentDecl), + Candidates(std::move(Candidates)), + ExtractedStmtRange(ExtractedStmtRange), Kind(Kind) { + 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 getRefactoringCandidates() override { + return Candidates; + } + + std::vector getAvailableSubActions() override { + std::vector SubActions; + if (isa(FunctionLikeParentDecl) || + isa(FunctionLikeParentDecl)) + 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 perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + llvm::Expected + performExpressionExtraction(ASTContext &Context, PrintingPolicy &PP); + + const Stmt *S, *ParentStmt; + const Decl *FunctionLikeParentDecl; + std::vector Candidates; + /// A set of extraction candidates that correspond to the extracted code. + SmallVector CandidateExtractionInfo; + Optional ExtractedStmtRange; + ExtractionKind Kind; +}; + +} // 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; +} + +/// 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(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 getExtractedStatements(const CompoundStmt *CS, + const Stmt *Begin, + const Stmt *End) { + if (CS->body_empty()) + return None; + assert(Begin && End); + CompoundStatementRange Result; + Result.First = findSelectedStmt(CS->body(), Begin); + if (Result.First == CS->body_end()) + return None; + Result.Last = findSelectedStmt( + CompoundStmt::body_const_range(Result.First, CS->body_end()), End); + if (Result.Last == CS->body_end()) + return None; + return Result; +} + +static RefactoringOperationResult +initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, + SourceLocation Location, SourceRange SelectionRange, + bool CreateOperation, + ExtractionKind Kind = ExtractionKind::Function) { + 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(Selected) && isSimpleExpression(cast(Selected))) + return RefactoringOperationResult("the selected expression is too simple"); + if (const auto *PRE = dyn_cast(Selected)) { + if (!PRE->isMessagingGetter()) + 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) + return Result; + + Optional ExtractedStmtRange; + + // Check if there are multiple candidates that can be extracted. + std::vector Candidates; + Optional FirstCandidateInfo; + if (const auto *BinOp = dyn_cast(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()->getEndLoc(), Context.getSourceManager(), + Context.getLangOpts()) == SelectionRange.getBegin()) && + Stmts.containsSelectionRangeEnd) { + SourceRange FirstCandidateRange = + SourceRange(SelectionRange.getBegin(), BinOp->getEndLoc()); + 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(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 = std::make_unique( + Selected, ParentStmt, ParentDecl, std::move(Candidates), + ExtractedStmtRange, FirstCandidateInfo, Kind); + auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo; + SourceRange Range; + if (ExtractedStmtRange) + Range = SourceRange(ExtractedStmtRange->getFirst()->getBeginLoc(), + ExtractedStmtRange->getLast()->getEndLoc()); + 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()) + .getEnd()); + } + 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, 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 = + llvm::PointerUnion; + +/// 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 Handler) { + E = E->IgnoreParenCasts(); + if (const auto *DRE = dyn_cast(E)) + return Handler(DRE); + + if (const auto *ME = dyn_cast(E)) { + if (isa(ME->getBase()->IgnoreParenCasts())) { + if (const auto *FD = dyn_cast_or_null(ME->getMemberDecl())) + Handler(FD); + return; + } + if (const auto *MD = ME->getMemberDecl()) { + if (isa(MD) || isa(MD)) + findEntitiesDirectlyReferencedInExpr(ME->getBase(), Handler); + } + return; + } + + if (const auto *CO = dyn_cast(E)) { + findEntitiesDirectlyReferencedInExpr(CO->getTrueExpr(), Handler); + findEntitiesDirectlyReferencedInExpr(CO->getFalseExpr(), Handler); + return; + } + + if (const auto *BO = dyn_cast(E)) { + if (BO->getOpcode() == BO_Comma) + return findEntitiesDirectlyReferencedInExpr(BO->getRHS(), Handler); + } +} + +template +static void +findMatchingParameters(Matcher &ParameterMatcher, const Stmt *S, + ASTContext &Context, StringRef Node, + llvm::function_ref Handler) { + using namespace clang::ast_matchers; + auto Matches = match(findAll(callExpr(ParameterMatcher)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.template getNodeAs(Node)); + Matches = match(findAll(cxxConstructExpr(ParameterMatcher)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.template getNodeAs(Node)); +} + +static void +findUseOfConstThis(const Stmt *S, ASTContext &Context, + llvm::function_ref 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("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 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("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("arg")); + Matches = match(findAll(cxxConstructExpr(RefParameter)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("arg")); +} + +static void findAddressExpressionsPassedByConstPointer( + const Stmt *S, ASTContext &Context, + llvm::function_ref 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("arg")); + Matches = match(findAll(cxxConstructExpr(ConstPtrParameter)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("arg")); +} + +static bool isImplicitInitializer(const VarDecl *VD) { + assert(VD->hasInit()); + const auto *E = VD->getInit(); + if (isa(E)) + return false; + const auto *Construct = dyn_cast(E); + if (!Construct) + return E->getBeginLoc() == VD->getLocation(); + return Construct->getParenOrBraceRange().isInvalid(); +} + +static const Expr *getInitializerExprWithLexicalRange(const Expr *E) { + if (const auto *EWC = dyn_cast(E)) { + if (const auto *Construct = dyn_cast(EWC->getSubExpr())) { + if (Construct->getNumArgs() == 1) { + if (const auto *ME = + dyn_cast(Construct->getArg(0))) + return ME; + } + } + } + return E; +} + +namespace { + +class ExtractedCodeVisitor : public RecursiveASTVisitor { + 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(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(E->getBase()->IgnoreParenCasts()); + if (!Base) + return RecursiveASTVisitor::TraverseMemberExpr(E); + const FieldDecl *FD = dyn_cast_or_null(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(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(S->getRHS())) + TraverseStmt(OVE->getSourceExpr()); + return RecursiveASTVisitor::TraverseBinAssign(S); + } + + void findCapturedVariableOrFieldsInExpression( + const Expr *E, llvm::function_ref Handler) { + findEntitiesDirectlyReferencedInExpr( + E, [&Handler, this](const ReferencedEntity &Entity) { + if (const auto *DRE = Entity.dyn_cast()) { + const VarDecl *VD = dyn_cast(DRE->getDecl()); + if (!VD || !VD->isLocalVarDeclOrParm() || VD->isImplicit()) + return; + return Handler(captureVariable(VD)); + } + return Handler(captureField(Entity.get())); + }); + } + + 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(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 CapturedVariables; + llvm::DenseMap CapturedFields; + llvm::SmallPtrSet + AddressExpressionsPassedToConstPointerParameter; + llvm::SmallPtrSet 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 &VariablesDefinedInExtractedCode; + llvm::SmallPtrSet VariablesUsedAfterExtraction; + + VariableDefinedInExtractedCodeUseAfterExtractionFinder( + const Stmt *LastExtractedStmt, + const llvm::SmallPtrSetImpl + &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(E->getDecl()); + if (!VD) + return true; + if (VariablesDefinedInExtractedCode.count(VD)) + VariablesUsedAfterExtraction.insert(VD); + return true; + } +}; + +class PossibleShadowingVariableFinder + : public RecursiveASTVisitor { + 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(S)); + } +}; + +/// Traverses the extracted code and rewrites the 'return' statements to ensure +/// that they now return some value. +class ReturnRewriter : public RecursiveASTVisitor { + 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->getEndLoc(), 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(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 { +public: + Rewriter &SourceRewriter; + const llvm::SmallPtrSetImpl &VariablesUsedAfterExtraction; + const PrintingPolicy &PP; + + DefinedInExtractedCodeDeclStmtRewriter( + Rewriter &SourceRewriter, const llvm::SmallPtrSetImpl + &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->getBeginLoc(); + for (const Decl *D : S->decls()) { + const auto *VD = dyn_cast(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->getEndLoc() : VD->getLocation()); + SourceRewriter.RemoveText(Range); + continue; + } + std::string Str; + llvm::raw_string_ostream OS(Str); + if (StartLoc != S->getBeginLoc()) + 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->getBeginLoc(); + if (const auto *Construct = dyn_cast(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->getEndLoc(), 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(D); + bool IsLast = D == S->decl_end()[-1]; + if (!VD) { + OS << "<>;"; + 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(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 +class PseudoObjectRewriter : public RecursiveASTVisitor { + typedef RecursiveASTVisitor 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(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(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 { +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(E); + if (!DRE) + return false; + return dyn_cast(DRE->getDecl()) == TargetVD; + } + + void dereferenceTargetVar(const Expr *E, bool WrapInParens = false) { + SourceRewriter.InsertTextBefore(E->getBeginLoc(), + WrapInParens ? "(*" : "*"); + if (WrapInParens) + SourceRewriter.InsertTextAfterToken(E->getEndLoc(), ")"); + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast(E->getDecl()); + if (VD != TargetVD) + return true; + dereferenceTargetVar(E); + return true; + } + + bool TraverseUnaryAddrOf(UnaryOperator *E) { + if (const auto *DRE = + dyn_cast(E->getSubExpr()->IgnoreParenCasts())) { + const VarDecl *VD = dyn_cast(DRE->getDecl()); + if (VD == TargetVD) { + // Remove the '&' as the variable is now a pointer. + SourceRewriter.RemoveText( + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getBeginLoc())); + return true; + } + } + return RecursiveASTVisitor::TraverseUnaryAddrOf(E); + } + + bool TraverseMemberExpr(MemberExpr *E) { + if (!E->isArrow()) { + if (const auto *DRE = + dyn_cast(E->getBase()->IgnoreParenCasts())) { + const VarDecl *VD = dyn_cast(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 { +public: + Rewriter &SourceRewriter; + llvm::SmallPtrSet RewrittenExpressions; + + CapturedThisReferenceRewriter(Rewriter &SourceRewriter) + : SourceRewriter(SourceRewriter) {} + + void rewriteThis(const CXXThisExpr *E) { + RewrittenExpressions.insert(E); + if (!E->isImplicit()) + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, "object"); + else + SourceRewriter.InsertText(E->getBeginLoc(), "object"); + } + + bool VisitMemberExpr(const MemberExpr *E) { + const auto *This = + dyn_cast(E->getBase()->IgnoreParenImpCasts()); + if (This) { + rewriteThis(This); + if (!This->isImplicit() && E->isArrow()) + SourceRewriter.ReplaceText(E->getOperatorLoc(), 2, "."); + else + SourceRewriter.InsertText(E->getBase()->getEndLoc(), "."); + } + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'this' into '&object'. +class CapturedThisPointerRewriter + : public PseudoObjectRewriter { +public: + Rewriter &SourceRewriter; + const llvm::SmallPtrSetImpl &RewrittenExpressions; + + CapturedThisPointerRewriter( + Rewriter &SourceRewriter, + const llvm::SmallPtrSetImpl &RewrittenExpressions) + : SourceRewriter(SourceRewriter), + RewrittenExpressions(RewrittenExpressions) {} + + void replace(const CXXThisExpr *E, StringRef Text) { + SourceRewriter.ReplaceText(E->getBeginLoc(), 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(E->getSubExpr()->IgnoreParenImpCasts())) { + if (!This->isImplicit()) { + // Remove the '*' as the variable is now a reference. + SourceRewriter.RemoveText( + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getBeginLoc())); + 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 { +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(E->getDecl()); + if (!VD || VD != SelfDecl) + return true; + if (E->getBeginLoc().isInvalid()) + return true; + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, "object"); + return true; + } + + void insertObjectForImplicitSelf(const Expr *E, SourceLocation Loc, + StringRef Text) { + const auto *DRE = dyn_cast(E); + if (!DRE) + return; + const VarDecl *VD = dyn_cast(DRE->getDecl()); + if (!VD || VD != SelfDecl || DRE->getBeginLoc().isValid()) + return; + SourceRewriter.InsertText(Loc, Text); + } + + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { + insertObjectForImplicitSelf(E->getBase()->IgnoreImpCasts(), + E->getBeginLoc(), "object->"); + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'self' into the name +/// of the class. +class CapturedClassSelfRewriter + : public PseudoObjectRewriter { +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(E->getDecl()); + if (!VD || VD != SelfDecl || E->getBeginLoc().isInvalid()) + return true; + SourceRewriter.ReplaceText(E->getBeginLoc(), 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 { +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(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 QualType getFunctionLikeParentDeclReturnType(const Decl *D) { + // FIXME: might need to handle ObjC blocks in the future. + if (const auto *M = dyn_cast(D)) + return M->getReturnType(); + return cast(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(D)) + return M->getBody(); + return cast(D)->getBody(); +} + +static bool isEnclosingMethodConst(const Decl *D) { + if (const auto *MD = dyn_cast(D)) + return MD->isConst(); + return false; +} + +static bool isEnclosingMethodStatic(const Decl *D) { + if (const auto *MD = dyn_cast(D)) + return MD->isStatic(); + return false; +} + +static bool isEnclosingMethodOutOfLine(const Decl *D) { + const auto *MD = dyn_cast(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(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(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(D->getLexicalDeclContext())) + D = RD; + } + return D->getBeginLoc(); +} + +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 +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->getEndLoc(); + } + return Loc.isValid() ? std::make_pair(Loc, MethodDeclarationPlacement::After) + : std::make_pair(RD->getEndLoc(), + 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(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 +ExtractOperation::performExpressionExtraction(ASTContext &Context, + PrintingPolicy &PP) { + assert(isExpressionExtraction() && "Not an expression extraction"); + std::vector Replacements; + const Expr *E = cast(S); + QualType VarType = findExpressionLexicalType(FunctionLikeParentDecl, E, + E->getType(), PP, Context); + StringRef VarName = "extractedExpr"; + auto CreatedSymbol = std::make_unique( + OldSymbolName(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 ExtractOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector 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; + + if (isExpressionExtraction()) + return performExpressionExtraction(Context, PP); + + const Stmt *S = + CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement + ? CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement + : this->S; + + const auto *EnclosingObjCMethod = + dyn_cast(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(S), Context); + } else + Visitor.InspectExtractedStmt(const_cast(S), Context); + // Compute the return type. + bool IsExpr = isLexicalExpression(S, ParentStmt); + QualType ReturnType; + if (IsExpr || Visitor.HasReturnInExtracted) { + if (const auto *E = dyn_cast(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 CapturedVariables; + llvm::SmallPtrSet 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(getEnclosingDeclBody(FunctionLikeParentDecl))); + struct RedeclaredVariable { + const VarDecl *VD; + int OrderingPriority; + }; + llvm::SmallVector RedeclaredVariables; + bool CanUseReturnForVariablesUsedAfterwards = + !isa(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(S)); + } else + ReturnsRewriter.TraverseStmt(const_cast(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(S)); + } else + DeclRewriter.TraverseStmt(const_cast(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(S)); + } else + ThisRewriter.TraverseStmt(const_cast(S)); + CapturedThisPointerRewriter PtrThisRewriter( + SourceRewriter, ThisRewriter.RewrittenExpressions); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + PtrThisRewriter.TraverseStmt(const_cast(S)); + } else + PtrThisRewriter.TraverseStmt(const_cast(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(S)); + } else + SelfRewriter.TraverseStmt(const_cast(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(S)); + } else + SelfRewriter.TraverseStmt(const_cast(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(S)); + } else + SuperRewriter.TraverseStmt(const_cast(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(S)); + } else + UseRewriter.TraverseStmt(const_cast(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 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 CreatedSymbol = + std::make_unique( + OldSymbolName(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 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(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, Context, PP); + OS << "\n"; + } + } + if (isMethodExtraction() && isEnclosingMethodStatic(FunctionLikeParentDecl)) + OS << "static "; + else if (!isMethodExtraction()) + OS << (isInHeader(FunctionExtractionLoc, SM) ? "inline " : "static "); + std::string QualifiedName; + llvm::raw_string_ostream NameOS(QualifiedName); + if (isMethodExtraction() && IsDefinition) + printEnclosingMethodScope(FunctionLikeParentDecl, NameOS, PP); + NameOS << ExtractedName; + NameOS << '('; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) + NameOS << ", "; + IsFirst = false; + Var.ParameterType.print(NameOS, PP, /*PlaceHolder=*/Var.getName()); + } + NameOS << ')'; + ReturnType.print(OS, PP, NameOS.str()); + unsigned NameOffset = OS.str().find(ExtractedName); + 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(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); + 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) + 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 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..ef4b8744cf941 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -0,0 +1,299 @@ +//===--- 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 "ExtractionUtils.h" +#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 ExtractRepeatedExpressionIntoVariableOperation + : public RefactoringOperation { +public: + ExtractRepeatedExpressionIntoVariableOperation( + const Expr *E, ArrayRef Duplicates, const Decl *ParentDecl) + : E(E), DuplicateExpressions(Duplicates.begin(), Duplicates.end()), + ParentDecl(ParentDecl) {} + + const Stmt *getTransformedStmt() const override { return E; } + + llvm::Expected perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const Expr *E; + SmallVector DuplicateExpressions; + const Decl *ParentDecl; +}; + +using UseOfDeclaration = std::pair; + +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(Child) || isa(Child) || + isa(Child) || + isa(Child)) + return false; + return true; +} + +/// Builds up a list of declarations that are used in an expression. +class DuplicateExprSemanticProfiler + : public RecursiveASTVisitor { + unsigned Index = 0; + llvm::SmallVectorImpl &DeclRefs; + +public: + DuplicateExprSemanticProfiler( + llvm::SmallVectorImpl &DeclRefs) + : DeclRefs(DeclRefs) { + DeclRefs.clear(); + } + + bool VisitStmt(const Stmt *S) { + if (!shouldIgnoreParens(dyn_cast(S))) + ++Index; + return true; + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (E->getDecl()) + DeclRefs.emplace_back(E->getDecl(), Index); + return true; + } +}; + +class DuplicateExprFinder : public RecursiveASTVisitor, + PrinterHelper { + const Expr *Target; + const ASTContext &Context; + const PrintingPolicy &PP; + Stmt::StmtClass ExprKind; + QualType T; + std::string ExprString, OSString; + llvm::SmallVector ExprDecls, DeclUses; + + void printExpr(std::string &Str, const Expr *E) { + llvm::raw_string_ostream OS(Str); + E->printPretty(OS, /*Helper=*/this, PP); + } + +public: + SmallVector DuplicateExpressions; + + 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(E)); + } + + bool handledStmt(Stmt *E, raw_ostream &OS) final override { + if (const auto *Paren = dyn_cast(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(S); + if (E == Target) { + 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; + // 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(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(E)) { + if (const auto *Fn = Call->getDirectCallee()) + return Fn->getReturnType(); + } else if (const auto *Msg = dyn_cast(E)) { + if (const auto *M = Msg->getMethodDecl()) + return M->getReturnType(); + } else if (const auto *PRE = dyn_cast(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(S)) + return Op->getOperator() == OO_Call || Op->getOperator() == OO_Subscript; + return isa(S) || isa(S) || + isa(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(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, Context.getPrintingPolicy()); + DupFinder.TraverseDecl(const_cast(ParentDecl)); + if (DupFinder.DuplicateExpressions.size() < 2) + return None; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = + std::make_unique( + E, DupFinder.DuplicateExpressions, ParentDecl); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +static StringRef nameForExtractedVariable(const Expr *E) { + auto SuggestedName = extract::nameForExtractedVariable(E); + if (!SuggestedName) + return "duplicate"; + return *SuggestedName; +} + +llvm::Expected +ExtractRepeatedExpressionIntoVariableOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + RefactoringResult Result(std::vector{}); + std::vector &Replacements = Result.Replacements; + + const SourceManager &SM = Context.getSourceManager(); + SourceLocation InsertionLoc = + extract::locationForExtractedVariableDeclaration(DuplicateExpressions, + ParentDecl, SM); + if (InsertionLoc.isInvalid()) + return llvm::make_error( + "no appropriate insertion location found"); + + StringRef Name = nameForExtractedVariable(E); + Result.AssociatedSymbols.push_back( + std::make_unique( + OldSymbolName(Name))); + RefactoringResultAssociatedSymbol *CreatedSymbol = + Result.AssociatedSymbols.back().get(); + + // 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); + // 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 << " = "; + 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, + RefactoringReplacement::AssociatedSymbolLocation( + llvm::makeArrayRef(NameOffset), /*IsDeclaration=*/true))); + + // Replace the duplicates with a reference to the variable. + for (const Expr *E : DuplicateExpressions) { + Replacements.push_back(RefactoringReplacement( + SourceRange(SM.getSpellingLoc(E->getBeginLoc()), + getPreciseTokenLocEnd(SM.getSpellingLoc(E->getEndLoc()), SM, + Context.getLangOpts())), + Name, CreatedSymbol, + /*NameOffset=*/llvm::makeArrayRef(unsigned(0)))); + } + + return std::move(Result); +} diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp new file mode 100644 index 0000000000000..5a8c2dc39a432 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp @@ -0,0 +1,137 @@ +//===--- 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" +#include "clang/AST/RecursiveASTVisitor.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace clang; + +Optional tooling::extract::nameForExtractedVariable(const Expr *E) { + if (const auto *Call = dyn_cast(E)) { + if (const auto *Fn = Call->getDirectCallee()) + return Fn->getName(); + } else if (const auto *Msg = dyn_cast(E)) { + if (const auto *M = Msg->getMethodDecl()) { + if (M->getSelector().isUnarySelector()) + return M->getSelector().getNameForSlot(0); + } + } else if (const auto *PRE = dyn_cast(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; +} + +namespace { + +/// Checks if a set of expressions is directly contained in some AST region. +class StmtReachabilityChecker + : public RecursiveASTVisitor { + const llvm::SmallPtrSetImpl &Expressions; + unsigned Count = 0; + + StmtReachabilityChecker( + const llvm::SmallPtrSetImpl &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 &Expressions) { + StmtReachabilityChecker Checker(Expressions); + Checker.TraverseStmt(S); + return Checker.areAllExpressionsReached(); + } +}; + +/// Figures out where the extracted variable should go. +class ExtractedVariableInsertionLocFinder + : public RecursiveASTVisitor { + llvm::SmallPtrSet Expressions; + llvm::SmallVector, 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 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 IsPrevCompoundStmtTracker(IsPrevCompoundStmt, + false); + if (auto *CS = dyn_cast(S)) { + IsPrevCompoundStmt = true; + InsertionCandidateStack.emplace_back(CS, nullptr); + RecursiveASTVisitor::TraverseStmt(S); + InsertionCandidateStack.pop_back(); + return true; + } + 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->getBeginLoc(); + break; + } + } + return false; + } + return true; + } +}; + +} // end anonymous namespace + +SourceLocation tooling::extract::locationForExtractedVariableDeclaration( + ArrayRef Expressions, const Decl *ParentDecl, + const SourceManager &SM) { + ExtractedVariableInsertionLocFinder LocFinder(Expressions); + LocFinder.TraverseDecl(const_cast(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 new file mode 100644 index 0000000000000..816a0815106de --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.h @@ -0,0 +1,40 @@ +//===--- 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; +class Decl; +class SourceManager; + +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 nameForExtractedVariable(const Expr *E); + +/// Returns an appropriate location for a variable declaration that will be +/// visible to all the given expressions. +SourceLocation +locationForExtractedVariableDeclaration(ArrayRef Expressions, + const Decl *ParentDecl, + const SourceManager &SM); + +} // end namespace extract +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H diff --git a/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp b/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp new file mode 100644 index 0000000000000..8338091b4c009 --- /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 + 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(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(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()) + ED = ET->getDecl(); + else { + // Enum literals are 'int' in C. + if (const auto *DRE = dyn_cast(Cond)) { + if (const auto *EC = dyn_cast(DRE->getDecl())) + ED = dyn_cast(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 = std::make_unique( + ED, Switch, dyn_cast(ParentDecl)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected +FillInEnumSwitchCasesOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector 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..f23b342e872ff --- /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 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(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 = + std::make_unique( + Class); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +namespace { + +class PureMethodSet { + llvm::DenseMap 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 + 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 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 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() || M->isImplicit()) + continue; + for (const CXXMethodDecl *OM : M->overridden_methods()) { + OM = OM->getCanonicalDecl(); + if (OM->getLexicalDeclContext() == AbstractClass) { + SourceLocation EndLoc = M->getEndLoc(); + if (EndLoc.isMacroID()) + EndLoc = SM.getExpansionRange(EndLoc).getEnd(); + 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(M) || isa(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 +FillInMissingMethodStubsFromAbstractClassesOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector Replacements; + + std::vector 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->getEndLoc(); + 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(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..0295bb0a4e916 --- /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 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(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(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 = std::make_unique( + Container, std::move(Impl)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected +FillInMissingProtocolStubsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector 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..b8bd7308b502e --- /dev/null +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -0,0 +1,468 @@ +//===--- 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 perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const IfStmt *If; +}; + +class ValidIfBodyVerifier : public RecursiveASTVisitor { + 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(If->getElse())) { + if (If->getConditionVariable() || If->getInit() || !If->getCond()) + return true; + } + return false; +} + +static Optional> +matchBinOp(const Expr *E, BinaryOperator::Opcode Kind) { + const auto *BinOp = dyn_cast(E->IgnoreParens()); + if (!BinOp || BinOp->getOpcode() != Kind) + return None; + return std::pair( + BinOp->getLHS()->IgnoreParenImpCasts(), BinOp->getRHS()->IgnoreParens()); +} + +typedef llvm::SmallDenseSet 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 &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. + Expr::EvalResult Result; + if (!RHS->EvaluateAsInt(Result, Context)) + return false; + // Only allow constant that fix into 64 bits. + if (Result.Val.getInt().getMinSignedBits() > 64 || + !RHSValues.insert(Result.Val.getInt().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(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 Ranges; + 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()->getEndLoc(), Then, SM)); + const auto *Else = CurrentIf->getElse(); + if (!Else) + break; + RangeStart = + findFirstLocationOfSourceConstruct(CurrentIf->getElseLoc(), Then, SM); + if (const auto *If = dyn_cast(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(If)); + if (!BodyVerifier.IsValid) + return RefactoringOperationResult( + "if's body contains a 'break'/'default'/'case' statement"); + + // FIXME: Use ASTMatchers if possible. + Optional MatchedLHSNodeID; + RHSValueSet RHSValues; + for (const IfStmt *CurrentIf = If; CurrentIf; + CurrentIf = dyn_cast_or_null(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 &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(Body); + if (!CS) + return !isa(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(S); + if (!DS) + return false; + for (const Decl *D : DS->decls()) { + if (isa(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(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 = SM.getSpellingLoc(isa(If->getThen()) + ? If->getThen()->getEndLoc() + : 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 &Replacements, + const SourceManager &SM, const LangOptions &LangOpts) { + SmallVector CaseValues; + gatherCaseValues(If->getCond(), CaseValues); + assert(!CaseValues.empty()); + Replacements.emplace_back( + SourceRange(CaseInfo.CaseStartLoc, + SM.getSpellingLoc(CaseValues[0]->getBeginLoc())), + CaseInfo.getCaseReplacementString()); + + SourceLocation PrevCaseEnd = getPreciseTokenLocEnd( + SM.getSpellingLoc(CaseValues[0]->getEndLoc()), SM, LangOpts); + for (const Expr *CaseValue : llvm::makeArrayRef(CaseValues).drop_front()) { + Replacements.emplace_back( + SourceRange(PrevCaseEnd, SM.getSpellingLoc(CaseValue->getBeginLoc())), + StringRef(":\ncase ")); + PrevCaseEnd = getPreciseTokenLocEnd( + SM.getSpellingLoc(CaseValue->getEndLoc()), SM, LangOpts); + } + + AreBracesNeeded = areBracesNeeded(If->getThen()); + StringRef ColonReplacement = AreBracesNeeded ? ": {" : ":"; + if (isa(If->getThen())) { + Replacements.emplace_back( + SourceRange( + PrevCaseEnd, + getPreciseTokenLocEnd( + SM.getSpellingLoc(If->getThen()->getBeginLoc()), SM, LangOpts)), + ColonReplacement); + } else { + // Find the location of the if's ')' + SourceLocation End = findClosingParenLocEnd( + SM.getSpellingLoc(If->getCond()->getEndLoc()), SM, LangOpts); + if (!End.isValid()) + return llvm::make_error( + "couldn't find the location of ')'"); + Replacements.emplace_back(SourceRange(PrevCaseEnd, End), ColonReplacement); + } + return llvm::Error::success(); +} + +llvm::Expected +IfSwitchConversionOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector 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(SM.getSpellingLoc(If->getBeginLoc()), + SM.getSpellingLoc(LHS->getBeginLoc())), + StringRef("switch (")); + + bool AreBracesNeeded = false; + if (auto Error = addCaseReplacements( + If, CasePlacement(getPreciseTokenLocEnd( + SM.getSpellingLoc(LHS->getEndLoc()), 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(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( + SM.getSpellingLoc(isa(Else) ? Else->getBeginLoc() + : 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(LastBody)) { + 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()) + TerminatingReplacementLoc = getLastLineLocationUnlessItHasOtherTokens( + TerminatingReplacementLoc, SM, LangOpts); + if (IsLastBreakNeeded) + OS << "\nbreak;"; + OS << "\n}"; + if (AreBracesNeeded) + OS << "\n}"; + } else { + TerminatingReplacementLoc = LastBody->getEndLoc(); + if (IsLastBreakNeeded) + OS << "break;\n"; + if (AreBracesNeeded) + OS << "}\n"; + } + + if (!OS.str().empty()) { + TerminatingReplacementLoc = SM.getSpellingLoc(TerminatingReplacementLoc); + Replacements.emplace_back( + SourceRange(TerminatingReplacementLoc, TerminatingReplacementLoc), + std::move(OS.str())); + } + + 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..cf022b914248d --- /dev/null +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -0,0 +1,446 @@ +//===--- 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 +class ImplementDeclaredMethodsOperation : public RefactoringOperation { +public: + ImplementDeclaredMethodsOperation( + const ClassType *Container, ArrayRef 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 Methods, + bool CreateOperation) { + if (Methods.empty()) + return None; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = std::make_unique(Container, Methods); + Result.RefactoringOp = std::move(Operation); + return Result; + } + + const ClassType *Container; + llvm::SmallVector SelectedMethods; +}; + +class ImplementDeclaredCXXMethodsOperation + : public ImplementDeclaredMethodsOperation< + CXXRecordDecl, CXXMethodDecl, ImplementDeclaredCXXMethodsOperation> { +public: + ImplementDeclaredCXXMethodsOperation( + const CXXRecordDecl *Container, + ArrayRef SelectedMethods) + : ImplementDeclaredMethodsOperation(Container, SelectedMethods) {} + + llvm::Expected + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + static void addInlineBody(const CXXMethodDecl *MD, const ASTContext &Context, + std::vector &Replacements); + + static llvm::Expected runInImplementationAST( + ASTContext &Context, const FileID &File, const CXXRecordDecl *Class, + ArrayRef> SelectedMethods); +}; + +class ImplementDeclaredObjCMethodsOperation + : public ImplementDeclaredMethodsOperation< + ObjCContainerDecl, ObjCMethodDecl, + ImplementDeclaredObjCMethodsOperation> { + const ObjCInterfaceDecl *Interface; + +public: + ImplementDeclaredObjCMethodsOperation( + const ObjCContainerDecl *Container, + ArrayRef SelectedMethods) + : ImplementDeclaredMethodsOperation(Container, SelectedMethods) { + if (const auto *CD = dyn_cast(Container)) + Interface = CD->getClassInterface(); + else + Interface = nullptr; + } + + llvm::Expected + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + static llvm::Expected runInImplementationAST( + ASTContext &Context, const FileID &File, + const ObjCContainerDecl *Container, const ObjCInterfaceDecl *Interface, + ArrayRef MethodDeclarations, + ArrayRef> 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(D) || isa(D) || + isa(D); + }); + if (!SelectedDecl) + return None; + // Look at the set of methods that intersect with the selection. + if (const auto *CXXClass = dyn_cast(SelectedDecl->getDecl())) { + if (CXXClass->isDependentType()) + return RefactoringOperationResult("templates are unsupported"); + llvm::SmallVector 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(SelectedDecl->getDecl()); + llvm::SmallVector SelectedMethods; + for (const ObjCMethodDecl *M : Container->methods()) { + if (M->isImplicit() || isImplemented(M)) + continue; + if (Slice.isSourceRangeSelected( + 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(Container)) { + if (Category->IsClassExtension()) + Container = Category->getClassInterface(); + } + return ImplementDeclaredObjCMethodsOperation::initiate( + Container, SelectedMethods, CreateOperation); +} + +llvm::Expected +ImplementDeclaredCXXMethodsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + if (Container->isLexicallyWithinFunctionOrMethod()) { + // Local methods can be implemented inline. + std::vector 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 &Replacements) { + SourceLocation EndLoc = MD->getEndLoc(); + 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(DC); DC = Result->getLexicalDeclContext()) + Result = cast(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(D)) { + if (UDD->getNominatedNamespace() == ND) + return true; + } + } + return false; +} + +llvm::Expected +ImplementDeclaredCXXMethodsOperation::runInImplementationAST( + ASTContext &Context, const FileID &File, const CXXRecordDecl *Class, + ArrayRef> SelectedMethods) { + if (!Class) + return llvm::make_error( + "the target class is not defined in the continuation AST unit"); + + SourceManager &SM = Context.getSourceManager(); + + // Find the defined methods of the class. + llvm::SmallVector 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->getBeginLoc()); + if (SM.getFileID(Loc) == File) + DefinedOutOfLineMethods.push_back(cast(MD)); + } + } + + std::vector 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->getEndLoc()).getEnd(); + 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 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->getEndLoc()).getEnd(); + 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 auto &I : SelectedMethods) { + const CXXMethodDecl *MD = I.Decl; + // Check if the method is already defined. + if (!MD) + continue; + + // Drop the 'virtual' specifier. + bool IsVirtual = MD->isVirtualAsWritten(); + const_cast(MD)->setVirtualAsWritten(false); + + // Drop the default arguments. + llvm::SmallVector, 4> DefaultArgs; + for (const ParmVarDecl *P : MD->parameters()) { + if (!P->hasDefaultArg()) + continue; + Expr *E = const_cast(P)->getDefaultArg(); + const_cast(P)->setDefaultArg(nullptr); + DefaultArgs.emplace_back(const_cast(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(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(MD)->setVirtualAsWritten(IsVirtual); + const_cast(MD)->setQualifierInfo(PrevQualifierInfo); + } + + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str()))); + + return std::move(Replacements); +} + +llvm::Expected +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 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, MethodDeclarations, + filter(llvm::makeArrayRef(SelectedMethods), + [](const DeclEntity &D) { return !D.isDefined(); })); +} + +static const ObjCImplDecl * +getImplementationContainer(const ObjCContainerDecl *Container, + const ObjCInterfaceDecl *Interface = nullptr) { + if (!Container) + return Interface ? getImplementationContainer(Interface) : nullptr; + if (const auto *ID = dyn_cast(Container)) + return ID->getImplementation(); + if (const auto *CD = dyn_cast(Container)) { + if (const auto *Impl = CD->getImplementation()) + return Impl; + return getImplementationContainer(Interface); + } + return nullptr; +} + +llvm::Expected +ImplementDeclaredObjCMethodsOperation::runInImplementationAST( + ASTContext &Context, const FileID &File, const ObjCContainerDecl *Container, + const ObjCInterfaceDecl *Interface, + ArrayRef MethodDeclarations, + ArrayRef> SelectedMethods) { + const ObjCImplDecl *ImplementationContainer = + getImplementationContainer(Container, Interface); + if (!ImplementationContainer) + return llvm::make_error( + "the target @interface is not implemented in the continuation AST " + "unit"); + + std::vector Replacements; + + std::string MethodString; + llvm::raw_string_ostream OS(MethodString); + + assert(MethodDeclarations.size() >= SelectedMethods.size() && + "fewer declarations than selected methods?"); + for (const auto &I : llvm::enumerate(SelectedMethods)) { + indexer::Indexed Decl = I.value(); + // Skip methods that are already defined. + if (!Decl.isNotDefined()) + continue; + + OS << StringRef(MethodDeclarations[I.index()]).drop_back(); // Drop the ';' + OS << " { \n <#code#>;\n}\n\n"; + } + SourceLocation InsertionLoc = ImplementationContainer->getEndLoc(); + + 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..e1d415527dde8 --- /dev/null +++ b/clang/lib/Tooling/Refactor/IndexerQueries.cpp @@ -0,0 +1,172 @@ +//===--- 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/Refactoring/RefactoringDiagnostic.h" +#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::create(const DeclPredicate &Predicate) { + return std::make_unique(Predicate); +} + +std::unique_ptr +DeclPredicateNode::create(const BoolDeclPredicate &Predicate) { + if (Predicate.IsInverted) + return std::make_unique( + create(Predicate.Predicate)); + return create(Predicate.Predicate); +} + +std::unique_ptr +clang::tooling::indexer::fileThatShouldContainImplementationOf(const Decl *D) { + return std::make_unique(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(D) << cast(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 { + std::string Name; + std::vector IntegerValues; +}; + +struct QueryYAMLNode { + std::string Name; + std::vector PredicateResults; + std::string FilenameResult; +}; + +} // end anonymous namespace + +LLVM_YAML_IS_SEQUENCE_VECTOR(QueryPredicateNode) +LLVM_YAML_IS_SEQUENCE_VECTOR(QueryYAMLNode) + +namespace llvm { +namespace yaml { + +template <> struct MappingTraits { + static void mapping(IO &Yaml, QueryPredicateNode &Predicate) { + Yaml.mapRequired("name", Predicate.Name); + Yaml.mapRequired("intValues", Predicate.IntegerValues); + } +}; + +template <> struct MappingTraits { + 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 Queries) { + std::vector QueryResults; + Input YamlIn(Source); + YamlIn >> QueryResults; + if (YamlIn.error()) + return llvm::make_error("Failed to parse query results", + YamlIn.error()); + if (QueryResults.size() != Queries.size()) + return llvm::make_error("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(Query)) { + const DeclPredicateNode &Predicate = DQ->getPredicateNode(); + DeclPredicate ActualPredicate(""); + bool IsNot = false; + if (const auto *Not = dyn_cast(&Predicate)) { + ActualPredicate = + cast(Not->getChild()).getPredicate(); + IsNot = true; + } else + ActualPredicate = + cast(Predicate).getPredicate(); + for (const auto &PredicateResult : Result.PredicateResults) { + if (PredicateResult.Name != ActualPredicate.Name) + continue; + std::vector>> Output; + for (const auto &ResultTuple : + zip(DQ->getInputs(), PredicateResult.IntegerValues)) { + const Decl *D = std::get<0>(ResultTuple); + int Result = std::get<1>(ResultTuple); + bool Value = (IsNot ? !Result : !!Result); + Output.push_back(Indexed>( + PersistentDeclRef::create(Value ? D : nullptr), + Value ? QueryBoolResult::Yes : QueryBoolResult::No)); + } + DQ->setOutput(std::move(Output)); + break; + } + } else if (auto *AQ = + dyn_cast(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..e0876b7d4649d --- /dev/null +++ b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp @@ -0,0 +1,83 @@ +//===--- 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 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( + SelectedSet->containsSelectionRange); + } else + E = cast_or_null( + Slice.nearestStmt(Stmt::ObjCStringLiteralClass)); + if (!E) + return None; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = std::make_unique(E); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected +LocalizeObjCStringLiteralOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector Replacements; + // TODO: New API: Replace by something like Node.wrap("NSLocalizedString(", ", + // @""") + SourceLocation LocStart = + Context.getSourceManager().getSpellingLoc(E->getBeginLoc()); + Replacements.emplace_back(SourceRange(LocStart, LocStart), + StringRef("NSLocalizedString(")); + SourceLocation LocEnd = getPreciseTokenLocEnd( + 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/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..d6592d9b71cdf --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringActions.cpp @@ -0,0 +1,31 @@ +//===--- 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" + } + llvm_unreachable("unexpected RefactoringActionType value"); +} + +} // 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..9e0ad9d334631 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringContinuations.h @@ -0,0 +1,398 @@ +//===--- 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 + +namespace clang { +namespace tooling { + +namespace detail { + +struct ValidBase {}; + +/// The ContinuationPassType determine which type is passed into the refactoring +/// continuation. +template struct ContinuationPassType { using Type = T; }; + +template struct ContinuationPassType> { + using Type = ArrayRef; +}; + +/// Refactoring operations can pass state to the continuations. Valid state +/// values should have a corresponding \c StateTraits specialization. +template 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::Type. + /// + /// PersistentType: The TU-independent type that's persisted even after the + /// TU in which the continuation was created is disposed. +}; + +template +struct StateTraits + : std::enable_if::value, ValidBase>::type { + using StoredResultType = const T *; + using PersistentType = PersistentDeclRef; +}; + +template +struct StateTraits> + : std::enable_if::value, ValidBase>::type { + using StoredResultType = std::vector; + using PersistentType = std::vector>; +}; + +template +struct StateTraits>> + : std::enable_if::value, ValidBase>::type { + using StoredResultType = std::vector>; + using PersistentType = std::vector>>; +}; + +template <> struct StateTraits> { + using StoredResultType = std::vector; + using PersistentType = std::vector; +}; + +/// Conversion functions convert the TU-specific state to a TU independent +/// state and vice-versa. +template +PersistentDeclRef convertToPersistentRepresentation( + const T *Declaration, + typename std::enable_if::value>::type * = + nullptr) { + return PersistentDeclRef::create(Declaration); +} + +template +std::vector> convertToPersistentRepresentation( + ArrayRef Declarations, + typename std::enable_if::value>::type * = + nullptr) { + std::vector> Result; + Result.reserve(Declarations.size()); + for (const T *D : Declarations) + Result.push_back(PersistentDeclRef::create(D)); + return Result; +} + +template +std::vector>> +convertToPersistentRepresentation( + std::unique_ptr> &Query, + typename std::enable_if::value>::type * = + nullptr) { + Query->invalidateTUSpecificState(); + return Query->getOutput(); +} + +inline std::vector +convertToPersistentRepresentation(const std::vector &Values) { + return Values; +} + +/// Converts the TU-independent state to the TU-specific state. +class PersistentToASTSpecificStateConverter { + ASTContext &Context; + llvm::StringMap 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 + bool addConvertible( + const PersistentDeclRef &Ref, + typename std::enable_if::value>::type * = + nullptr) { + if (!Ref.USR.empty()) + ConvertedDeclRefs[Ref.USR] = nullptr; + return true; + } + + template + const T * + convert(const PersistentDeclRef &Ref, + typename std::enable_if::value>::type * = + nullptr) { + return dyn_cast_or_null(lookupDecl(Ref.USR)); + } + + template + bool addConvertible( + const std::vector> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + for (const auto &Ref : Refs) { + if (!Ref.USR.empty()) + ConvertedDeclRefs[Ref.USR] = nullptr; + } + return true; + } + + template + std::vector + convert(const std::vector> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + std::vector 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(lookupDecl(Ref.USR))); + return Results; + } + + template + bool addConvertible( + const std::vector>> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + for (const auto &Ref : Refs) { + if (!Ref.Decl.USR.empty()) + ConvertedDeclRefs[Ref.Decl.USR] = nullptr; + } + return true; + } + + template + std::vector> + convert(const std::vector>> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + std::vector> 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( + dyn_cast_or_null(lookupDecl(Ref.Decl.USR)), Ref.IsNotDefined)); + return Results; + } + + bool addConvertible(const PersistentFileID &) { + // Do nothing since FileIDs are converted one-by-one. + return true; + } + + FileID convert(const PersistentFileID &Ref); + + bool addConvertible(const std::vector &) { return true; } + + std::vector convert(const std::vector &Values) { + return Values; + } + + /// Converts the added persistent state into TU-specific state using one + /// efficient operation. + void runCoalescedConversions(); +}; + +template +struct ContinuationFunction { + using Type = llvm::Expected (*)( + ASTContext &, const T &, + typename ContinuationPassType< + typename StateTraits::StoredResultType>::Type...); + + template + static llvm::Expected dispatch( + Type Fn, detail::PersistentToASTSpecificStateConverter &Converter, + ASTContext &Context, const ASTQueryType &Query, + const std::tuple::StoredResultType...> + &Arguments, + std::index_sequence) { + auto ASTQueryResult = Converter.convert(Query.getResult()); + return Fn(Context, ASTQueryResult, std::get(Arguments)...); + } +}; + +template +struct ContinuationFunction { + using Type = llvm::Expected (*)( + ASTContext &, + typename ContinuationPassType< + typename StateTraits::StoredResultType>::Type...); + + template + static llvm::Expected dispatch( + Type Fn, detail::PersistentToASTSpecificStateConverter &, + ASTContext &Context, const ASTQueryType &, + const std::tuple::StoredResultType...> + &Arguments, + std::index_sequence) { + return Fn(Context, std::get(Arguments)...); + } +}; + +/// The refactoring contination contains a set of structures that implement +/// the refactoring operation continuation mechanism. +template +class SpecificRefactoringContinuation final : public RefactoringContinuation { +public: + static_assert(std::is_base_of::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::Type; + +private: + ConsumerFn Consumer; + std::unique_ptr ASTQuery; + /// Inputs store state that's dependent on the original TU. + llvm::Optional> Inputs; + /// State contains TU-independent values. + llvm::Optional< + std::tuple::PersistentType...>> + State; + + /// Converts a tuple that contains the TU dependent state to a tuple with + /// TU independent state. + template + std::tuple::PersistentType...> + convertToPersistentImpl(std::index_sequence) { + assert(Inputs && "TU-dependent state is already converted"); + return std::make_tuple( + detail::convertToPersistentRepresentation(std::get(*Inputs))...); + } + + template + bool gatherQueries( + std::vector &Queries, + const std::unique_ptr &Query, + typename std::enable_if< + std::is_base_of::value>::type * = nullptr) { + Queries.push_back(Query.get()); + return true; + } + + template + bool gatherQueries(std::vector &, const T &) { + // This input element is not a query. + return true; + } + + template + std::vector + gatherQueries(std::index_sequence) { + assert(Inputs && "TU-dependent state is already converted"); + std::vector Queries; + std::make_tuple(gatherQueries(Queries, std::get(*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 + llvm::Expected dispatch(ASTContext &Context, + std::index_sequence Seq) { + assert(State && "TU-independent state is not yet produced"); + detail::PersistentToASTSpecificStateConverter Converter(Context); + (void)std::make_tuple(Converter.addConvertible(std::get(*State))...); + Converter.runCoalescedConversions(); + auto Results = std::make_tuple(Converter.convert(std::get(*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 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 getAdditionalIndexerQueries() override { + return gatherQueries(std::index_sequence_for()); + } + + /// Query results are fetched. State is converted to a persistent + /// representation. + void persistTUSpecificState() override { + ASTQuery->invalidateTUSpecificState(); + State = + convertToPersistentImpl(std::index_sequence_for()); + Inputs = None; + } + + /// The state is converted to the AST representation in the given ASTContext + /// and the continuation is dispatched. + llvm::Expected + runInExternalASTUnit(ASTContext &Context) override { + return dispatch(Context, std::index_sequence_for()); + } +}; + +} // 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 std::enable_if< + std::is_base_of::value, + std::unique_ptr>::type +continueInExternalASTUnit( + std::unique_ptr ASTQuery, + typename detail::SpecificRefactoringContinuation< + ASTQueryType, QueryOrState...>::ConsumerFn Consumer, + QueryOrState... Inputs) { + return std::make_unique< + detail::SpecificRefactoringContinuation>( + 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..9b94cffe00d52 --- /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 = std::make_unique(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 = std::make_unique(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..35389f5bb8fde --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp @@ -0,0 +1,69 @@ +//===--- 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(); + } +} + +namespace llvm { +namespace yaml { +template <> struct CustomMappingTraits { + 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::parse(StringRef Source) { + Input YamlIn(Source); + // FIXME: Don't dump errors to stderr. + RefactoringOptionSet Result; + YamlIn >> Result; + if (YamlIn.error()) + return llvm::make_error("Failed to parse the option set", + YamlIn.error()); + return std::move(Result); +} + +void OldRefactoringOption::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..47dc31390d9be --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -0,0 +1,671 @@ +//===--- 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/LiteralSupport.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 Symbols, IndexedFileOccurrenceConsumer &Consumer, + 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) { + 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, + 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, + 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. + SourceLocation BeginLoc = SM.translateLineCol( + SM.getMainFileID(), Occurrence.Line, Occurrence.Column); + if (BeginLoc.isInvalid()) + return MatchKind::None; + StringRef SymbolNameStart = Symbol.Name[0]; + // 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) { + 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; + // 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 +findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, + ArrayRef 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 OldSymbolName &Name; + + ParseState stateForToken(const Token &RawTok); + +public: + unsigned SymbolIndex; + llvm::SmallVector SelectorLocations; + + SelectorParser(const OldSymbolName &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); } +}; + +/// 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) { + 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"); + 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 NamePiece.empty() ? ExpectingRParen : ExpectingRParenOrColon; + } + return NamePiece.empty() ? ExpectingSelectorPiece : ExpectingColon; + } + case ExpectingColon: + if (RawTok.is(tok::colon)) + return ExpectingSelectorPiece; + break; + case ExpectingRParenOrColon: + if (RawTok.is(tok::colon)) + return ExpectingRParen; + LLVM_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) { + 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; + State = None; + 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 Symbols, SourceLocation CommentLoc, + StringRef Comment, llvm::SmallVectorImpl &Result) { + for (const auto &Symbol : llvm::enumerate(Symbols)) { + const OldSymbolName &Name = Symbol.value().Name; + if (Name.containsEmptyPiece()) // Ignore Objective-C selectors with empty + // pieces. + continue; + size_t Offset = 0; + while (true) { + Offset = Comment.find(Name[0], /*From=*/Offset); + if (Offset == StringRef::npos) + break; + Result.push_back( + {CommentLoc.getLocWithOffset(Offset), (unsigned)Symbol.index()}); + Offset += 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 Symbols, + ArrayRef TextualMatches, SourceRange CommentRange, + llvm::function_ref Locations, + unsigned SymbolIndex)> + MatchHandler) { + std::string Source = + Lexer::getSourceText(CharSourceRange::getCharRange(CommentRange), SM, + LangOpts) + .str(); + OldSymbolOccurrence::OccurrenceKind Kind = + RawComment(SM, CommentRange, LangOpts.CommentOpts, /*Merged=*/false) + .isDocumentation() + ? OldSymbolOccurrence::MatchingDocComment + : OldSymbolOccurrence::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( + Preprocessor &PP, const SourceManager &SM, const LangOptions &LangOpts, + ArrayRef Symbols, + llvm::function_ref Locations, + unsigned SymbolIndex)> + MatchHandler) { + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, LangOpts); + RawLex.SetCommentRetentionState(true); + + llvm::SmallVector CommentMatches; + llvm::SmallVector SelectorParsers; + for (const auto &Symbol : llvm::enumerate(Symbols)) { + if (Symbol.value().IsObjCSelector) + SelectorParsers.push_back( + SelectorParser(Symbol.value().Name, Symbol.index())); + } + llvm::SmallVector 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()); + 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 (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); + } +} + +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::header_name)) + 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; + OldSymbolOccurrence Result( + OldSymbolOccurrence::MatchingFilename, + /*IsMacroExpansion=*/false, SymbolIndex, + RawTok.getLocation().getLocWithOffset( + NameOffset + (Filename.data() - RawTok.getLiteralData()))); + Consumer.handleOccurrence(Result, SM, LangOpts); +} + +void IndexedFileOccurrenceProducer::ExecuteAction() { + Lock.unlock(); // The execution should now be thread-safe. + 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; + } + SourceRange SymbolRange; + MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM, + LangOpts, SymbolRange, + /*AllowObjCSetterProp=*/true); + if (Match == MatchKind::None) + continue; + llvm::SmallVector Locs; + Locs.push_back(SymbolRange.getBegin()); + bool IsImpProp = Match == MatchKind::SourcePropSetterMatch; + if (IsImpProp) + Locs.push_back(SymbolRange.getEnd()); + OldSymbolOccurrence Result( + IsImpProp ? OldSymbolOccurrence::MatchingImplicitProperty + : OldSymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/Match == MatchKind::MacroExpansion, + Symbol.index(), Locs); + Consumer.handleOccurrence(Result, SM, LangOpts); + } + } + } + + if (Options && Options->get(option::AvoidTextualMatches())) + return; + findMatchingTextualOccurrences( + PP, SM, LangOpts, Symbols, + [&](OldSymbolOccurrence::OccurrenceKind Kind, + ArrayRef Locations, unsigned SymbolIndex) { + OldSymbolOccurrence Result(Kind, /*IsMacroExpansion=*/false, + SymbolIndex, Locations); + Consumer.handleOccurrence(Result, SM, LangOpts); + }); +} + +namespace { + +/// Maps from source locations to the indexed occurrences. +typedef llvm::DenseMap> + SourceLocationsToIndexedOccurrences; + +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 Tokens, const OldSymbolName &Name, + SmallVectorImpl &Pieces, + ObjCSymbolSelectorKind Kind) { + assert(!Tokens.empty() && "no tokens"); + assert(Name[0].empty() || 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 = Name[0].empty() ? 1 : 2; Index < Last; ++Index) { + const auto &Tok = Tokens[Index]; + + bool NoScoping = SquareCount == 0 && BraceCount == 0 && ParenCount == 0; + 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()) + 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 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; + SourceRange SymbolRange; + MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM, + LangOpts, SymbolRange); + if (Match == MatchKind::None) + continue; + SourceLocation Loc = SymbolRange.getBegin(); + if (Match == MatchKind::MacroExpansion) { + OldSymbolOccurrence Result(OldSymbolOccurrence::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 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; + 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; + + // Scan the source for the remaining selector pieces. + SmallVector 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)) { + OldSymbolOccurrence Result(OldSymbolOccurrence::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..c4f7d3403254b --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenamedSymbol.cpp @@ -0,0 +1,42 @@ +//===--- 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 + +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(FoundDecl)) + ObjCSelector = MD->getSelector(); +} + +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 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()); +} + +} // 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..6c6d1284f5e27 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenamingOperation.cpp @@ -0,0 +1,99 @@ +//===--- 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 OldSymbolName &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 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(OldSymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl &NewNames, + const LangOptions &LangOpts) { + auto Symbols = Operation.symbols(); + assert(!Symbols.empty()); + NewNames.push_back(std::move(NewName)); + if (const auto *PropertyDecl = + dyn_cast(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(OldSymbolName(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..7c55e653e9afb --- /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 + +namespace clang { +namespace tooling { + +SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd, + const Stmt *Body, + const SourceManager &SM) { + SourceLocation BodyStart = SM.getSpellingLoc(Body->getBeginLoc()); + 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::max()); + if (LineLocThatPrecedesBody.isValid()) + return LineLocThatPrecedesBody; + } + // We want to include the location of the '{'. + return isa(Body) ? BodyStart : BodyStart.getLocWithOffset(-1); +} + +SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart, + const Stmt *PreviousBody, + const SourceManager &SM) { + if (!isa(PreviousBody)) + return HeaderStart; + SourceLocation BodyEnd = SM.getSpellingLoc(PreviousBody->getEndLoc()); + unsigned BodyLine = SM.getSpellingLineNumber(BodyEnd); + unsigned HeaderLine = SM.getSpellingLineNumber(HeaderStart); + if (BodyLine >= HeaderLine) + return BodyEnd; + return HeaderStart; +} + +bool isLocationInAnyRange(SourceLocation Location, ArrayRef 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(S); + if (!CS) + return S->getEndLoc(); + return CS->body_back() ? CS->body_back()->getEndLoc() : 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::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 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..d5644b29c1b33 --- /dev/null +++ b/clang/lib/Tooling/Refactor/StmtUtils.cpp @@ -0,0 +1,72 @@ +//===--- 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(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(S)) + return false; + if (const auto *If = dyn_cast(S)) + return isSemicolonRequiredAfter(If->getElse() ? If->getElse() + : If->getThen()); + if (const auto *While = dyn_cast(S)) + return isSemicolonRequiredAfter(While->getBody()); + if (const auto *For = dyn_cast(S)) + return isSemicolonRequiredAfter(For->getBody()); + if (const auto *CXXFor = dyn_cast(S)) + return isSemicolonRequiredAfter(CXXFor->getBody()); + if (const auto *ObjCFor = dyn_cast(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; + } +} + +static bool isAssignmentOperator(const Stmt *S) { + if (const auto *PseudoExpr = dyn_cast(S)) + return isAssignmentOperator(PseudoExpr->getSyntacticForm()); + if (const auto *BO = dyn_cast(S)) + return BO->isAssignmentOp(); + return false; +} + +bool clang::tooling::isLexicalExpression(const Stmt *S, const Stmt *Parent) { + if (!isa(S)) + return false; + // Assignment operators should be treated as statements unless they are a part + // of an expression. + if (isAssignmentOperator(S) && (!Parent || !isa(Parent))) + return false; + return !cast(S)->getType()->isVoidType(); +} diff --git a/clang/lib/Tooling/Refactor/StmtUtils.h b/clang/lib/Tooling/Refactor/StmtUtils.h new file mode 100644 index 0000000000000..5bc319528e469 --- /dev/null +++ b/clang/lib/Tooling/Refactor/StmtUtils.h @@ -0,0 +1,38 @@ +//===--- 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); + +/// 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 + +#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..61da1873e38aa --- /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 &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()); +} + +OldSymbolName::OldSymbolName(StringRef Name, const LangOptions &LangOpts) { + initNames(Strings, Name, LangOpts.ObjC); +} + +OldSymbolName::OldSymbolName(StringRef Name, bool IsObjectiveCSelector) { + initNames(Strings, Name, IsObjectiveCSelector); +} + +OldSymbolName::OldSymbolName(ArrayRef Name) { + for (const auto &Piece : Name) + Strings.push_back(Piece.str()); +} + +void OldSymbolName::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 OldSymbolName &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..aa90503847d3b --- /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 { +public: + explicit SymbolOccurrenceFinderASTVisitor( + const SymbolOperation &Operation, const ASTContext &Context, + std::vector &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, + OldSymbolOccurrence::OccurrenceKind Kind = + OldSymbolOccurrence::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(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 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 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(), + 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(), + OldSymbolOccurrence::MatchingImplicitProperty); + return true; + } + checkDecl(Expr->getExplicitProperty(), Expr->getLocation()); + return true; + } + + // Other visitors: + + bool VisitTypeLoc(const TypeLoc Loc) { + TypedefTypeLoc TTL = Loc.getAs(); + 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(); + if (TSTL) { + checkDecl(Loc.getType()->getAsTagDecl(), TSTL.getNameLoc()); + } + if (const auto *TemplateTypeParm = + dyn_cast(Loc.getType())) { + checkDecl(TemplateTypeParm->getDecl(), Loc.getBeginLoc()); + } + if (const auto *TemplateSpecType = + dyn_cast(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 Locations, + OldSymbolOccurrence::OccurrenceKind Kind = + OldSymbolOccurrence::MatchingSymbol) { + if (Locations.size() != Operation.symbols()[SymbolIndex].Name.size()) + return; + + SmallVector 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(OldSymbolOccurrence( + 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(OldSymbolOccurrence(Kind, /*IsMacroExpansion=*/false, + SymbolIndex, StringLocations)); + } + + /// Adds a location without checking if the name is actually there. + void addLocation(unsigned SymbolIndex, SourceLocation Location, + OldSymbolOccurrence::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( + OldSymbolOccurrence(Kind, IsMacroExpansion, SymbolIndex, Location)); + } + + const SymbolOperation &Operation; + const ASTContext &Context; + std::vector &Occurrences; +}; +} // namespace + +std::vector +findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl) { + std::vector 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(Ctx)) + ReturnType = FD->getReturnType(); + else if (const auto *BD = dyn_cast(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()) + 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 struct C { T x; }; + // struct Foo { struct Bar { }; }; + // return C(); + // } + // + // 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(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(FoundDecl) || + isa(FoundDecl) || + isa(FoundDecl)) + return true; + + if (const auto *VD = dyn_cast(FoundDecl)) + return VD->isLocalVarDeclOrParm(); + + // Objective-C selector renames must be global. + if (isa(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(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(FoundDecl)) + return !escapesEnclosingDecl(FD->getParent(), LangOpts); + + if (const auto *MD = dyn_cast(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(FoundDecl)) { + // We need the implementation TU when the IVAR is declared in an @interface + // without an @implementation. + if (const auto *ID = + dyn_cast(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(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(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(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 + +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 { +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(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(FoundDecl)) { + handleCXXRecordDecl(RecordDecl, USRSet); + } else if (const auto *TemplateDecl = + dyn_cast(FoundDecl)) { + handleClassTemplateDecl(TemplateDecl, USRSet); + } else if (const auto *MethodDecl = dyn_cast(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(FoundDecl)) + if (DeclarationName::compare(MethodDecl->getDeclName(), + FoundMethodDecl->getDeclName()) == 0 && + MethodDecl->isOverriding()) + PotentialObjCMethodOverridders.push_back(MethodDecl); + return true; + } + + bool VisitClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *PartialSpec) { + if (!isa(FoundDecl) && !isa(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(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 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 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 OverriddenMethods; + std::vector PartialSpecs; + /// \brief An array of Objective-C methods that potentially override the + /// found Objective-C method declaration \p FoundDecl. + std::vector 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..03c8a5bc45b39 --- /dev/null +++ b/clang/lib/Tooling/Refactor/TypeUtils.cpp @@ -0,0 +1,200 @@ +//===--- 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 { +public: + NSAPI API; + + BOOLUseFinder(const ASTContext &Context) + : API(const_cast(Context)) {} + + bool VisitStmt(const Stmt *S) { + if (const auto *E = dyn_cast(S)) + return !API.isObjCBOOLType(E->getType()); + return true; + } + + static bool hasUseOfObjCBOOL(const ASTContext &Ctx, const Expr *E) { + return !BOOLUseFinder(Ctx).TraverseStmt(const_cast(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(); + 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().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 + // we should use bool. + if (isa(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(DC); + if (!ND) + return false; + + while (const DeclContext *Parent = ND->getParent()) { + if (!isa(Parent)) + break; + ND = cast(Parent); + } + + return ND->isStdNamespace(); +} + +static QualType desugarStdTypedef(QualType T) { + const auto *TT = T->getAs(); + 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()) + DesugaredType = PT->getPointeeType(); + DesugaredType = desugarStdTypedef(DesugaredType); + if (DesugaredType.isNull()) + return T; + if (const auto *ET = DesugaredType->getAs()) + 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(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(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(E)) { + if (PRE->isMessagingGetter()) { + 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)); + } + } + } + + // 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().ObjC) + return T; + E = E->IgnoreParenImpCasts(); + if (const auto *BinOp = dyn_cast(E)) { + if (BinOp->isLogicalOp() || BinOp->isComparisonOp()) + return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx); + } else if (const auto *UnOp = dyn_cast(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..3768dcbaaef96 --- /dev/null +++ b/clang/lib/Tooling/Refactor/USRFinder.cpp @@ -0,0 +1,706 @@ +//===--- 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/Refactoring/RefactoringDiagnostic.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace llvm; + +namespace clang { +namespace tooling { +namespace rename { + +typedef std::function + 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 { +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(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(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) { + SourceLocation Loc = Decl->getSelectorLoc(I); + if (!checkOccurrence(Decl, Loc, + Loc.getLocWithOffset( + 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) { + SourceLocation Loc = Expr->getSelectorLoc(I); + if (!checkOccurrence(Decl, Loc, + Loc.getLocWithOffset( + 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(Loc.getType())) + return checkOccurrence(TemplateTypeParm->getDecl(), TypeBeginLoc, + TypeEndLoc); + if (const auto *TemplateSpecType = + dyn_cast(Loc.getType())) { + return checkOccurrence( + TemplateSpecType->getTemplateName().getAsTemplateDecl(), TypeBeginLoc, + TypeEndLoc); + } + TypedefTypeLoc TTL = Loc.getAs(); + 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(); + 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(Decl)) + return true; + if (const auto *FD = dyn_cast(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()) + return A; + if (const auto *DCD = dyn_cast(D->getDeclContext())) { + if (const auto *A = DCD->getAttr()) + return A; + } + return nullptr; +} + +static bool overridesSystemMethod(const ObjCMethodDecl *MD, + const SourceManager &SM) { + SmallVector Overrides; + MD->getOverriddenMethods(Overrides); + for (const auto *Override : Overrides) { + SourceLocation Loc = Override->getBeginLoc(); + 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(D)) { + TKind = SD->getSpecializationKind(); + } else if (const FunctionDecl *FD = dyn_cast(D)) { + TKind = FD->getTemplateSpecializationKind(); + } else if (auto *VD = dyn_cast(D)) { + TKind = VD->getTemplateSpecializationKind(); + } else if (const auto *RD = dyn_cast(D)) { + if (RD->getInstantiatedFromMemberClass()) + TKind = RD->getTemplateSpecializationKind(); + } else if (const auto *ED = dyn_cast(D)) { + if (ED->getInstantiatedFromMemberEnum()) + TKind = ED->getTemplateSpecializationKind(); + } else if (isa(D) || isa(D) || + isa(D)) { + if (const auto *Parent = dyn_cast(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(D->getDeclContext())) + return CTSD->getTemplateInstantiationPattern(); + else if (const auto *RD = dyn_cast(D->getDeclContext())) + return RD->getInstantiatedFromMemberClass(); + return nullptr; +} + +static const NamedDecl * +adjustTemplateImplicitInstantiation(const NamedDecl *D) { + if (const ClassTemplateSpecializationDecl *SD = + dyn_cast(D)) { + return SD->getTemplateInstantiationPattern(); + } else if (const FunctionDecl *FD = dyn_cast(D)) { + return FD->getTemplateInstantiationPattern(); + } else if (auto *VD = dyn_cast(D)) { + return VD->getTemplateInstantiationPattern(); + } else if (const auto *RD = dyn_cast(D)) { + return RD->getInstantiatedFromMemberClass(); + } else if (const auto *ED = dyn_cast(D)) { + return ED->getInstantiatedFromMemberEnum(); + } else if (isa(D) || isa(D)) { + const auto *ND = cast(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(D)) { + if (const auto *ED = dyn_cast(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); + // FIXME: If point is in a system header, return early here. + + 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. + FileID InitiationFile = Context.getSourceManager().getFileID(Point); + for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { + 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 (Context.getSourceManager().getFileID(FileLoc) == InitiationFile) + Visitor.TraverseDecl(CurrDecl); + if (Visitor.isDone()) + break; + } + + if (!Visitor.isDone()) { + NestedNameSpecifierLocFinder Finder(const_cast(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(ND)) + ND = CtorDecl->getParent(); + else if (const auto *DtorDecl = dyn_cast(ND)) + ND = DtorDecl->getParent(); + + if (isTemplateImplicitInstantiation(ND)) + ND = adjustTemplateImplicitInstantiation(ND); + + // Builtins can't be renamed. + if (const auto *FD = dyn_cast(ND)) { + if (FD->getBuiltinID()) { + Diag(diag::err_rename_builtin_function) << ND->getDeclName(); + return nullptr; + } + } + // Declarations with invalid locations are probably implicit. + if (ND->getBeginLoc().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->getBeginLoc())) + return nullptr; + if (const auto *TD = dyn_cast(ND)) { + if (const TypedefNameDecl *CTD = TD->getCanonicalDecl()) { + if (CheckSystemLoc(CTD->getBeginLoc())) + return nullptr; + } + } else if (const auto *TD = dyn_cast(ND)) { + if (const TagDecl *CTD = TD->getCanonicalDecl()) { + if (CheckSystemLoc(CTD->getBeginLoc())) + return nullptr; + } + } else if (const auto *FD = dyn_cast(ND)) { + if (const FunctionDecl *CFD = FD->getCanonicalDecl()) { + if (CheckSystemLoc(CFD->getBeginLoc())) + return nullptr; + } + } else if (const auto *VD = dyn_cast(ND)) { + if (const VarDecl *CVD = VD->getCanonicalDecl()) { + if (CheckSystemLoc(CVD->getBeginLoc())) + 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(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 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/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/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/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 +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/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/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/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/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 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/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes new file mode 100644 index 0000000000000..c584ea0d76724 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes @@ -0,0 +1,48 @@ +Name: SimpleKit +Tags: +- Name: RenamedAgainInAPINotesA + 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 +- Name: getCFAuditedToUnowned_DUMP + RetainCountConvention: CFReturnsNotRetained +- Name: getCFAuditedToOwned_DUMP + RetainCountConvention: CFReturnsRetained +- Name: getCFAuditedToNone_DUMP + RetainCountConvention: none +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 new file mode 100644 index 0000000000000..669e9b9d4d061 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h @@ -0,0 +1,29 @@ +struct RenamedAgainInAPINotesA { + int field; +} __attribute__((swift_name("bad"))); + +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); + +#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)); +@end 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/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes new file mode 100644 index 0000000000000..817af123fc77b --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -0,0 +1,74 @@ +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 + - 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: + - Name: A + Methods: + - Selector: "transform:integer:" + 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/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.apinotes b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes new file mode 100644 index 0000000000000..ff88fdbaeac83 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes @@ -0,0 +1,98 @@ +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: "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 + 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 + - 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: + - Name: A + Methods: + - Selector: "transform:integer:" + 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 new file mode 100644 index 0000000000000..1a192f5432fd1 --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -0,0 +1,60 @@ +#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 + +@interface ProcessInfo : A ++(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 + +#import + +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 + +@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/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/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/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/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 + +@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 + +@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/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/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.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" 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/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/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes new file mode 100644 index 0000000000000..572c714b3d61a --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -0,0 +1,156 @@ +Name: VersionedKit +Classes: + - Name: TestProperties + SwiftObjCMembers: true + 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 +Functions: + - Name: unversionedRenameDUMP + SwiftName: 'unversionedRename_NOTES()' +Tags: + - Name: APINotedFlagEnum + FlagEnum: true + - Name: APINotedOpenEnum + 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 +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: + - Name: MyReferenceType + SwiftBridge: '' + - Name: TestGenericDUMP + SwiftImportAsNonGeneric: true + - Name: TestProperties + SwiftObjCMembers: false + 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 + - Name: Swift3RenamedOnlyDUMP + SwiftName: SpecialSwift3Name + - Name: Swift3RenamedAlsoDUMP + SwiftName: SpecialSwift3Also + Functions: + - Name: moveToPointDUMP + SwiftName: 'moveTo(a:b:)' + - Name: acceptClosure + Parameters: + - Position: 0 + NoEscape: false + - Name: privateFunc + SwiftPrivate: false + 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 + - 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. + Classes: + - Name: Swift4RenamedDUMP + SwiftName: SpecialSwift4Name + 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 new file mode 100644 index 0000000000000..9ce95633c523b --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h @@ -0,0 +1,137 @@ +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)); + +typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct))); + +#if __OBJC__ +@class NSString; + +extern NSString *MyErrorDomain; + +enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorCode { + MyErrorCodeFailed = 1 +}; + +__attribute__((swift_bridge("MyValueType"))) +@interface MyReferenceType +@end + +@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 + +@interface Base +@end + +@interface TestGenericDUMP : Base +- (Element)element; +@end + +@interface Swift3RenamedOnlyDUMP +@end + +__attribute__((swift_name("Swift4Name"))) +@interface Swift3RenamedAlsoDUMP +@end + +@interface Swift4RenamedDUMP +@end + +#endif + + +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, +}; + + +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))); + + +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/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/Inputs/Headers/APINotes.apinotes b/clang/test/APINotes/Inputs/Headers/APINotes.apinotes new file mode 100644 index 0000000000000..08210fc705651 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/APINotes.apinotes @@ -0,0 +1,18 @@ +Name: HeaderLib +SwiftInferImportAsMember: true +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/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/HeaderLib.apinotes b/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes new file mode 100644 index 0000000000000..c822964ad29c7 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.apinotes @@ -0,0 +1,37 @@ +Name: HeaderLib +SwiftInferImportAsMember: true +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 ] + - Name: do_something_with_arrays + Parameters: + - Position: 0 + Nullability: N + - Position: 1 + Nullability: N + - Name: take_pointer_and_int + Parameters: + - Position: 0 + Nullability: N + 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 \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/Headers/HeaderLib.h b/clang/test/APINotes/Inputs/Headers/HeaderLib.h new file mode 100644 index 0000000000000..8065249607851 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/HeaderLib.h @@ -0,0 +1,19 @@ +#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); +void do_something_with_arrays(int simple[], int nested[][2]); + +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/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/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 new file mode 100644 index 0000000000000..aa014296ca7d2 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/ModuleWithWrongCasePrivate.h @@ -0,0 +1 @@ +extern int ModuleWithWrongCasePrivate; 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/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/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.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap new file mode 100644 index 0000000000000..5b44e7b055843 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -0,0 +1,19 @@ +module HeaderLib { + header "HeaderLib.h" +} + +module InstancetypeModule { + header "InstancetypeModule.h" +} + +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 new file mode 100644 index 0000000000000..2ecf322ed18d9 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/module.private.modulemap @@ -0,0 +1,5 @@ +module PrivateLib { + header "PrivateLib.h" +} + +module ModuleWithWrongCasePrivate.Inner {} diff --git a/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.apinotes b/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.apinotes new file mode 100644 index 0000000000000..77db844008990 --- /dev/null +++ b/clang/test/APINotes/Inputs/yaml-reader-errors/UIKit.apinotes @@ -0,0 +1,65 @@ +--- +Name: UIKit +Classes: + - Name: UIFont + Methods: + - Selector: 'fontWithName:size:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: O + DesignatedInit: true +# CHECK: duplicate definition of method '-[UIFont fontWithName:size:]' + - Selector: 'fontWithName:size:' + MethodKind: Instance + Nullability: [ N ] + NullabilityOfRet: O + DesignatedInit: true + Properties: + - Name: familyName + Nullability: N + - Name: fontName + Nullability: N +# CHECK: duplicate definition of instance property 'UIFont.familyName' + - Name: familyName + Nullability: N +# 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 + - Name: 'globalFoo2' + Nullability: [ N, N, O, S ] + NullabilityOfRet: O +Globals: + - Name: globalVar + Nullability: O + - 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 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/availability.m b/clang/test/APINotes/availability.m new file mode 100644 index 0000000000000..2ddc2a73da804 --- /dev/null +++ b/clang/test/APINotes/availability.m @@ -0,0 +1,48 @@ +// RUN: rm -rf %t +// 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 +#import + +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}} + + unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}} + // 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: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}} + + id 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}} + + [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; +} + diff --git a/clang/test/APINotes/broken_types.m b/clang/test/APINotes/broken_types.m new file mode 100644 index 0000000000000..ee33ff7c4b4b9 --- /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 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2> %t.err +// RUN: FileCheck %s < %t.err + +#include "BrokenTypes.h" + +// CHECK: :1:1: error: unknown type name 'not_a_type' +// CHECK-NEXT: not_a_type +// CHECK-NEXT: ^ + +// CHECK: :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/case-for-private-apinotes-file.c b/clang/test/APINotes/case-for-private-apinotes-file.c new file mode 100644 index 0000000000000..6aff3db54918e --- /dev/null +++ b/clang/test/APINotes/case-for-private-apinotes-file.c @@ -0,0 +1,22 @@ +// 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 +#include +#include +#include +#include + +// 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: 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'}} +} diff --git a/clang/test/APINotes/module-cache.m b/clang/test/APINotes/module-cache.m new file mode 100644 index 0000000000000..5dcaf1181f9dc --- /dev/null +++ b/clang/test/APINotes/module-cache.m @@ -0,0 +1,65 @@ +// RUN: rm -rf %t + +// 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 -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 -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 + +// 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 -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 -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 +// 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/test/APINotes/nullability.c b/clang/test/APINotes/nullability.c new file mode 100644 index 0000000000000..e07fc2e5c1174 --- /dev/null +++ b/clang/test/APINotes/nullability.c @@ -0,0 +1,21 @@ +// 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 + +#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}} + + 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'}} + return 0; +} + diff --git a/clang/test/APINotes/nullability.m b/clang/test/APINotes/nullability.m new file mode 100644 index 0000000000000..a684a4bbde743 --- /dev/null +++ b/clang/test/APINotes/nullability.m @@ -0,0 +1,44 @@ +// 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 + +// 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 -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 + +int main() { + A *a; + +#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 + + [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}} + + [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/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 okayProto, + PerfectlyNormalClass *badClass // expected-error {{'PerfectlyNormalClass' is unavailable}} +) { + // expected-note@LayeredKitImpl/LayeredKitImpl.h:4 {{'PerfectlyNormalClass' has been explicitly marked unavailable here}} +} diff --git a/clang/test/APINotes/objc_designated_inits.m b/clang/test/APINotes/objc_designated_inits.m new file mode 100644 index 0000000000000..1f2b8ed534b7a --- /dev/null +++ b/clang/test/APINotes/objc_designated_inits.m @@ -0,0 +1,17 @@ +// 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 "HeaderLib.h" +#import + +@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/properties.m b/clang/test/APINotes/properties.m new file mode 100644 index 0000000000000..60423633e9a64 --- /dev/null +++ b/clang/test/APINotes/properties.m @@ -0,0 +1,45 @@ +// RUN: rm -rf %t && mkdir -p %t + +// 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. + +@import VersionedKit; + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnly 'id' +// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-NOT: Attr + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClass 'id' +// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-NOT: Attr + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id' +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}} +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-NOT: Attr + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id' +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}} +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-NOT: Attr + +// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id' +// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// 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 {{.+}} <> +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} <> +// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} +// CHECK-NOT: Attr + +// CHECK-LABEL: Decl diff --git a/clang/test/APINotes/retain-count-convention.m b/clang/test/APINotes/retain-count-convention.m new file mode 100644 index 0000000000000..0c0ad7320f29d --- /dev/null +++ b/clang/test/APINotes/retain-count-convention.m @@ -0,0 +1,38 @@ +// 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 + +// CHECK: void *getCFOwnedToUnowned() __attribute__((cf_returns_not_retained)); +// CHECK: void *getCFUnownedToOwned() __attribute__((cf_returns_retained)); +// 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))); +// 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 + +// 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 diff --git a/clang/test/APINotes/search-order.m b/clang/test/APINotes/search-order.m new file mode 100644 index 0000000000000..aa2f21a2eaaa3 --- /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 -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 -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 -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 +} 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 +#include + +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'}} diff --git a/clang/test/APINotes/types.m b/clang/test/APINotes/types.m new file mode 100644 index 0000000000000..133d504713d76 --- /dev/null +++ b/clang/test/APINotes/types.m @@ -0,0 +1,28 @@ +// RUN: rm -rf %t && mkdir -p %t +// 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 +#import + +// 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)'}} + + 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}} 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 + +// 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 new file mode 100644 index 0000000000000..db5a9d3b5f1e3 --- /dev/null +++ b/clang/test/APINotes/versioned.m @@ -0,0 +1,187 @@ +// 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 +// 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-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-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 + +// 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 moveToPointDUMP +// CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)" +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <> "moveTo(a:b:)" +// CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <> "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 {{.+}} <> +// CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} <> +// CHECK-DUMP-NOT: Attr + +// CHECK-DUMP-LABEL: Dumping Swift3RenamedOnlyDUMP +// CHECK-DUMP: in VersionedKit Swift3RenamedOnlyDUMP +// 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 {{.+}} <> "SpecialSwift3Name" +// CHECK-DUMP-NOT: Attr + +// CHECK-DUMP-LABEL: Dumping Swift3RenamedAlsoDUMP +// CHECK-DUMP: in VersionedKit Swift3RenamedAlsoDUMP +// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}} +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "Swift4Name" +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "Swift4Name" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}} +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <> "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 {{.+}} <> "SpecialSwift4Name" +// CHECK-DUMP-NOT: Attr + +// CHECK-DUMP-NOT: Dumping + +// 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 __attribute__((ns_error_domain(MyErrorDomain))) MyErrorCode { +// CHECK-UNVERSIONED-NEXT: MyErrorCodeFailed = 1 +// CHECK-UNVERSIONED-NEXT: }; + +// CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType"))) +// CHECK-UNVERSIONED: @interface MyReferenceType + +// CHECK-VERSIONED: void privateFunc(); + +// CHECK-VERSIONED: typedef double MyDoubleWrapper; + +// CHECK-VERSIONED: enum MyErrorCode { +// CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1 +// CHECK-VERSIONED-NEXT: }; + +// CHECK-VERSIONED-NOT: __attribute__((swift_bridge("MyValueType"))) +// CHECK-VERSIONED: @interface MyReferenceType + +// CHECK-UNVERSIONED: __attribute__((swift_objc_members) +// CHECK-UNVERSIONED-NEXT: @interface TestProperties +// CHECK-VERSIONED-NOT: __attribute__((swift_objc_members) +// CHECK-VERSIONED: @interface TestProperties + +// CHECK-UNVERSIONED-LABEL: enum __attribute__((flag_enum)) FlagEnum { +// CHECK-UNVERSIONED-NEXT: FlagEnumA = 1, +// CHECK-UNVERSIONED-NEXT: FlagEnumB = 2 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((flag_enum)) NewlyFlagEnum { +// CHECK-UNVERSIONED-NEXT: NewlyFlagEnumA = 1, +// CHECK-UNVERSIONED-NEXT: NewlyFlagEnumB = 2 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((flag_enum)) APINotedFlagEnum { +// CHECK-UNVERSIONED-NEXT: APINotedFlagEnumA = 1, +// CHECK-UNVERSIONED-NEXT: APINotedFlagEnumB = 2 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) OpenEnum { +// CHECK-UNVERSIONED-NEXT: OpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) NewlyOpenEnum { +// CHECK-UNVERSIONED-NEXT: NewlyOpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) NewlyClosedEnum { +// CHECK-UNVERSIONED-NEXT: NewlyClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) ClosedToOpenEnum { +// CHECK-UNVERSIONED-NEXT: ClosedToOpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) OpenToClosedEnum { +// CHECK-UNVERSIONED-NEXT: OpenToClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) APINotedOpenEnum { +// CHECK-UNVERSIONED-NEXT: APINotedOpenEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) APINotedClosedEnum { +// CHECK-UNVERSIONED-NEXT: APINotedClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; + +// CHECK-VERSIONED-LABEL: enum __attribute__((flag_enum)) FlagEnum { +// CHECK-VERSIONED-NEXT: FlagEnumA = 1, +// CHECK-VERSIONED-NEXT: FlagEnumB = 2 +// 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 __attribute__((flag_enum)) APINotedFlagEnum { +// CHECK-VERSIONED-NEXT: APINotedFlagEnumA = 1, +// CHECK-VERSIONED-NEXT: APINotedFlagEnumB = 2 +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) OpenEnum { +// CHECK-VERSIONED-NEXT: OpenEnumA = 1 +// 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 __attribute__((enum_extensibility("closed"))) ClosedToOpenEnum { +// CHECK-VERSIONED-NEXT: ClosedToOpenEnumA = 1 +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) OpenToClosedEnum { +// CHECK-VERSIONED-NEXT: OpenToClosedEnumA = 1 +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) APINotedOpenEnum { +// CHECK-VERSIONED-NEXT: APINotedOpenEnumA = 1 +// CHECK-VERSIONED-NEXT: }; +// CHECK-VERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) APINotedClosedEnum { +// CHECK-VERSIONED-NEXT: APINotedClosedEnumA = 1 +// CHECK-VERSIONED-NEXT: }; + +// These don't actually have versioned information, so we just check them once. +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) SoonToBeCFEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeCFEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) SoonToBeNSEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeNSEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)) SoonToBeCFOptions { +// CHECK-UNVERSIONED-NEXT: SoonToBeCFOptionsA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("open"))) __attribute__((flag_enum)) SoonToBeNSOptions { +// CHECK-UNVERSIONED-NEXT: SoonToBeNSOptionsA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) SoonToBeCFClosedEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeCFClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum __attribute__((enum_extensibility("closed"))) SoonToBeNSClosedEnum { +// CHECK-UNVERSIONED-NEXT: SoonToBeNSClosedEnumA = 1 +// CHECK-UNVERSIONED-NEXT: }; +// CHECK-UNVERSIONED-LABEL: enum UndoAllThatHasBeenDoneToMe { +// CHECK-UNVERSIONED-NEXT: UndoAllThatHasBeenDoneToMeA = 1 +// CHECK-UNVERSIONED-NEXT: }; diff --git a/clang/test/APINotes/yaml-convert-diags.c b/clang/test/APINotes/yaml-convert-diags.c new file mode 100644 index 0000000000000..8d5c0fb70568e --- /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 %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-parse-diags.c b/clang/test/APINotes/yaml-parse-diags.c new file mode 100644 index 0000000000000..a7b370aee942e --- /dev/null +++ b/clang/test/APINotes/yaml-parse-diags.c @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fapinotes %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.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/AST/ast-dump-decl-json.m b/clang/test/AST/ast-dump-decl-json.m index 235533d0bff9b..e22232b8b90ac 100644 --- a/clang/test/AST/ast-dump-decl-json.m +++ b/clang/test/AST/ast-dump-decl-json.m @@ -1019,11 +1019,11 @@ void f() { // CHECK: "kind": "ObjCCompatibleAliasDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 898, +// CHECK-NEXT: "offset": 919, // 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": { @@ -1475,7 +1475,173 @@ void f() { // CHECK-NEXT: "qualType": "int" // CHECK-NEXT: } // CHECK-NEXT: } -// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ObjCMethodDecl", +// CHECK-NEXT: "loc": { +// CHECK-NEXT: "offset": 1109, +// CHECK-NEXT: "line": 70, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 1109, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 1121, +// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "getterFoo", +// CHECK-NEXT: "returnType": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "instance": true +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ObjCMethodDecl", +// CHECK-NEXT: "loc": { +// CHECK-NEXT: "offset": 1109, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 1109, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 1121, +// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "setterFoo:", +// CHECK-NEXT: "returnType": { +// CHECK-NEXT: "qualType": "void" +// CHECK-NEXT: }, +// CHECK-NEXT: "instance": true, +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ParmVarDecl", +// CHECK-NEXT: "loc": { +// CHECK-NEXT: "offset": 1033, +// CHECK-NEXT: "line": 63, +// CHECK-NEXT: "col": 52, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 1033, +// CHECK-NEXT: "col": 52, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 1033, +// CHECK-NEXT: "col": 52, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "name": "foo", +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ObjCMethodDecl", +// CHECK-NEXT: "loc": { +// CHECK-NEXT: "offset": 1128, +// CHECK-NEXT: "line": 71, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 1128, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 1140, +// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "bar", +// CHECK-NEXT: "returnType": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: }, +// CHECK-NEXT: "instance": true +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ObjCMethodDecl", +// CHECK-NEXT: "loc": { +// CHECK-NEXT: "offset": 1128, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 1128, +// CHECK-NEXT: "col": 1, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 1140, +// CHECK-NEXT: "col": 13, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "setBar:", +// CHECK-NEXT: "returnType": { +// CHECK-NEXT: "qualType": "void" +// CHECK-NEXT: }, +// CHECK-NEXT: "instance": true, +// CHECK-NEXT: "inner": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "ParmVarDecl", +// CHECK-NEXT: "loc": { +// CHECK-NEXT: "offset": 1052, +// CHECK-NEXT: "line": 64, +// CHECK-NEXT: "col": 15, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 1052, +// CHECK-NEXT: "col": 15, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 1052, +// CHECK-NEXT: "col": 15, +// CHECK-NEXT: "tokLen": 3 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "name": "bar", +// CHECK-NEXT: "type": { +// CHECK-NEXT: "qualType": "int" +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } // CHECK-NEXT: ] // CHECK-NEXT: } diff --git a/clang/test/AST/ast-dump-decl.m b/clang/test/AST/ast-dump-decl.m index 6cef98925f015..50b32e141220d 100644 --- a/clang/test/AST/ast-dump-decl.m +++ b/clang/test/AST/ast-dump-decl.m @@ -148,3 +148,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/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/Analysis/Inputs/expected-plists/nullability-notes.m.plist b/clang/test/Analysis/Inputs/expected-plists/nullability-notes.m.plist index 314af1e4c952b..5e915d63ffde8 100644 --- a/clang/test/Analysis/Inputs/expected-plists/nullability-notes.m.plist +++ b/clang/test/Analysis/Inputs/expected-plists/nullability-notes.m.plist @@ -188,7 +188,6 @@ 0 - 10 14 16 17 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/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}} diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index 5bdbcd9415ab0..b609c7274c0fe 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -55,6 +55,7 @@ list(APPEND CLANG_TEST_DEPS clang-format clang-tblgen clang-offload-bundler + clang-refactor-test clang-import-test clang-rename clang-refactor @@ -63,7 +64,7 @@ list(APPEND CLANG_TEST_DEPS diagtool hmaptool ) - + if(CLANG_ENABLE_STATIC_ANALYZER) list(APPEND CLANG_TEST_DEPS clang-check 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. diff --git a/clang/test/CodeGen/atomic_ops.c b/clang/test/CodeGen/atomic_ops.c index 0af1d387192d2..a853ba9f739c7 100644 --- a/clang/test/CodeGen/atomic_ops.c +++ b/clang/test/CodeGen/atomic_ops.c @@ -37,3 +37,56 @@ void baz(int y) { // CHECK: {{store atomic|call void @__atomic_store}} x += y; } + +_Atomic(int) compound_add(_Atomic(int) in) { +// CHECK-LABEL: @compound_add +// CHECK: [[OLD:%.*]] = atomicrmw add i32* {{.*}}, i32 5 seq_cst +// CHECK: [[NEW:%.*]] = add i32 [[OLD]], 5 +// CHECK: ret i32 [[NEW]] + + return (in += 5); +} + +_Atomic(int) compound_sub(_Atomic(int) in) { +// CHECK-LABEL: @compound_sub +// CHECK: [[OLD:%.*]] = atomicrmw sub i32* {{.*}}, i32 5 seq_cst +// CHECK: [[NEW:%.*]] = sub i32 [[OLD]], 5 +// CHECK: ret i32 [[NEW]] + + return (in -= 5); +} + +_Atomic(int) compound_xor(_Atomic(int) in) { +// CHECK-LABEL: @compound_xor +// CHECK: [[OLD:%.*]] = atomicrmw xor i32* {{.*}}, i32 5 seq_cst +// CHECK: [[NEW:%.*]] = xor i32 [[OLD]], 5 +// CHECK: ret i32 [[NEW]] + + return (in ^= 5); +} + +_Atomic(int) compound_or(_Atomic(int) in) { +// CHECK-LABEL: @compound_or +// CHECK: [[OLD:%.*]] = atomicrmw or i32* {{.*}}, i32 5 seq_cst +// CHECK: [[NEW:%.*]] = or i32 [[OLD]], 5 +// CHECK: ret i32 [[NEW]] + + return (in |= 5); +} + +_Atomic(int) compound_and(_Atomic(int) in) { +// CHECK-LABEL: @compound_and +// CHECK: [[OLD:%.*]] = atomicrmw and i32* {{.*}}, i32 5 seq_cst +// CHECK: [[NEW:%.*]] = and i32 [[OLD]], 5 +// CHECK: ret i32 [[NEW]] + + return (in &= 5); +} + +_Atomic(int) compound_mul(_Atomic(int) in) { +// CHECK-LABEL: @compound_mul +// CHECK: cmpxchg i32* {{%.*}}, i32 {{%.*}}, i32 [[NEW:%.*]] seq_cst seq_cst +// CHECK: ret i32 [[NEW]] + + return (in *= 5); +} diff --git a/clang/test/CodeGen/debug-info-extern-call.c b/clang/test/CodeGen/debug-info-extern-call.c index 7f58fe59a6351..e35669b78f938 100644 --- a/clang/test/CodeGen/debug-info-extern-call.c +++ b/clang/test/CodeGen/debug-info-extern-call.c @@ -1,21 +1,8 @@ -// When entry values are emitted, expect a subprogram for extern decls so that -// the dwarf generator can describe call site parameters at extern call sites. -// -// RUN: %clang -Xclang -femit-debug-entry-values -g -O2 -target x86_64-none-linux-gnu -S -emit-llvm %s -o - | FileCheck %s -check-prefix=ENTRY-VAL -// ENTRY-VAL: !DISubprogram(name: "fn1" +// RUN: %clang -Xclang -femit-debug-entry-values -g -O2 -target x86_64-none-linux-gnu -S -emit-llvm %s -o - | FileCheck %s -check-prefix=CHECK-EXT +// CHECK-EXT: !DISubprogram(name: "fn1" -// Similarly, when the debugger tuning is gdb, expect a subprogram for extern -// decls so that the dwarf generator can describe information needed for tail -// call frame reconstrution. -// -// RUN: %clang -g -O2 -target x86_64-none-linux-gnu -ggdb -S -emit-llvm %s -o - | FileCheck %s -check-prefix=GDB -// GDB: !DISubprogram(name: "fn1" -// -// Do not emit a subprogram for extern decls when entry values are disabled and -// the tuning is not set to gdb. -// -// RUN: %clang -g -O2 -target x86_64-none-linux-gnu -gsce -S -emit-llvm %s -o - | FileCheck %s -check-prefix=SCE -// SCE-NOT: !DISubprogram(name: "fn1" +// RUN: %clang -g -O2 -target x86_64-none-linux-gnu -S -emit-llvm %s -o - | FileCheck %s +// CHECK-NOT: !DISubprogram(name: "fn1" extern int fn1(int a, int b); 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/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/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-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- 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-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/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/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/CodeGen/split-cold-code.c b/clang/test/CodeGen/split-cold-code.c new file mode 100644 index 0000000000000..30d81bf42f026 --- /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 +// +// Split 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 +// +// 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 +// +// 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-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 diff --git a/clang/test/CodeGenCXX/apple-kext.cpp b/clang/test/CodeGenCXX/apple-kext.cpp index 0d7ccfb3be1ae..990bc146c57c5 100644 --- a/clang/test/CodeGenCXX/apple-kext.cpp +++ b/clang/test/CodeGenCXX/apple-kext.cpp @@ -4,6 +4,25 @@ // CHECK: @llvm.global_ctors = appending global {{.*}} { i32 65535, void ()* [[CTOR0:@.*]], i8* null } // CHECK: @llvm.global_dtors = appending global {{.*}} { i32 65535, void ()* [[DTOR0:@.*]], i8* null } +// Check that the base destructor is marked as always_inline when generating +// code for kext. + +namespace testBaseDestructor { +#pragma clang optimize off +struct D { + virtual ~D(); +}; + +D::~D() {} +#pragma clang optimize on +} + +// CHECK: define void @_ZN18testBaseDestructor1DD2Ev({{.*}}) unnamed_addr #[[ATTR0:.*]] align 2 { + +// CHECK: define void @_ZN18testBaseDestructor1DD1Ev({{.*}}) unnamed_addr #[[ATTR1:.*]] align 2 { + +// CHECK: define void @_ZN18testBaseDestructor1DD0Ev({{.*}}) unnamed_addr #[[ATTR1]] align 2 { + // rdar://11241230 namespace test0 { struct A { A(); ~A(); }; @@ -20,3 +39,6 @@ namespace test0 { // CHECK: define internal void [[DTOR0]]() // CHECK: call void @_ZN5test01AD1Ev([[A]]* @_ZN5test01aE) // CHECK-NEXT: ret void + +// CHECK: attributes #[[ATTR0]] = { alwaysinline nounwind {{.*}} } +// CHECK: attributes #[[ATTR1]] = { noinline nounwind {{.*}} } diff --git a/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp b/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp index 667c2469b55ea..1cb2b6c609f38 100644 --- a/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp +++ b/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp @@ -56,7 +56,6 @@ // NO-ATTR-NOT: FlagAllCallsDescribed -// HAS-ATTR-DAG: DISubprogram(name: "declaration1", {{.*}}, flags: DIFlagPrototyped // HAS-ATTR-DAG: DISubprogram(name: "declaration2", {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition // HAS-ATTR-DAG: DISubprogram(name: "struct1", {{.*}}, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) // HAS-ATTR-DAG: DISubprogram(name: "struct1", {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition 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 (&)[sizeof(int)]); template void test(int (&)[sizeof((A){}, T())]) {} // expected-error {{cannot yet mangle}} template void test(int (&)[sizeof(A)]); +#elif N == 3 +// __builtin_ptrauth_type_discriminator +template +struct S1 {}; + +template +void func(S1 s1) { // expected-error {{cannot yet mangle __builtin_ptrauth_type_discriminator expression}} +} + +void testfunc1() { + func(S1()); +} + // FIXME: There are several more cases we can't yet mangle. #else diff --git a/clang/test/CodeGenCXX/nrvo.cpp b/clang/test/CodeGenCXX/nrvo.cpp index aab26890ea988..a1445bb812be3 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 -fno-experimental-new-pass-manager -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fno-experimental-new-pass-manager -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-experimental-new-pass-manager -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 -fno-experimental-new-pass-manager -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fno-split-cold-code -fno-experimental-new-pass-manager -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 -fno-experimental-new-pass-manager -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/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 +struct Templ { + virtual void f() {} + virtual void g() {} +}; +template +struct SubTempl : public Templ { + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl* 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's vtable. Hence, Templ::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 +struct Templ { + virtual ~Templ(); // Out-of-line so that the destructor doesn't cause a vtable + virtual void f() {} + virtual void g() {} +}; +template +struct SubTempl : public Templ { + virtual ~SubTempl() {} // override + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl* 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's vtable. Hence, Templ::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-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(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(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(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(&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(&Base0::nonvirtual0); +MethodTy0 gmethod1 = reinterpret_cast(&Derived0::nonvirtual5); +MethodTy0 gmethod2 = reinterpret_cast(&Derived0::virtual1); 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(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(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(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(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/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 + +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-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) 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*)) 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]] diff --git a/clang/test/CodeGenObjC/debug-info-direct-method.m b/clang/test/CodeGenObjC/debug-info-direct-method.m new file mode 100644 index 0000000000000..f822088f946ca --- /dev/null +++ b/clang/test/CodeGenObjC/debug-info-direct-method.m @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -dwarf-version=5 -emit-llvm -debug-info-kind=limited -w -triple x86_64-apple-darwin10 %s -o - | FileCheck %s +// RUN: %clang_cc1 -dwarf-version=4 -emit-llvm -debug-info-kind=limited -w -triple x86_64-apple-darwin10 %s -o - | FileCheck %s + +__attribute__((objc_root_class)) +@interface Root +@end + +@implementation Root +- (int)getInt __attribute__((objc_direct)) { + return 42; +} +@end + +// Test that objc_direct methods are always (even in DWARF < 5) emitted +// as members of their containing class. + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Root", +// CHECK-SAME: elements: ![[MEMBERS:[0-9]+]], +// CHECK-SAME: runtimeLang: DW_LANG_ObjC) +// CHECK: ![[MEMBERS]] = !{![[GETTER:[0-9]+]]} +// CHECK: ![[GETTER]] = !DISubprogram(name: "-[Root getInt]", diff --git a/clang/test/CodeGenObjC/debug-info-objc-property-dwarf5.m b/clang/test/CodeGenObjC/debug-info-objc-property-dwarf5.m new file mode 100644 index 0000000000000..2b3a86fe39971 --- /dev/null +++ b/clang/test/CodeGenObjC/debug-info-objc-property-dwarf5.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -dwarf-version=5 %s -o - | FileCheck %s + +@protocol NSObject +@end + +@interface NSObject {} +@end + +struct Bar {}; + +@protocol BarProto +@property struct Bar *bar; +@end + +@interface Foo +@end + +@implementation Foo {} +@synthesize bar = _bar; +- (void)f {} +@end + +// CHECK: ![[FOO:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo" + +// CHECK: ![[DECL:[0-9]+]] = !DISubprogram(name: "-[Foo setBar:]", +// CHECK-SAME: scope: ![[FOO]] + +// CHECK: distinct !DISubprogram(name: "-[Foo setBar:]", +// CHECK-SAME: declaration: ![[DECL:[0-9]+]] diff --git a/clang/test/CodeGenObjC/debug-info-synthesis.m b/clang/test/CodeGenObjC/debug-info-synthesis.m index f954256282814..7fbbc6dd3182e 100644 --- a/clang/test/CodeGenObjC/debug-info-synthesis.m +++ b/clang/test/CodeGenObjC/debug-info-synthesis.m @@ -30,8 +30,8 @@ int main(int argc, char *argv[]) { } } -// CHECK: ![[FILE:.*]] = !DIFile(filename: "{{[^"]+}}foo.h" +// CHECK: ![[FILE:.*]] = !DIFile(filename: "foo.m" // CHECK: !DISubprogram(name: "-[Foo setDict:]" // CHECK-SAME: file: ![[FILE]], -// CHECK-SAME: line: 8, +// CHECK-SAME: line: 7, // CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition diff --git a/clang/test/CodeGenObjC/debug-property-synth.m b/clang/test/CodeGenObjC/debug-property-synth.m index 124c61ea88cde..ddcf4d998c5cd 100644 --- a/clang/test/CodeGenObjC/debug-property-synth.m +++ b/clang/test/CodeGenObjC/debug-property-synth.m @@ -7,6 +7,10 @@ @interface I { int _p1; } +@property int p1; +@end + +@implementation I // Test that the linetable entries for the synthesized getter and // setter are correct. // @@ -22,10 +26,6 @@ @interface I { // CHECK: ![[DBG1]] = !DILocation(line: [[@LINE+3]], // CHECK: !DISubprogram(name: "-[I setP1:]",{{.*}} line: [[@LINE+2]],{{.*}} DISPFlagLocalToUnit | DISPFlagDefinition // CHECK: ![[DBG2]] = !DILocation(line: [[@LINE+1]], -@property int p1; -@end - -@implementation I @synthesize p1 = _p1; @end diff --git a/clang/test/CodeGenObjC/debuginfo-properties.m b/clang/test/CodeGenObjC/debuginfo-properties.m index c0de620abd966..53f5e2de890bd 100644 --- a/clang/test/CodeGenObjC/debuginfo-properties.m +++ b/clang/test/CodeGenObjC/debuginfo-properties.m @@ -11,19 +11,6 @@ @interface NSObject {} @protocol HasASelection @property (nonatomic, retain) Selection* selection; -// CHECK: !DISubprogram(name: "-[MyClass selection]" -// CHECK-SAME: line: [[@LINE-2]] -// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition -// CHECK: !DISubprogram(name: "-[MyClass setSelection:]" -// CHECK-SAME: line: [[@LINE-5]] -// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition -// CHECK: !DISubprogram(name: "-[OtherClass selection]" -// CHECK-SAME: line: [[@LINE-8]] -// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition -// CHECK: !DISubprogram(name: "-[OtherClass setSelection:]" -// CHECK-SAME: line: [[@LINE-11]] -// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition - @end @interface MyClass : NSObject { @@ -33,6 +20,12 @@ @interface MyClass : NSObject { @implementation MyClass @synthesize selection = _selection; +// CHECK: !DISubprogram(name: "-[MyClass selection]" +// CHECK-SAME: line: [[@LINE-2]] +// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition +// CHECK: !DISubprogram(name: "-[MyClass setSelection:]" +// CHECK-SAME: line: [[@LINE-5]] +// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition @end @interface OtherClass : NSObject { @@ -41,4 +34,10 @@ @interface OtherClass : NSObject { @end @implementation OtherClass @synthesize selection = _selection; +// CHECK: !DISubprogram(name: "-[OtherClass selection]" +// CHECK-SAME: line: [[@LINE-2]] +// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition +// CHECK: !DISubprogram(name: "-[OtherClass setSelection:]" +// CHECK-SAME: line: [[@LINE-5]] +// CHECK-SAME: DISPFlagLocalToUnit | DISPFlagDefinition @end diff --git a/clang/test/CodeGenObjC/direct-method.m b/clang/test/CodeGenObjC/direct-method.m new file mode 100644 index 0000000000000..373bd22a84cdd --- /dev/null +++ b/clang/test/CodeGenObjC/direct-method.m @@ -0,0 +1,203 @@ +// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %s -o - | FileCheck %s + +struct my_complex_struct { + int a, b; +}; + +struct my_aggregate_struct { + int a, b; + char buf[128]; +}; + +__attribute__((objc_root_class)) +@interface Root +- (int)getInt __attribute__((objc_direct)); +@property(direct, readonly) int intProperty; +@property(direct, readonly) int intProperty2; +@end + +@implementation Root +// CHECK-LABEL: define hidden i32 @"\01-[Root intProperty2]" +- (int)intProperty2 { + return 42; +} + +// CHECK-LABEL: define hidden i32 @"\01-[Root getInt]"( +- (int)getInt __attribute__((objc_direct)) { + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[RETVAL:%.*]] = alloca + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // self nil-check + // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]], + // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null + // CHECK-NEXT: br i1 [[NILCHECK]], + + // setting return value to nil + // CHECK-LABEL: objc_direct_method.self_is_nil: + // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0, + // CHECK-NEXT: br label + + // set value + // CHECK-LABEL: objc_direct_method.cont: + // CHECK: store{{.*}}[[RETVAL]], + // CHECK-NEXT: br label + + // return + // CHECK-LABEL: return: + // CHECK: {{%.*}} = load{{.*}}[[RETVAL]], + // CHECK-NEXT: ret + return 42; +} + +// CHECK-LABEL: define hidden i32 @"\01+[Root classGetInt]"( ++ (int)classGetInt __attribute__((objc_direct)) { + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca i8*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store i8* %{{.*}}, i8** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // [self self] + // CHECK-NEXT: [[SELF:%.*]] = load i8*, i8** [[SELFADDR]], + // CHECK-NEXT: [[SELFSEL:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[SELF0:%.*]] = call {{.*}} @objc_msgSend + // CHECK-NEXT: store i8* [[SELF0]], i8** [[SELFADDR]], + + // return + // CHECK-NEXT: ret + return 42; +} + +// CHECK-LABEL: define hidden i64 @"\01-[Root getComplex]"( +- (struct my_complex_struct)getComplex __attribute__((objc_direct)) { + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[RETVAL:%.*]] = alloca + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // self nil-check + // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]], + // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null + // CHECK-NEXT: br i1 [[NILCHECK]], + + // setting return value to nil + // CHECK-LABEL: objc_direct_method.self_is_nil: + // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0, + // CHECK-NEXT: br label + + // set value + // CHECK-LABEL: objc_direct_method.cont: + // CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memcpy{{[^(]*}}({{[^,]*}}[[RET1]], + // CHECK-NEXT: br label + + // return + // CHECK-LABEL: return: + // CHECK: [[RET2:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: {{%.*}} = load{{.*}}[[RET2]], + // CHECK-NEXT: ret + struct my_complex_struct st = {.a = 42}; + return st; +} + +// CHECK-LABEL: define hidden i64 @"\01+[Root classGetComplex]"( ++ (struct my_complex_struct)classGetComplex __attribute__((objc_direct)) { + struct my_complex_struct st = {.a = 42}; + return st; + // CHECK: ret i64 +} + +// CHECK-LABEL: define hidden void @"\01-[Root getAggregate]"( +- (struct my_aggregate_struct)getAggregate __attribute__((objc_direct)) { + // CHECK: %struct.my_aggregate_struct* noalias sret [[RETVAL:%[^,]*]], + + // loading parameters + // CHECK-LABEL: entry: + // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*, + // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*, + // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]], + // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]], + + // self nil-check + // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]], + // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null + // CHECK-NEXT: br i1 [[NILCHECK]], + + // setting return value to nil + // CHECK-LABEL: objc_direct_method.self_is_nil: + // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0, + // CHECK-NEXT: br label + + // set value + // CHECK-LABEL: objc_direct_method.cont: + // CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]] + // CHECK: br label + + // return + // CHECK-LABEL: return: + // CHECK: ret void + struct my_aggregate_struct st = {.a = 42}; + return st; +} + +// CHECK-LABEL: define hidden void @"\01+[Root classGetAggregate]"( ++ (struct my_aggregate_struct)classGetAggregate __attribute__((objc_direct)) { + struct my_aggregate_struct st = {.a = 42}; + return st; + // CHECK: ret void +} + +@end +// CHECK-LABEL: define hidden i32 @"\01-[Root intProperty]" + +@interface Foo : Root { + id __strong _cause_cxx_destruct; +} +@property(nonatomic, readonly, direct) int getDirect_setDynamic; +@property(nonatomic, readonly) int getDynamic_setDirect; +@end + +@interface Foo () +@property(nonatomic, readwrite) int getDirect_setDynamic; +@property(nonatomic, readwrite, direct) int getDynamic_setDirect; +@end + +__attribute__((objc_direct_members)) +@implementation Foo +// CHECK-LABEL: define hidden i32 @"\01-[Foo getDirect_setDynamic]"( +// CHECK-LABEL: define internal void @"\01-[Foo setGetDirect_setDynamic:]"( +// CHECK-LABEL: define internal i32 @"\01-[Foo getDynamic_setDirect]"( +// CHECK-LABEL: define hidden void @"\01-[Foo setGetDynamic_setDirect:]"( +// CHECK-LABEL: define internal void @"\01-[Foo .cxx_destruct]"( +@end + +int useRoot(Root *r) { + // CHECK-LABEL: define i32 @useRoot + // CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Root getInt]" + // CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Root intProperty]" + // CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[Root intProperty2]" + return [r getInt] + [r intProperty] + [r intProperty2]; +} + +__attribute__((objc_root_class)) +@interface RootDeclOnly +@property(direct, readonly) int intProperty; +@end + +int useRootDeclOnly(RootDeclOnly *r) { + // CHECK-LABEL: define i32 @useRootDeclOnly + // CHECK: %{{[^ ]*}} = call i32 bitcast {{.*}} @"\01-[RootDeclOnly intProperty]" + return [r intProperty]; +} 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

-@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 4d81b9e44db07..6c92be68a1e7d 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 +(Class) getClass; @@ -19,8 +19,8 @@ int main() { } // CHECK: @"_OBJC_PROTOCOL_$_P0" = weak hidden global -// CHECK: @"_OBJC_LABEL_PROTOCOL_$_P0" = weak hidden global // CHECK: @"_OBJC_CLASS_PROTOCOLS_$_A" = internal global +// CHECK: @"_OBJC_LABEL_PROTOCOL_$_P0" = weak hidden global // CHECK: @"_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: "_OBJC_$_CLASS_METHODS_A" -// CHECK-SAME: OBJC_CLASS_NAME_.1 // CHECK-SAME: "_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 4969f5c72c16e..7e56e7ae11ddb 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/instance-method-metadata.m b/clang/test/CodeGenObjC/instance-method-metadata.m index 96f499c9fa94a..e08de8fdacec2 100644 --- a/clang/test/CodeGenObjC/instance-method-metadata.m +++ b/clang/test/CodeGenObjC/instance-method-metadata.m @@ -1,6 +1,5 @@ // REQUIRES: x86-registered-target -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -S -o %t %s -// RUN: FileCheck < %t %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -S %s -o - | FileCheck %s // rdar://9072317 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 79a1d5535576b..401a73bd42410 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

@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 -@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 // 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 9f8abd4ea45ab..70bc2b4084e79 100644 --- a/clang/test/CodeGenObjC/protocols.m +++ b/clang/test/CodeGenObjC/protocols.m @@ -22,8 +22,7 @@ +(int) maxValue; -(int) conformsTo: (id) x; @end -@protocol P0 -@end +@protocol P0; @protocol P1 +(void) classMethodReq0; 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) 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];}); +} 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

+- (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]; +} 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. 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 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/Driver/aarch64-cpus.c b/clang/test/Driver/aarch64-cpus.c index d429f9183332c..11c9a86bff733 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 @@ -250,6 +253,16 @@ // 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 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/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/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c new file mode 100644 index 0000000000000..40b471c8f130d --- /dev/null +++ b/clang/test/Driver/arch-arm64e.c @@ -0,0 +1,69 @@ +// 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" +// NONE-NOT: "-fptrauth-soft" + +// 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" + +// RUN: %clang -arch arm64 -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 +// 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 +// 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/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index f02f94d8c5a8f..ceaa12ed78347 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -544,25 +544,25 @@ // CHECK-MSAN-OPENBSD: unsupported option '-fsanitize=memory' 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 x86_64-linux-gnu -fvisibility=hidden -fsanitize=cfi -flto -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI diff --git a/clang/test/Driver/sanitizer-ld.c b/clang/test/Driver/sanitizer-ld.c index 82a033fda96f0..68ad9d433629a 100644 --- a/clang/test/Driver/sanitizer-ld.c +++ b/clang/test/Driver/sanitizer-ld.c @@ -582,14 +582,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 \ 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..642a70cf34d7e --- /dev/null +++ b/clang/test/FixIt/fixit-fill-in-protocol-requirements.m @@ -0,0 +1,84 @@ +// 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 + +- (void)p2Method; + +@end + +@protocol P2 + +- (void)p1Method; + +@end + +@interface I + +@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) // 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 // 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 // expected-warning {{class 'ThreeProtocols' does not conform to protocols 'P2', 'P1', 'P3', ...}} expected-note {{add stubs for missing protocol requirements}} +@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 +@end + +@implementation ImplementsAllAvailable // No warning! + +- (void)availableMethod { } +- (void)deprecatedMethod { } + +@end + +@interface FixitJustAvailable +@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" 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 // 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/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/empty-unit.c b/clang/test/Index/Store/empty-unit.c new file mode 100644 index 0000000000000..701f9030f78ad --- /dev/null +++ b/clang/test/Index/Store/empty-unit.c @@ -0,0 +1,18 @@ +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 + +// 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: 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 +// RUN: FileCheck %s -input-file %t2.err -check-prefix ERR-RECORD +// ERR-RECORD: error loading record: empty file 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..1b4f89d9251f3 --- /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 | Decl | rel: 0 + +void test(id 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(protocol)/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..0136a9c9f86a4 --- /dev/null +++ b/clang/test/Index/Store/handle-prebuilt-module.m @@ -0,0 +1,23 @@ +// 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..a02210aaee50b --- /dev/null +++ b/clang/test/Index/Store/json-with-module.m @@ -0,0 +1,7 @@ +// RUN: rm -rf %t.idx %t.mcp +// 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 + +@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..5c9bf3a397243 --- /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:@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..c8bffa035ed0c --- /dev/null +++ b/clang/test/Index/Store/json-with-pch.c @@ -0,0 +1,10 @@ +// RUN: rm -rf %t.idx +// 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 + +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..c9be85f9112a4 --- /dev/null +++ b/clang/test/Index/Store/json.c @@ -0,0 +1,8 @@ +// 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 diff --git a/clang/test/Index/Store/print-record.mm b/clang/test/Index/Store/print-record.mm new file mode 100644 index 0000000000000..cbca273481968 --- /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 + + + +@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..31de79667d729 --- /dev/null +++ b/clang/test/Index/Store/print-unit.c @@ -0,0 +1,39 @@ + + +#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{{$}} +// 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..3e2fea4206da0 --- /dev/null +++ b/clang/test/Index/Store/print-units-with-modules.m @@ -0,0 +1,57 @@ +// 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 + +@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{{$}} +// CHECK: DEPEND END (3) + +// CHECK: print-units-with-modules.m.tmp.o +// CHECK: is-system: 0 +// CHECK: is-module: 0 +// CHECK: module-name: +// 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{{$}} +// CHECK: File | user | {{.*}}/Inputs/module/module.modulemap{{$}} +// 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..8bf0082e3a45c --- /dev/null +++ b/clang/test/Index/Store/print-units-with-pch.c @@ -0,0 +1,27 @@ +// RUN: rm -rf %t.idx +// 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 + +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-invalid-name.cpp b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp new file mode 100644 index 0000000000000..5c049567de024 --- /dev/null +++ b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp @@ -0,0 +1,15 @@ +// Makes sure it doesn't crash. + +// 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 +} 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..68501edfef9d4 --- /dev/null +++ b/clang/test/Index/Store/record-hash-crash.cpp @@ -0,0 +1,29 @@ +// Makes sure it doesn't crash. + +// 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 []() {}; } +} + +namespace crash2 { +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Decl,RelChild | rel: 1 +template +class Foo; // canonical decl + +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Def,RelChild | rel: 1 +template +class Foo {}; + +// CHECK: [[@LINE+2]]:8 | struct(Gen)/C++ | c:@N@crash2@ST>1#t>1#pT@Wrapper | Def,RelChild | rel: 1 +template