diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000000..32a1cddaa240f --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @apple/llvm-project-branch-managers 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/README.md b/README.md index da06ed76b8adf..b83ac2e7989c7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# Apple's fork of llvm-project + +This is Apple's fork of llvm-project. For more information on Apple's +branching scheme, please see +[apple-docs/AppleBranchingScheme.md](https://github.com/apple/llvm-project/tree/apple/master/apple-docs/AppleBranchingScheme.md). + +The LLVM project's main README follows. + # The LLVM Compiler Infrastructure This directory and its subdirectories contain source code for LLVM, diff --git a/apple-docs/AppleBranchingScheme.md b/apple-docs/AppleBranchingScheme.md new file mode 100644 index 0000000000000..806d0a39416ff --- /dev/null +++ b/apple-docs/AppleBranchingScheme.md @@ -0,0 +1,105 @@ +# Apple's branching scheme for llvm-project + +There are currently three namespaces for branches on +[github.com/apple/llvm-project](https://github.com/apple/llvm-project): + + 1. `llvm.org/*`, for forwarded branches from + [github.com/llvm](https://github.com/llvm/llvm-project); + 2. `apple/*`, for standalone downstream content; and + 3. `swift/*`, for downstream content that depends on + [Swift](https://github.com/apple/swift). + +## Forwarded branches from [github.com/llvm](https://github.com/llvm/llvm-project) + +The `llvm.org/*` branches are forwarded, unchanged, from +[github.com/llvm/llvm-project](https://github.com/llvm/llvm-project). These +are read-only, exact copies of the upstream LLVM project's branches. They are +forwarded here as a convenience for easy reference, to avoid the need for extra +remotes. + +- [llvm.org/master](https://github.com/apple/llvm-project/tree/llvm.org/master) + is the most important branch here, matching the LLVM project's + [master](https://github.com/llvm/llvm-project/tree/master) branch. + +## Downstream branches that are standalone + +The `apple/*` branches have downstream content, besides what is in the LLVM +project. This content includes some patches that have not yet been fully +upstreamed to the LLVM project, including some special support for Swift. +Critically, however, none of these branches *depend on* the +[github.com/apple/swift](https://github.com/apple/swift) repository. + +Although there are a few non-trivial differences from LLVM, the goal is to +minimize this difference, and do almost all development upstream. + +- [apple/master](https://github.com/apple/llvm-project/tree/apple/master) is + directly downstream of + [llvm.org/master](https://github.com/apple/llvm-project/tree/llvm.org/master). + There is a gated automerger that does testing before merging in. Most + changes to this branch should be redirected to + (see also ). +- `apple/stable/*`: These branches are periodic stabilization branches, where + fixes are cherry-picked from LLVM. At time of writing: + - [apple/stable/20191106](https://github.com/apple/llvm-project/tree/apple/stable/20191106) + is the most recent stabilization branch. + - [apple/stable/20190619](https://github.com/apple/llvm-project/tree/apple/stable/20190619) + is the current stabilization branch for + [swift/master](https://github.com/apple/llvm-project/tree/swift/master) + (see below). + +## Downstream branches that depend on [Swift](https://github.com/apple/swift) + +The `swift/*` branches are downstream of `apple/*`, and include content that +depends [Swift](https://github.com/apple/swift). The naming scheme is +`swift/`, where `` is the aligned Swift branch. + +The branches are automerged from a branch in the `apple/*` namespace. They are +expected to have zero differences outside the `lldb/` and `apple-llvm-config/` +directories. + +These are the most important branches: + +- [swift/master-next](https://github.com/apple/llvm-project/tree/swift/master-next) + is downstream of + [apple/master](https://github.com/apple/llvm-project/tree/apple/master) and + aligned with Swift's + [master-next](https://github.com/apple/swift/tree/master-next) branch. +- [swift/master](https://github.com/apple/llvm-project/tree/swift/master) is + downstream of a stabilization branch in `apple/stable/*` + ([apple/stable/20190619](https://github.com/apple/llvm-project/tree/apple/stable/20190619), + as of time of writing) and aligned with Swift's + [master](https://github.com/apple/swift/tree/master) branch. + +## Historical trivia: mappings to branches from before the monorepo transition + +Before the LLVM project's monorepo transition, Apple maintained downstream +forks of various split repositories. Here is a mapping from a few of the new +branches in the llvm-project monorepo to their original split repositories. + +- [apple/master](https://github.com/apple/llvm-project/tree/apple/master) was + generated from the `upstream-with-swift` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/master-next](https://github.com/apple/llvm-project/tree/swift/master-next) + was generated from the `upstream-with-swift` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from [apple/master](https://github.com/apple/llvm-project/tree/apple/master). +- [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104) + was generated from the `swift-5.1-branch` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/swift-5.1-branch](https://github.com/apple/llvm-project/tree/swift/swift-5.1-branch) + was generated from the `swift-5.1-branch` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from + [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104). +- [swift/master](https://github.com/apple/llvm-project/tree/swift/master) was + generated from the `stable` branch from all six split repos. 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/clangd/Shutdown.cpp b/clang-tools-extra/clangd/Shutdown.cpp index dfea46d8dfeb8..919536a21ba10 100644 --- a/clang-tools-extra/clangd/Shutdown.cpp +++ b/clang-tools-extra/clangd/Shutdown.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace clang { namespace clangd { diff --git a/clang-tools-extra/test/clang-tidy/checkers/darwin-avoid-spinlock.m b/clang-tools-extra/test/clang-tidy/checkers/darwin-avoid-spinlock.m index c870e0a0fed06..f395386a02f43 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/darwin-avoid-spinlock.m +++ b/clang-tools-extra/test/clang-tidy/checkers/darwin-avoid-spinlock.m @@ -2,6 +2,10 @@ typedef int OSSpinLock; +void OSSpinlockLock(OSSpinLock *l); +void OSSpinlockTry(OSSpinLock *l); +void OSSpinlockUnlock(OSSpinLock *l); + @implementation Foo - (void)f { int i = 1; 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 b0f57202e0797..343dd7daf8a0a 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 ============ @@ -2552,6 +2553,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/analyzer/developer-docs/DebugChecks.rst b/clang/docs/analyzer/developer-docs/DebugChecks.rst index 3f9bed78604f0..05b3e2480d3b7 100644 --- a/clang/docs/analyzer/developer-docs/DebugChecks.rst +++ b/clang/docs/analyzer/developer-docs/DebugChecks.rst @@ -275,6 +275,28 @@ ExprInspection checks See clang_analyzer_denote(). +- ``void clang_analyzer_isTainted(a single argument of any type);`` + + Queries the analyzer whether the expression used as argument is tainted or not. + This is useful in tests, where we don't want to issue warning for all tainted + expressions but only check for certain expressions. + This would help to reduce the *noise* that the `TaintTest` debug checker would + introduce and let you focus on the `expected-warning`s that you really care + about. + + Example usage:: + + int read_integer() { + int n; + clang_analyzer_isTainted(n); // expected-warning{{NO}} + scanf("%d", &n); + clang_analyzer_isTainted(n); // expected-warning{{YES}} + clang_analyzer_isTainted(n + 2); // expected-warning{{YES}} + clang_analyzer_isTainted(n > 0); // expected-warning{{YES}} + int next_tainted_value = n; // no-warning + return n; + } + Statistics ========== 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 b3a0b9d66d5f8..510f8d6f300ef 100644 --- a/clang/include/clang-c/CXErrorCode.h +++ b/clang/include/clang-c/CXErrorCode.h @@ -53,7 +53,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 }; LLVM_CLANG_C_EXTERN_C_END diff --git a/clang/include/clang-c/Dependencies.h b/clang/include/clang-c/Dependencies.h new file mode 100644 index 0000000000000..5ea6e791aa1fd --- /dev/null +++ b/clang/include/clang-c/Dependencies.h @@ -0,0 +1,223 @@ +/*==-- clang-c/Dependencies.h - Dependency Discovery C Interface --*- 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 header provides a dependency discovery interface similar to *| +|* clang-scan-deps. *| +|* *| +|* An example of its usage is available in c-index-test/core_main.cpp. *| +|* *| +|* EXPERIMENTAL: These interfaces are experimental and will change. If you *| +|* use these be prepared for them to change without notice on any commit. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_DEPENDENCIES_H +#define LLVM_CLANG_C_DEPENDENCIES_H + +#include "clang-c/BuildSystem.h" +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" +#include "clang-c/Platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup SCAN_DEPS Dependency scanning service. + * @{ + */ + +typedef struct { + CXString Name; + /** + * The context hash of a module represents the set of compiler options that + * may make one version of a module incompatible from another. This includes + * things like language mode, predefined macros, header search paths, etc... + * + * Modules with the same name but a different \c ContextHash should be treated + * as separate modules for the purpose of a build. + */ + CXString ContextHash; + + /** + * The path to the modulemap file which defines this module. + * + * This can be used to explicitly build this module. This file will + * additionally appear in \c FileDeps as a dependency. + */ + CXString ModuleMapPath; + + /** + * The list of files which this module directly depends on. + * + * If any of these change then the module needs to be rebuilt. + */ + CXStringSet *FileDeps; + + /** + * The list of modules which this module direct depends on. + * + * This does include the context hash. The format is + * `:` + */ + CXStringSet *ModuleDeps; + + /** + * The full command line needed to build this module. + * + * Not including `-fmodule-file=` or `-o`. + */ + CXStringSet *BuildArguments; +} CXModuleDependency; + +typedef struct { + int Count; + CXModuleDependency *Modules; +} CXModuleDependencySet; + +/** + * See \c CXModuleDependency for the meaning of these fields, with the addition + * that they represent only the direct dependencies for \c CXDependencyMode_Full + * mode. + */ +typedef struct { + CXString ContextHash; + CXStringSet *FileDeps; + CXStringSet *ModuleDeps; + + /** + * Additional arguments to append to the build of this file. + * + * This contains things like disabling implicit modules. This does not include + * the `-fmodule-file=` arguments that are needed. + */ + CXStringSet *AdditionalArguments; +} CXFileDependencies; + +CINDEX_LINKAGE void +clang_experimental_ModuleDependencySet_dispose(CXModuleDependencySet *MD); + +CINDEX_LINKAGE void +clang_experimental_FileDependencies_dispose(CXFileDependencies *ID); + +/** + * Object encapsulating instance of a dependency scanner service. + * + * The dependency scanner service is a global instance that owns the + * global cache and other global state that's shared between the dependency + * scanner workers. The service APIs are thread safe. + */ +typedef struct CXOpaqueDependencyScannerService *CXDependencyScannerService; + +/** + * The mode to report module dependencies in. + */ +typedef enum { + /** + * Flatten all module dependencies. This reports the full transitive set of + * header and module map dependencies needed to do an implicit module build. + */ + CXDependencyMode_Flat, + + /** + * Report the full module graph. This reports only the direct dependencies of + * each file, and calls a callback for each module that is discovered. + */ + CXDependencyMode_Full, +} CXDependencyMode; + +/** + * Create a \c CXDependencyScannerService object. + * Must be disposed with \c clang_DependencyScannerService_dispose(). + */ +CINDEX_LINKAGE CXDependencyScannerService +clang_experimental_DependencyScannerService_create_v0(CXDependencyMode Format); + +/** + * Dispose of a \c CXDependencyScannerService object. + * + * The service object must be disposed of after the workers are disposed of. + */ +CINDEX_LINKAGE void clang_experimental_DependencyScannerService_dispose_v0( + CXDependencyScannerService); + +/** + * Object encapsulating instance of a dependency scanner worker. + * + * The dependency scanner workers are expected to be used in separate worker + * threads. An individual worker is not thread safe. + * + * Operations on a worker are not thread-safe and should only be used from a + * single thread at a time. They are intended to be used by a single dedicated + * thread in a thread pool, but they are not inherently pinned to a thread. + */ +typedef struct CXOpaqueDependencyScannerWorker *CXDependencyScannerWorker; + +/** + * Create a \c CXDependencyScannerWorker object. + * Must be disposed with + * \c clang_experimental_DependencyScannerWorker_dispose_v0(). + */ +CINDEX_LINKAGE CXDependencyScannerWorker + clang_experimental_DependencyScannerWorker_create_v0( + CXDependencyScannerService); + +CINDEX_LINKAGE void clang_experimental_DependencyScannerWorker_dispose_v0( + CXDependencyScannerWorker); + +/** + * A callback that is called whenever a module is discovered when in + * \c CXDependencyMode_Full mode. + * + * \param Context the context that was passed to + * \c clang_experimental_DependencyScannerWorker_getFileDependencies_v0. + * \param MDS the list of discovered modules. Must be freed by calling + * \c clang_experimental_ModuleDependencySet_dispose. + */ +typedef void CXModuleDiscoveredCallback(void *Context, + CXModuleDependencySet *MDS); + +/** + * Returns the list of file dependencies for a particular compiler invocation. + * + * \param argc the number of compiler invocation arguments (including argv[0]). + * \param argv the compiler invocation arguments (including argv[0]). + * the invocation may be a -cc1 clang invocation or a driver + * invocation. + * \param WorkingDirectory the directory in which the invocation runs. + * \param MDC a callback that is called whenever a new module is discovered. + * This may receive the same module on different workers. This should + * be NULL if + * \c clang_experimental_DependencyScannerService_create_v0 was + * called with \c CXDependencyMode_Flat. This callback will be called + * on the same thread that called this function. + * \param Context the context that will be passed to \c MDC each time it is + * called. + * \param [out] error the error string to pass back to client (if any). + * + * \returns A pointer to a CXFileDependencies on success, NULL otherwise. The + * CXFileDependencies must be freed by calling + * \c clang_experimental_FileDependencies_dispose. + */ +CINDEX_LINKAGE CXFileDependencies * +clang_experimental_DependencyScannerWorker_getFileDependencies_v0( + CXDependencyScannerWorker Worker, int argc, const char *const *argv, + const char *WorkingDirectory, CXModuleDiscoveredCallback *MDC, + void *Context, CXString *error); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif // LLVM_CLANG_C_DEPENDENCIES_H diff --git a/clang/include/clang-c/Driver.h b/clang/include/clang-c/Driver.h new file mode 100644 index 0000000000000..54dd1b0b043a3 --- /dev/null +++ b/clang/include/clang-c/Driver.h @@ -0,0 +1,78 @@ +/*==-- clang-c/Driver.h - A C Interface for the Clang Driver ------*- 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 header provides a C API for extracting information from the clang *| +|* driver. *| +|* *| +\*===----------------------------------------------------------------------===*/ + + +#ifndef CLANG_CLANG_C_DRIVER +#define CLANG_CLANG_C_DRIVER + +#include "clang-c/Index.h" +#include "clang-c/Platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Contains the command line arguments for an external action. Same format as + * provided to main. + */ +typedef struct { + /* Number of arguments in ArgV */ + int ArgC; + /* Null terminated array of pointers to null terminated argument strings */ + const char **ArgV; +} CXExternalAction; + +/** + * Contains the list of external actions clang would invoke. + */ +typedef struct { + int Count; + CXExternalAction **Actions; +} CXExternalActionList; + +/** + * Get the external actions that the clang driver will invoke for the given + * command line. + * + * \param ArgC number of arguments in \p ArgV. + * \param ArgV array of null terminated arguments. Doesn't need to be null + * terminated. + * \param Environment must be null. + * \param WorkingDirectory a null terminated path to the working directory to + * use for this invocation. `nullptr` to use the current working directory of + * the process. + * \param OutDiags will be set to a \c CXDiagnosticSet if there's an error. + * Must be freed by calling \c clang_disposeDiagnosticSet . + * \returns A pointer to a \c CXExternalActionList on success, null on failure. + * The returned \c CXExternalActionList must be freed by calling + * \c clang_Driver_ExternalActionList_dispose . + */ +CINDEX_LINKAGE CXExternalActionList * +clang_Driver_getExternalActionsForCommand_v0(int ArgC, const char **ArgV, + const char **Environment, + const char *WorkingDirectory, + CXDiagnosticSet *OutDiags); + +/** + * Deallocate a \c CXExternalActionList + */ +CINDEX_LINKAGE void +clang_Driver_ExternalActionList_dispose(CXExternalActionList *EAL); + +#ifdef __cplusplus +} +#endif + +#endif // CLANG_CLANG_C_DRIVER 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..d7b054496b10a --- /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(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 d5ade9340c8eb..e4a7617bbd174 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1143,6 +1143,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 @@ -1992,6 +1995,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/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 490b34bf95e82..205d7ec67754f 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -16,6 +16,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" @@ -349,6 +350,10 @@ class TypeSourceInfo; return ToOrErr.takeError(); } + /// Import cleanup objects owned by ExprWithCleanup. + llvm::Expected + Import(ExprWithCleanups::CleanupObject From); + /// Import the given type from the "from" context into the "to" /// context. A null type is imported as a null type (no error). /// diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h index d7b3a9da88ec2..1ee63d0640ca0 100644 --- a/clang/include/clang/AST/AbstractBasicReader.h +++ b/clang/include/clang/AST/AbstractBasicReader.h @@ -178,9 +178,9 @@ class DataStreamBasicReader : public BasicReaderBase { } Qualifiers readQualifiers() { - static_assert(sizeof(Qualifiers().getAsOpaqueValue()) <= sizeof(uint32_t), + static_assert(sizeof(Qualifiers().getAsOpaqueValue()) <= sizeof(uint64_t), "update this if the value size changes"); - uint32_t value = asImpl().readUInt32(); + uint64_t value = asImpl().readUInt64(); return Qualifiers::fromOpaqueValue(value); } diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h index 0a6730c86bbfe..f16add0c96cd1 100644 --- a/clang/include/clang/AST/AbstractBasicWriter.h +++ b/clang/include/clang/AST/AbstractBasicWriter.h @@ -164,9 +164,9 @@ class DataStreamBasicWriter : public BasicWriterBase { } void writeQualifiers(Qualifiers value) { - static_assert(sizeof(value.getAsOpaqueValue()) <= sizeof(uint32_t), + static_assert(sizeof(value.getAsOpaqueValue()) <= sizeof(uint64_t), "update this if the value size changes"); - asImpl().writeUInt32(value.getAsOpaqueValue()); + asImpl().writeUInt64(value.getAsOpaqueValue()); } void writeExceptionSpecInfo( diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index bbaa46363d971..339a0ac6751a3 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" @@ -108,6 +109,10 @@ class Attr : public AttributeCommonInfo { // Pretty print this attribute. void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; + + // Compare two attributes, used for sorting attributes in a Subject. + // FIXME: this should be auto generated from Attr.td + static bool compare(const Attr *A, const Attr *B); }; class TypeAttr : public Attr { 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/CommentSema.h b/clang/include/clang/AST/CommentSema.h index 307618fa5363c..6dfe0f4920d06 100644 --- a/clang/include/clang/AST/CommentSema.h +++ b/clang/include/clang/AST/CommentSema.h @@ -217,6 +217,9 @@ class Sema { bool isTemplateOrSpecialization(); bool isRecordLikeDecl(); bool isClassOrStructDecl(); + /// \return \c true if the declaration that this comment is attached to + /// declares either struct, class or tag typedef. + bool isClassOrStructOrTagTypedefDecl(); bool isUnionDecl(); bool isObjCInterfaceDecl(); bool isObjCProtocolDecl(); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index cd97c6dcf8d5c..f5f5a0840b4dc 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3500,6 +3500,7 @@ class EnumDecl : public TagDecl { /// negative enumerators of this enum. (see getNumNegativeBits) void setNumNegativeBits(unsigned Num) { EnumDeclBits.NumNegativeBits = Num; } +public: /// True if this tag declaration is a scoped enumeration. Only /// possible in C++11 mode. void setScoped(bool Scoped = true) { EnumDeclBits.IsScoped = Scoped; } @@ -3516,6 +3517,7 @@ class EnumDecl : public TagDecl { /// Microsoft-style enumeration with a fixed underlying type. void setFixed(bool Fixed = true) { EnumDeclBits.IsFixed = Fixed; } +private: /// True if a valid hash is stored in ODRHash. bool hasODRHash() const { return EnumDeclBits.HasODRHash; } void setHasODRHash(bool Hash = true) { EnumDeclBits.HasODRHash = Hash; } @@ -3716,6 +3718,7 @@ class RecordDecl : public TagDecl { // to save some space. Use the provided accessors to access it. public: friend class DeclContext; + friend class ASTDeclReader; /// Enum that represents the different ways arguments are passed to and /// returned from function calls. This takes into account the target-specific /// and version-specific rules along with the rules determined by the @@ -3960,9 +3963,16 @@ class RecordDecl : public TagDecl { /// nullptr is returned if no named data member exists. const FieldDecl *findFirstNamedDataMember() const; + /// Get precomputed ODRHash or add a new one. + unsigned getODRHash(); + private: /// Deserialize just the fields. void LoadFieldsFromExternalStorage() const; + + /// True if a valid hash is stored in ODRHash. + bool hasODRHash() const { return RecordDeclBits.ODRHash; } + void setODRHash(unsigned Hash) { RecordDeclBits.ODRHash = Hash; } }; class FileScopeAsmDecl : public Decl { diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 91c372585b071..dd639dec9ef02 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -626,7 +626,16 @@ class alignas(8) Decl { setModuleOwnershipKind(ModuleOwnershipKind::ModulePrivate); } - /// Set the owning module ID. +public: + /// Set the FromASTFile flag. This indicates that this declaration + /// was deserialized and not parsed from source code and enables + /// features such as module ownership information. + void setFromASTFile() { + FromASTFile = true; + } + + /// Set the owning module ID. This may only be called for + /// deserialized Decls. void setOwningModuleID(unsigned ID) { assert(isFromASTFile() && "Only works on a deserialized declaration"); *((unsigned*)this - 2) = ID; @@ -1452,10 +1461,14 @@ class DeclContext { /// Represents the way this type is passed to a function. uint64_t ArgPassingRestrictions : 2; + + /// True if a valid hash is stored in ODRHash. This should shave off some + /// extra storage and prevent CXXRecordDecl to store unused bits. + uint64_t ODRHash : 28; }; /// Number of non-inherited bits in RecordDeclBitfields. - enum { NumRecordDeclBits = 14 }; + enum { NumRecordDeclBits = 42 }; /// Stores the bits used by OMPDeclareReductionDecl. /// If modified NumOMPDeclareReductionDeclBits and the accessor @@ -1628,10 +1641,13 @@ class DeclContext { /// Indicates if the method was a definition but its body was skipped. uint64_t HasSkippedBody : 1; + + /// True if a valid hash is stored in ODRHash. + uint64_t HasODRHash : 1; }; /// Number of non-inherited bits in ObjCMethodDeclBitfields. - enum { NumObjCMethodDeclBits = 24 }; + enum { NumObjCMethodDeclBits = 25 }; /// Stores the bits used by ObjCContainerDecl. /// If modified NumObjCContainerDeclBits and the accessor diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 1c6f99438fc3e..3fd329393ec82 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1696,6 +1696,10 @@ class CXXRecordDecl : public RecordDecl { /// actually abstract. bool mayBeAbstract() const; + /// Determine whether it's impossible for a class to be derived from this + /// class. This is best-effort, and may conservatively return false. + bool isEffectivelyFinal() const; + /// If this is the closure type of a lambda expression, retrieve the /// number to be used for name mangling in the Itanium C++ ABI. /// @@ -2383,17 +2387,6 @@ class CXXConstructorDecl final : ExplicitSpecKind::ResolvedFalse); } - void setExplicitSpecifier(ExplicitSpecifier ES) { - assert((!ES.getExpr() || - CXXConstructorDeclBits.HasTrailingExplicitSpecifier) && - "cannot set this explicit specifier. no trail-allocated space for " - "explicit"); - if (ES.getExpr()) - *getCanonicalDecl()->getTrailingObjects() = ES; - else - CXXConstructorDeclBits.IsSimpleExplicit = ES.isExplicit(); - } - enum TraillingAllocKind { TAKInheritsConstructor = 1, TAKHasTailExplicit = 1 << 1, @@ -2418,6 +2411,17 @@ class CXXConstructorDecl final ConstexprSpecKind ConstexprKind, InheritedConstructor Inherited = InheritedConstructor()); + void setExplicitSpecifier(ExplicitSpecifier ES) { + assert((!ES.getExpr() || + CXXConstructorDeclBits.HasTrailingExplicitSpecifier) && + "cannot set this explicit specifier. no trail-allocated space for " + "explicit"); + if (ES.getExpr()) + *getCanonicalDecl()->getTrailingObjects() = ES; + else + CXXConstructorDeclBits.IsSimpleExplicit = ES.isExplicit(); + } + ExplicitSpecifier getExplicitSpecifier() { return getCanonicalDecl()->getExplicitSpecifierInternal(); } @@ -2684,8 +2688,6 @@ class CXXConversionDecl : public CXXMethodDecl { ExplicitSpecifier ExplicitSpec; - void setExplicitSpecifier(ExplicitSpecifier ES) { ExplicitSpec = ES; } - public: friend class ASTDeclReader; friend class ASTDeclWriter; @@ -2707,6 +2709,7 @@ class CXXConversionDecl : public CXXMethodDecl { /// Return true if the declartion is already resolved to be explicit. bool isExplicit() const { return getExplicitSpecifier().isExplicit(); } + void setExplicitSpecifier(ExplicitSpecifier ES) { ExplicitSpec = ES; } /// Returns the type that this conversion function is converting to. QualType getConversionType() const { diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index b98aef6b499d4..b81452b85fa38 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -224,6 +224,15 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext { /// Otherwise it will return itself. ObjCMethodDecl *getNextRedeclarationImpl() override; + /// Store the ODR hash for this decl. + unsigned ODRHash = 0; + + /// Whether an ODRHash has been stored. + bool hasODRHash() const { return ObjCMethodDeclBits.HasODRHash; } + + /// State that an ODRHash has been stored. + void setHasODRHash(bool B = true) { ObjCMethodDeclBits.HasODRHash = B; } + public: friend class ASTDeclReader; friend class ASTDeclWriter; @@ -540,6 +549,10 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext { static ObjCMethodDecl *castFromDeclContext(const DeclContext *DC) { return static_cast(const_cast(DC)); } + + /// Get precomputed ODRHash or add a new one. + unsigned getODRHash(); + unsigned getODRHash() const; }; /// Describes the variance of a given generic parameter. @@ -917,6 +930,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; } @@ -925,6 +943,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; } @@ -1186,6 +1209,7 @@ class ObjCContainerDecl : public NamedDecl, public DeclContext { class ObjCInterfaceDecl : public ObjCContainerDecl , public Redeclarable { friend class ASTContext; + friend class ASTReader; /// TypeForDecl - This indicates the Type object that represents this /// TypeDecl. It is a cache maintained by ASTContext::getObjCInterfaceType @@ -1243,6 +1267,12 @@ class ObjCInterfaceDecl : public ObjCContainerDecl /// One of the \c InheritedDesignatedInitializersState enumeratos. mutable unsigned InheritedDesignatedInitializers : 2; + /// Tracks whether a ODR hash has been computed for this interface. + unsigned HasODRHash : 1; + + /// A hash of parts of the class to help in ODR checking. + unsigned ODRHash = 0; + /// The location of the last location in this declaration, before /// the properties/methods. For example, this will be the '>', '}', or /// identifier, @@ -1251,7 +1281,7 @@ class ObjCInterfaceDecl : public ObjCContainerDecl DefinitionData() : ExternallyCompleted(false), IvarListMissingImplementation(true), HasDesignatedInitializers(false), - InheritedDesignatedInitializers(IDI_Unknown) {} + InheritedDesignatedInitializers(IDI_Unknown), HasODRHash(false) {} }; /// The type parameters associated with this class, if any. @@ -1936,7 +1966,14 @@ class ObjCInterfaceDecl : public ObjCContainerDecl static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCInterface; } + /// Get precomputed ODRHash or add a new one. + unsigned getODRHash(); + private: + /// True if a valid hash is stored in ODRHash. + bool hasODRHash() const; + void setHasODRHash(bool Hash = true); + const ObjCInterfaceDecl *findInterfaceWithDesignatedInitializers() const; bool inheritsDesignatedInitializers() const; }; @@ -2083,6 +2120,12 @@ class ObjCProtocolDecl : public ObjCContainerDecl, /// Referenced protocols ObjCProtocolList ReferencedProtocols; + + /// Tracks whether a ODR hash has been computed for this protocol. + unsigned HasODRHash : 1; + + /// A hash of parts of the class to help in ODR checking. + unsigned ODRHash = 0; }; /// Contains a pointer to the data associated with this class, @@ -2119,6 +2162,10 @@ class ObjCProtocolDecl : public ObjCContainerDecl, return getMostRecentDecl(); } + /// True if a valid hash is stored in ODRHash. + bool hasODRHash() const; + void setHasODRHash(bool Hash = true); + public: friend class ASTDeclReader; friend class ASTDeclWriter; @@ -2273,6 +2320,9 @@ class ObjCProtocolDecl : public ObjCContainerDecl, static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCProtocol; } + + /// Get precomputed ODRHash or add a new one. + unsigned getODRHash(); }; /// ObjCCategoryDecl - Represents a category declaration. A category allows @@ -2291,15 +2341,62 @@ class ObjCProtocolDecl : public ObjCContainerDecl, /// Categories were originally inspired by dynamic languages such as Common /// Lisp and Smalltalk. More traditional class-based languages (C++, Java) /// don't support this level of dynamism, which is both powerful and dangerous. -class ObjCCategoryDecl : public ObjCContainerDecl { +class ObjCCategoryDecl : public ObjCContainerDecl, + public Redeclarable { + friend class ASTContext; + friend class ASTReader; + /// Interface belonging to this category ObjCInterfaceDecl *ClassInterface; /// The type parameters associated with this category, if any. ObjCTypeParamList *TypeParamList = nullptr; - /// referenced protocols in this category. - ObjCProtocolList ReferencedProtocols; + struct DefinitionData { + /// The definition of this class, for quick access from any + /// declaration. + ObjCCategoryDecl *Definition = nullptr; + + /// Indicates that the contents of this Objective-C category will be + /// completed by the external AST source when required. + mutable unsigned ExternallyCompleted : 1; + + /// Tracks whether a ODR hash has been computed for this category. + unsigned HasODRHash : 1; + + /// referenced protocols in this category. + ObjCProtocolList ReferencedProtocols; + + /// A hash of parts of the class to help in ODR checking. + unsigned ODRHash = 0; + }; + + DefinitionData &data() const { + assert(Data.getPointer() && "Declaration has no definition!"); + return *Data.getPointer(); + } + + /// Allocate the definition data for this class. + void allocateDefinitionData(); + + using redeclarable_base = Redeclarable; + + ObjCCategoryDecl *getNextRedeclarationImpl() override { + return getNextRedeclaration(); + } + + ObjCCategoryDecl *getPreviousDeclImpl() override { return getPreviousDecl(); } + + ObjCCategoryDecl *getMostRecentDeclImpl() override { + return getMostRecentDecl(); + } + + /// Contains a pointer to the data associated with this class, + /// which will be NULL if this class has not yet been defined. + /// + /// The bit indicates when we don't need to check for out-of-date + /// declarations. It will be set unless modules are enabled. + llvm::PointerIntPair Data; /// Next category belonging to this class. /// FIXME: this should not be a singly-linked list. Move storage elsewhere. @@ -2312,28 +2409,30 @@ class ObjCCategoryDecl : public ObjCContainerDecl { SourceLocation IvarLBraceLoc; SourceLocation IvarRBraceLoc; - ObjCCategoryDecl(DeclContext *DC, SourceLocation AtLoc, + ObjCCategoryDecl(const ASTContext &C, DeclContext *DC, SourceLocation AtLoc, SourceLocation ClassNameLoc, SourceLocation CategoryNameLoc, IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, - ObjCTypeParamList *typeParamList, + ObjCCategoryDecl *PrevDecl, ObjCTypeParamList *typeParamList, SourceLocation IvarLBraceLoc = SourceLocation(), SourceLocation IvarRBraceLoc = SourceLocation()); + /// True if a valid hash is stored in ODRHash. + bool hasODRHash() const; + void setHasODRHash(bool Hash = true); + void anchor() override; public: friend class ASTDeclReader; friend class ASTDeclWriter; - static ObjCCategoryDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation AtLoc, - SourceLocation ClassNameLoc, - SourceLocation CategoryNameLoc, - IdentifierInfo *Id, - ObjCInterfaceDecl *IDecl, - ObjCTypeParamList *typeParamList, - SourceLocation IvarLBraceLoc=SourceLocation(), - SourceLocation IvarRBraceLoc=SourceLocation()); + static ObjCCategoryDecl * + Create(ASTContext &C, DeclContext *DC, SourceLocation AtLoc, + SourceLocation ClassNameLoc, SourceLocation CategoryNameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, + ObjCCategoryDecl *PrevDecl, ObjCTypeParamList *typeParamList, + SourceLocation IvarLBraceLoc = SourceLocation(), + SourceLocation IvarRBraceLoc = SourceLocation()); static ObjCCategoryDecl *CreateDeserialized(ASTContext &C, unsigned ID); ObjCInterfaceDecl *getClassInterface() { return ClassInterface; } @@ -2357,11 +2456,12 @@ class ObjCCategoryDecl : public ObjCContainerDecl { /// implements. void setProtocolList(ObjCProtocolDecl *const*List, unsigned Num, const SourceLocation *Locs, ASTContext &C) { - ReferencedProtocols.set(List, Num, Locs, C); + data().ReferencedProtocols.set(List, Num, Locs, C); } const ObjCProtocolList &getReferencedProtocols() const { - return ReferencedProtocols; + assert(hasDefinition() && "Category must always have a definition"); + return data().ReferencedProtocols; } using protocol_iterator = ObjCProtocolList::iterator; @@ -2372,11 +2472,18 @@ class ObjCCategoryDecl : public ObjCContainerDecl { } protocol_iterator protocol_begin() const { - return ReferencedProtocols.begin(); + assert(hasDefinition() && "Category must always have a definition"); + return data().ReferencedProtocols.begin(); } - protocol_iterator protocol_end() const { return ReferencedProtocols.end(); } - unsigned protocol_size() const { return ReferencedProtocols.size(); } + protocol_iterator protocol_end() const { + assert(hasDefinition() && "Category must always have a definition"); + return data().ReferencedProtocols.end(); + } + unsigned protocol_size() const { + assert(hasDefinition() && "Category must always have a definition"); + return data().ReferencedProtocols.size(); + } using protocol_loc_iterator = ObjCProtocolList::loc_iterator; using protocol_loc_range = llvm::iterator_range; @@ -2386,11 +2493,13 @@ class ObjCCategoryDecl : public ObjCContainerDecl { } protocol_loc_iterator protocol_loc_begin() const { - return ReferencedProtocols.loc_begin(); + assert(hasDefinition() && "Category must always have a definition"); + return data().ReferencedProtocols.loc_begin(); } protocol_loc_iterator protocol_loc_end() const { - return ReferencedProtocols.loc_end(); + assert(hasDefinition() && "Category must always have a definition"); + return data().ReferencedProtocols.loc_end(); } ObjCCategoryDecl *getNextClassCategory() const { return NextClassCategory; } @@ -2434,6 +2543,47 @@ class ObjCCategoryDecl : public ObjCContainerDecl { static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCCategory; } + + /// Starts the definition of this Objective-C category. + void startDefinition(); + + using redeclarable_base::getMostRecentDecl; + using redeclarable_base::getPreviousDecl; + using redeclarable_base::isFirstDecl; + using redeclarable_base::redecls; + using redeclarable_base::redecls_begin; + using redeclarable_base::redecls_end; + + /// Retrieves the canonical declaration of this Objective-C class. + ObjCCategoryDecl *getCanonicalDecl() override { return getFirstDecl(); } + const ObjCCategoryDecl *getCanonicalDecl() const { return getFirstDecl(); } + + /// Determine whether this class has been defined. + bool hasDefinition() const { + // If the name of this class is out-of-date, bring it up-to-date, which + // might bring in a definition. + // Note: a null value indicates that we don't have a definition and that + // modules are enabled. + if (!Data.getOpaqueValue()) + getMostRecentDecl(); + + return Data.getPointer(); + } + + /// Retrieve the definition of this category/extension. + ObjCCategoryDecl *getDefinition() { + assert(hasDefinition() && "Category must always have a definition"); + return Data.getPointer()->Definition; + } + + /// Retrieve the definition of this category/extension. + const ObjCCategoryDecl *getDefinition() const { + assert(hasDefinition() && "Category must always have a definition"); + return Data.getPointer()->Definition; + } + + /// Get precomputed ODRHash or add a new one. + unsigned getODRHash(); }; class ObjCImplDecl : public ObjCContainerDecl { @@ -2743,17 +2893,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); @@ -2762,6 +2920,17 @@ class ObjCCompatibleAliasDecl : public NamedDecl { ObjCInterfaceDecl *getClassInterface() { return AliasedClass; } void setClassInterface(ObjCInterfaceDecl *D) { AliasedClass = D; } + SourceLocation getClassInterfaceLoc() const { return AliasedClassLoc; } + + void setClassInterfaceLoc(SourceLocation Loc) { AliasedClassLoc = Loc; } + + SourceLocation getAtLoc() const { return AtLoc; } + void setAtLoc(SourceLocation Loc) { AtLoc = Loc; } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(AtLoc, AtLoc); + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCCompatibleAlias; } }; diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 8b2c271fbf25c..a974c8baad9b9 100755 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1730,6 +1730,10 @@ class ClassTemplateSpecializationDecl return *TemplateArgs; } + void setTemplateArgs(TemplateArgumentList *Args) { + TemplateArgs = Args; + } + /// Determine the kind of specialization that this /// declaration represents. TemplateSpecializationKind getSpecializationKind() const { @@ -1762,6 +1766,10 @@ class ClassTemplateSpecializationDecl getTemplateSpecializationKind()); } + void setSpecializedTemplate(ClassTemplateDecl *Specialized) { + SpecializedTemplate = Specialized; + } + void setSpecializationKind(TemplateSpecializationKind TSK) { SpecializationKind = TSK; } 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/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 1a16aa7aacec6..d16bd27edc23e 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3306,13 +3306,15 @@ class DependentScopeDeclRefExpr final /// literal is the extent of the enclosing scope. class ExprWithCleanups final : public FullExpr, - private llvm::TrailingObjects { + private llvm::TrailingObjects< + ExprWithCleanups, + llvm::PointerUnion> { public: /// The type of objects that are kept in the cleanup. - /// It's useful to remember the set of blocks; we could also - /// remember the set of temporaries, but there's currently - /// no need. - using CleanupObject = BlockDecl *; + /// It's useful to remember the set of blocks and block-scoped compound + /// literals; we could also remember the set of temporaries, but there's + /// currently no need. + using CleanupObject = llvm::PointerUnion; private: friend class ASTStmtReader; diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h index dbb2b2ff7099e..abe5b63b62d38 100644 --- a/clang/include/clang/AST/ExprObjC.h +++ b/clang/include/clang/AST/ExprObjC.h @@ -1697,13 +1697,23 @@ class ObjCBridgedCastExpr final /// expressions. /// class ObjCAvailabilityCheckExpr : public Expr { +public: + struct VersionAsWritten { + /// Platform version canonicalized for use with availability checks. + VersionTuple Version; + /// Platform version as written in the source. + VersionTuple SourceVersion; + }; + +private: friend class ASTStmtReader; - VersionTuple VersionToCheck; + VersionAsWritten VersionToCheck; SourceLocation AtLoc, RParen; public: - ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, SourceLocation AtLoc, + ObjCAvailabilityCheckExpr(VersionAsWritten VersionToCheck, + SourceLocation AtLoc, SourceLocation RParen, QualType Ty) : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_RValue, OK_Ordinary, false, false, false, false), @@ -1717,8 +1727,9 @@ class ObjCAvailabilityCheckExpr : public Expr { SourceRange getSourceRange() const { return {AtLoc, RParen}; } /// This may be '*', in which case this should fold to true. - bool hasVersion() const { return !VersionToCheck.empty(); } - VersionTuple getVersion() { return VersionToCheck; } + bool hasVersion() const { return !VersionToCheck.Version.empty(); } + VersionTuple getVersion() { return VersionToCheck.Version; } + VersionTuple getVersionAsWritten() { return VersionToCheck.SourceVersion; } child_range children() { return child_range(child_iterator(), child_iterator()); diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h index 899ac3f669371..68888fb02b3dd 100644 --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -173,7 +173,7 @@ class ExternalASTSource : public RefCountedBase { StringRef Path; StringRef ASTFile; ASTFileSignature Signature; - const Module *ClangModule = nullptr; + Module *ClangModule = nullptr; public: ASTSourceDescriptor() = default; @@ -181,13 +181,13 @@ class ExternalASTSource : public RefCountedBase { ASTFileSignature Signature) : PCHModuleName(std::move(Name)), Path(std::move(Path)), ASTFile(std::move(ASTFile)), Signature(Signature) {} - ASTSourceDescriptor(const Module &M); + ASTSourceDescriptor(Module &M); std::string getModuleName() const; StringRef getPath() const { return Path; } StringRef getASTFile() const { return ASTFile; } ASTFileSignature getSignature() const { return Signature; } - const Module *getModuleOrNull() const { return ClangModule; } + Module *getModuleOrNull() const { return ClangModule; } }; /// Return a descriptor for the corresponding module, if one exists. 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..e9403763ab78b 100644 --- a/clang/include/clang/AST/NonTrivialTypeVisitor.h +++ b/clang/include/clang/AST/NonTrivialTypeVisitor.h @@ -1,4 +1,4 @@ -//===-- NonTrivialTypeVisitor.h - Visitor for non-trivial Types *- C++ --*-===// +//===-- NonTrivialTypeVisitor.h - Visitor for non-trivial Types -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -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/ODRHash.h b/clang/include/clang/AST/ODRHash.h index cd4a6f37f5db3..0e49acef050ee 100644 --- a/clang/include/clang/AST/ODRHash.h +++ b/clang/include/clang/AST/ODRHash.h @@ -15,9 +15,11 @@ #ifndef LLVM_CLANG_AST_ODRHASH_H #define LLVM_CLANG_AST_ODRHASH_H +#include "clang/AST/Attr.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Type.h" #include "clang/AST/TemplateBase.h" +#include "clang/AST/DeclObjC.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerUnion.h" @@ -55,6 +57,26 @@ class ODRHash { // more information than the AddDecl class. void AddCXXRecordDecl(const CXXRecordDecl *Record); + // Use this for ODR checking records in C/Objective-C between modules. This + // method compares more information than the AddDecl class. + void AddRecordDecl(const RecordDecl *Record); + + // Use this for ODR checking ObjC interfaces. This + // method compares more information than the AddDecl class. + void AddObjCInterfaceDecl(const ObjCInterfaceDecl *Record); + + // Use this for ODR checking ObjC protocols. This + // method compares more information than the AddDecl class. + void AddObjCProtocolDecl(const ObjCProtocolDecl *P); + + // Use this for ODR checking ObjC interfaces. This + // method compares more information than the AddDecl class. + void AddObjCMethodDecl(const ObjCMethodDecl *Method); + + // Use this for ODR checking ObjC interfaces. This + // method compares more information than the AddDecl class. + void AddObjCCategoryDecl(const ObjCCategoryDecl *Cat); + // Use this for ODR checking functions between modules. This method compares // more information than the AddDecl class. SkipBody will process the // hash as if the function has no body. @@ -68,6 +90,10 @@ class ODRHash { // while AddDecl does not. void AddSubDecl(const Decl *D); + // Process attributes attached to a type that is being hashed. + void AddAttrs(const NamedDecl *D); + void AddAttr(const Attr *A); + // Reset the object for reuse. void clear(); @@ -90,6 +116,7 @@ class ODRHash { void AddBoolean(bool value); static bool isWhitelistedDecl(const Decl* D, const DeclContext *Parent); + static bool isWhitelistedAttr(const Attr *A); private: void AddDeclarationNameImpl(DeclarationName Name); 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/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index d293ea190aa43..c675beef63553 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -184,6 +184,7 @@ class TextNodeDumper void dumpBareDeclRef(const Decl *D); void dumpName(const NamedDecl *ND); void dumpAccessSpecifier(AccessSpecifier AS); + void dumpCleanupObject(const ExprWithCleanups::CleanupObject &C); void dumpDeclRef(const Decl *D, StringRef Label = {}); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index f5955c45fafc5..78aa294f37143 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -133,6 +133,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 @@ -188,6 +281,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) { @@ -223,6 +318,13 @@ class Qualifiers { L.removeAddressSpace(); R.removeAddressSpace(); } + + if (L.PtrAuth == R.PtrAuth) { + Q.PtrAuth = L.PtrAuth; + L.PtrAuth = PointerAuthQualifier(); + R.PtrAuth = PointerAuthQualifier(); + } + return Q; } @@ -245,15 +347,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; } @@ -386,6 +489,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(); } @@ -408,7 +521,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); @@ -416,8 +531,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) { @@ -434,6 +549,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. @@ -451,6 +569,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 @@ -462,7 +583,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. @@ -498,6 +622,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. @@ -530,8 +656,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(); } @@ -569,6 +699,7 @@ class Qualifiers { void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Mask); + PtrAuth.Profile(ID); } private: @@ -576,6 +707,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; @@ -1091,6 +1224,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 @@ -1136,6 +1277,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 @@ -6345,6 +6489,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/Analysis/AnalysisDeclContext.h b/clang/include/clang/Analysis/AnalysisDeclContext.h index 9faa78cde89c2..ed554feedead6 100644 --- a/clang/include/clang/Analysis/AnalysisDeclContext.h +++ b/clang/include/clang/Analysis/AnalysisDeclContext.h @@ -1,4 +1,4 @@ -// AnalysisDeclContext.h - Analysis context for Path Sens analysis -*- C++ -*-// +//===- AnalysisDeclContext.h - Context for path sensitivity -----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,11 @@ // //===----------------------------------------------------------------------===// // -// This file defines AnalysisDeclContext, a class that manages the analysis -// context data for path sensitive analysis. +/// \file +/// This file defines AnalysisDeclContext, a class that manages the analysis +/// context data for context sensitive and path sensitive analysis. +/// It also defines the helper classes to model entering, leaving or inlining +/// function calls. // //===----------------------------------------------------------------------===// @@ -64,14 +67,14 @@ class ManagedAnalysis { // which creates the analysis object given an AnalysisDeclContext. }; -/// AnalysisDeclContext contains the context data for the function or method -/// under analysis. +/// AnalysisDeclContext contains the context data for the function, method +/// or block under analysis. class AnalysisDeclContext { - /// Backpoint to the AnalysisManager object that created this - /// AnalysisDeclContext. This may be null. - AnalysisDeclContextManager *Manager; + // Backpoint to the AnalysisManager object that created this + // AnalysisDeclContext. This may be null. + AnalysisDeclContextManager *ADCMgr; - const Decl * const D; + const Decl *const D; std::unique_ptr cfg, completeCFG; std::unique_ptr cfgStmtMap; @@ -86,45 +89,36 @@ class AnalysisDeclContext { llvm::BumpPtrAllocator A; - llvm::DenseMap *ReferencedBlockVars = nullptr; + llvm::DenseMap *ReferencedBlockVars = nullptr; void *ManagedAnalyses = nullptr; public: - AnalysisDeclContext(AnalysisDeclContextManager *Mgr, - const Decl *D); + AnalysisDeclContext(AnalysisDeclContextManager *Mgr, const Decl *D); - AnalysisDeclContext(AnalysisDeclContextManager *Mgr, - const Decl *D, - const CFG::BuildOptions &BuildOptions); + AnalysisDeclContext(AnalysisDeclContextManager *Mgr, const Decl *D, + const CFG::BuildOptions &BuildOptions); ~AnalysisDeclContext(); ASTContext &getASTContext() const { return D->getASTContext(); } + const Decl *getDecl() const { return D; } - /// Return the AnalysisDeclContextManager (if any) that created - /// this AnalysisDeclContext. - AnalysisDeclContextManager *getManager() const { - return Manager; - } + AnalysisDeclContextManager *getManager() const { return ADCMgr; } - /// Return the build options used to construct the CFG. - CFG::BuildOptions &getCFGBuildOptions() { - return cfgBuildOptions; - } + CFG::BuildOptions &getCFGBuildOptions() { return cfgBuildOptions; } const CFG::BuildOptions &getCFGBuildOptions() const { return cfgBuildOptions; } - /// getAddEHEdges - Return true iff we are adding exceptional edges from - /// callExprs. If this is false, then try/catch statements and blocks - /// reachable from them can appear to be dead in the CFG, analysis passes must - /// cope with that. + /// \returns Whether we are adding exception handling edges from CallExprs. + /// If this is false, then try/catch statements and blocks reachable from them + /// can appear to be dead in the CFG, analysis passes must cope with that. bool getAddEHEdges() const { return cfgBuildOptions.AddEHEdges; } bool getUseUnoptimizedCFG() const { - return !cfgBuildOptions.PruneTriviallyFalseEdges; + return !cfgBuildOptions.PruneTriviallyFalseEdges; } bool getAddImplicitDtors() const { return cfgBuildOptions.AddImplicitDtors; } bool getAddInitializers() const { return cfgBuildOptions.AddInitializers; } @@ -132,25 +126,25 @@ class AnalysisDeclContext { void registerForcedBlockExpression(const Stmt *stmt); const CFGBlock *getBlockForRegisteredExpression(const Stmt *stmt); - /// Get the body of the Declaration. + /// \returns The body of the stored Decl \c D. Stmt *getBody() const; - /// Get the body of the Declaration. + /// \copydoc AnalysisDeclContext::getBody() /// \param[out] IsAutosynthesized Specifies if the body is auto-generated /// by the BodyFarm. Stmt *getBody(bool &IsAutosynthesized) const; - /// Checks if the body of the Decl is generated by the BodyFarm. + /// \returns Whether the body of the Decl \c D is generated by the BodyFarm. /// - /// Note, the lookup is not free. We are going to call getBody behind + /// \note The lookup is not free. We are going to call getBody behind /// the scenes. /// \sa getBody bool isBodyAutosynthesized() const; - /// Checks if the body of the Decl is generated by the BodyFarm from a - /// model file. + /// \returns Whether the body of the Decl \c D is generated by the BodyFarm + /// from a model file. /// - /// Note, the lookup is not free. We are going to call getBody behind + /// \note The lookup is not free. We are going to call getBody behind /// the scenes. /// \sa getBody bool isBodyAutosynthesizedFromModelFile() const; @@ -161,40 +155,41 @@ class AnalysisDeclContext { CFGReverseBlockReachabilityAnalysis *getCFGReachablityAnalysis(); - /// Return a version of the CFG without any edges pruned. + /// \returns A version of the CFG without any edges pruned. CFG *getUnoptimizedCFG(); void dumpCFG(bool ShowColors); - /// Returns true if we have built a CFG for this analysis context. - /// Note that this doesn't correspond to whether or not a valid CFG exists, it + /// \returns Whether we have built a CFG for this analysis context. + /// + /// \note This doesn't correspond to whether or not a valid CFG exists, it /// corresponds to whether we *attempted* to build one. bool isCFGBuilt() const { return builtCFG; } ParentMap &getParentMap(); - using referenced_decls_iterator = const VarDecl * const *; + using referenced_decls_iterator = const VarDecl *const *; llvm::iterator_range getReferencedBlockVars(const BlockDecl *BD); - /// Return the ImplicitParamDecl* associated with 'self' if this - /// AnalysisDeclContext wraps an ObjCMethodDecl. Returns NULL otherwise. + /// \returns The ImplicitParamDecl associated with \c self if this + /// AnalysisDeclContext wraps an ObjCMethodDecl or nullptr otherwise. const ImplicitParamDecl *getSelfDecl() const; - const StackFrameContext *getStackFrame(LocationContext const *Parent, + /// \copydoc LocationContextManager::getStackFrame() + const StackFrameContext *getStackFrame(LocationContext const *ParentLC, const Stmt *S, const CFGBlock *Blk, - unsigned BlockCount, unsigned Idx); + unsigned BlockCount, unsigned Index); + /// \copydoc LocationContextManager::getBlockInvocationContext() const BlockInvocationContext * - getBlockInvocationContext(const LocationContext *parent, - const BlockDecl *BD, - const void *ContextData); - - /// Return the specified analysis object, lazily running the analysis if - /// necessary. Return NULL if the analysis could not run. - template - T *getAnalysis() { + getBlockInvocationContext(const LocationContext *ParentLC, + const BlockDecl *BD, const void *Data); + + /// \returns The specified analysis object, lazily running the analysis if + /// necessary or nullptr if the analysis could not run. + template T *getAnalysis() { const void *tag = T::getTag(); ManagedAnalysis *&data = getAnalysisImpl(tag); if (!data) { @@ -203,19 +198,22 @@ class AnalysisDeclContext { return static_cast(data); } - /// Returns true if the root namespace of the given declaration is the 'std' - /// C++ namespace. + /// \returns Whether the root namespace of \p D is the \c std C++ namespace. static bool isInStdNamespace(const Decl *D); private: - ManagedAnalysis *&getAnalysisImpl(const void* tag); + ManagedAnalysis *&getAnalysisImpl(const void *tag); LocationContextManager &getLocationContextManager(); }; +/// It wraps the AnalysisDeclContext to represent both the call stack with +/// the help of StackFrameContext and inside the function calls the +/// BlockInvocationContext. It is needed for context sensitive analysis to +/// model entering, leaving or inlining function calls. class LocationContext : public llvm::FoldingSetNode { public: - enum ContextKind { StackFrame, Scope, Block }; + enum ContextKind { StackFrame, Block }; private: ContextKind Kind; @@ -229,8 +227,7 @@ class LocationContext : public llvm::FoldingSetNode { protected: LocationContext(ContextKind k, AnalysisDeclContext *ctx, - const LocationContext *parent, - int64_t ID) + const LocationContext *parent, int64_t ID) : Kind(k), Ctx(ctx), Parent(parent), ID(ID) {} public: @@ -238,9 +235,7 @@ class LocationContext : public llvm::FoldingSetNode { ContextKind getKind() const { return Kind; } - int64_t getID() const { - return ID; - } + int64_t getID() const { return ID; } AnalysisDeclContext *getAnalysisDeclContext() const { return Ctx; } @@ -248,58 +243,61 @@ class LocationContext : public llvm::FoldingSetNode { bool isParentOf(const LocationContext *LC) const; - const Decl *getDecl() const { return getAnalysisDeclContext()->getDecl(); } + const Decl *getDecl() const { return Ctx->getDecl(); } - CFG *getCFG() const { return getAnalysisDeclContext()->getCFG(); } + CFG *getCFG() const { return Ctx->getCFG(); } - template - T *getAnalysis() const { - return getAnalysisDeclContext()->getAnalysis(); - } + template T *getAnalysis() const { return Ctx->getAnalysis(); } - const ParentMap &getParentMap() const { - return getAnalysisDeclContext()->getParentMap(); - } + const ParentMap &getParentMap() const { return Ctx->getParentMap(); } - const ImplicitParamDecl *getSelfDecl() const { - return Ctx->getSelfDecl(); - } + /// \copydoc AnalysisDeclContext::getSelfDecl() + const ImplicitParamDecl *getSelfDecl() const { return Ctx->getSelfDecl(); } const StackFrameContext *getStackFrame() const; - /// Return true if the current LocationContext has no caller context. + /// \returns Whether the current LocationContext has no caller context. virtual bool inTopFrame() const; virtual void Profile(llvm::FoldingSetNodeID &ID) = 0; - void dumpStack( - raw_ostream &Out, const char *NL = "\n", - std::function printMoreInfoPerContext = - [](const LocationContext *) {}) const; + /// Prints out the call stack. + /// + /// \param Out The out stream. + LLVM_DUMP_METHOD void dumpStack(raw_ostream &Out) const; + /// Prints out the call stack in \c json format. + /// + /// \param Out The out stream. + /// \param NL The newline. + /// \param Space The space count for indentation. + /// \param IsDot Whether the output format is \c dot. + /// \param printMoreInfoPerContext + /// A callback to print more information for each context, for example: + /// \code + /// [&](const LocationContext *LC) { LC->dump(); } + /// \endcode void printJson( raw_ostream &Out, const char *NL = "\n", unsigned int Space = 0, bool IsDot = false, std::function printMoreInfoPerContext = [](const LocationContext *) {}) const; - void dump() const; + LLVM_DUMP_METHOD void dump() const; -public: - static void ProfileCommon(llvm::FoldingSetNodeID &ID, - ContextKind ck, + static void ProfileCommon(llvm::FoldingSetNodeID &ID, ContextKind ck, AnalysisDeclContext *ctx, - const LocationContext *parent, - const void *data); + const LocationContext *parent, const void *data); }; +/// It represents a stack frame of the call stack (based on CallEvent). class StackFrameContext : public LocationContext { friend class LocationContextManager; - // The callsite where this stack frame is established. + // The call site where this stack frame is established. const Stmt *CallSite; - // The parent block of the callsite. + // The parent block of the call site. const CFGBlock *Block; // The number of times the 'Block' has been visited. @@ -307,14 +305,14 @@ class StackFrameContext : public LocationContext { // called multiple times in a loop. const unsigned BlockCount; - // The index of the callsite in the CFGBlock. + // The index of the call site in the CFGBlock. const unsigned Index; - StackFrameContext(AnalysisDeclContext *ctx, const LocationContext *parent, - const Stmt *s, const CFGBlock *blk, unsigned blockCount, - unsigned idx, int64_t ID) - : LocationContext(StackFrame, ctx, parent, ID), CallSite(s), Block(blk), - BlockCount(blockCount), Index(idx) {} + StackFrameContext(AnalysisDeclContext *ADC, const LocationContext *ParentLC, + const Stmt *S, const CFGBlock *Block, unsigned BlockCount, + unsigned Index, int64_t ID) + : LocationContext(StackFrame, ADC, ParentLC, ID), CallSite(S), + Block(Block), BlockCount(BlockCount), Index(Index) {} public: ~StackFrameContext() override = default; @@ -323,117 +321,98 @@ class StackFrameContext : public LocationContext { const CFGBlock *getCallSiteBlock() const { return Block; } - /// Return true if the current LocationContext has no caller context. - bool inTopFrame() const override { return getParent() == nullptr; } + bool inTopFrame() const override { return getParent() == nullptr; } unsigned getIndex() const { return Index; } void Profile(llvm::FoldingSetNodeID &ID) override; - static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ctx, - const LocationContext *parent, const Stmt *s, - const CFGBlock *blk, unsigned blockCount, unsigned idx) { - ProfileCommon(ID, StackFrame, ctx, parent, s); - ID.AddPointer(blk); - ID.AddInteger(blockCount); - ID.AddInteger(idx); + static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ADC, + const LocationContext *ParentLC, const Stmt *S, + const CFGBlock *Block, unsigned BlockCount, + unsigned Index) { + ProfileCommon(ID, StackFrame, ADC, ParentLC, S); + ID.AddPointer(Block); + ID.AddInteger(BlockCount); + ID.AddInteger(Index); } - static bool classof(const LocationContext *Ctx) { - return Ctx->getKind() == StackFrame; - } -}; - -class ScopeContext : public LocationContext { - friend class LocationContextManager; - - const Stmt *Enter; - - ScopeContext(AnalysisDeclContext *ctx, const LocationContext *parent, - const Stmt *s, int64_t ID) - : LocationContext(Scope, ctx, parent, ID), Enter(s) {} - -public: - ~ScopeContext() override = default; - - void Profile(llvm::FoldingSetNodeID &ID) override; - - static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ctx, - const LocationContext *parent, const Stmt *s) { - ProfileCommon(ID, Scope, ctx, parent, s); - } - - static bool classof(const LocationContext *Ctx) { - return Ctx->getKind() == Scope; + static bool classof(const LocationContext *LC) { + return LC->getKind() == StackFrame; } }; +/// It represents a block invocation (based on BlockCall). class BlockInvocationContext : public LocationContext { friend class LocationContextManager; const BlockDecl *BD; // FIXME: Come up with a more type-safe way to model context-sensitivity. - const void *ContextData; + const void *Data; - BlockInvocationContext(AnalysisDeclContext *ctx, - const LocationContext *parent, const BlockDecl *bd, - const void *contextData, int64_t ID) - : LocationContext(Block, ctx, parent, ID), BD(bd), - ContextData(contextData) {} + BlockInvocationContext(AnalysisDeclContext *ADC, + const LocationContext *ParentLC, const BlockDecl *BD, + const void *Data, int64_t ID) + : LocationContext(Block, ADC, ParentLC, ID), BD(BD), Data(Data) {} public: ~BlockInvocationContext() override = default; const BlockDecl *getBlockDecl() const { return BD; } - const void *getContextData() const { return ContextData; } + const void *getData() const { return Data; } void Profile(llvm::FoldingSetNodeID &ID) override; - static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ctx, - const LocationContext *parent, const BlockDecl *bd, - const void *contextData) { - ProfileCommon(ID, Block, ctx, parent, bd); - ID.AddPointer(contextData); + static void Profile(llvm::FoldingSetNodeID &ID, AnalysisDeclContext *ADC, + const LocationContext *ParentLC, const BlockDecl *BD, + const void *Data) { + ProfileCommon(ID, Block, ADC, ParentLC, BD); + ID.AddPointer(Data); } - static bool classof(const LocationContext *Ctx) { - return Ctx->getKind() == Block; + static bool classof(const LocationContext *LC) { + return LC->getKind() == Block; } }; class LocationContextManager { llvm::FoldingSet Contexts; - /// ID used for generating a new location context. + // ID used for generating a new location context. int64_t NewID = 0; public: ~LocationContextManager(); - const StackFrameContext *getStackFrame(AnalysisDeclContext *ctx, - const LocationContext *parent, - const Stmt *s, const CFGBlock *blk, - unsigned blockCount, unsigned idx); - - const ScopeContext *getScope(AnalysisDeclContext *ctx, - const LocationContext *parent, - const Stmt *s); - + /// Obtain a context of the call stack using its parent context. + /// + /// \param ADC The AnalysisDeclContext. + /// \param ParentLC The parent context of this newly created context. + /// \param S The call. + /// \param Block The basic block. + /// \param BlockCount The current count of entering into \p Blk. + /// \param Index The index of \p Blk. + /// \returns The context for \p D with parent context \p ParentLC. + const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC, + const LocationContext *ParentLC, + const Stmt *S, const CFGBlock *Block, + unsigned BlockCount, unsigned Index); + + /// Obtain a context of the block invocation using its parent context. + /// + /// \param ADC The AnalysisDeclContext. + /// \param ParentLC The parent context of this newly created context. + /// \param BD The BlockDecl. + /// \param Data The raw data to store as part of the context. const BlockInvocationContext * - getBlockInvocationContext(AnalysisDeclContext *ctx, - const LocationContext *parent, - const BlockDecl *BD, - const void *ContextData); + getBlockInvocationContext(AnalysisDeclContext *ADC, + const LocationContext *ParentLC, + const BlockDecl *BD, const void *Data); /// Discard all previously created LocationContext objects. void clear(); -private: - template - const LOC *getLocationContext(AnalysisDeclContext *ctx, - const LocationContext *parent, - const DATA *d); }; class AnalysisDeclContextManager { @@ -441,36 +420,31 @@ class AnalysisDeclContextManager { llvm::DenseMap>; ContextMap Contexts; - LocationContextManager LocContexts; + LocationContextManager LocCtxMgr; CFG::BuildOptions cfgBuildOptions; - /// Pointer to an interface that can provide function bodies for - /// declarations from external source. + // Pointer to an interface that can provide function bodies for + // declarations from external source. std::unique_ptr Injector; - /// A factory for creating and caching implementations for common - /// methods during the analysis. + // A factory for creating and caching implementations for common + // methods during the analysis. BodyFarm FunctionBodyFarm; - /// Flag to indicate whether or not bodies should be synthesized - /// for well-known functions. + // Flag to indicate whether or not bodies should be synthesized + // for well-known functions. bool SynthesizeBodies; public: - AnalysisDeclContextManager(ASTContext &ASTCtx, bool useUnoptimizedCFG = false, - bool addImplicitDtors = false, - bool addInitializers = false, - bool addTemporaryDtors = false, - bool addLifetime = false, - bool addLoopExit = false, - bool addScopes = false, - bool synthesizeBodies = false, - bool addStaticInitBranches = false, - bool addCXXNewAllocator = true, - bool addRichCXXConstructors = true, - bool markElidedCXXConstructors = true, - bool addVirtualBaseBranches = true, - CodeInjector *injector = nullptr); + AnalysisDeclContextManager( + ASTContext &ASTCtx, bool useUnoptimizedCFG = false, + bool addImplicitDtors = false, bool addInitializers = false, + bool addTemporaryDtors = false, bool addLifetime = false, + bool addLoopExit = false, bool addScopes = false, + bool synthesizeBodies = false, bool addStaticInitBranches = false, + bool addCXXNewAllocator = true, bool addRichCXXConstructors = true, + bool markElidedCXXConstructors = true, bool addVirtualBaseBranches = true, + CodeInjector *injector = nullptr); AnalysisDeclContext *getContext(const Decl *D); @@ -478,37 +452,27 @@ class AnalysisDeclContextManager { return !cfgBuildOptions.PruneTriviallyFalseEdges; } - CFG::BuildOptions &getCFGBuildOptions() { - return cfgBuildOptions; - } + CFG::BuildOptions &getCFGBuildOptions() { return cfgBuildOptions; } - /// Return true if faux bodies should be synthesized for well-known - /// functions. + /// \returns Whether faux bodies should be synthesized for known functions. bool synthesizeBodies() const { return SynthesizeBodies; } - const StackFrameContext *getStackFrame(AnalysisDeclContext *Ctx, - const LocationContext *Parent, - const Stmt *S, const CFGBlock *Blk, - unsigned BlockCount, unsigned Idx) { - return LocContexts.getStackFrame(Ctx, Parent, S, Blk, BlockCount, Idx); - } - - // Get the top level stack frame. + /// Obtain the beginning context of the analysis. + /// + /// \returns The top level stack frame for \p D. const StackFrameContext *getStackFrame(const Decl *D) { - return LocContexts.getStackFrame(getContext(D), nullptr, nullptr, nullptr, - 0, 0); + return LocCtxMgr.getStackFrame(getContext(D), nullptr, nullptr, nullptr, 0, + 0); } - // Get a stack frame with parent. - StackFrameContext const *getStackFrame(const Decl *D, + /// \copydoc LocationContextManager::getStackFrame() + const StackFrameContext *getStackFrame(AnalysisDeclContext *ADC, const LocationContext *Parent, - const Stmt *S, const CFGBlock *Blk, - unsigned BlockCount, unsigned Idx) { - return LocContexts.getStackFrame(getContext(D), Parent, S, Blk, BlockCount, - Idx); + const Stmt *S, const CFGBlock *Block, + unsigned BlockCount, unsigned Index) { + return LocCtxMgr.getStackFrame(ADC, Parent, S, Block, BlockCount, Index); } - /// Get a reference to {@code BodyFarm} instance. BodyFarm &getBodyFarm(); /// Discard all previously created AnalysisDeclContexts. @@ -517,9 +481,7 @@ class AnalysisDeclContextManager { private: friend class AnalysisDeclContext; - LocationContextManager &getLocationContextManager() { - return LocContexts; - } + LocationContextManager &getLocationContextManager() { return LocCtxMgr; } }; } // namespace clang diff --git a/clang/include/clang/Analysis/AnyCall.h b/clang/include/clang/Analysis/AnyCall.h index 97a94d299e64c..16371eb1da183 100644 --- a/clang/include/clang/Analysis/AnyCall.h +++ b/clang/include/clang/Analysis/AnyCall.h @@ -41,6 +41,9 @@ class AnyCall { /// An implicit or explicit C++ constructor call Constructor, + /// A C++ inherited constructor produced by a "using T::T" directive + InheritedConstructor, + /// A C++ allocation function call (operator `new`), via C++ new-expression Allocator, @@ -84,6 +87,9 @@ class AnyCall { AnyCall(const CXXConstructExpr *NE) : E(NE), D(NE->getConstructor()), K(Constructor) {} + AnyCall(const CXXInheritedCtorInitExpr *CIE) + : E(CIE), D(CIE->getConstructor()), K(InheritedConstructor) {} + AnyCall(const CXXDestructorDecl *D) : E(nullptr), D(D), K(Destructor) {} AnyCall(const CXXConstructorDecl *D) : E(nullptr), D(D), K(Constructor) {} @@ -114,6 +120,8 @@ class AnyCall { return AnyCall(CXDE); } else if (const auto *CXCE = dyn_cast(E)) { return AnyCall(CXCE); + } else if (const auto *CXCIE = dyn_cast(E)) { + return AnyCall(CXCIE); } else { return None; } @@ -169,6 +177,7 @@ class AnyCall { return cast(E)->getCallReturnType(Ctx); case Destructor: case Constructor: + case InheritedConstructor: case Allocator: case Deallocator: return cast(D)->getReturnType(); diff --git a/clang/include/clang/Analysis/ConstructionContext.h b/clang/include/clang/Analysis/ConstructionContext.h index f1564f9fe740e..4fa5c8b454a03 100644 --- a/clang/include/clang/Analysis/ConstructionContext.h +++ b/clang/include/clang/Analysis/ConstructionContext.h @@ -110,6 +110,9 @@ class ConstructionContextItem { ConstructionContextItem(const CXXConstructExpr *CE, unsigned Index) : Data(CE), Kind(ArgumentKind), Index(Index) {} + ConstructionContextItem(const CXXInheritedCtorInitExpr *CE, unsigned Index) + : Data(CE), Kind(ArgumentKind), Index(Index) {} + ConstructionContextItem(const ObjCMessageExpr *ME, unsigned Index) : Data(ME), Kind(ArgumentKind), Index(Index) {} @@ -117,7 +120,7 @@ class ConstructionContextItem { ConstructionContextItem(const Expr *E, unsigned Index) : Data(E), Kind(ArgumentKind), Index(Index) { assert(isa(E) || isa(E) || - isa(E)); + isa(E) || isa(E)); } ConstructionContextItem(const CXXCtorInitializer *Init) 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 d9ca121b6510f..5751476679603 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">; @@ -234,6 +238,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; @@ -1777,6 +1784,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]>; @@ -1870,6 +1883,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>; @@ -1891,7 +1910,7 @@ def ObjCDirect : Attr { def ObjCDirectMembers : Attr { let Spellings = [Clang<"objc_direct_members">]; - let Subjects = SubjectList<[ObjCImpl, ObjCCategory], ErrorDiag>; + let Subjects = SubjectList<[ObjCImpl, ObjCInterface, ObjCCategory], ErrorDiag>; let LangOpts = [ObjC]; let Documentation = [ObjCDirectMembersDocs]; } @@ -1999,6 +2018,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]; @@ -2332,6 +2446,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 515476df3fddc..e745f0456f373 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1425,6 +1425,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 = [{ @@ -3327,6 +3486,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"; @@ -4105,7 +4330,7 @@ documentation for more information. def ObjCDirectMembersDocs : Documentation { let Category = DocCatDecl; let Content = [{ -The ``objc_direct_members`` attribute can be placed on an Objective-C +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. @@ -4114,9 +4339,7 @@ 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. +method was annotated with the ``objc_direct`` attribute. When ``objc_direct_members`` is placed on an ``@implementation`` block, methods defined in the block are considered to be declared as direct unless diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 51d3500df8aee..2b9b68d71a8b2 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -869,12 +869,12 @@ LANGBUILTIN(_ReturnAddress, "v*", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotl8, "UcUcUc", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotl16, "UsUsUc", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotl, "UiUii", "n", ALL_MS_LANGUAGES) -LANGBUILTIN(_lrotl, "ULiULii", "n", ALL_MS_LANGUAGES) +LANGBUILTIN(_lrotl, "UNiUNii", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotl64, "UWiUWii", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotr8, "UcUcUc", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotr16, "UsUsUc", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotr, "UiUii", "n", ALL_MS_LANGUAGES) -LANGBUILTIN(_lrotr, "ULiULii", "n", ALL_MS_LANGUAGES) +LANGBUILTIN(_lrotr, "UNiUNii", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_rotr64, "UWiUWii", "n", ALL_MS_LANGUAGES) LANGBUILTIN(__va_start, "vc**.", "nt", ALL_MS_LANGUAGES) LANGBUILTIN(__fastfail, "vUi", "nr", ALL_MS_LANGUAGES) @@ -1503,6 +1503,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 7f26ca8b4d619..393b66232abd3 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -63,7 +63,6 @@ CODEGENOPT(ExperimentalNewPassManager, 1, 0) ///< Enables the new, experimental CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new ///< pass manager. CODEGENOPT(DisableRedZone , 1, 0) ///< Set when -mno-red-zone is enabled. -CODEGENOPT(EnableDebugEntryValues, 1, 0) ///< Emit call site parameter dbg info CODEGENOPT(IndirectTlsSegRefs, 1, 0) ///< Set when -mno-tls-direct-seg-refs ///< is specified. CODEGENOPT(DisableTailCalls , 1, 0) ///< Do not emit tail calls. @@ -132,6 +131,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 @@ -286,10 +286,6 @@ CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists. CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program /// vtable optimization. -CODEGENOPT(VirtualFunctionElimination, 1, 0) ///< Whether to apply the dead - /// virtual function elimination - /// optimization. - /// Whether to use public LTO visibility for entities in std and stdext /// namespaces. This is enabled by clang-cl's /MT and /MTd flags. CODEGENOPT(LTOVisibilityPublicStd, 1, 0) diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 900620a39292d..c5e1c6d54eade 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/ADT/FloatingPointMode.h" @@ -298,6 +299,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.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 39242c972ea28..8bb20aa84d657 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -206,6 +206,8 @@ def warn_invalid_ios_deployment_target : Warning< "invalid iOS deployment version '%0', iOS 10 is the maximum deployment " "target for 32-bit targets">, InGroup, DefaultError; +def err_invalid_macos_32bit_deployment_target : Error< + "32-bit targets are not supported when building for Mac Catalyst">; def err_drv_conflicting_deployment_targets : Error< "conflicting deployment targets, both '%0' and '%1' are present in environment">; def err_arc_unsupported_on_runtime : Error< @@ -237,6 +239,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 a15fb908c5374..d5fd557271069 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -411,6 +411,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">; @@ -534,6 +535,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">; @@ -700,8 +702,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">; @@ -717,6 +718,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">; def MisleadingIndentation : DiagGroup<"misleading-indentation">; // This covers both the deprecated case (in C++98) 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 63221d8758fc6..0858e322302f9 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -265,7 +265,7 @@ def err_label_end_of_compound_statement : Error< def err_address_of_label_outside_fn : Error< "use of address-of-label extension outside of a function body">; def err_asm_operand_wide_string_literal : Error< - "cannot use %select{unicode|wide}0 string literal in 'asm'">; + "cannot use %select{unicode|wide|an empty}0 string literal in 'asm'">; def err_expected_selector_for_method : Error< "expected selector for Objective-C method">; def err_expected_property_name : Error<"expected property name">; @@ -277,7 +277,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">; @@ -1259,6 +1259,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 dd9649bcb5c31..c849b224c01b2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -741,6 +741,61 @@ 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'">; +def note_ptrauth_virtual_function_pointer_incomplete_arg_ret : + Note<"cannot take an address of a virtual member function if its return or " + "argument types are incomplete">; +def note_ptrauth_virtual_function_incomplete_arg_ret_type : + Note<"%0 is incomplete">; + +// __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">, @@ -911,8 +966,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">, @@ -996,8 +1051,8 @@ 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_duplicate_decl : Error< - "%select{|direct }0method declaration conflicts " - "with previous %select{|direct }1declaration of method %2">; + "%select{|direct }0%select{method|property}1 declaration conflicts " + "with previous %select{|direct }2declaration of %select{method|property}1 %3">; def err_objc_direct_impl_decl_mismatch : Error< "direct method was declared in %select{the primary interface|an extension|a category}0 " "but is implemented in %select{the primary interface|a category|a different category}1">; @@ -1013,6 +1068,8 @@ def warn_objc_direct_ignored : Warning< def warn_objc_direct_property_ignored : Warning< "direct attribute on property %0 ignored (not implemented by this Objective-C runtime)">, InGroup; +def err_objc_direct_dynamic_property : Error< + "direct property cannot be @dynamic">; def warn_conflicting_overriding_ret_types : Warning< "conflicting return type in " @@ -1331,6 +1388,12 @@ 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">, @@ -3603,6 +3666,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< @@ -3743,6 +3809,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">, @@ -4008,6 +4135,10 @@ def note_ovl_candidate_bad_ownership : Note< "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}4 ownership," " but parameter has %select{no|__unsafe_unretained|__strong|__weak|" "__autoreleasing}5 ownership">; +def note_ovl_candidate_bad_ptrauth : Note< + "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: " + "%ordinal8 argument (%3) has %select{no ptrauth|%5}4 qualifier," + " but parameter has %select{no ptrauth|%7}6 qualifier">; def note_ovl_candidate_bad_cvr_this : Note< "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: " "'this' argument has type %3, but method is not marked " @@ -4954,7 +5085,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 " @@ -5378,6 +5509,8 @@ def note_enters_block_captures_weak : Note< def note_enters_block_captures_non_trivial_c_struct : Note< "jump enters lifetime of block which captures a C struct that is non-trivial " "to destroy">; +def note_enters_compound_literal_scope : Note< + "jump enters lifetime of a compound literal that is non-trivial to destruct">; def note_exits_cleanup : Note< "jump exits scope of variable with __attribute__((cleanup))">; @@ -5421,6 +5554,8 @@ def note_exits_block_captures_weak : Note< def note_exits_block_captures_non_trivial_c_struct : Note< "jump exits lifetime of block which captures a C struct that is non-trivial " "to destroy">; +def note_exits_compound_literal_scope : Note< + "jump exits lifetime of a compound literal that is non-trivial to destruct">; def err_func_returning_qualified_void : ExtWarn< "function cannot return qualified void type %0">, @@ -7406,6 +7541,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">; @@ -7708,6 +7856,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">, @@ -8674,6 +8824,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">, @@ -8976,6 +9127,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< @@ -9276,6 +9435,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 f499996470c3f..469dc34b18816 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -153,6 +153,21 @@ def note_module_odr_violation_definition_data : Note < "%ordinal2 base class %3 with " "%select{public|protected|private|no}4 access specifier}1">; +def err_module_odr_violation_objc_container_definition_data : Error < + "%q0 has different definitions in different modules; first difference is " + "%select{definition in module '%2'|defined here}1 found " + "%select{" + "%select{no super class|super class with type %5}4|" + "%4 referenced %plural{1:protocol|:protocols}4|" + "}3">; + +def note_module_odr_violation_objc_container_definition_data : Note < + "but in '%0' found " + "%select{" + "%select{no super class|super class with type %3}2|" + "%2 referenced %plural{1:protocol|:protocols}2|" + "}1">; + def err_module_odr_violation_template_parameter : Error < "%q0 has different definitions in different modules; first difference is " "%select{definition in module '%2'|defined here}1 found " @@ -176,14 +191,16 @@ def err_module_odr_violation_mismatch_decl : Error< "%select{definition in module '%2'|defined here}1 found " "%select{end of class|public access specifier|private access specifier|" "protected access specifier|static assert|field|method|type alias|typedef|" - "data member|friend declaration|function template}3">; + "data member|friend declaration|function template|method|" + "instance variable}3">; def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found " "%select{end of class|public access specifier|private access specifier|" "protected access specifier|static assert|field|method|type alias|typedef|" - "data member|friend declaration|function template}1">; + "data member|friend declaration|function template|method|" + "instance variable}1">; def err_module_odr_violation_mismatch_decl_diff : Error< - "%q0 has different definitions in different modules; first difference is " + "%0 has different definitions in different modules; first difference is " "%select{definition in module '%2'|defined here}1 found " "%select{" "static assert with condition|" @@ -253,6 +270,22 @@ def err_module_odr_violation_mismatch_decl_diff : Error< "function template %4 with %ordinal5 template parameter with one type|" "function template %4 with %ordinal5 template parameter %select{not |}6" "being a template parameter pack|" + "return type is %4|" + "method name %4|" + "%select{class|instance}4 method|" + "method with %select{no designater initializer|designater initializer}4|" + "%select{no direct|direct}4 method|" + "instance variable '%4' access control is " + "%select{|@private|@protected|@public|@package}5|" + "property name %4|" + "property %4 with type %5|" + "%select{default |}5'%select{none|readonly|getter|assign|readwrite|" + "retain|copy|nonatomic|setter|atomic|weak|strong|" + "unsafe_unretained|nullability|null_resettable|class|}4'" + " property attribute|" + "%select{no|'required'|'optional'}4 %select{property|method}5 control|" + "with %ordinal4 protocol named %5|" + "%select{no attribute|%5}4|" "}3">; def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " @@ -324,6 +357,19 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " "function template %2 with %ordinal3 template parameter with different type|" "function template %2 with %ordinal3 template parameter %select{not |}4" "being a template parameter pack|" + "different return type %2|" + "method name %2|" + "%select{class|instance}2 method|" + "method with %select{no designater initializer|designater initializer}2|" + "%select{no direct|direct}2 method|" + "instance variable '%2' access control is " + "%select{|@private|@protected|@public|@package}3|" + "property name %2|" + "property %2 with type %3|" + "no written or default attribute for property|" + "%select{no|'required'|'optional'}2 %select{property|method}3 control|" + "with %ordinal2 protocol named %3|" + "%select{no attribute|%3}2|" "}1">; def err_module_odr_violation_function : Error< @@ -380,12 +426,13 @@ def err_module_odr_violation_mismatch_decl_unknown : Error< "%q0 %select{with definition in module '%2'|defined here}1 has different " "definitions in different modules; first difference is this " "%select{||||static assert|field|method|type alias|typedef|data member|" - "friend declaration|unexpected decl}3">; + "friend declaration|unexpected decl|method|instance variable|property}3">; def note_module_odr_violation_mismatch_decl_unknown : Note< "but in '%0' found " "%select{||||different static assert|different field|different method|" "different type alias|different typedef|different data member|" - "different friend declaration|another unexpected decl}1">; + "different friend declaration|another unexpected decl|method|" + "instance variable|property}1">; def warn_duplicate_module_file_extension : Warning< "duplicate module file extension block name '%0'">, 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/FileManager.h b/clang/include/clang/Basic/FileManager.h index fed43786d4107..b481f5846936d 100644 --- a/clang/include/clang/Basic/FileManager.h +++ b/clang/include/clang/Basic/FileManager.h @@ -378,15 +378,19 @@ class FileManager : public RefCountedBase { /// Open the specified file as a MemoryBuffer, returning a new /// MemoryBuffer if successful, otherwise returning null. llvm::ErrorOr> - getBufferForFile(const FileEntry *Entry, bool isVolatile = false); + getBufferForFile(const FileEntry *Entry, bool isVolatile = false, + bool RequiresNullTerminator = true); llvm::ErrorOr> - getBufferForFile(StringRef Filename, bool isVolatile = false) { - return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile); + getBufferForFile(StringRef Filename, bool isVolatile = false, + bool RequiresNullTerminator = true) { + return getBufferForFileImpl(Filename, /*FileSize=*/-1, isVolatile, + RequiresNullTerminator); } private: llvm::ErrorOr> - getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile); + getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile, + bool RequiresNullTerminator); public: /// Get the 'stat' information for the given \p Path. diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index ea5d7adeb2da6..bbdc818a6e5c4 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 068f206f44847..5e6ed4a1ba2fc 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -144,6 +144,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") @@ -167,6 +173,15 @@ 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(ODRCheckAttributes, 1, 0, "enable ODR hash checking for attributes") +COMPATIBLE_LANGOPT(ODRCheckCategories, 1, 0, "disable ODR hash checking for categories") +COMPATIBLE_LANGOPT(ODRCheckInterfaces, 1, 0, "disable ODR hash checking for interfaces") +COMPATIBLE_LANGOPT(ODRCheckProtocols, 1, 0, "disable ODR hash checking for protocols") +COMPATIBLE_LANGOPT(ODRCheckRecords, 1, 0, "disable ODR hash checking for records") +COMPATIBLE_LANGOPT(ODRCheckProperties, 1, 0, "disable ODR hash checking for properties") +COMPATIBLE_LANGOPT(ODRCheckIvars, 1, 0, "disable ODR hash checking for ivars") +COMPATIBLE_LANGOPT(ODRCheckMethods, 1, 0, "disable ODR hash checking for methods") 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__)") @@ -312,6 +327,9 @@ ENUM_LANGOPT(VtorDispMode, MSVtorDispMode, 2, MSVtorDispMode::ForVBaseOverride, 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..3ae4f2ca48907 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -109,10 +109,16 @@ class Module { /// The name of the umbrella entry, as written in the module map. std::string UmbrellaAsWritten; + // The path to the umbrella entry relative to the root module's \c Directory. + std::string UmbrellaRelativeToRootModuleDirectory; + /// The module through which entities defined in this module will /// eventually be exposed, for use in "private" modules. std::string ExportAsModule; + /// For the debug info, the path to this module's .apinotes file, if any. + std::string APINotesFile; + /// Does this Module scope describe part of the purview of a named C++ module? bool isModulePurview() const { return Kind == ModuleInterfaceUnit || Kind == PrivateModuleFragment; @@ -156,6 +162,7 @@ class Module { /// file. struct Header { std::string NameAsWritten; + std::string PathRelativeToRootModuleDirectory; const FileEntry *Entry; explicit operator bool() { return Entry; } @@ -165,6 +172,7 @@ class Module { /// file. struct DirectoryName { std::string NameAsWritten; + std::string PathRelativeToRootModuleDirectory; const DirectoryEntry *Entry; explicit operator bool() { return Entry; } @@ -268,6 +276,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 { @@ -488,7 +499,8 @@ class Module { /// module. Header getUmbrellaHeader() const { if (auto *E = Umbrella.dyn_cast()) - return Header{UmbrellaAsWritten, E}; + return Header{UmbrellaAsWritten, UmbrellaRelativeToRootModuleDirectory, + E}; return Header{}; } diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h new file mode 100644 index 0000000000000..936ba12148b8d --- /dev/null +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -0,0 +1,203 @@ +//===--- 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 : unsigned { + None, + Soft, + ARM8_3, + }; + + /// Software pointer-signing "keys". If you add a new key, make sure this->Key + /// has a large enough bit-width. + enum class SoftKey : unsigned { + FunctionPointers = 0, + BlockInvocationFunctionPointers = 1, + BlockHelperFunctionPointers = 2, + 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 : unsigned { + ASIA = 0, + ASIB = 1, + ASDA = 2, + ASDB = 3 + }; + + /// Forms of extra discrimination. + enum class Discrimination : unsigned { + /// No additional discrimination. + None, + + /// Include a hash of the entity's type. + Type, + + /// Include a hash of the entity's identity. + Decl, + + /// Discriminate using a constant value. + Constant, + }; + +private: + Kind TheKind : 2; + unsigned IsAddressDiscriminated : 1; + Discrimination DiscriminationKind : 2; + unsigned Key : 3; + unsigned ConstantDiscriminator : 16; + +public: + PointerAuthSchema() : TheKind(Kind::None) {} + + PointerAuthSchema(SoftKey key, bool isAddressDiscriminated, + Discrimination otherDiscrimination, + Optional constantDiscriminator = None) + : TheKind(Kind::Soft), IsAddressDiscriminated(isAddressDiscriminated), + DiscriminationKind(otherDiscrimination), Key(unsigned(key)) { + assert((getOtherDiscrimination() != Discrimination::Constant || + constantDiscriminator) && + "constant discrimination requires a constant!"); + if (constantDiscriminator) + ConstantDiscriminator = *constantDiscriminator; + } + + PointerAuthSchema(ARM8_3Key key, bool isAddressDiscriminated, + Discrimination otherDiscrimination, + Optional constantDiscriminator = None) + : TheKind(Kind::ARM8_3), IsAddressDiscriminated(isAddressDiscriminated), + DiscriminationKind(otherDiscrimination), Key(unsigned(key)) { + assert((getOtherDiscrimination() != Discrimination::Constant || + constantDiscriminator) && + "constant discrimination requires a constant!"); + if (constantDiscriminator) + ConstantDiscriminator = *constantDiscriminator; + } + + Kind getKind() const { return TheKind; } + + explicit operator bool() const { return isEnabled(); } + + bool isEnabled() const { return getKind() != Kind::None; } + + bool isAddressDiscriminated() const { + assert(getKind() != Kind::None); + return IsAddressDiscriminated; + } + + bool hasOtherDiscrimination() const { + return getOtherDiscrimination() != Discrimination::None; + } + + Discrimination getOtherDiscrimination() const { + assert(getKind() != Kind::None); + return DiscriminationKind; + } + + uint16_t getConstantDiscrimination() const { + assert(getOtherDiscrimination() == Discrimination::Constant); + return (uint16_t)ConstantDiscriminator; + } + + 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(Key); + } + + ARM8_3Key getARM8_3Key() const { + assert(getKind() == Kind::ARM8_3); + return ARM8_3Key(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/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index ec1b0bcf9897c..c04c01c430179 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -811,6 +811,11 @@ class SourceManager : public RefCountedBase { MainFileID = FID; } + /// Returns true when the given FileEntry corresponds to the main file. + /// + /// The main file should be set prior to calling this function. + bool isMainFile(FileEntryRef SourceFile); + /// Set the file ID for the precompiled preamble. void setPreambleFileID(FileID Preamble) { assert(PreambleFileID.isInvalid() && "PreambleFileID already set!"); 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 3a8e35524695c..4d4c88e2b126c 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -198,6 +198,8 @@ class TargetInfo : public virtual TransferrableTargetInfo, unsigned HasAArch64SVETypes : 1; + unsigned PointerAuthSupported : 1; + // TargetInfo Constructor. Default initializes all fields. TargetInfo(const llvm::Triple &T); @@ -1197,6 +1199,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. @@ -1240,6 +1250,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..7a21e8bcff358 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -25,9 +25,13 @@ #include "clang/AST/CanonicalType.h" #include "clang/AST/Type.h" +#include "clang/Basic/ABI.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "llvm/IR/BasicBlock.h" namespace llvm { + class AttrBuilder; + class Constant; class DataLayout; class Module; class Function; @@ -37,11 +41,14 @@ namespace llvm { namespace clang { class ASTContext; +class CXXConstructorDecl; +class CXXDestructorDecl; class CXXRecordDecl; class CXXMethodDecl; class CodeGenOptions; class CoverageSourceInfo; class DiagnosticsEngine; +class GlobalDecl; class HeaderSearchOptions; class ObjCMethodDecl; class PreprocessorOptions; @@ -50,6 +57,16 @@ namespace CodeGen { class CGFunctionInfo; class CodeGenModule; +/// Additional implicit arguments to add to a constructor argument list. +struct ImplicitCXXConstructorArgs { + /// Implicit arguments to add before the explicit arguments, but after the + /// `*this` argument (which always comes first). + SmallVector Prefix; + + /// Implicit arguments to add after the explicit arguments. + SmallVector Suffix; +}; + const CGFunctionInfo &arrangeObjCMessageSendSignature(CodeGenModule &CGM, const ObjCMethodDecl *MD, QualType receiverType); @@ -71,6 +88,17 @@ const CGFunctionInfo &arrangeFreeFunctionCall(CodeGenModule &CGM, FunctionType::ExtInfo info, RequiredArgs args); +/// Returns the implicit arguments to add to a complete, non-delegating C++ +/// constructor call. +ImplicitCXXConstructorArgs +getImplicitCXXConstructorArgs(CodeGenModule &CGM, const CXXConstructorDecl *D); + +llvm::Value * +getCXXDestructorImplicitParam(CodeGenModule &CGM, llvm::BasicBlock *InsertBlock, + llvm::BasicBlock::iterator InsertPoint, + const CXXDestructorDecl *D, CXXDtorType Type, + bool ForVirtualBase, bool Delegating); + /// Returns null if the function type is incomplete and can't be lowered. llvm::FunctionType *convertFreeFunctionType(CodeGenModule &CGM, const FunctionDecl *FD); @@ -84,6 +112,45 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T); unsigned getLLVMFieldNumber(CodeGenModule &CGM, const RecordDecl *RD, const FieldDecl *FD); +/// Given the language and code-generation options that Clang was configured +/// with, set the default LLVM IR attributes for a function definition. +/// The attributes set here are mostly global target-configuration and +/// pipeline-configuration options like the target CPU, variant stack +/// rules, whether to optimize for size, and so on. This is useful for +/// frontends (such as Swift) that generally intend to interoperate with +/// C code and rely on Clang's target configuration logic. +/// +/// As a general rule, this function assumes that meaningful attributes +/// haven't already been added to the builder. It won't intentionally +/// displace any existing attributes, but it also won't check to avoid +/// overwriting them. Callers should generally apply customizations after +/// making this call. +/// +/// This function assumes that the caller is not defining a function that +/// requires special no-builtin treatment. +void addDefaultFunctionDefinitionAttributes(CodeGenModule &CGM, + llvm::AttrBuilder &attrs); + +/// 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..6c45b80f769b6 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -12,6 +12,18 @@ let Flags = [CC1Option, NoDriverOption] in { +//===----------------------------------------------------------------------===// +// Option Options +//===----------------------------------------------------------------------===// + +def remove_preceeding_explicit_module_build_incompatible_options : + Flag<["-"], "remove-preceeding-explicit-module-build-incompatible-options">, + HelpText<"Removes any arguments before this one that would be incompatible " + "with explicitly building a module. This includes things like -o " + "and input files. This option can be used to append arguments to " + "convert a build of a translation unit with implicit modules " + "into an explicit build of a specific module.">; + //===----------------------------------------------------------------------===// // Target Options //===----------------------------------------------------------------------===// @@ -250,6 +262,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">, @@ -387,8 +403,6 @@ def flto_visibility_public_std: def flto_unit: Flag<["-"], "flto-unit">, HelpText<"Emit IR to support LTO unit features (CFI, whole program vtable opt)">; def fno_lto_unit: Flag<["-"], "fno-lto-unit">; -def femit_debug_entry_values : Flag<["-"], "femit-debug-entry-values">, - HelpText<"Enables debug info about call site parameter's entry values">; def fdebug_pass_manager : Flag<["-"], "fdebug-pass-manager">, HelpText<"Prints debug information for the new pass manager">; def fno_debug_pass_manager : Flag<["-"], "fno-debug-pass-manager">, @@ -411,6 +425,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,12 +564,38 @@ 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. " "The argument is parsed as blockname:major:minor:hashed:user info">; def fconcepts_ts : Flag<["-"], "fconcepts-ts">, HelpText<"Enable C++ Extensions for Concepts.">; +def fodr_hash_attributes: + Flag<["-"], "fodr-hash-attributes">, + HelpText<"Enable ODR hash checking for attributes ">; +def fno_odr_hash_categories: + Flag<["-"], "fno-odr-hash-categories">, + HelpText<"Disable ODR hash checking for categories">; +def fno_odr_hash_interfaces: + Flag<["-"], "fno-odr-hash-interfaces">, + HelpText<"Disable ODR hash checking for interfaces">; +def fno_odr_hash_protocols: + Flag<["-"], "fno-odr-hash-protocols">, + HelpText<"Disable ODR hash checking for protocols">; +def fno_odr_hash_records: + Flag<["-"], "fno-odr-hash-records">, + HelpText<"Disable ODR hash checking for records">; +def fno_odr_hash_properties: + Flag<["-"], "fno-odr-hash-properties">, + HelpText<"Disable ODR hash checking for properties">; +def fno_odr_hash_ivars: + Flag<["-"], "fno-odr-hash-ivars">, + HelpText<"Disable ODR hash checking for ivars">; +def fno_odr_hash_methods: + Flag<["-"], "fno-odr-hash-methods">, + HelpText<"Disable ODR hash checking for methods">; let Group = Action_Group in { diff --git a/clang/include/clang/Driver/DarwinSDKInfo.h b/clang/include/clang/Driver/DarwinSDKInfo.h index f7075a8d3b7f9..3f8ae7391e2de 100644 --- a/clang/include/clang/Driver/DarwinSDKInfo.h +++ b/clang/include/clang/Driver/DarwinSDKInfo.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_DRIVER_DARWIN_SDK_INFO_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Support/Error.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualFileSystem.h" @@ -20,12 +21,26 @@ namespace driver { /// The information about the darwin SDK that was used during this compilation. class DarwinSDKInfo { public: + /// Represents the mapping between macOS and IosMac versions. + class IOSMacVersionMapping { + public: + VersionTuple getiOSMacVersionForMacOSVersion(); + + llvm::StringMap MacOS2iOSMacMapping; + llvm::StringMap IosMac2macOSMapping; + }; + DarwinSDKInfo(llvm::VersionTuple Version) : Version(Version) {} const llvm::VersionTuple &getVersion() const { return Version; } + IOSMacVersionMapping &getVersionMap() { return VersionMap; } + + const IOSMacVersionMapping &getVersionMap() const { return VersionMap; } + private: llvm::VersionTuple Version; + IOSMacVersionMapping VersionMap; }; /// Parse the SDK information from the SDKSettings.json file. diff --git a/clang/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 33b331fd8777d..1b1aefdfb58fa 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]>; @@ -803,6 +810,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]>, @@ -1516,7 +1538,7 @@ def fno_merge_all_constants : Flag<["-"], "fno-merge-all-constants">, Group, Group, Flags<[DriverOption]>; def fno_implicit_module_maps : Flag <["-"], "fno-implicit-module-maps">, Group, - Flags<[DriverOption]>; + Flags<[DriverOption, CC1Option]>; def fno_module_maps : Flag <["-"], "fno-module-maps">, Alias; def fno_modules_decluse : Flag <["-"], "fno-modules-decluse">, Group, Flags<[DriverOption]>; @@ -1915,13 +1937,6 @@ def fforce_emit_vtables : Flag<["-"], "fforce-emit-vtables">, Group, HelpText<"Emits more virtual tables to improve devirtualization">; def fno_force_emit_vtables : Flag<["-"], "fno-force-emit-vtables">, Group, Flags<[CoreOption]>; - -def fvirtual_function_elimination : Flag<["-"], "fvirtual-function-elimination">, Group, - Flags<[CoreOption, CC1Option]>, - HelpText<"Enables dead virtual function elimination optimization. Requires -flto=full">; -def fno_virtual_function_elimination : Flag<["-"], "fno-virtual-function_elimination">, Group, - Flags<[CoreOption]>; - def fwrapv : Flag<["-"], "fwrapv">, Group, Flags<[CC1Option]>, HelpText<"Treat signed integer overflow as two's complement">; def fwritable_strings : Flag<["-"], "fwritable-strings">, Group, Flags<[CC1Option]>, @@ -1954,6 +1969,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">; @@ -2071,8 +2109,14 @@ def gno_embed_source : Flag<["-"], "gno-embed-source">, Group, def headerpad__max__install__names : Joined<["-"], "headerpad_max_install_names">; def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption]>, HelpText<"Display available options">; +def ibuiltininc : Flag<["-"], "ibuiltininc">, + HelpText<"Enable builtin #include directories even when -nostdinc is used " + "before or after -ibuiltininc. " + "Using -nobuiltininc after the option disables it">; 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 e501dde465cc5..04133847e5978 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(); } @@ -822,6 +835,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/FrontendAction.h b/clang/include/clang/Frontend/FrontendAction.h index e994e24cf5afa..c9f9f080c1413 100644 --- a/clang/include/clang/Frontend/FrontendAction.h +++ b/clang/include/clang/Frontend/FrontendAction.h @@ -312,6 +312,7 @@ class WrapperFrontendAction : public FrontendAction { bool BeginSourceFileAction(CompilerInstance &CI) override; void ExecuteAction() override; void EndSourceFileAction() override; + bool shouldEraseOutputFiles() override; public: /// Construct a WrapperFrontendAction from an existing action, taking diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index 09969b596d637..0016a3867d5c9 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -368,6 +368,10 @@ class FrontendOptions { std::string MTMigrateDir; std::string ARCMTMigrateReportOut; + std::string IndexStorePath; + unsigned IndexIgnoreSystemSymbols : 1; + unsigned IndexRecordCodegenName : 1; + /// The input files and their types. SmallVector Inputs; @@ -445,7 +449,9 @@ class FrontendOptions { UseGlobalModuleIndex(true), GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), ModulesEmbedAllFiles(false), - IncludeTimestamps(true), UseTemporary(true), TimeTraceGranularity(500) {} + IncludeTimestamps(true), UseTemporary(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/LogDiagnosticPrinter.h b/clang/include/clang/Frontend/LogDiagnosticPrinter.h index 4816275cdc604..2c5444f4b71b9 100644 --- a/clang/include/clang/Frontend/LogDiagnosticPrinter.h +++ b/clang/include/clang/Frontend/LogDiagnosticPrinter.h @@ -19,6 +19,38 @@ class DiagnosticOptions; class LangOptions; class LogDiagnosticPrinter : public DiagnosticConsumer { + struct DiagEntryLocation { + /// The source file name, if available and if different from DiagEntry's + /// Filename. + std::string Filename; + + /// The source file line number, if available. + unsigned Line; + + /// The source file column number, if available. + unsigned Column; + + /// The source file offset, if available. + unsigned Offset; + }; + + struct DiagEntryRange { + /// The range start. + LogDiagnosticPrinter::DiagEntryLocation Start; + + /// The range end. + LogDiagnosticPrinter::DiagEntryLocation End; + }; + + struct DiagEntryFixIt { + /// The range of existing source file to act upon. + LogDiagnosticPrinter::DiagEntryRange RemoveRange; + + /// The code to insert at the start of the range, + /// after removal of the range; may be empty for pure removal. + std::string CodeToInsert; + }; + struct DiagEntry { /// The primary message line of the diagnostic. std::string Message; @@ -40,8 +72,18 @@ class LogDiagnosticPrinter : public DiagnosticConsumer { /// The level of the diagnostic. DiagnosticsEngine::Level DiagnosticLevel; + + /// The source ranges of the diagnostic. + SmallVector SourceRanges; + + /// The fix-its for the diagnostic. + SmallVector FixIts; }; + void + EmitDiagEntryLocation(llvm::raw_ostream &OS, StringRef Indent, + const LogDiagnosticPrinter::DiagEntryLocation &Del); + void EmitDiagEntry(llvm::raw_ostream &OS, const LogDiagnosticPrinter::DiagEntry &DE); diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index b5834921b9edd..3717c17e8d2d1 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -140,6 +140,7 @@ class DependencyFileGenerator : public DependencyCollector { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; + bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; }; diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h index 965a144108321..7d99bb394937b 100644 --- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h +++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h @@ -189,11 +189,10 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer, /// class Directive { public: - static std::unique_ptr create(bool RegexKind, - SourceLocation DirectiveLoc, - SourceLocation DiagnosticLoc, - bool MatchAnyLine, StringRef Text, - unsigned Min, unsigned Max); + static std::unique_ptr + create(bool RegexKind, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc, bool MatchAnyFileAndLine, + bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max); public: /// Constant representing n or more matches. @@ -204,6 +203,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer, const std::string Text; unsigned Min, Max; bool MatchAnyLine; + bool MatchAnyFileAndLine; // `MatchAnyFileAndLine` implies `MatchAnyLine`. Directive(const Directive &) = delete; Directive &operator=(const Directive &) = delete; @@ -218,9 +218,11 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer, protected: Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max) - : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), - Text(Text), Min(Min), Max(Max), MatchAnyLine(MatchAnyLine) { + bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max) + : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc), Text(Text), + Min(Min), Max(Max), MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine), + MatchAnyFileAndLine(MatchAnyFileAndLine) { assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!"); assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) && "DiagnosticLoc is invalid!"); 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..887c71d36b0ee 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; @@ -413,6 +424,7 @@ class ModuleMap { /// Is this a compiler builtin header? static bool isBuiltinHeader(StringRef FileName); + bool isBuiltinHeader(const FileEntry *File); /// Add a module map callback. void addModuleMapCallbacks(std::unique_ptr Callback) { @@ -642,12 +654,14 @@ class ModuleMap { /// Sets the umbrella header of the given module to the given /// header. void setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader, - Twine NameAsWritten); + Twine NameAsWritten, + Twine PathRelativeToRootModuleDirectory); /// Sets the umbrella directory of the given module to the given /// directory. void setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir, - Twine NameAsWritten); + Twine NameAsWritten, + Twine PathRelativeToRootModuleDirectory); /// Adds this header to the given module. /// \param Role The role of the header wrt the module. 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 e7130d2fe68e7..603ad58645b15 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1532,10 +1532,9 @@ class Parser : public CodeCompletionHandler { const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), LateParsedAttrList *LateParsedAttrs = nullptr); void ParseKNRParamDeclarations(Declarator &D); - // EndLoc, if non-NULL, is filled with the location of the last token of - // the simple-asm. - ExprResult ParseSimpleAsm(SourceLocation *EndLoc = nullptr); - ExprResult ParseAsmStringLiteral(); + // EndLoc is filled with the location of the last token of the simple-asm. + ExprResult ParseSimpleAsm(bool ForAsmLabel, SourceLocation *EndLoc); + ExprResult ParseAsmStringLiteral(bool ForAsmLabel); // Objective-C External Declarations void MaybeSkipAttributes(tok::ObjCKeywordKind Kind); @@ -2638,6 +2637,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, @@ -2657,6 +2664,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); @@ -3109,11 +3118,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/Sema.h b/clang/include/clang/Sema/Sema.h index 2d7aa9462db1e..46f5b710d565c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -30,6 +30,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/BitmaskEnum.h" #include "clang/Basic/ExpressionTraits.h" @@ -58,6 +59,7 @@ #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Frontend/OpenMP/OMPConstants.h" #include +#include #include #include #include @@ -384,6 +386,7 @@ class Sema final { ASTConsumer &Consumer; DiagnosticsEngine &Diags; SourceManager &SourceMgr; + api_notes::APINotesManager APINotes; /// Flag indicating whether or not to collect detailed statistics. bool CollectStats; @@ -600,9 +603,8 @@ class Sema final { CleanupInfo Cleanup; /// ExprCleanupObjects - This is the stack of objects requiring - /// cleanup that are created by the current full expression. The - /// element type here is ExprWithCleanups::Object. - SmallVector ExprCleanupObjects; + /// cleanup that are created by the current full expression. + SmallVector ExprCleanupObjects; /// Store a set of either DeclRefExprs or MemberExprs that contain a reference /// to a variable (constant) that may or may not be odr-used in this Expr, and @@ -722,6 +724,10 @@ class Sema final { OpaqueParser = P; } + /// \brief Callback to the parser to parse a type expressed as a string. + std::function + ParseTypeFromStringCallback; + class DelayedDiagnostics; class DelayedDiagnosticsState { @@ -1687,6 +1693,24 @@ class Sema final { } }; + /// 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 @@ -2123,6 +2147,9 @@ class Sema final { 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, @@ -2211,6 +2238,9 @@ class Sema final { 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, @@ -2829,6 +2859,8 @@ class Sema final { 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); @@ -3738,6 +3770,12 @@ class Sema final { 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 @@ -3775,6 +3813,29 @@ class Sema final { /// 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, @@ -9097,6 +9158,12 @@ class Sema final { 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); @@ -10345,9 +10412,8 @@ class Sema final { bool Diagnose = true); // DefaultLvalueConversion - performs lvalue-to-rvalue conversion on - // the operand. This is DefaultFunctionArrayLvalueConversion, - // except that it assumes the operand isn't of function or array - // type. + // the operand. This function is a no-op if the operand has a function type + // or an array type. ExprResult DefaultLvalueConversion(Expr *E); // DefaultArgumentPromotion (C99 6.5.2.2p6). Used for function calls that @@ -11722,6 +11788,7 @@ class Sema final { /// 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 1bfcbda8c9f1e..e2e057714742d 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. @@ -1895,6 +1876,9 @@ namespace serialization { CTOR_INITIALIZER_INDIRECT_MEMBER }; + /// Kinds of cleanup objects owned by ExprWithCleanups. + enum CleanupObjectKind { COK_Block, COK_CompoundLiteral }; + /// Describes the redeclarations of a declaration. struct LocalRedeclarationsInfo { // The ID of the first declaration diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index e74bf00e08727..628f0553ab48d 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_SERIALIZATION_ASTREADER_H #define LLVM_CLANG_SERIALIZATION_ASTREADER_H +#include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" @@ -744,13 +745,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. //@{ @@ -1094,6 +1088,12 @@ class ASTReader using DataPointers = std::pair; + using IDDataPointers = std::pair; + using ProtoDataPointers = std::pair; + using CatDataPointers = + std::pair; /// Record definitions in which we found an ODR violation. llvm::SmallDenseMap, 2> @@ -1107,6 +1107,25 @@ class ASTReader llvm::SmallDenseMap, 2> PendingEnumOdrMergeFailures; + /// C/ObjC definitions in which the structural equivalence check fails + llvm::SmallDenseMap, 2> + PendingRecordOdrMergeFailures; + + /// ObjCInterfaceDecl in which we found an ODR violation. + llvm::SmallDenseMap, + 2> + PendingObjCInterfaceOdrMergeFailures; + + /// ObjCProtocolDecl in which we found an ODR violation. + llvm::SmallDenseMap, + 2> + PendingObjCProtocolOdrMergeFailures; + + /// ObjCCategoryDecl in which we found an ODR violation. + llvm::SmallDenseMap, + 2> + PendingObjCCategoryOdrMergeFailures; + /// DeclContexts in which we have diagnosed an ODR violation. llvm::SmallPtrSet DiagnosedOdrMergeFailures; @@ -1318,6 +1337,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); @@ -1710,9 +1731,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/InMemoryModuleCache.h b/clang/include/clang/Serialization/InMemoryModuleCache.h index 7b5b5c1cf1be9..c4814c40e1dfe 100644 --- a/clang/include/clang/Serialization/InMemoryModuleCache.h +++ b/clang/include/clang/Serialization/InMemoryModuleCache.h @@ -45,61 +45,35 @@ class InMemoryModuleCache : public llvm::RefCountedBase { llvm::StringMap PCMs; public: - /// There are four states for a PCM. It must monotonically increase. - /// - /// 1. Unknown: the PCM has neither been read from disk nor built. - /// 2. Tentative: the PCM has been read from disk but not yet imported or - /// built. It might work. - /// 3. ToBuild: the PCM read from disk did not work but a new one has not - /// been built yet. - /// 4. Final: indicating that the current PCM was either built in this - /// process or has been successfully imported. - enum State { Unknown, Tentative, ToBuild, Final }; - - /// Get the state of the PCM. - State getPCMState(llvm::StringRef Filename) const; - /// Store the PCM under the Filename. /// - /// \pre state is Unknown - /// \post state is Tentative + /// \pre PCM for the same Filename shouldn't be in cache already. /// \return a reference to the buffer as a convenience. llvm::MemoryBuffer &addPCM(llvm::StringRef Filename, std::unique_ptr Buffer); - /// Store a just-built PCM under the Filename. + /// Store a final PCM under the Filename. /// - /// \pre state is Unknown or ToBuild. - /// \pre state is not Tentative. + /// \pre PCM for the same Filename shouldn't be in cache already. /// \return a reference to the buffer as a convenience. - llvm::MemoryBuffer &addBuiltPCM(llvm::StringRef Filename, + llvm::MemoryBuffer &addFinalPCM(llvm::StringRef Filename, std::unique_ptr Buffer); - /// Try to remove a buffer from the cache. No effect if state is Final. + /// Try to remove a PCM from the cache. No effect if it is Final. /// - /// \pre state is Tentative/Final. - /// \post Tentative => ToBuild or Final => Final. - /// \return false on success, i.e. if Tentative => ToBuild. - bool tryToDropPCM(llvm::StringRef Filename); + /// \return false on success. + bool tryToRemovePCM(llvm::StringRef Filename); /// Mark a PCM as final. - /// - /// \pre state is Tentative or Final. - /// \post state is Final. void finalizePCM(llvm::StringRef Filename); - /// Get a pointer to the pCM if it exists; else nullptr. + /// Get a pointer to the PCM if it exists; else nullptr. llvm::MemoryBuffer *lookupPCM(llvm::StringRef Filename) const; /// Check whether the PCM is final and has been shown to work. /// /// \return true iff state is Final. bool isPCMFinal(llvm::StringRef Filename) const; - - /// Check whether the PCM is waiting to be built. - /// - /// \return true iff state is ToBuild. - bool shouldBuildPCM(llvm::StringRef Filename) const; }; } // end namespace clang diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h index 8f3eb02206373..f8efb7b791d22 100644 --- a/clang/include/clang/Serialization/ModuleFile.h +++ b/clang/include/clang/Serialization/ModuleFile.h @@ -338,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/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def index 00febf6881954..ee67f60df9488 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -306,8 +306,8 @@ ANALYZER_OPTION(bool, ShouldTrackConditionsDebug, "track-conditions-debug", "Whether to place an event at each tracked condition.", false) -ANALYZER_OPTION(bool, ShouldEmitFixItHintsAsRemarks, "fixits-as-remarks", - "Emit fix-it hints as remarks for testing purposes", +ANALYZER_OPTION(bool, ShouldApplyFixIts, "apply-fixits", + "Apply the fix-it hints to the files", false) //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index 69593e2b6c931..7112fa125ef0e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -722,7 +722,8 @@ class BugReporterContext { class NoteTag : public ProgramPointTag { public: using Callback = - std::function; + std::function; private: static int Kind; @@ -739,7 +740,7 @@ class NoteTag : public ProgramPointTag { } Optional generateMessage(BugReporterContext &BRC, - BugReport &R) const { + PathSensitiveBugReport &R) const { std::string Msg = Cb(BRC, R); if (Msg.empty()) return None; diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h index 246ff8f90d354..4454d7603b27f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -140,14 +140,14 @@ class CheckerManager { void finishedCheckerRegistration(); const LangOptions &getLangOpts() const { return LangOpts; } - AnalyzerOptions &getAnalyzerOptions() { return AOptions; } - ASTContext &getASTContext() { return Context; } + AnalyzerOptions &getAnalyzerOptions() const { return AOptions; } + ASTContext &getASTContext() const { return Context; } /// Emits an error through a DiagnosticsEngine about an invalid user supplied /// checker option value. void reportInvalidCheckerOptionValue(const CheckerBase *C, StringRef OptionName, - StringRef ExpectedValueDesc); + StringRef ExpectedValueDesc) const; using CheckerRef = CheckerBase *; using CheckerTag = const void *; @@ -620,7 +620,7 @@ class CheckerManager { /// Returns the checkers that have registered for callbacks of the /// given \p Kind. const std::vector & - getObjCMessageCheckers(ObjCMessageVisitKind Kind); + getObjCMessageCheckers(ObjCMessageVisitKind Kind) const; std::vector PreObjCMessageCheckers; std::vector PostObjCMessageCheckers; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index fc1cc91388266..323a1a212bce8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -63,6 +63,9 @@ enum CallEventKind { CE_BEG_CXX_INSTANCE_CALLS = CE_CXXMember, CE_END_CXX_INSTANCE_CALLS = CE_CXXDestructor, CE_CXXConstructor, + CE_CXXInheritedConstructor, + CE_BEG_CXX_CONSTRUCTOR_CALLS = CE_CXXConstructor, + CE_END_CXX_CONSTRUCTOR_CALLS = CE_CXXInheritedConstructor, CE_CXXAllocator, CE_BEG_FUNCTION_CALLS = CE_Function, CE_END_FUNCTION_CALLS = CE_CXXAllocator, @@ -811,10 +814,38 @@ class CXXDestructorCall : public CXXInstanceCall { } }; +/// Represents any constructor invocation. This includes regular constructors +/// and inherited constructors. +class AnyCXXConstructorCall : public AnyFunctionCall { +protected: + AnyCXXConstructorCall(const Expr *E, const MemRegion *Target, + ProgramStateRef St, const LocationContext *LCtx) + : AnyFunctionCall(E, St, LCtx) { + assert(E && (isa(E) || isa(E))); + // Target may be null when the region is unknown. + Data = Target; + } + + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; + + void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const override; + +public: + /// Returns the value of the implicit 'this' object. + SVal getCXXThisVal() const; + + static bool classof(const CallEvent *Call) { + return Call->getKind() >= CE_BEG_CXX_CONSTRUCTOR_CALLS && + Call->getKind() <= CE_END_CXX_CONSTRUCTOR_CALLS; + } +}; + /// Represents a call to a C++ constructor. /// /// Example: \c T(1) -class CXXConstructorCall : public AnyFunctionCall { +class CXXConstructorCall : public AnyCXXConstructorCall { friend class CallEventManager; protected: @@ -827,17 +858,12 @@ class CXXConstructorCall : public AnyFunctionCall { /// \param LCtx The location context at this point in the program. CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *Target, ProgramStateRef St, const LocationContext *LCtx) - : AnyFunctionCall(CE, St, LCtx) { - Data = Target; - } + : AnyCXXConstructorCall(CE, Target, St, LCtx) {} CXXConstructorCall(const CXXConstructorCall &Other) = default; void cloneTo(void *Dest) const override { new (Dest) CXXConstructorCall(*this); } - void getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const override; - public: virtual const CXXConstructExpr *getOriginExpr() const { return cast(AnyFunctionCall::getOriginExpr()); @@ -853,12 +879,6 @@ class CXXConstructorCall : public AnyFunctionCall { return getOriginExpr()->getArg(Index); } - /// Returns the value of the implicit 'this' object. - SVal getCXXThisVal() const; - - void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, - BindingsTy &Bindings) const override; - Kind getKind() const override { return CE_CXXConstructor; } static bool classof(const CallEvent *CA) { @@ -866,6 +886,83 @@ class CXXConstructorCall : public AnyFunctionCall { } }; +/// Represents a call to a C++ inherited constructor. +/// +/// Example: \c class T : public S { using S::S; }; T(1); +/// +// Note, it is difficult to model the parameters. This is one of the reasons +// why we skip analysis of inheriting constructors as top-level functions. +// CXXInheritedCtorInitExpr doesn't take arguments and doesn't model parameter +// initialization because there is none: the arguments in the outer +// CXXConstructExpr directly initialize the parameters of the base class +// constructor, and no copies are made. (Making a copy of the parameter is +// incorrect, at least if it's done in an observable way.) The derived class +// constructor doesn't even exist in the formal model. +/// E.g., in: +/// +/// struct X { X *p = this; ~X() {} }; +/// struct A { A(X x) : b(x.p == &x) {} bool b; }; +/// struct B : A { using A::A; }; +/// B b = X{}; +/// +/// ... b.b is initialized to true. +class CXXInheritedConstructorCall : public AnyCXXConstructorCall { + friend class CallEventManager; + +protected: + CXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *CE, + const MemRegion *Target, ProgramStateRef St, + const LocationContext *LCtx) + : AnyCXXConstructorCall(CE, Target, St, LCtx) {} + + CXXInheritedConstructorCall(const CXXInheritedConstructorCall &Other) = + default; + + void cloneTo(void *Dest) const override { + new (Dest) CXXInheritedConstructorCall(*this); + } + +public: + virtual const CXXInheritedCtorInitExpr *getOriginExpr() const { + return cast(AnyFunctionCall::getOriginExpr()); + } + + const CXXConstructorDecl *getDecl() const override { + return getOriginExpr()->getConstructor(); + } + + /// Obtain the stack frame of the inheriting constructor. Argument expressions + /// can be found on the call site of that stack frame. + const StackFrameContext *getInheritingStackFrame() const; + + /// Obtain the CXXConstructExpr for the sub-class that inherited the current + /// constructor (possibly indirectly). It's the statement that contains + /// argument expressions. + const CXXConstructExpr *getInheritingConstructor() const { + return cast(getInheritingStackFrame()->getCallSite()); + } + + unsigned getNumArgs() const override { + return getInheritingConstructor()->getNumArgs(); + } + + const Expr *getArgExpr(unsigned Index) const override { + return getInheritingConstructor()->getArg(Index); + } + + virtual SVal getArgSVal(unsigned Index) const override { + return getState()->getSVal( + getArgExpr(Index), + getInheritingStackFrame()->getParent()->getStackFrame()); + } + + Kind getKind() const override { return CE_CXXInheritedConstructor; } + + static bool classof(const CallEvent *CA) { + return CA->getKind() == CE_CXXInheritedConstructor; + } +}; + /// Represents the memory allocation call in a C++ new-expression. /// /// This is a call to "operator new". @@ -1225,6 +1322,13 @@ class CallEventManager { return create(E, Target, State, LCtx); } + CallEventRef + getCXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *E, + const MemRegion *Target, ProgramStateRef State, + const LocationContext *LCtx) { + return create(E, Target, State, LCtx); + } + CallEventRef getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger, const MemRegion *Target, bool IsBase, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index bd8760cf0ce0c..f4181f3beab99 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -256,10 +256,12 @@ class CheckerContext { /// @param IsPrunable Whether the note is prunable. It allows BugReporter /// to omit the note from the report if it would make the displayed /// bug path significantly shorter. - const NoteTag *getNoteTag(std::function &&Cb, - bool IsPrunable = false) { + const NoteTag + *getNoteTag(std::function &&Cb, + bool IsPrunable = false) { return getNoteTag( - [Cb](BugReporterContext &, BugReport &BR) { return Cb(BR); }, + [Cb](BugReporterContext &, + PathSensitiveBugReport &BR) { return Cb(BR); }, IsPrunable); } @@ -272,7 +274,8 @@ class CheckerContext { /// bug path significantly shorter. const NoteTag *getNoteTag(std::function &&Cb, bool IsPrunable = false) { - return getNoteTag([Cb](BugReporterContext &, BugReport &) { return Cb(); }, + return getNoteTag([Cb](BugReporterContext &, + PathSensitiveBugReport &) { return Cb(); }, IsPrunable); } @@ -284,7 +287,9 @@ class CheckerContext { /// bug path significantly shorter. const NoteTag *getNoteTag(StringRef Note, bool IsPrunable = false) { return getNoteTag( - [Note](BugReporterContext &, BugReport &) { return Note; }, IsPrunable); + [Note](BugReporterContext &, + PathSensitiveBugReport &) { return std::string(Note); }, + IsPrunable); } /// Returns the word that should be used to refer to the declaration diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 6e676c512b89c..c66c54116a0c6 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -527,6 +527,9 @@ class ExprEngine : public SubEngine { void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst, @@ -807,10 +810,15 @@ class ExprEngine : public SubEngine { /// or unusable for any reason, a dummy temporary region is returned, and the /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts. /// Returns the updated program state and the new object's this-region. - std::pair prepareForObjectConstruction( + std::pair handleConstructionContext( const Expr *E, ProgramStateRef State, const LocationContext *LCtx, const ConstructionContext *CC, EvalCallOptions &CallOpts); + /// Common code that handles either a CXXConstructExpr or a + /// CXXInheritedCtorInitExpr. + void handleConstructor(const Expr *E, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + /// Store the location of a C++ object corresponding to a statement /// until the statement is actually encountered. For example, if a DeclStmt /// has CXXConstructExpr as its initializer, the object would be considered diff --git a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h index bc258160ada49..269c226f2e1d1 100644 --- a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -204,16 +204,14 @@ class CheckerRegistry { using PackageInfoList = llvm::SmallVector; -private: - template static void initializeManager(CheckerManager &mgr) { + template static void addToCheckerMgr(CheckerManager &mgr) { mgr.registerChecker(); } - template static bool returnTrue(const LangOptions &LO) { + static bool returnTrue(const LangOptions &LO) { return true; } -public: /// Adds a checker to the registry. Use this non-templated overload when your /// checker requires custom initialization. void addChecker(InitializationFunction Fn, ShouldRegisterFunction sfn, @@ -227,9 +225,8 @@ class CheckerRegistry { bool IsHidden = false) { // Avoid MSVC's Compiler Error C2276: // http://msdn.microsoft.com/en-us/library/850cstw1(v=VS.80).aspx - addChecker(&CheckerRegistry::initializeManager, - &CheckerRegistry::returnTrue, FullName, Desc, DocsUri, - IsHidden); + addChecker(&CheckerRegistry::addToCheckerMgr, + &CheckerRegistry::returnTrue, FullName, Desc, DocsUri, IsHidden); } /// Makes the checker with the full name \p fullName depends on the checker diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h index a0c1900f7ed98..1c106ed4b765a 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -11,13 +11,69 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" #include "clang/Tooling/JSONCompilationDatabase.h" +#include "llvm/ADT/StringSet.h" #include namespace clang{ namespace tooling{ namespace dependencies{ +/// The full dependencies and module graph for a specific input. +struct FullDependencies { + /// The name of the C++20 module this translation unit exports. This may + /// include `:` for C++20 module partitons. + /// + /// If the translation unit is not a module then this will be empty. + std::string ExportedModuleName; + + /// The context hash represents the set of compiler options that may make one + /// version of a module incompatible with another. This includes things like + /// language mode, predefined macros, header search paths, etc... + /// + /// Modules with the same name but a different \c ContextHash should be + /// treated as separate modules for the purpose of a build. + std::string ContextHash; + + /// A collection of absolute paths to files that this translation unit + /// directly depends on, not including transitive dependencies. + std::vector FileDeps; + + /// A list of modules this translation unit directly depends on, not including + /// transitive dependencies. + /// + /// This may include modules with a different context hash when it can be + /// determined that the differences are benign for this compilation. + std::vector ClangModuleDeps; + + /// A partial addtional set of command line arguments that can be used to + /// build this translation unit. + /// + /// Call \c getFullAdditionalCommandLine() to get a command line suitable for + /// appending to the original command line to pass to clang. + std::vector AdditionalNonPathCommandLine; + + /// Gets the full addtional command line suitable for appending to the + /// original command line to pass to clang. + /// + /// \param LookupPCMPath this function is called to fill in `-fmodule-file=` + /// flags and for the `-o` flag. It needs to return a + /// path for where the PCM for the given module is to + /// be located. + /// \param LookupModuleDeps this fucntion is called to collect the full + /// transitive set of dependencies for this + /// compilation. + std::vector getAdditionalCommandLine( + std::function LookupPCMPath, + std::function LookupModuleDeps) const; +}; + +struct FullDependenciesResult { + FullDependencies FullDeps; + std::vector DiscoveredModules; +}; + /// The high-level implementation of the dependency discovery tool that runs on /// an individual worker thread. class DependencyScanningTool { @@ -35,8 +91,23 @@ class DependencyScanningTool { getDependencyFile(const tooling::CompilationDatabase &Compilations, StringRef CWD); + /// Collect the full module depenedency graph for the input, ignoring any + /// modules which have already been seen. + /// + /// \param AlreadySeen this is used to not report modules that have previously + /// been reported. Use the same `llvm::StringSet<>` for all + /// calls to `getFullDependencies` for a single + /// `DependencyScanningTool` for a single build. Use a + /// different one for different tools, and clear it between + /// builds. + /// + /// \returns a \c StringError with the diagnostic output if clang errors + /// occurred, \c FullDependencies otherwise. + llvm::Expected + getFullDependencies(const tooling::CompilationDatabase &Compilations, + StringRef CWD, const llvm::StringSet<> &AlreadySeen); + private: - const ScanningOutputFormat Format; DependencyScanningWorker Worker; }; diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index 689119330c412..22c84e032fd31 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -63,6 +63,15 @@ class DependencyScanningWorker { const CompilationDatabase &CDB, DependencyConsumer &Consumer); + llvm::Error + computeDependenciesForClangInvocation(StringRef WorkingDirectory, + ArrayRef Arguments, + DependencyConsumer &Consumer); + + ScanningOutputFormat getFormat() const { return Format; } + + llvm::StringSet<> AlreadySeen; + private: IntrusiveRefCntPtr DiagOpts; std::shared_ptr PCHContainerOps; diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h index 7a9fc276fcaa7..c5d12fc73e1a9 100644 --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -28,16 +28,82 @@ namespace dependencies { class DependencyConsumer; +/// This is used to refer to a specific module. +/// +/// See \c ModuleDeps for details about what these members mean. +struct ClangModuleDep { + std::string ModuleName; + std::string ContextHash; +}; + struct ModuleDeps { + /// The name of the module. This may include `:` for C++20 module partitons, + /// or a header-name for C++20 header units. std::string ModuleName; - std::string ClangModuleMapFile; - std::string ModulePCMPath; + + /// The context hash of a module represents the set of compiler options that + /// may make one version of a module incompatible with another. This includes + /// things like language mode, predefined macros, header search paths, etc... + /// + /// Modules with the same name but a different \c ContextHash should be + /// treated as separate modules for the purpose of a build. std::string ContextHash; + + /// The path to the modulemap file which defines this module. + /// + /// This can be used to explicitly build this module. This file will + /// additionally appear in \c FileDeps as a dependency. + std::string ClangModuleMapFile; + + /// The path to where an implicit build would put the PCM for this module. + std::string ImplicitModulePCMPath; + + /// A collection of absolute paths to files that this module directly depends + /// on, not including transitive dependencies. llvm::StringSet<> FileDeps; - llvm::StringSet<> ClangModuleDeps; + + /// A list of modules this module directly depends on, not including + /// transitive dependencies. + /// + /// This may include modules with a different context hash when it can be + /// determined that the differences are benign for this compilation. + std::vector ClangModuleDeps; + + /// A partial command line that can be used to build this module. + /// + /// Call \c getFullCommandLine() to get a command line suitable for passing to + /// clang. + std::vector NonPathCommandLine; + + // Used to track which modules that were discovered were directly imported by + // the primary TU. bool ImportedByMainFile = false; + + /// Gets the full command line suitable for passing to clang. + /// + /// \param LookupPCMPath this function is called to fill in `-fmodule-file=` + /// flags and for the `-o` flag. It needs to return a + /// path for where the PCM for the given module is to + /// be located. + /// \param LookupModuleDeps this fucntion is called to collect the full + /// transitive set of dependencies for this + /// compilation. + std::vector getFullCommandLine( + std::function LookupPCMPath, + std::function LookupModuleDeps) const; }; +namespace detail { +/// Append the `-fmodule-file=` and `-fmodule-map-file=` arguments for the +/// modules in \c Modules transitively, along with other needed arguments to +/// use explicitly built modules. +void appendCommonModuleArguments( + llvm::ArrayRef Modules, + std::function LookupPCMPath, + std::function LookupModuleDeps, + std::vector &Result); +} // namespace detail + class ModuleDepCollector; class ModuleDepCollectorPP final : public PPCallbacks { @@ -54,6 +120,8 @@ class ModuleDepCollectorPP final : public PPCallbacks { StringRef SearchPath, StringRef RelativePath, const Module *Imported, SrcMgr::CharacteristicKind FileType) override; + void moduleImport(SourceLocation ImportLoc, ModuleIdPath Path, + const Module *Imported) override; void EndOfMainFile() override; @@ -62,16 +130,18 @@ class ModuleDepCollectorPP final : public PPCallbacks { ModuleDepCollector &MDC; llvm::DenseSet DirectDeps; + void handleImport(const Module *Imported); void handleTopLevelModule(const Module *M); - void addAllSubmoduleDeps(const Module *M, ModuleDeps &MD); - void addModuleDep(const Module *M, ModuleDeps &MD); - - void addDirectDependencies(const Module *Mod); + void addAllSubmoduleDeps(const Module *M, ModuleDeps &MD, + llvm::DenseSet &AddedModules); + void addModuleDep(const Module *M, ModuleDeps &MD, + llvm::DenseSet &AddedModules); }; class ModuleDepCollector final : public DependencyCollector { public: - ModuleDepCollector(CompilerInstance &I, DependencyConsumer &C); + ModuleDepCollector(std::unique_ptr Opts, + CompilerInstance &I, DependencyConsumer &C); void attachToPreprocessor(Preprocessor &PP) override; void attachToASTReader(ASTReader &R) override; @@ -85,6 +155,7 @@ class ModuleDepCollector final : public DependencyCollector { std::string ContextHash; std::vector MainDeps; std::unordered_map Deps; + std::unique_ptr Opts; }; } // end namespace dependencies 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 b3e2108d3fa6c..036c2087e6efc 100644 --- a/clang/include/clang/module.modulemap +++ b/clang/include/clang/module.modulemap @@ -156,6 +156,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..c1bdc8f2cf38e --- /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 = 2, + INDEXSTORE_UNIT_EVENT_MODIFIED = 3, + INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 4, + INDEXSTORE_UNIT_EVENT_FAILURE = 5, +} 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..106d7b84323df --- /dev/null +++ b/clang/lib/APINotes/APINotesManager.cpp @@ -0,0 +1,458 @@ +//===--- 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( + 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]) { + module->APINotesFile = file->getName().str(); + ++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(); + if (!getCurrentModuleReaders().empty()) { + module->APINotesFile = file->getName().str(); + return true; + } + return false; + } + } + } + + // 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..6b1200273d985 --- /dev/null +++ b/clang/lib/APINotes/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + BitReader + BitstreamReader + 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 d26e7f789d0ab..0fd47cf04dc49 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -38,6 +38,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" @@ -493,10 +494,20 @@ void ASTContext::attachCommentsToJustParsedDecls(ArrayRef Decls, if (Comments.empty() || Decls.empty()) return; - // See if there are any new comments that are not attached to a decl. - // The location doesn't have to be precise - we care only about the file. - const FileID File = - SourceMgr.getDecomposedLoc((*Decls.begin())->getLocation()).first; + FileID File; + for (Decl *D : Decls) { + SourceLocation Loc = D->getLocation(); + if (Loc.isValid()) { + // See if there are any new comments that are not attached to a decl. + // The location doesn't have to be precise - we care only about the file. + File = SourceMgr.getDecomposedLoc(Loc).first; + break; + } + } + + if (File.isInvalid()) + return; + auto CommentsInThisFile = Comments.getCommentsInFile(File); if (!CommentsInThisFile || CommentsInThisFile->empty() || CommentsInThisFile->rbegin()->second->isAttached()) @@ -2809,6 +2820,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); @@ -3608,10 +3629,10 @@ ASTContext::getDependentVectorType(QualType VecType, Expr *SizeExpr, (void)CanonCheck; DependentVectorTypes.InsertNode(New, InsertPos); } else { - QualType Canon = getDependentSizedExtVectorType(CanonVecTy, SizeExpr, - SourceLocation()); + QualType CanonTy = getDependentVectorType(CanonVecTy, SizeExpr, + SourceLocation(), VecKind); New = new (*this, TypeAlignment) DependentVectorType( - *this, VecType, Canon, SizeExpr, AttrLoc, VecKind); + *this, VecType, CanonTy, SizeExpr, AttrLoc, VecKind); } } @@ -4709,10 +4730,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 = @@ -6259,6 +6276,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()) @@ -8935,6 +8955,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 567d2bf7d228d..3b6cea7271079 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -4194,15 +4194,14 @@ ExpectedDecl ASTNodeImporter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { else return Imp.takeError(); - if (GetImportedOrCreateDecl(ToCategory, D, Importer.getToContext(), DC, - ToAtStartLoc, Loc, - ToCategoryNameLoc, - Name.getAsIdentifierInfo(), ToInterface, - /*TypeParamList=*/nullptr, - ToIvarLBraceLoc, - ToIvarRBraceLoc)) + if (GetImportedOrCreateDecl( + ToCategory, D, Importer.getToContext(), DC, ToAtStartLoc, Loc, + ToCategoryNameLoc, Name.getAsIdentifierInfo(), ToInterface, + /*PrevDecl=*/nullptr, + /*TypeParamList=*/nullptr, ToIvarLBraceLoc, ToIvarRBraceLoc)) return ToCategory; + ToCategory->startDefinition(); ToCategory->setLexicalDeclContext(LexicalDC); LexicalDC->addDeclInternal(ToCategory); // Import the type parameter list after MapImported, to avoid @@ -8046,6 +8045,18 @@ void ASTImporter::RegisterImportedDecl(Decl *FromD, Decl *ToD) { MapImported(FromD, ToD); } +llvm::Expected +ASTImporter::Import(ExprWithCleanups::CleanupObject From) { + if (auto *CLE = From.dyn_cast()) { + if (Expected R = Import(CLE)) + return ExprWithCleanups::CleanupObject(cast(*R)); + } + + // FIXME: Handle BlockDecl when we implement importing BlockExpr in + // ASTNodeImporter. + return make_error(ImportError::UnsupportedConstruct); +} + Expected ASTImporter::Import(QualType FromT) { if (FromT.isNull()) return QualType{}; @@ -8250,15 +8261,22 @@ Expected ASTImporter::ImportContext(DeclContext *FromDC) { // need it to have a definition. if (auto *ToRecord = dyn_cast(ToDC)) { auto *FromRecord = cast(FromDC); - if (ToRecord->isCompleteDefinition()) { - // Do nothing. - } else if (FromRecord->isCompleteDefinition()) { + if (ToRecord->isCompleteDefinition()) + return ToDC; + + // If FromRecord is not defined we need to force it to be. + // Simply calling CompleteDecl(...) for a RecordDecl will break some cases + // it will start the definition but we never finish it. + // If there are base classes they won't be imported and we will + // be missing anything that we inherit from those bases. + if (FromRecord->getASTContext().getExternalSource() && + !FromRecord->isCompleteDefinition()) + FromRecord->getASTContext().getExternalSource()->CompleteType(FromRecord); + + if (FromRecord->isCompleteDefinition()) if (Error Err = ASTNodeImporter(*this).ImportDefinition( FromRecord, ToRecord, ASTNodeImporter::IDK_Basic)) return std::move(Err); - } else { - CompleteDecl(ToRecord); - } } else if (auto *ToEnum = dyn_cast(ToDC)) { auto *FromEnum = cast(FromDC); if (ToEnum->isCompleteDefinition()) { diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp index 0ef925ec1c901..996806ba9dead 100644 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -17,3 +17,33 @@ using namespace clang; #include "clang/AST/AttrImpl.inc" + +// FIXME: this should be auto generated from Attr.td +bool Attr::compare(const Attr *A, const Attr *B) { + if (A->getKind() != B->getKind()) + return A->getKind() < B->getKind(); + + switch (A->getKind()) { + case attr::ObjCBridge: { + auto *MA = cast(A); + auto *MB = cast(B); + if (!MA->getBridgedType()) + return true; + if (!MB->getBridgedType()) + return false; + return MA->getBridgedType()->getName() < MB->getBridgedType()->getName(); + } + case attr::ObjCBridgeMutable: { + auto *MA = cast(A); + auto *MB = cast(B); + if (!MA->getBridgedType()) + return true; + if (!MB->getBridgedType()) + return false; + return MA->getBridgedType()->getName() < MB->getBridgedType()->getName(); + } + default: + llvm_unreachable("Not implemented"); + } + return false; +} diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index bd9b0934591c5..95a092b92040f 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -93,6 +93,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/CXXInheritance.cpp b/clang/lib/AST/CXXInheritance.cpp index a3a3794b2edd4..0377bd324cb65 100644 --- a/clang/lib/AST/CXXInheritance.cpp +++ b/clang/lib/AST/CXXInheritance.cpp @@ -758,6 +758,8 @@ CXXRecordDecl::getFinalOverriders(CXXFinalOverriderMap &FinalOverriders) const { return false; }; + // FIXME: IsHidden reads from Overriding from the middle of a remove_if + // over the same sequence! Is this guaranteed to work? Overriding.erase( std::remove_if(Overriding.begin(), Overriding.end(), IsHidden), Overriding.end()); diff --git a/clang/lib/AST/CommentSema.cpp b/clang/lib/AST/CommentSema.cpp index 53c1832d1dd25..8102f0115cc78 100644 --- a/clang/lib/AST/CommentSema.cpp +++ b/clang/lib/AST/CommentSema.cpp @@ -12,6 +12,7 @@ #include "clang/AST/CommentDiagnostic.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallString.h" @@ -134,7 +135,9 @@ void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) { unsigned DiagSelect; switch (Comment->getCommandID()) { case CommandTraits::KCI_class: - DiagSelect = (!isClassOrStructDecl() && !isClassTemplateDecl()) ? 1 : 0; + DiagSelect = + (!isClassOrStructOrTagTypedefDecl() && !isClassTemplateDecl()) ? 1 + : 0; // Allow @class command on @interface declarations. // FIXME. Currently, \class and @class are indistinguishable. So, // \class is also allowed on an @interface declaration @@ -148,7 +151,7 @@ void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) { DiagSelect = !isObjCProtocolDecl() ? 3 : 0; break; case CommandTraits::KCI_struct: - DiagSelect = !isClassOrStructDecl() ? 4 : 0; + DiagSelect = !isClassOrStructOrTagTypedefDecl() ? 4 : 0; break; case CommandTraits::KCI_union: DiagSelect = !isUnionDecl() ? 5 : 0; @@ -935,15 +938,50 @@ bool Sema::isUnionDecl() { return RD->isUnion(); return false; } +static bool isClassOrStructDeclImpl(const Decl *D) { + if (auto *record = dyn_cast_or_null(D)) + return !record->isUnion(); + + return false; +} bool Sema::isClassOrStructDecl() { if (!ThisDeclInfo) return false; if (!ThisDeclInfo->IsFilled) inspectThisDecl(); - return ThisDeclInfo->CurrentDecl && - isa(ThisDeclInfo->CurrentDecl) && - !isUnionDecl(); + + if (!ThisDeclInfo->CurrentDecl) + return false; + + return isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl); +} + +bool Sema::isClassOrStructOrTagTypedefDecl() { + if (!ThisDeclInfo) + return false; + if (!ThisDeclInfo->IsFilled) + inspectThisDecl(); + + if (!ThisDeclInfo->CurrentDecl) + return false; + + if (isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl)) + return true; + + if (auto *ThisTypedefDecl = dyn_cast(ThisDeclInfo->CurrentDecl)) { + auto UnderlyingType = ThisTypedefDecl->getUnderlyingType(); + if (auto ThisElaboratedType = dyn_cast(UnderlyingType)) { + auto DesugaredType = ThisElaboratedType->desugar(); + if (auto *DesugaredTypePtr = DesugaredType.getTypePtrOrNull()) { + if (auto *ThisRecordType = dyn_cast(DesugaredTypePtr)) { + return isClassOrStructDeclImpl(ThisRecordType->getAsRecordDecl()); + } + } + } + } + + return false; } bool Sema::isClassTemplateDecl() { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 6cfd4c2a2a218..37f639c669b0d 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4360,6 +4360,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C, setHasNonTrivialToPrimitiveCopyCUnion(false); setParamDestroyedInCallee(false); setArgPassingRestrictions(APK_CanPassInRegs); + RecordDeclBits.ODRHash = 0; } RecordDecl *RecordDecl::Create(const ASTContext &C, TagKind TK, DeclContext *DC, @@ -4507,6 +4508,19 @@ const FieldDecl *RecordDecl::findFirstNamedDataMember() const { return nullptr; } +unsigned RecordDecl::getODRHash() { + if (hasODRHash()) + return RecordDeclBits.ODRHash; + + // Only calculate hash on first call of getODRHash per record. + class ODRHash Hash; + Hash.AddRecordDecl(this); + // For RecordDecl the ODRHash is stored in the remaining 28 + // bit of RecordDeclBits, adjust the hash to accomodate. + setODRHash(Hash.CalculateHash() >> 4); + return RecordDeclBits.ODRHash; +} + //===----------------------------------------------------------------------===// // BlockDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 6ee767ccecf7d..13f5c807ad5c4 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1200,6 +1200,9 @@ DeclContext *DeclContext::getPrimaryContext() { return this; case Decl::ObjCCategory: + if (auto *OCD = dyn_cast(this)) + if (auto *Def = OCD->getDefinition()) + return Def; return this; case Decl::ObjCImplementation: diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index caa60408b5b67..77efdf61df3e6 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -484,7 +484,7 @@ unsigned CXXRecordDecl::getODRHash() const { return DefinitionData->ODRHash; // Only calculate hash on first call of getODRHash per record. - ODRHash Hash; + class ODRHash Hash; Hash.AddCXXRecordDecl(getDefinition()); DefinitionData->HasODRHash = true; DefinitionData->ODRHash = Hash.CalculateHash(); @@ -1032,6 +1032,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; @@ -1917,6 +1942,18 @@ bool CXXRecordDecl::mayBeAbstract() const { return false; } +bool CXXRecordDecl::isEffectivelyFinal() const { + auto *Def = getDefinition(); + if (!Def) + return false; + if (Def->hasAttr()) + return true; + if (const auto *Dtor = Def->getDestructor()) + if (Dtor->hasAttr()) + return true; + return false; +} + void CXXDeductionGuideDecl::anchor() {} bool ExplicitSpecifier::isEquivalent(const ExplicitSpecifier Other) const { @@ -2022,17 +2059,36 @@ CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD, if (auto *MD = getCorrespondingMethodDeclaredInClass(RD, MayBeBase)) return MD; + llvm::SmallVector FinalOverriders; + auto AddFinalOverrider = [&](CXXMethodDecl *D) { + // If this function is overridden by a candidate final overrider, it is not + // a final overrider. + for (CXXMethodDecl *OtherD : FinalOverriders) { + if (declaresSameEntity(D, OtherD) || recursivelyOverrides(OtherD, D)) + return; + } + + // Other candidate final overriders might be overridden by this function. + FinalOverriders.erase( + std::remove_if(FinalOverriders.begin(), FinalOverriders.end(), + [&](CXXMethodDecl *OtherD) { + return recursivelyOverrides(D, OtherD); + }), + FinalOverriders.end()); + + FinalOverriders.push_back(D); + }; + for (const auto &I : RD->bases()) { const RecordType *RT = I.getType()->getAs(); if (!RT) continue; const auto *Base = cast(RT->getDecl()); - CXXMethodDecl *T = this->getCorrespondingMethodInClass(Base); - if (T) - return T; + if (CXXMethodDecl *D = this->getCorrespondingMethodInClass(Base)) + AddFinalOverrider(D); } - return nullptr; + return FinalOverriders.size() == 1 ? FinalOverriders.front() : nullptr; } CXXMethodDecl *CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, @@ -2086,6 +2142,11 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base, CXXMethodDecl *DevirtualizedMethod = getCorrespondingMethodInClass(BestDynamicDecl); + // If there final overrider in the dynamic type is ambiguous, we can't + // devirtualize this call. + if (!DevirtualizedMethod) + return nullptr; + // If that method is pure virtual, we can't devirtualize. If this code is // reached, the result would be UB, not a direct call to the derived class // function, and we can't assume the derived class function is defined. @@ -2099,12 +2160,8 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base, // Similarly, if the class itself or its destructor is marked 'final', // the class can't be derived from and we can therefore devirtualize the // member function call. - if (BestDynamicDecl->hasAttr()) + if (BestDynamicDecl->isEffectivelyFinal()) return DevirtualizedMethod; - if (const auto *dtor = BestDynamicDecl->getDestructor()) { - if (dtor->hasAttr()) - return DevirtualizedMethod; - } if (const auto *DRE = dyn_cast(Base)) { if (const auto *VD = dyn_cast(DRE->getDecl())) diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 9a84e3c4a5107..813e95444088f 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -771,6 +772,33 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod( return Method; } +unsigned ObjCInterfaceDecl::getODRHash() { + assert(hasDefinition() && "ODRHash only for records with definitions"); + + // Previously calculated hash is stored in DefinitionData. + if (hasODRHash()) + return data().ODRHash; + + // Only calculate hash on first call of getODRHash per record. + class ODRHash Hash; + Hash.AddObjCInterfaceDecl(getDefinition()); + setHasODRHash(); + data().ODRHash = Hash.CalculateHash(); + + return data().ODRHash; +} + +bool ObjCInterfaceDecl::hasODRHash() const { + if (!hasDefinition()) + return false; + return data().HasODRHash; +} + +void ObjCInterfaceDecl::setHasODRHash(bool Hash) { + assert(hasDefinition() && "Cannot set ODRHash without definition"); + data().HasODRHash = Hash; +} + //===----------------------------------------------------------------------===// // ObjCMethodDecl //===----------------------------------------------------------------------===// @@ -801,6 +829,7 @@ ObjCMethodDecl::ObjCMethodDecl( setSelLocsKind(SelLoc_StandardNoSpace); setOverriding(false); setHasSkippedBody(false); + setHasODRHash(false); setImplicit(isImplicitlyDeclared); } @@ -1401,6 +1430,25 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { return nullptr; } +unsigned ObjCMethodDecl::getODRHash() const { + assert(hasODRHash()); + return ODRHash; +} + +unsigned ObjCMethodDecl::getODRHash() { + // Previously calculated hash is stored in DefinitionData. + if (hasODRHash()) + return ODRHash; + + // Only calculate hash on first call of getODRHash per record. + class ODRHash Hash; + Hash.AddObjCMethodDecl(this); + setHasODRHash(); + ODRHash = Hash.CalculateHash(); + + return ODRHash; +} + //===----------------------------------------------------------------------===// // ObjCTypeParamDecl //===----------------------------------------------------------------------===// @@ -1415,12 +1463,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, @@ -1936,6 +1980,7 @@ void ObjCProtocolDecl::allocateDefinitionData() { assert(!Data.getPointer() && "Protocol already has a definition!"); Data.setPointer(new (getASTContext()) DefinitionData); Data.getPointer()->Definition = this; + Data.getPointer()->HasODRHash = false; } void ObjCProtocolDecl::startDefinition() { @@ -1990,38 +2035,70 @@ ObjCProtocolDecl::getObjCRuntimeNameAsString() const { return getName(); } +unsigned ObjCProtocolDecl::getODRHash() { + assert(hasDefinition() && "ODRHash only for records with definitions"); + + // Previously calculated hash is stored in DefinitionData. + if (hasODRHash()) + return data().ODRHash; + + // Only calculate hash on first call of getODRHash per record. + class ODRHash Hash; + Hash.AddObjCProtocolDecl(getDefinition()); + setHasODRHash(); + data().ODRHash = Hash.CalculateHash(); + + return data().ODRHash; +} + +bool ObjCProtocolDecl::hasODRHash() const { + if (!hasDefinition()) + return false; + return data().HasODRHash; +} + +void ObjCProtocolDecl::setHasODRHash(bool Hash) { + assert(hasDefinition() && "Cannot set ODRHash without definition"); + data().HasODRHash = Hash; +} + //===----------------------------------------------------------------------===// // ObjCCategoryDecl //===----------------------------------------------------------------------===// void ObjCCategoryDecl::anchor() {} -ObjCCategoryDecl::ObjCCategoryDecl(DeclContext *DC, SourceLocation AtLoc, - SourceLocation ClassNameLoc, - SourceLocation CategoryNameLoc, - IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, - ObjCTypeParamList *typeParamList, - SourceLocation IvarLBraceLoc, - SourceLocation IvarRBraceLoc) +ObjCCategoryDecl::ObjCCategoryDecl( + const ASTContext &C, DeclContext *DC, SourceLocation AtLoc, + SourceLocation ClassNameLoc, SourceLocation CategoryNameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, ObjCCategoryDecl *PrevDecl, + ObjCTypeParamList *typeParamList, SourceLocation IvarLBraceLoc, + SourceLocation IvarRBraceLoc) : ObjCContainerDecl(ObjCCategory, DC, Id, ClassNameLoc, AtLoc), - ClassInterface(IDecl), CategoryNameLoc(CategoryNameLoc), - IvarLBraceLoc(IvarLBraceLoc), IvarRBraceLoc(IvarRBraceLoc) { + redeclarable_base(C), ClassInterface(IDecl), + CategoryNameLoc(CategoryNameLoc), IvarLBraceLoc(IvarLBraceLoc), + IvarRBraceLoc(IvarRBraceLoc) { + setPreviousDecl(PrevDecl); + + // Copy the 'data' pointer over. + if (PrevDecl) + Data = PrevDecl->Data; + setTypeParamList(typeParamList); + // allocateDefinitionData(); } -ObjCCategoryDecl *ObjCCategoryDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation AtLoc, - SourceLocation ClassNameLoc, - SourceLocation CategoryNameLoc, - IdentifierInfo *Id, - ObjCInterfaceDecl *IDecl, - ObjCTypeParamList *typeParamList, - SourceLocation IvarLBraceLoc, - SourceLocation IvarRBraceLoc) { - auto *CatDecl = - new (C, DC) ObjCCategoryDecl(DC, AtLoc, ClassNameLoc, CategoryNameLoc, Id, - IDecl, typeParamList, IvarLBraceLoc, - IvarRBraceLoc); +ObjCCategoryDecl *ObjCCategoryDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation AtLoc, + SourceLocation ClassNameLoc, SourceLocation CategoryNameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *IDecl, ObjCCategoryDecl *PrevDecl, + ObjCTypeParamList *typeParamList, SourceLocation IvarLBraceLoc, + SourceLocation IvarRBraceLoc) { + auto *CatDecl = new (C, DC) + ObjCCategoryDecl(C, DC, AtLoc, ClassNameLoc, CategoryNameLoc, Id, IDecl, + PrevDecl, typeParamList, IvarLBraceLoc, IvarRBraceLoc); + CatDecl->Data.setInt(!C.getLangOpts().Modules); + if (IDecl) { // Link this category into its class's category list. CatDecl->NextClassCategory = IDecl->getCategoryListRaw(); @@ -2037,9 +2114,11 @@ ObjCCategoryDecl *ObjCCategoryDecl::Create(ASTContext &C, DeclContext *DC, ObjCCategoryDecl *ObjCCategoryDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) ObjCCategoryDecl(nullptr, SourceLocation(), - SourceLocation(), SourceLocation(), - nullptr, nullptr, nullptr); + auto *Result = new (C, ID) + ObjCCategoryDecl(C, nullptr, SourceLocation(), SourceLocation(), + SourceLocation(), nullptr, nullptr, nullptr, nullptr); + Result->Data.setInt(!C.getLangOpts().Modules); + return Result; } ObjCCategoryImplDecl *ObjCCategoryDecl::getImplementation() const { @@ -2060,6 +2139,47 @@ void ObjCCategoryDecl::setTypeParamList(ObjCTypeParamList *TPL) { typeParam->setDeclContext(this); } +void ObjCCategoryDecl::allocateDefinitionData() { + assert(!hasDefinition() && "ObjC category already has a definition"); + Data.setPointer(new (getASTContext()) DefinitionData()); + Data.getPointer()->Definition = this; +} + +unsigned ObjCCategoryDecl::getODRHash() { + assert(hasDefinition() && "ODRHash only for records with definitions"); + + // Previously calculated hash is stored in DefinitionData. + if (hasODRHash()) + return data().ODRHash; + + // Only calculate hash on first call of getODRHash per record. + class ODRHash Hash; + Hash.AddObjCCategoryDecl(getDefinition()); + setHasODRHash(); + data().ODRHash = Hash.CalculateHash(); + + return data().ODRHash; +} + +void ObjCCategoryDecl::setHasODRHash(bool Hash) { + assert(hasDefinition() && "Cannot set ODRHash without definition"); + data().HasODRHash = Hash; +} + +bool ObjCCategoryDecl::hasODRHash() const { + if (!hasDefinition()) + return false; + return data().HasODRHash; +} + +void ObjCCategoryDecl::startDefinition() { + allocateDefinitionData(); + + // Update all of the declarations with a pointer to the definition. + for (auto *RD : redecls()) + RD->Data = this->Data; +} + //===----------------------------------------------------------------------===// // ObjCCategoryImplDecl //===----------------------------------------------------------------------===// @@ -2221,18 +2341,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 6b17dd4dde759..22ac11b8fcf0b 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 "; @@ -1332,6 +1334,9 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) { return; } bool eolnOut = false; + prettyPrintAttributes(OID); + if (OID->hasAttrs()) Out << "\n"; + Out << "@interface " << I; if (auto TypeParams = OID->getTypeParamListAsWritten()) { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 73ddbc62482dd..b25afb87bb5d0 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1685,6 +1685,11 @@ MemberExpr *MemberExpr::Create( CXXRecordDecl *RD = dyn_cast_or_null(DC); if (RD && RD->isDependentContext() && RD->isCurrentInstantiation(DC)) E->setTypeDependent(T->isDependentType()); + + // Bitfield with value-dependent width is type-dependent. + FieldDecl *FD = dyn_cast(MemberDecl); + if (FD && FD->isBitField() && FD->getBitWidth()->isValueDependent()) + E->setTypeDependent(true); } if (HasQualOrFound) { @@ -3206,6 +3211,9 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, switch (getStmtClass()) { default: break; + case Stmt::ExprWithCleanupsClass: + return cast(this)->getSubExpr()->isConstantInitializer( + Ctx, IsForRef, Culprit); case StringLiteralClass: case ObjCEncodeExprClass: return true; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7e33b9d354b0f..7aad0f2138a70 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -47,6 +47,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" @@ -1852,6 +1853,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... @@ -1893,7 +1907,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; @@ -3124,6 +3138,13 @@ struct CompleteObject { : Base(Base), Value(Value), Type(Type) {} bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const { + // If this isn't a "real" access (eg, if it's just accessing the type + // info), allow it. We assume the type doesn't change dynamically for + // subobjects of constexpr objects (even though we'd hit UB here if it + // did). FIXME: Is this right? + if (!isAnyAccess(AK)) + return true; + // In C++14 onwards, it is permitted to read a mutable member whose // lifetime began within the evaluation. // FIXME: Should we also allow this in C++11? @@ -8268,6 +8289,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: @@ -8528,6 +8551,10 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, APValue &Result, const InitListExpr *ILE, QualType AllocType); +static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This, + APValue &Result, + const CXXConstructExpr *CCE, + QualType AllocType); bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { if (!Info.getLangOpts().CPlusPlus2a) @@ -8577,6 +8604,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { const Expr *Init = E->getInitializer(); const InitListExpr *ResizedArrayILE = nullptr; + const CXXConstructExpr *ResizedArrayCCE = nullptr; QualType AllocType = E->getAllocatedType(); if (Optional ArraySize = E->getArraySize()) { @@ -8620,7 +8648,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { // -- the new-initializer is a braced-init-list and the number of // array elements for which initializers are provided [...] // exceeds the number of elements to initialize - if (Init) { + if (Init && !isa(Init)) { auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType()); assert(CAT && "unexpected type for array initializer"); @@ -8643,6 +8671,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { // special handling for this case when we initialize. if (InitBound != AllocBound) ResizedArrayILE = cast(Init); + } else if (Init) { + ResizedArrayCCE = cast(Init); } AllocType = Info.Ctx.getConstantArrayType(AllocType, ArrayBound, nullptr, @@ -8707,6 +8737,10 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE, AllocType)) return false; + } else if (ResizedArrayCCE) { + if (!EvaluateArrayNewConstructExpr(Info, Result, *Val, ResizedArrayCCE, + AllocType)) + return false; } else if (Init) { if (!EvaluateInPlace(*Val, Info, Result, Init)) return false; @@ -9531,6 +9565,16 @@ static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, .VisitInitListExpr(ILE, AllocType); } +static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This, + APValue &Result, + const CXXConstructExpr *CCE, + QualType AllocType) { + assert(CCE->isRValue() && CCE->getType()->isArrayType() && + "not an array rvalue"); + return ArrayExprEvaluator(Info, This, Result) + .VisitCXXConstructExpr(CCE, This, &Result, AllocType); +} + // Return true iff the given array filler may depend on the element index. static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { // For now, just whitelist non-class value-initialization and initialization @@ -10697,6 +10741,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: { @@ -12012,6 +12063,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/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp index 837be5527fce3..656d22e6d5aa2 100644 --- a/clang/lib/AST/ExternalASTSource.cpp +++ b/clang/lib/AST/ExternalASTSource.cpp @@ -38,7 +38,7 @@ ExternalASTSource::hasExternalDefinitions(const Decl *D) { return EK_ReplyHazy; } -ExternalASTSource::ASTSourceDescriptor::ASTSourceDescriptor(const Module &M) +ExternalASTSource::ASTSourceDescriptor::ASTSourceDescriptor(Module &M) : Signature(M.Signature), ClangModule(&M) { if (M.Directory) Path = M.Directory->getName(); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0d567edac5216..a2f719c6b941a 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2329,38 +2329,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'; @@ -4005,6 +4008,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 c30b07137edcf..569ac14c8f90a 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1239,6 +1239,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; } @@ -1333,7 +1335,16 @@ void JSONNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *EWC) { if (EWC->getNumObjects()) { JOS.attributeArray("cleanups", [this, EWC] { for (const ExprWithCleanups::CleanupObject &CO : EWC->getObjects()) - JOS.value(createBareDeclRef(CO)); + if (auto *BD = CO.dyn_cast()) { + JOS.value(createBareDeclRef(BD)); + } else if (auto *CLE = CO.dyn_cast()) { + llvm::json::Object Obj; + Obj["id"] = createPointerRepresentation(CLE); + Obj["kind"] = CLE->getStmtClassName(); + JOS.value(std::move(Obj)); + } else { + llvm_unreachable("unexpected cleanup object type"); + } }); } } 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/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 3b89c630b451e..b0b3f56b26557 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -79,6 +79,7 @@ void ODRHash::AddDeclarationNameImpl(DeclarationName Name) { AddIdentifierInfo(II); } } + ID.AddString(S.getAsString()); break; } case DeclarationName::CXXConstructorName: @@ -319,6 +320,37 @@ class ODRDeclVisitor : public ConstDeclVisitor { Inherited::VisitStaticAssertDecl(D); } + void VisitObjCIvarDecl(const ObjCIvarDecl *D) { + ID.AddInteger(D->getAccessControl()); + Hash.AddBoolean(D->getSynthesize()); + Inherited::VisitFieldDecl(static_cast(D)); + } + + void VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { + unsigned AttrsAsWritten = D->getPropertyAttributesAsWritten(); + unsigned Attrs = D->getPropertyAttributes(); + + // If null_resettable is present but was not written, it came from + // APINotes. This workaround is for not allowing ODR mismatches between + // non-modular content annotated in a module by APINotes versus the + // same non-modular content found elsewhere. Long term we need to get + // rid of this kind of annotations. + if (Attrs & ObjCPropertyDecl::OBJC_PR_null_resettable && + !(AttrsAsWritten & ObjCPropertyDecl::OBJC_PR_null_resettable)) + Attrs &= ~ObjCPropertyDecl::OBJC_PR_null_resettable; + + QualType T = D->getType(); + if (D->getType()->canHaveNullability()) + AttributedType::stripOuterNullability(T); + AddQualType(T); + + ID.AddInteger(Attrs); + ID.AddInteger(D->getPropertyImplementation()); + AddDecl(D); + + Inherited::VisitObjCPropertyDecl(D); + } + void VisitFieldDecl(const FieldDecl *D) { const bool IsBitfield = D->isBitField(); Hash.AddBoolean(IsBitfield); @@ -346,6 +378,11 @@ class ODRDeclVisitor : public ConstDeclVisitor { Inherited::VisitCXXMethodDecl(D); } + void VisitObjCMethodDecl(const ObjCMethodDecl *D) { + ID.AddInteger(D->getODRHash()); + Inherited::VisitObjCMethodDecl(D); + } + void VisitTypedefNameDecl(const TypedefNameDecl *D) { AddQualType(D->getUnderlyingType()); @@ -438,6 +475,9 @@ class ODRDeclVisitor : public ConstDeclVisitor { bool ODRHash::isWhitelistedDecl(const Decl *D, const DeclContext *Parent) { if (D->isImplicit()) return false; if (D->getDeclContext() != Parent) return false; + bool ShouldHashIvar = D->getASTContext().getLangOpts().ODRCheckIvars; + bool ShouldHashProperties = D->getASTContext().getLangOpts().ODRCheckProperties; + bool ShouldHashMethods = D->getASTContext().getLangOpts().ODRCheckMethods; switch (D->getKind()) { default: @@ -455,6 +495,72 @@ bool ODRHash::isWhitelistedDecl(const Decl *D, const DeclContext *Parent) { case Decl::Typedef: case Decl::Var: return true; + case Decl::ObjCMethod: + return ShouldHashMethods; + case Decl::ObjCIvar: + return ShouldHashIvar; + case Decl::ObjCProperty: + return ShouldHashProperties; + } +} + +// Only a small portion of Attr's are considered right now. +bool ODRHash::isWhitelistedAttr(const Attr *A) { + // FIXME: This should be auto-generated as part of Attr.td + switch (A->getKind()) { + default: + return false; + case attr::ObjCBridge: + case attr::ObjCBridgeMutable: + return true; + } +} + +void ODRHash::AddAttrs(const NamedDecl *D) { + if (!D->hasAttrs()) + return; + + // Go over attributes + llvm::SmallVector Attrs; + for (const Attr *A : D->getAttrs()) { + if (isWhitelistedAttr(A)) + Attrs.push_back(A); + } + + // Sort attributes by name + other per subject kind. This allows + // for extra flexibility when redeclarations use different order. + // FIXME: In case the order matters for a group of attributes we + // decide to hash, then we might need to change this. + // FIXME: This should be auto-generated as part of Attr.td + llvm::sort(Attrs, + [](const Attr *A, const Attr *B) { return Attr::compare(A, B); }); + + for (const Attr *A : Attrs) + AddAttr(A); +} + +void ODRHash::AddAttr(const Attr *A) { + assert(A && "Expecting non-null pointer."); + ID.AddInteger(A->getKind()); + + // FIXME: This should be auto-generated as part of Attr.td + switch (A->getKind()) { + case attr::ObjCBridge: { + auto *M = cast(A); + AddBoolean(M->getBridgedType()); + if (M->getBridgedType()) + ID.AddString(M->getBridgedType()->getName()); + break; + } + case attr::ObjCBridgeMutable: { + auto *M = cast(A); + AddBoolean(M->getBridgedType()); + if (M->getBridgedType()) + ID.AddString(M->getBridgedType()->getName()); + break; + } + default: + llvm_unreachable("Not expecting other attribute kinds"); } } @@ -464,6 +570,162 @@ void ODRHash::AddSubDecl(const Decl *D) { ODRDeclVisitor(ID, *this).Visit(D); } +void ODRHash::AddObjCCategoryDecl(const ObjCCategoryDecl *Cat) { + // Nothing to compute for extensions, there can be as many as + // wanted and ODR checking doesn't apply. + if (Cat->IsClassExtension()) + return; + + AddDecl(Cat); + + // Trigger ODR computation for methods (if not yet computed) + for (auto *M : Cat->methods()) + reinterpret_cast(M)->getODRHash(); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector Decls; + for (Decl *SubDecl : Cat->decls()) + if (isWhitelistedDecl(SubDecl, Cat)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto *SubDecl : Decls) + AddSubDecl(SubDecl); +} + +void ODRHash::AddObjCInterfaceDecl(const ObjCInterfaceDecl *IF) { + AddDecl(IF); + + // Trigger ODR computation for methods (if not yet computed) + for (auto *M : IF->methods()) + reinterpret_cast(M)->getODRHash(); + + auto *SuperClass = IF->getSuperClass(); + AddBoolean(SuperClass); + if (SuperClass) + ID.AddInteger(SuperClass->getODRHash()); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector Decls; + for (Decl *SubDecl : IF->decls()) + if (isWhitelistedDecl(SubDecl, IF)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto *SubDecl : Decls) + AddSubDecl(SubDecl); +} + +void ODRHash::AddObjCProtocolDecl(const ObjCProtocolDecl *P) { + AddDecl(P); + + // Trigger ODR computation for methods. + for (auto *M : P->methods()) + reinterpret_cast(M)->getODRHash(); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector Decls; + for (Decl *SubDecl : P->decls()) + if (isWhitelistedDecl(SubDecl, P)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto *SubDecl : Decls) + AddSubDecl(SubDecl); +} + +void ODRHash::AddObjCMethodDecl(const ObjCMethodDecl *Method) { + assert(Method && "Expecting non-null pointer."); + + ID.AddInteger(Method->getDeclKind()); + AddBoolean(Method->isInstanceMethod()); // false if class method + AddBoolean(Method->isPropertyAccessor()); + AddBoolean(Method->isVariadic()); + AddBoolean(Method->isSynthesizedAccessorStub()); + AddBoolean(Method->isDefined()); + AddBoolean(Method->isOverriding()); + AddBoolean(Method->isDirectMethod()); + AddBoolean(Method->isThisDeclarationADesignatedInitializer()); + AddBoolean(Method->hasSkippedBody()); + AddBoolean(Method->isPropertyAccessor()); + AddBoolean(Method->isDeprecated()); + + ID.AddInteger(Method->getImplementationControl()); + ID.AddInteger(Method->getMethodFamily()); + ImplicitParamDecl *Cmd = Method->getCmdDecl(); + AddBoolean(Cmd); + if (Cmd) + ID.AddInteger(Cmd->getParameterKind()); + + ImplicitParamDecl *Self = Method->getSelfDecl(); + AddBoolean(Self); + if (Self) + ID.AddInteger(Self->getParameterKind()); + + AddDecl(Method); + + AddQualType(Method->getReturnType()); + ID.AddInteger(Method->param_size()); + for (auto Param : Method->parameters()) + AddSubDecl(Param); + + bool SkipBody = Method->hasBody(); + if (SkipBody) { + AddBoolean(false); + return; + } + + const bool HasBody = Method->isThisDeclarationADefinition(); + AddBoolean(HasBody); + if (!HasBody) { + return; + } + + auto *Body = Method->getBody(); + AddBoolean(Body); + if (Body) + AddStmt(Body); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector Decls; + for (Decl *SubDecl : Method->decls()) + if (isWhitelistedDecl(SubDecl, Method)) + Decls.push_back(SubDecl); + + ID.AddInteger(Decls.size()); + for (auto SubDecl : Decls) + AddSubDecl(SubDecl); +} + +void ODRHash::AddRecordDecl(const RecordDecl *Record) { + AddDecl(Record); + + // Filter out sub-Decls which will not be processed in order to get an + // accurate count of Decl's. + llvm::SmallVector Decls; + for (Decl *SubDecl : Record->decls()) { + if (auto *SubRD = dyn_cast(SubDecl)) { + if (!SubRD->isAnonymousStructOrUnion()) + continue; + ID.AddInteger(SubRD->getODRHash()); + continue; + } + if (isWhitelistedDecl(SubDecl, Record)) + Decls.push_back(SubDecl); + } + + ID.AddInteger(Decls.size()); + for (auto SubDecl : Decls) + AddSubDecl(SubDecl); + + if (Record->getASTContext().getLangOpts().ODRCheckAttributes) + AddAttrs(Record); +} + void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { assert(Record && Record->hasDefinition() && "Expected non-null record to be a definition."); 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 c14bb886bb11a..3debcaa01a33d 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1290,6 +1290,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 fa7f4130b761d..86ae7eedfed68 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -447,6 +447,23 @@ void TextNodeDumper::dumpAccessSpecifier(AccessSpecifier AS) { } } +void TextNodeDumper::dumpCleanupObject( + const ExprWithCleanups::CleanupObject &C) { + if (auto *BD = C.dyn_cast()) + dumpDeclRef(BD, "cleanup"); + else if (auto *CLE = C.dyn_cast()) + AddChild([=] { + OS << "cleanup "; + { + ColorScope Color(OS, ShowColors, StmtColor); + OS << CLE->getStmtClassName(); + } + dumpPointer(CLE); + }); + else + llvm_unreachable("unexpected cleanup type"); +} + void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) { if (!D) return; @@ -816,6 +833,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; @@ -949,7 +969,7 @@ void TextNodeDumper::VisitMaterializeTemporaryExpr( void TextNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) { for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i) - dumpDeclRef(Node->getObject(i), "cleanup"); + dumpCleanupObject(Node->getObject(i)); } void TextNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *Node) { @@ -1894,6 +1914,8 @@ void TextNodeDumper::VisitObjCCompatibleAliasDecl( const ObjCCompatibleAliasDecl *D) { dumpName(D); dumpDeclRef(D->getClassInterface()); + OS << " "; + dumpLocation(D->getClassInterfaceLoc()); } void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c5ad711d872e0..ace7a4974c9c3 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) { @@ -2369,6 +2364,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 c2f4baec989ee..d1a87a5750982 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"); @@ -1743,6 +1755,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)); @@ -1772,6 +1808,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; } @@ -1865,6 +1905,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/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp index 9f58b5079c760..96d5807bcdfc0 100644 --- a/clang/lib/Analysis/AnalysisDeclContext.cpp +++ b/clang/lib/Analysis/AnalysisDeclContext.cpp @@ -52,16 +52,16 @@ using namespace clang; using ManagedAnalysisMap = llvm::DenseMap; -AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, - const Decl *d, - const CFG::BuildOptions &buildOptions) - : Manager(Mgr), D(d), cfgBuildOptions(buildOptions) { +AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *ADCMgr, + const Decl *D, + const CFG::BuildOptions &Options) + : ADCMgr(ADCMgr), D(D), cfgBuildOptions(Options) { cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; } -AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, - const Decl *d) - : Manager(Mgr), D(d) { +AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *ADCMgr, + const Decl *D) + : ADCMgr(ADCMgr), D(D) { cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; } @@ -96,8 +96,8 @@ Stmt *AnalysisDeclContext::getBody(bool &IsAutosynthesized) const { Stmt *Body = FD->getBody(); if (auto *CoroBody = dyn_cast_or_null(Body)) Body = CoroBody->getBody(); - if (Manager && Manager->synthesizeBodies()) { - Stmt *SynthesizedBody = Manager->getBodyFarm().getBody(FD); + if (ADCMgr && ADCMgr->synthesizeBodies()) { + Stmt *SynthesizedBody = ADCMgr->getBodyFarm().getBody(FD); if (SynthesizedBody) { Body = SynthesizedBody; IsAutosynthesized = true; @@ -107,8 +107,8 @@ Stmt *AnalysisDeclContext::getBody(bool &IsAutosynthesized) const { } else if (const auto *MD = dyn_cast(D)) { Stmt *Body = MD->getBody(); - if (Manager && Manager->synthesizeBodies()) { - Stmt *SynthesizedBody = Manager->getBodyFarm().getBody(MD); + if (ADCMgr && ADCMgr->synthesizeBodies()) { + Stmt *SynthesizedBody = ADCMgr->getBodyFarm().getBody(MD); if (SynthesizedBody) { Body = SynthesizedBody; IsAutosynthesized = true; @@ -309,19 +309,17 @@ AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) { BodyFarm &AnalysisDeclContextManager::getBodyFarm() { return FunctionBodyFarm; } const StackFrameContext * -AnalysisDeclContext::getStackFrame(LocationContext const *Parent, const Stmt *S, - const CFGBlock *Blk, unsigned BlockCount, - unsigned Idx) { - return getLocationContextManager().getStackFrame(this, Parent, S, Blk, - BlockCount, Idx); +AnalysisDeclContext::getStackFrame(const LocationContext *ParentLC, + const Stmt *S, const CFGBlock *Blk, + unsigned BlockCount, unsigned Index) { + return getLocationContextManager().getStackFrame(this, ParentLC, S, Blk, + BlockCount, Index); } -const BlockInvocationContext * -AnalysisDeclContext::getBlockInvocationContext(const LocationContext *parent, - const BlockDecl *BD, - const void *ContextData) { - return getLocationContextManager().getBlockInvocationContext(this, parent, - BD, ContextData); +const BlockInvocationContext *AnalysisDeclContext::getBlockInvocationContext( + const LocationContext *ParentLC, const BlockDecl *BD, const void *Data) { + return getLocationContextManager().getBlockInvocationContext(this, ParentLC, + BD, Data); } bool AnalysisDeclContext::isInStdNamespace(const Decl *D) { @@ -340,9 +338,10 @@ bool AnalysisDeclContext::isInStdNamespace(const Decl *D) { } LocationContextManager &AnalysisDeclContext::getLocationContextManager() { - assert(Manager && - "Cannot create LocationContexts without an AnalysisDeclContextManager!"); - return Manager->getLocationContextManager(); + assert( + ADCMgr && + "Cannot create LocationContexts without an AnalysisDeclContextManager!"); + return ADCMgr->getLocationContextManager(); } //===----------------------------------------------------------------------===// @@ -365,36 +364,14 @@ void StackFrameContext::Profile(llvm::FoldingSetNodeID &ID) { BlockCount, Index); } -void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getAnalysisDeclContext(), getParent(), Enter); -} - void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getAnalysisDeclContext(), getParent(), BD, ContextData); + Profile(ID, getAnalysisDeclContext(), getParent(), BD, Data); } //===----------------------------------------------------------------------===// // LocationContext creation. //===----------------------------------------------------------------------===// -template -const LOC* -LocationContextManager::getLocationContext(AnalysisDeclContext *ctx, - const LocationContext *parent, - const DATA *d) { - llvm::FoldingSetNodeID ID; - LOC::Profile(ID, ctx, parent, d); - void *InsertPos; - - LOC *L = cast_or_null(Contexts.FindNodeOrInsertPos(ID, InsertPos)); - - if (!L) { - L = new LOC(ctx, parent, d, ++NewID); - Contexts.InsertNode(L, InsertPos); - } - return L; -} - const StackFrameContext *LocationContextManager::getStackFrame( AnalysisDeclContext *ctx, const LocationContext *parent, const Stmt *s, const CFGBlock *blk, unsigned blockCount, unsigned idx) { @@ -410,26 +387,17 @@ const StackFrameContext *LocationContextManager::getStackFrame( return L; } -const ScopeContext * -LocationContextManager::getScope(AnalysisDeclContext *ctx, - const LocationContext *parent, - const Stmt *s) { - return getLocationContext(ctx, parent, s); -} - -const BlockInvocationContext * -LocationContextManager::getBlockInvocationContext(AnalysisDeclContext *ctx, - const LocationContext *parent, - const BlockDecl *BD, - const void *ContextData) { +const BlockInvocationContext *LocationContextManager::getBlockInvocationContext( + AnalysisDeclContext *ADC, const LocationContext *ParentLC, + const BlockDecl *BD, const void *Data) { llvm::FoldingSetNodeID ID; - BlockInvocationContext::Profile(ID, ctx, parent, BD, ContextData); + BlockInvocationContext::Profile(ID, ADC, ParentLC, BD, Data); void *InsertPos; auto *L = cast_or_null(Contexts.FindNodeOrInsertPos(ID, InsertPos)); if (!L) { - L = new BlockInvocationContext(ctx, parent, BD, ContextData, ++NewID); + L = new BlockInvocationContext(ADC, ParentLC, BD, Data, ++NewID); Contexts.InsertNode(L, InsertPos); } return L; @@ -473,9 +441,7 @@ static void printLocation(raw_ostream &Out, const SourceManager &SM, Loc.print(Out, SM); } -void LocationContext::dumpStack(raw_ostream &Out, const char *NL, - std::function - printMoreInfoPerContext) const { +void LocationContext::dumpStack(raw_ostream &Out) const { ASTContext &Ctx = getAnalysisDeclContext()->getASTContext(); PrintingPolicy PP(Ctx.getLangOpts()); PP.TerseOutput = 1; @@ -498,9 +464,6 @@ void LocationContext::dumpStack(raw_ostream &Out, const char *NL, printLocation(Out, SM, S->getBeginLoc()); } break; - case Scope: - Out << "Entering scope"; - break; case Block: Out << "Invoking block"; if (const Decl *D = cast(LCtx)->getDecl()) { @@ -509,9 +472,7 @@ void LocationContext::dumpStack(raw_ostream &Out, const char *NL, } break; } - Out << NL; - - printMoreInfoPerContext(LCtx); + Out << '\n'; } } @@ -548,9 +509,6 @@ void LocationContext::printJson(raw_ostream &Out, const char *NL, Out << ", \"items\": "; break; - case Scope: - Out << "Entering scope\" "; - break; case Block: Out << "Invoking block\" "; if (const Decl *D = cast(LCtx)->getDecl()) { diff --git a/clang/lib/Analysis/RetainSummaryManager.cpp b/clang/lib/Analysis/RetainSummaryManager.cpp index 6f46917b2dfc6..583be169c8024 100644 --- a/clang/lib/Analysis/RetainSummaryManager.cpp +++ b/clang/lib/Analysis/RetainSummaryManager.cpp @@ -145,7 +145,9 @@ static bool isSubclass(const Decl *D, } static bool isOSObjectSubclass(const Decl *D) { - return D && isSubclass(D, "OSMetaClassBase"); + // OSSymbols are particular OSObjects that are allocated globally + // and therefore aren't really refcounted, so we ignore them. + return D && isSubclass(D, "OSMetaClassBase") && !isSubclass(D, "OSSymbol"); } static bool isOSObjectDynamicCast(StringRef S) { @@ -662,6 +664,7 @@ RetainSummaryManager::getSummary(AnyCall C, switch (C.getKind()) { case AnyCall::Function: case AnyCall::Constructor: + case AnyCall::InheritedConstructor: case AnyCall::Allocator: case AnyCall::Deallocator: Summ = getFunctionSummary(cast_or_null(C.getDecl())); 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/FileManager.cpp b/clang/lib/Basic/FileManager.cpp index 079a4bbfc82fc..37f4356d684cd 100644 --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -458,7 +458,8 @@ void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { } llvm::ErrorOr> -FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile) { +FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile, + bool RequiresNullTerminator) { uint64_t FileSize = Entry->getSize(); // If there's a high enough chance that the file have changed since we // got its size, force a stat before opening it. @@ -468,28 +469,29 @@ FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile) { StringRef Filename = Entry->getName(); // If the file is already open, use the open file descriptor. if (Entry->File) { - auto Result = - Entry->File->getBuffer(Filename, FileSize, - /*RequiresNullTerminator=*/true, isVolatile); + auto Result = Entry->File->getBuffer(Filename, FileSize, + RequiresNullTerminator, isVolatile); Entry->closeFile(); return Result; } // Otherwise, open the file. - return getBufferForFileImpl(Filename, FileSize, isVolatile); + return getBufferForFileImpl(Filename, FileSize, isVolatile, + RequiresNullTerminator); } llvm::ErrorOr> FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize, - bool isVolatile) { + bool isVolatile, + bool RequiresNullTerminator) { if (FileSystemOpts.WorkingDir.empty()) - return FS->getBufferForFile(Filename, FileSize, - /*RequiresNullTerminator=*/true, isVolatile); + return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator, + isVolatile); SmallString<128> FilePath(Filename); FixupRelativePath(FilePath); - return FS->getBufferForFile(FilePath, FileSize, - /*RequiresNullTerminator=*/true, isVolatile); + return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator, + isVolatile); } /// getStatValue - Get the 'stat' information for the specified path, diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 541431dbbe7d9..dd3e8cba0625c 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()) @@ -237,9 +243,10 @@ bool Module::fullModuleNameIs(ArrayRef nameParts) const { Module::DirectoryName Module::getUmbrellaDir() const { if (Header U = getUmbrellaHeader()) - return {"", U.Entry->getDir()}; + return {"", "", U.Entry->getDir()}; - return {UmbrellaAsWritten, Umbrella.dyn_cast()}; + return {UmbrellaAsWritten, UmbrellaRelativeToRootModuleDirectory, + Umbrella.dyn_cast()}; } ArrayRef Module::getTopHeaders(FileManager &FileMgr) { @@ -431,6 +438,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/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp index 5f457d6f9e3df..13efeedbe9202 100644 --- a/clang/lib/Basic/SourceManager.cpp +++ b/clang/lib/Basic/SourceManager.cpp @@ -389,6 +389,14 @@ void SourceManager::clearIDTables() { createExpansionLoc(SourceLocation(), SourceLocation(), SourceLocation(), 1); } +bool SourceManager::isMainFile(FileEntryRef SourceFile) { + assert(MainFileID.isValid() && "expected initialized SourceManager"); + auto FE = getFileEntryRefForID(MainFileID); + if (!FE) + return false; + return FE->getUID() == SourceFile.getUID(); +} + void SourceManager::initializeForReplay(const SourceManager &Old) { assert(MainFileID.isInvalid() && "expected uninitialized SourceManager"); @@ -1250,23 +1258,18 @@ static void ComputeLineNumbers(DiagnosticsEngine &Diag, ContentCache *FI, const unsigned char *Buf = (const unsigned char *)Buffer->getBufferStart(); const unsigned char *End = (const unsigned char *)Buffer->getBufferEnd(); + const std::size_t BufLen = End - Buf; unsigned I = 0; - while (true) { - // Skip over the contents of the line. - while (Buf[I] != '\n' && Buf[I] != '\r' && Buf[I] != '\0') - ++I; - - if (Buf[I] == '\n' || Buf[I] == '\r') { + while (I < BufLen) { + if (Buf[I] == '\n') { + LineOffsets.push_back(I + 1); + } else if (Buf[I] == '\r') { // If this is \r\n, skip both characters. - if (Buf[I] == '\r' && Buf[I+1] == '\n') + if (I + 1 < BufLen && Buf[I + 1] == '\n') ++I; - ++I; - LineOffsets.push_back(I); - } else { - // Otherwise, this is a NUL. If end of file, exit. - if (Buf+I == End) break; - ++I; + LineOffsets.push_back(I + 1); } + ++I; } // Copy the offsets into the FileInfo structure. 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 cba3e3ada7ea5..9b9ea10912ef9 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" #include "llvm/ADT/StringSwitch.h" @@ -97,6 +98,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; } @@ -535,6 +539,11 @@ int AArch64TargetInfo::getEHDataRegisterNumber(unsigned RegNo) const { return -1; } +bool AArch64TargetInfo::validatePointerAuthKey( + const llvm::APSInt &value) const { + return 0 <= value && value <= 3; +} + bool AArch64TargetInfo::hasInt128Type() const { return true; } AArch64leTargetInfo::AArch64leTargetInfo(const llvm::Triple &Triple, @@ -699,6 +708,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 5e78237743c90..d14fc41a3d9ab 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -101,6 +101,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo { int getEHDataRegisterNumber(unsigned RegNo) const override; + bool validatePointerAuthKey(const llvm::APSInt &value) const override; + bool hasInt128Type() const override; }; diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp index d4ffffc64ba8d..8ebf1588c92da 100644 --- a/clang/lib/Basic/Targets/OSTargets.cpp +++ b/clang/lib/Basic/Targets/OSTargets.cpp @@ -25,12 +25,15 @@ void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, Builder.defineMacro("__APPLE_CC__", "6000"); Builder.defineMacro("__APPLE__"); Builder.defineMacro("__STDC_NO_THREADS__"); - Builder.defineMacro("OBJC_NEW_PROPERTIES"); + // AddressSanitizer doesn't play well with source fortification, which is on // by default on Darwin. 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 c69d13b2f689b..d5605a2e985f5 100644 --- a/clang/lib/Basic/Version.cpp +++ b/clang/lib/Basic/Version.cpp @@ -142,4 +142,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/ABIInfo.h b/clang/lib/CodeGen/ABIInfo.h index 0c3a076da0b58..3b6573f15e7aa 100644 --- a/clang/lib/CodeGen/ABIInfo.h +++ b/clang/lib/CodeGen/ABIInfo.h @@ -28,7 +28,6 @@ namespace clang { namespace CodeGen { class ABIArgInfo; - class Address; class CGCXXABI; class CGFunctionInfo; class CodeGenFunction; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index ed881f2ddf688..3989ac3131002 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -340,6 +340,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); @@ -479,7 +484,6 @@ static void initTargetOptions(llvm::TargetOptions &Options, Options.DebuggerTuning = CodeGenOpts.getDebuggerTuning(); Options.EmitStackSizeSection = CodeGenOpts.StackSizeSection; Options.EmitAddrsig = CodeGenOpts.Addrsig; - Options.EnableDebugEntryValues = CodeGenOpts.EnableDebugEntryValues; Options.ForceDwarfFrameSection = CodeGenOpts.ForceDwarfFrameSection; Options.MCOptions.SplitDwarfFile = CodeGenOpts.SplitDwarfFile; @@ -580,6 +584,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; @@ -679,6 +684,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) @@ -1149,6 +1161,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 b0295ce69bfff..ca6ff0a80a7d6 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -70,6 +70,7 @@ namespace { /// entity that's captured by a block. enum class BlockCaptureEntityKind { CXXRecord, // Copy or destroy + AddressDiscriminatedPointerAuth, ARCWeak, ARCStrong, NonTrivialCStruct, @@ -124,7 +125,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) @@ -223,14 +224,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)) @@ -624,6 +628,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; @@ -860,13 +868,13 @@ static void enterBlockScope(CodeGenFunction &CGF, BlockDecl *block) { } /// Enter a full-expression with a non-trivial number of objects to -/// clean up. This is in this file because, at the moment, the only -/// kind of cleanup object is a BlockDecl*. +/// clean up. void CodeGenFunction::enterNonTrivialFullExpression(const FullExpr *E) { if (const auto EWC = dyn_cast(E)) { assert(EWC->getNumObjects() != 0); for (const ExprWithCleanups::CleanupObject &C : EWC->getObjects()) - enterBlockScope(*this, C); + if (auto *BD = C.dyn_cast()) + enterBlockScope(*this, BD); } } @@ -967,7 +975,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; @@ -1010,11 +1018,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, @@ -1022,7 +1045,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. @@ -1262,6 +1286,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. @@ -1279,7 +1305,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 { @@ -1287,7 +1313,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); @@ -1310,7 +1336,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); @@ -1412,14 +1445,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)); @@ -1703,6 +1747,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) @@ -1723,6 +1771,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()) @@ -1848,6 +1900,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(); @@ -1963,6 +2022,7 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, } break; } + case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: case BlockCaptureEntityKind::None: break; } @@ -2033,11 +2093,13 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { FunctionDecl *FD = FunctionDecl::Create( C, C.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, FunctionTy, nullptr, SC_Static, false, false); - setBlockHelperAttributesVisibility(blockInfo.CapturesNonExternalType, Fn, FI, CGM); + // This is necessary to avoid inheriting the previous line number. + FD->setImplicit(); StartFunction(FD, ReturnTy, Fn, FI, args); - ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getBeginLoc()}; + auto AL = ApplyDebugLocation::CreateArtificial(*this); + llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); Address src = GetAddrOfLocalVar(&SrcDecl); @@ -2067,6 +2129,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. @@ -2228,10 +2298,12 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { setBlockHelperAttributesVisibility(blockInfo.CapturesNonExternalType, Fn, FI, CGM); + // This is necessary to avoid inheriting the previous line number. + FD->setImplicit(); StartFunction(FD, ReturnTy, Fn, FI, args); markAsIgnoreThreadCheckingAtRuntime(Fn); - ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getBeginLoc()}; + auto AL = ApplyDebugLocation::CreateArtificial(*this); llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); @@ -2410,6 +2482,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 { @@ -2629,6 +2728,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 || @@ -2827,8 +2931,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++; @@ -2911,9 +3023,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 4b89b1b83a6a0..9e19f92d1bd98 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1251,6 +1251,8 @@ llvm::Function *CodeGenFunction::generateBuiltinOSLogHelperFunction( FunctionDecl *FD = FunctionDecl::Create( Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, FuncionTy, nullptr, SC_PrivateExtern, false, false); + // Avoid generating debug location info for the function. + FD->setImplicit(); StartFunction(FD, ReturnTy, Fn, FI, Args); @@ -1320,14 +1322,31 @@ RValue CodeGenFunction::emitBuiltinOSLogFormat(const CallExpr &E) { } else if (const Expr *TheExpr = Item.getExpr()) { ArgVal = EmitScalarExpr(TheExpr, /*Ignore*/ false); - // Check if this is a retainable type. - if (TheExpr->getType()->isObjCRetainableType()) { + // If this is a retainable type, push a lifetime-extended cleanup to + // ensure the lifetime of the argument is extended to the end of the + // enclosing block scope. + // FIXME: We only have to do this if the argument is a temporary, which + // gets released after the full expression. + if (TheExpr->getType()->isObjCRetainableType() && + getLangOpts().ObjCAutoRefCount) { assert(getEvaluationKind(TheExpr->getType()) == TEK_Scalar && "Only scalar can be a ObjC retainable type"); - // Check if the object is constant, if not, save it in - // RetainableOperands. - if (!isa(ArgVal)) - RetainableOperands.push_back(ArgVal); + if (!isa(ArgVal)) { + CleanupKind Cleanup = getARCCleanupKind(); + QualType Ty = TheExpr->getType(); + Address Alloca = Address::invalid(); + Address Addr = CreateMemTemp(Ty, "os.log.arg", &Alloca); + ArgVal = EmitARCRetain(Ty, ArgVal); + Builder.CreateStore(ArgVal, Addr); + pushLifetimeExtendedDestroy(Cleanup, Alloca, Ty, + CodeGenFunction::destroyARCStrongPrecise, + Cleanup & EHCleanup); + + // Push a clang.arc.use call to ensure ARC optimizer knows that the + // argument has to be alive. + if (CGM.getCodeGenOpts().OptimizationLevel != 0) + pushCleanupAfterFullExpr(Cleanup, ArgVal); + } } } else { ArgVal = Builder.getInt32(Item.getConstValue().getQuantity()); @@ -1349,18 +1368,6 @@ RValue CodeGenFunction::emitBuiltinOSLogFormat(const CallExpr &E) { llvm::Function *F = CodeGenFunction(CGM).generateBuiltinOSLogHelperFunction( Layout, BufAddr.getAlignment()); EmitCall(FI, CGCallee::forDirect(F), ReturnValueSlot(), Args); - - // Push a clang.arc.use cleanup for each object in RetainableOperands. The - // cleanup will cause the use to appear after the final log call, keeping - // the object valid while it’s held in the log buffer. Note that if there’s - // a release cleanup on the object, it will already be active; since - // cleanups are emitted in reverse order, the use will occur before the - // object is released. - if (!RetainableOperands.empty() && getLangOpts().ObjCAutoRefCount && - CGM.getCodeGenOpts().OptimizationLevel != 0) - for (llvm::Value *Object : RetainableOperands) - pushFullExprCleanup(getARCCleanupKind(), Object); - return RValue::get(BufAddr.getPointer()); } @@ -3599,6 +3606,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()); @@ -4226,7 +4305,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 1928e0df38090..d546c63610379 100644 --- a/clang/lib/CodeGen/CGCXX.cpp +++ b/clang/lib/CodeGen/CGCXX.cpp @@ -265,7 +265,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/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp index 7ada4032b3eef..928fbaea82787 100644 --- a/clang/lib/CodeGen/CGCXXABI.cpp +++ b/clang/lib/CodeGen/CGCXXABI.cpp @@ -313,3 +313,20 @@ CatchTypeInfo CGCXXABI::getCatchAllTypeInfo() { std::vector CGCXXABI::getVBPtrOffsets(const CXXRecordDecl *RD) { return std::vector(); } + +CGCXXABI::AddedStructorArgCounts CGCXXABI::addImplicitConstructorArgs( + CodeGenFunction &CGF, const CXXConstructorDecl *D, CXXCtorType Type, + bool ForVirtualBase, bool Delegating, CallArgList &Args) { + AddedStructorArgs AddedArgs = + getImplicitConstructorArgs(CGF, D, Type, ForVirtualBase, Delegating); + for (size_t i = 0; i < AddedArgs.Prefix.size(); ++i) { + Args.insert(Args.begin() + 1 + i, + CallArg(RValue::get(AddedArgs.Prefix[i].Value), + AddedArgs.Prefix[i].Type)); + } + for (const auto &arg : AddedArgs.Suffix) { + Args.add(RValue::get(arg.Value), arg.Type); + } + return AddedStructorArgCounts(AddedArgs.Prefix.size(), + AddedArgs.Suffix.size()); +} diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h index bff49be7a3c45..83aaf50d4abba 100644 --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -16,6 +16,7 @@ #include "CodeGenFunction.h" #include "clang/Basic/LLVM.h" +#include "clang/CodeGen/CodeGenABITypes.h" namespace llvm { class Constant; @@ -287,24 +288,44 @@ class CGCXXABI { /// Emit constructor variants required by this ABI. virtual void EmitCXXConstructors(const CXXConstructorDecl *D) = 0; - /// Notes how many arguments were added to the beginning (Prefix) and ending - /// (Suffix) of an arg list. + /// Additional implicit arguments to add to the beginning (Prefix) and end + /// (Suffix) of a constructor / destructor arg list. /// - /// Note that Prefix actually refers to the number of args *after* the first - /// one: `this` arguments always come first. + /// Note that Prefix should actually be inserted *after* the first existing + /// arg; `this` arguments always come first. struct AddedStructorArgs { + struct Arg { + llvm::Value *Value; + QualType Type; + }; + SmallVector Prefix; + SmallVector Suffix; + AddedStructorArgs() = default; + AddedStructorArgs(SmallVector P, SmallVector S) + : Prefix(std::move(P)), Suffix(std::move(S)) {} + static AddedStructorArgs prefix(SmallVector Args) { + return {std::move(Args), {}}; + } + static AddedStructorArgs suffix(SmallVector Args) { + return {{}, std::move(Args)}; + } + }; + + /// Similar to AddedStructorArgs, but only notes the number of additional + /// arguments. + struct AddedStructorArgCounts { unsigned Prefix = 0; unsigned Suffix = 0; - AddedStructorArgs() = default; - AddedStructorArgs(unsigned P, unsigned S) : Prefix(P), Suffix(S) {} - static AddedStructorArgs prefix(unsigned N) { return {N, 0}; } - static AddedStructorArgs suffix(unsigned N) { return {0, N}; } + AddedStructorArgCounts() = default; + AddedStructorArgCounts(unsigned P, unsigned S) : Prefix(P), Suffix(S) {} + static AddedStructorArgCounts prefix(unsigned N) { return {N, 0}; } + static AddedStructorArgCounts suffix(unsigned N) { return {0, N}; } }; /// Build the signature of the given constructor or destructor variant by /// adding any required parameters. For convenience, ArgTys has been /// initialized with the type of 'this'. - virtual AddedStructorArgs + virtual AddedStructorArgCounts buildStructorSignature(GlobalDecl GD, SmallVectorImpl &ArgTys) = 0; @@ -365,14 +386,26 @@ class CGCXXABI { /// Emit the ABI-specific prolog for the function. virtual void EmitInstanceFunctionProlog(CodeGenFunction &CGF) = 0; + virtual AddedStructorArgs + getImplicitConstructorArgs(CodeGenFunction &CGF, const CXXConstructorDecl *D, + CXXCtorType Type, bool ForVirtualBase, + bool Delegating) = 0; + /// Add any ABI-specific implicit arguments needed to call a constructor. /// /// \return The number of arguments added at the beginning and end of the /// call, which is typically zero or one. - virtual AddedStructorArgs + AddedStructorArgCounts addImplicitConstructorArgs(CodeGenFunction &CGF, const CXXConstructorDecl *D, CXXCtorType Type, bool ForVirtualBase, - bool Delegating, CallArgList &Args) = 0; + bool Delegating, CallArgList &Args); + + /// Get the implicit (second) parameter that comes after the "this" pointer, + /// or nullptr if there is isn't one. + virtual llvm::Value * + getCXXDestructorImplicitParam(CodeGenFunction &CGF, + const CXXDestructorDecl *DD, CXXDtorType Type, + bool ForVirtualBase, bool Delegating) = 0; /// Emit the destructor call. virtual void EmitDestructorCall(CodeGenFunction &CGF, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index b49b194d61122..baa28bda35552 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -325,7 +325,7 @@ CodeGenTypes::arrangeCXXStructorDeclaration(GlobalDecl GD) { if (PassParams) appendParameterTypes(*this, argTypes, paramInfos, FTP); - CGCXXABI::AddedStructorArgs AddedArgs = + CGCXXABI::AddedStructorArgCounts AddedArgs = TheCXXABI.buildStructorSignature(GD, argTypes); if (!paramInfos.empty()) { // Note: prefix implies after the first param. @@ -1702,8 +1702,9 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx, FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); } -void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone, - bool AttrOnCallSite, +void CodeGenModule::getDefaultFunctionAttributes(StringRef Name, + bool HasOptnone, + bool AttrOnCallSite, llvm::AttrBuilder &FuncAttrs) { // OptimizeNoneAttr takes precedence over -Os or -Oz. No warning needed. if (!HasOptnone) { @@ -1796,6 +1797,16 @@ void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone, FuncAttrs.addAttribute("stackrealign"); if (CodeGenOpts.Backchain) FuncAttrs.addAttribute("backchain"); + if (CodeGenOpts.EnableSegmentedStacks) + FuncAttrs.addAttribute("split-stack"); + 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); @@ -1826,31 +1837,98 @@ void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone, } } -void CodeGenModule::AddDefaultFnAttrs(llvm::Function &F) { +void CodeGenModule::addDefaultFunctionDefinitionAttributes(llvm::Function &F) { llvm::AttrBuilder FuncAttrs; - ConstructDefaultFnAttrList(F.getName(), F.hasOptNone(), - /* AttrOnCallSite = */ false, FuncAttrs); + getDefaultFunctionAttributes(F.getName(), F.hasOptNone(), + /* AttrOnCallSite = */ false, FuncAttrs); + // TODO: call GetCPUAndFeaturesAttributes? F.addAttributes(llvm::AttributeList::FunctionIndex, FuncAttrs); } +void CodeGenModule::addDefaultFunctionDefinitionAttributes( + llvm::AttrBuilder &attrs) { + getDefaultFunctionAttributes(/*function name*/ "", /*optnone*/ false, + /*for call*/ false, attrs); + GetCPUAndFeaturesAttributes(GlobalDecl(), attrs); +} + +static void addNoBuiltinAttributes(llvm::AttrBuilder &FuncAttrs, + const LangOptions &LangOpts, + const NoBuiltinAttr *NBA = nullptr) { + auto AddNoBuiltinAttr = [&FuncAttrs](StringRef BuiltinName) { + SmallString<32> AttributeName; + AttributeName += "no-builtin-"; + AttributeName += BuiltinName; + FuncAttrs.addAttribute(AttributeName); + }; + + // First, handle the language options passed through -fno-builtin[-] + if (LangOpts.NoBuiltin) { + // -fno-builtin disables them all. + FuncAttrs.addAttribute("no-builtins"); + return; + } + + // Then, add attributes for builtins specified through -fno-builtin-. + llvm::for_each(LangOpts.NoBuiltinFuncs, AddNoBuiltinAttr); + + // Now, let's check the __attribute__((no_builtin("...")) attribute added to + // the source. + if (!NBA) + return; + + // If there is a wildcard in the builtin names specified through the + // attribute, disable them all. + if (llvm::is_contained(NBA->builtinNames(), "*")) { + FuncAttrs.addAttribute("no-builtins"); + return; + } + + // And last, add the rest of the builtin names. + llvm::for_each(NBA->builtinNames(), AddNoBuiltinAttr); +} + +/// Construct the IR attribute list of a function or call. +/// +/// When adding an attribute, please consider where it should be handled: +/// +/// - getDefaultFunctionAttributes is for attributes that are essentially +/// part of the global target configuration (but perhaps can be +/// overridden on a per-function basis). Adding attributes there +/// will cause them to also be set in frontends that build on Clang's +/// target-configuration logic, as well as for code defined in library +/// modules such as CUDA's libdevice. +/// +/// - ConstructAttributeList builds on top of getDefaultFunctionAttributes +/// and adds declaration-specific, convention-specific, and +/// frontend-specific logic. The last is of particular importance: +/// attributes that restrict how the frontend generates code must be +/// added here rather than getDefaultFunctionAttributes. +/// void CodeGenModule::ConstructAttributeList( StringRef Name, const CGFunctionInfo &FI, CGCalleeInfo CalleeInfo, llvm::AttributeList &AttrList, unsigned &CallingConv, bool AttrOnCallSite) { llvm::AttrBuilder FuncAttrs; llvm::AttrBuilder RetAttrs; + // Collect function IR attributes from the CC lowering. + // We'll collect the paramete and result attributes later. CallingConv = FI.getEffectiveCallingConvention(); if (FI.isNoReturn()) FuncAttrs.addAttribute(llvm::Attribute::NoReturn); - // If we have information about the function prototype, we can learn - // attributes from there. + // Collect function IR attributes from the callee prototype if we have one. AddAttributesFromFunctionProtoType(getContext(), FuncAttrs, CalleeInfo.getCalleeFunctionProtoType()); const Decl *TargetDecl = CalleeInfo.getCalleeDecl().getDecl(); bool HasOptnone = false; + // The NoBuiltinAttr attached to the target FunctionDecl. + const NoBuiltinAttr *NBA = nullptr; + + // Collect function IR attributes based on declaration-specific + // information. // FIXME: handle sseregparm someday... if (TargetDecl) { if (TargetDecl->hasAttr()) @@ -1876,22 +1954,7 @@ void CodeGenModule::ConstructAttributeList( if (!(AttrOnCallSite && IsVirtualCall)) { if (Fn->isNoReturn()) FuncAttrs.addAttribute(llvm::Attribute::NoReturn); - - const auto *NBA = Fn->getAttr(); - bool HasWildcard = NBA && llvm::is_contained(NBA->builtinNames(), "*"); - if (getLangOpts().NoBuiltin || HasWildcard) - FuncAttrs.addAttribute("no-builtins"); - else { - auto AddNoBuiltinAttr = [&FuncAttrs](StringRef BuiltinName) { - SmallString<32> AttributeName; - AttributeName += "no-builtin-"; - AttributeName += BuiltinName; - FuncAttrs.addAttribute(AttributeName); - }; - llvm::for_each(getLangOpts().NoBuiltinFuncs, AddNoBuiltinAttr); - if (NBA) - llvm::for_each(NBA->builtinNames(), AddNoBuiltinAttr); - } + NBA = Fn->getAttr(); } } @@ -1924,70 +1987,90 @@ void CodeGenModule::ConstructAttributeList( FuncAttrs.addAllocSizeAttr(AllocSize->getElemSizeParam().getLLVMIndex(), NumElemsParam); } + + if (TargetDecl->hasAttr()) { + if (getLangOpts().OpenCLVersion <= 120) { + // OpenCL v1.2 Work groups are always uniform + FuncAttrs.addAttribute("uniform-work-group-size", "true"); + } else { + // OpenCL v2.0 Work groups may be whether uniform or not. + // '-cl-uniform-work-group-size' compile option gets a hint + // to the compiler that the global work-size be a multiple of + // the work-group size specified to clEnqueueNDRangeKernel + // (i.e. work groups are uniform). + FuncAttrs.addAttribute("uniform-work-group-size", + llvm::toStringRef(CodeGenOpts.UniformWGSize)); + } + } } - ConstructDefaultFnAttrList(Name, HasOptnone, AttrOnCallSite, FuncAttrs); + // Attach "no-builtins" attributes to: + // * call sites: both `nobuiltin` and "no-builtins" or "no-builtin-". + // * definitions: "no-builtins" or "no-builtin-" only. + // The attributes can come from: + // * LangOpts: -ffreestanding, -fno-builtin, -fno-builtin- + // * FunctionDecl attributes: __attribute__((no_builtin(...))) + addNoBuiltinAttributes(FuncAttrs, getLangOpts(), NBA); - // This must run after constructing the default function attribute list - // to ensure that the speculative load hardening attribute is removed - // in the case where the -mspeculative-load-hardening flag was passed. + // Collect function IR attributes based on global settiings. + getDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite, FuncAttrs); + + // Override some default IR attributes based on declaration-specific + // information. if (TargetDecl) { if (TargetDecl->hasAttr()) FuncAttrs.removeAttribute(llvm::Attribute::SpeculativeLoadHardening); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening); - } - - if (CodeGenOpts.EnableSegmentedStacks && - !(TargetDecl && TargetDecl->hasAttr())) - FuncAttrs.addAttribute("split-stack"); - - // Add NonLazyBind attribute to function declarations when -fno-plt - // is used. - if (TargetDecl && CodeGenOpts.NoPLT) { - if (auto *Fn = dyn_cast(TargetDecl)) { - if (!Fn->isDefined() && !AttrOnCallSite) { - FuncAttrs.addAttribute(llvm::Attribute::NonLazyBind); + if (TargetDecl->hasAttr()) + FuncAttrs.removeAttribute("split-stack"); + + // Add NonLazyBind attribute to function declarations when -fno-plt + // is used. + // FIXME: what if we just haven't processed the function definition + // yet, or if it's an external definition like C99 inline? + if (CodeGenOpts.NoPLT) { + if (auto *Fn = dyn_cast(TargetDecl)) { + if (!Fn->isDefined() && !AttrOnCallSite) { + FuncAttrs.addAttribute(llvm::Attribute::NonLazyBind); + } } } } - if (TargetDecl && TargetDecl->hasAttr()) { - if (getLangOpts().OpenCLVersion <= 120) { - // OpenCL v1.2 Work groups are always uniform - FuncAttrs.addAttribute("uniform-work-group-size", "true"); - } else { - // OpenCL v2.0 Work groups may be whether uniform or not. - // '-cl-uniform-work-group-size' compile option gets a hint - // to the compiler that the global work-size be a multiple of - // the work-group size specified to clEnqueueNDRangeKernel - // (i.e. work groups are uniform). - FuncAttrs.addAttribute("uniform-work-group-size", - llvm::toStringRef(CodeGenOpts.UniformWGSize)); - } - } - + // Collect non-call-site function IR attributes from declaration-specific + // information. if (!AttrOnCallSite) { - bool DisableTailCalls = false; + // Whether tail calls are enabled. + auto shouldDisableTailCalls = [&] { + // Should this be honored in getDefaultFunctionAttributes? + if (CodeGenOpts.DisableTailCalls) + return true; + + if (!TargetDecl) + return false; - if (CodeGenOpts.DisableTailCalls) - DisableTailCalls = true; - else if (TargetDecl) { if (TargetDecl->hasAttr() || TargetDecl->hasAttr()) - DisableTailCalls = true; - else if (CodeGenOpts.NoEscapingBlockTailCalls) { + return true; + + if (CodeGenOpts.NoEscapingBlockTailCalls) { if (const auto *BD = dyn_cast(TargetDecl)) if (!BD->doesNotEscape()) - DisableTailCalls = true; + return true; } - } + return false; + }; FuncAttrs.addAttribute("disable-tail-calls", - llvm::toStringRef(DisableTailCalls)); + llvm::toStringRef(shouldDisableTailCalls())); + + // CPU/feature overrides. addDefaultFunctionDefinitionAttributes + // handles these separately to set them based on the global defaults. GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs); } + // Collect attributes from arguments and return values. ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI); QualType RetTy = FI.getReturnType(); @@ -2024,8 +2107,8 @@ void CodeGenModule::ConstructAttributeList( if (const auto *RefTy = RetTy->getAs()) { QualType PTy = RefTy->getPointeeType(); if (!PTy->isIncompleteType() && PTy->isConstantSizeType()) - RetAttrs.addDereferenceableAttr(getContext().getTypeSizeInChars(PTy) - .getQuantity()); + RetAttrs.addDereferenceableAttr( + getMinimumObjectSize(PTy).getQuantity()); else if (getContext().getTargetAddressSpace(PTy) == 0 && !CodeGenOpts.NullPointerIsValid) RetAttrs.addAttribute(llvm::Attribute::NonNull); @@ -2041,6 +2124,7 @@ void CodeGenModule::ConstructAttributeList( hasUsedSRet = true; if (RetAI.getInReg()) SRETAttrs.addAttribute(llvm::Attribute::InReg); + SRETAttrs.addAlignmentAttr(RetAI.getIndirectAlign().getQuantity()); ArgAttrs[IRFunctionArgs.getSRetArgNo()] = llvm::AttributeSet::get(getLLVMContext(), SRETAttrs); } @@ -2134,8 +2218,8 @@ void CodeGenModule::ConstructAttributeList( if (const auto *RefTy = ParamType->getAs()) { QualType PTy = RefTy->getPointeeType(); if (!PTy->isIncompleteType() && PTy->isConstantSizeType()) - Attrs.addDereferenceableAttr(getContext().getTypeSizeInChars(PTy) - .getQuantity()); + Attrs.addDereferenceableAttr( + getMinimumObjectSize(PTy).getQuantity()); else if (getContext().getTargetAddressSpace(PTy) == 0 && !CodeGenOpts.NullPointerIsValid) Attrs.addAttribute(llvm::Attribute::NonNull); @@ -3006,6 +3090,11 @@ void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV) { if (!CurCodeDecl) return; + // If the return block isn't reachable, neither is this check, so don't emit + // it. + if (ReturnBlock.isValid() && ReturnBlock.getBlock()->use_empty()) + return; + ReturnsNonNullAttr *RetNNAttr = nullptr; if (SanOpts.has(SanitizerKind::ReturnsNonnullAttribute)) RetNNAttr = CurCodeDecl->getAttr(); @@ -3026,7 +3115,7 @@ void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV) { } else { if (auto *DD = dyn_cast(CurCodeDecl)) if (auto *TSI = DD->getTypeSourceInfo()) - if (auto FTL = TSI->getTypeLoc().castAs()) + if (auto FTL = TSI->getTypeLoc().getAsAdjusted()) AttrLoc = FTL.getReturnLoc().findNullabilityLoc(); CheckKind = SanitizerKind::NullabilityReturn; Handler = SanitizerHandler::NullabilityReturn; @@ -3648,7 +3737,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); @@ -3816,7 +3906,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()); @@ -3891,7 +3982,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); @@ -4395,6 +4489,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, SmallVector BundleList = getBundlesForFunclet(CalleePtr); + // Add the pointer-authentication bundle. + EmitPointerAuthOperandBundle(ConcreteCallee.getPointerAuthInfo(), BundleList); + if (const FunctionDecl *FD = dyn_cast_or_null(CurFuncDecl)) if (FD->usesFPIntrin()) // All calls within a strictfp function are marked strictfp @@ -4512,7 +4609,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(); @@ -4632,6 +4736,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall) LifetimeEnd.Emit(*this, /*Flags=*/{}); + if (!ReturnValue.isExternallyDestructed() && + RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct) + pushDestroy(QualType::DK_nontrivial_c_struct, Ret.getAggregateAddress(), + RetTy); + return Ret; } diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index 34558be5adb1b..ba3a6e4c0a674 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; @@ -357,27 +401,26 @@ class FunctionArgList : public SmallVector {}; /// ReturnValueSlot - Contains the address where the return value of a /// function can be stored, and whether the address is volatile or not. class ReturnValueSlot { - llvm::PointerIntPair Value; - CharUnits Alignment; + Address Addr = Address::invalid(); // Return value slot flags - enum Flags { - IS_VOLATILE = 0x1, - IS_UNUSED = 0x2, - }; + unsigned IsVolatile : 1; + unsigned IsUnused : 1; + unsigned IsExternallyDestructed : 1; public: - ReturnValueSlot() {} - ReturnValueSlot(Address Addr, bool IsVolatile, bool IsUnused = false) - : Value(Addr.isValid() ? Addr.getPointer() : nullptr, - (IsVolatile ? IS_VOLATILE : 0) | (IsUnused ? IS_UNUSED : 0)), - Alignment(Addr.isValid() ? Addr.getAlignment() : CharUnits::Zero()) {} - - bool isNull() const { return !getValue().isValid(); } - - bool isVolatile() const { return Value.getInt() & IS_VOLATILE; } - Address getValue() const { return Address(Value.getPointer(), Alignment); } - bool isUnused() const { return Value.getInt() & IS_UNUSED; } + ReturnValueSlot() + : IsVolatile(false), IsUnused(false), IsExternallyDestructed(false) {} + ReturnValueSlot(Address Addr, bool IsVolatile, bool IsUnused = false, + bool IsExternallyDestructed = false) + : Addr(Addr), IsVolatile(IsVolatile), IsUnused(IsUnused), + IsExternallyDestructed(IsExternallyDestructed) {} + + bool isNull() const { return !Addr.isValid(); } + bool isVolatile() const { return IsVolatile; } + Address getValue() const { return Addr; } + bool isUnused() const { return IsUnused; } + bool isExternallyDestructed() const { return IsExternallyDestructed; } }; } // end namespace CodeGen diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 3f3825b762759..911b0fe457c16 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -35,20 +35,37 @@ using namespace CodeGen; /// Return the best known alignment for an unknown pointer to a /// particular class. CharUnits CodeGenModule::getClassPointerAlignment(const CXXRecordDecl *RD) { - if (!RD->isCompleteDefinition()) + if (!RD->hasDefinition()) return CharUnits::One(); // Hopefully won't be used anywhere. auto &layout = getContext().getASTRecordLayout(RD); // If the class is final, then we know that the pointer points to an // object of that type and can use the full alignment. - if (RD->hasAttr()) { + if (RD->isEffectivelyFinal()) return layout.getAlignment(); // Otherwise, we have to assume it could be a subclass. - } else { - return layout.getNonVirtualAlignment(); - } + return layout.getNonVirtualAlignment(); +} + +/// Return the smallest possible amount of storage that might be allocated +/// starting from the beginning of an object of a particular class. +/// +/// This may be smaller than sizeof(RD) if RD has virtual base classes. +CharUnits CodeGenModule::getMinimumClassObjectSize(const CXXRecordDecl *RD) { + if (!RD->hasDefinition()) + return CharUnits::One(); + + auto &layout = getContext().getASTRecordLayout(RD); + + // If the class is final, then we know that the pointer points to an + // object of that type and can use the full alignment. + if (RD->isEffectivelyFinal()) + return layout.getSize(); + + // Otherwise, we have to assume it could be a subclass. + return std::max(layout.getNonVirtualSize(), CharUnits::One()); } /// Return the best known alignment for a pointer to a virtual base, @@ -907,6 +924,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; } @@ -2148,7 +2168,7 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, } // Insert any ABI-specific implicit constructor arguments. - CGCXXABI::AddedStructorArgs ExtraArgs = + CGCXXABI::AddedStructorArgCounts ExtraArgs = CGM.getCXXABI().addImplicitConstructorArgs(*this, D, Type, ForVirtualBase, Delegating, Args); @@ -2494,6 +2514,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 +2620,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); @@ -2787,16 +2821,11 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD, bool CodeGenFunction::ShouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *RD) { if (!CGM.getCodeGenOpts().WholeProgramVTables || + !SanOpts.has(SanitizerKind::CFIVCall) || + !CGM.getCodeGenOpts().SanitizeTrap.has(SanitizerKind::CFIVCall) || !CGM.HasHiddenLTOVisibility(RD)) return false; - if (CGM.getCodeGenOpts().VirtualFunctionElimination) - return true; - - if (!SanOpts.has(SanitizerKind::CFIVCall) || - !CGM.getCodeGenOpts().SanitizeTrap.has(SanitizerKind::CFIVCall)) - return false; - std::string TypeName = RD->getQualifiedNameAsString(); return !getContext().getSanitizerBlacklist().isBlacklistedType( SanitizerKind::CFIVCall, TypeName); @@ -2819,13 +2848,8 @@ llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad( TypeId}); llvm::Value *CheckResult = Builder.CreateExtractValue(CheckedLoad, 1); - std::string TypeName = RD->getQualifiedNameAsString(); - if (SanOpts.has(SanitizerKind::CFIVCall) && - !getContext().getSanitizerBlacklist().isBlacklistedType( - SanitizerKind::CFIVCall, TypeName)) { - EmitCheck(std::make_pair(CheckResult, SanitizerKind::CFIVCall), - SanitizerHandler::CFICheckFail, {}, {}); - } + EmitCheck(std::make_pair(CheckResult, SanitizerKind::CFIVCall), + SanitizerHandler::CFICheckFail, nullptr, nullptr); return Builder.CreateBitCast( Builder.CreateExtractValue(CheckedLoad, 0), @@ -2850,7 +2874,9 @@ void CodeGenFunction::EmitForwardingCallToLambda( if (!resultType->isVoidType() && calleeFnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect && !hasScalarEvaluationKind(calleeFnInfo.getReturnType())) - returnSlot = ReturnValueSlot(ReturnValue, resultType.isVolatileQualified()); + returnSlot = + ReturnValueSlot(ReturnValue, resultType.isVolatileQualified(), + /*IsUnused=*/false, /*IsExternallyDestructed=*/true); // We don't need to separately arrange the call arguments because // the call can't be variadic anyway --- it's impossible to forward diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 675df309e3f0f..a965528e471dd 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -609,6 +609,16 @@ void CGDebugInfo::CreateCompileUnit() { remapDIPath(MainFileName), remapDIPath(getCurrentDirname()), CSInfo, getSource(SM, SM.getMainFileID())); + StringRef Sysroot, SDK; + if (CGM.getCodeGenOpts().getDebuggerTuning() == llvm::DebuggerKind::LLDB) { + Sysroot = CGM.getHeaderSearchOpts().Sysroot; + auto B = llvm::sys::path::rbegin(Sysroot); + auto E = llvm::sys::path::rend(Sysroot); + auto It = std::find_if(B, E, [](auto SDK) { return SDK.endswith(".sdk"); }); + if (It != E) + SDK = *It; + } + // Create new compile unit. TheCU = DBuilder.createCompileUnit( LangTag, CUFile, CGOpts.EmitVersionIdentMetadata ? Producer : "", @@ -619,7 +629,7 @@ void CGDebugInfo::CreateCompileUnit() { ? llvm::DICompileUnit::DebugNameTableKind::None : static_cast( CGOpts.DebugNameTable), - CGOpts.DebugRangesBaseAddress); + CGOpts.DebugRangesBaseAddress, remapDIPath(Sysroot), SDK); } llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { @@ -842,6 +852,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); @@ -867,8 +886,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); } @@ -990,10 +1009,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. @@ -2428,6 +2446,17 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod, assert(StringRef(M->Name).startswith(CGM.getLangOpts().ModuleName) && "clang module without ASTFile must be specified by -fmodule-name"); + // Return a StringRef to the remapped Path. + auto RemapPath = [this](StringRef Path) -> std::string { + std::string Remapped = remapDIPath(Path); + StringRef Relative(Remapped); + StringRef CompDir = TheCU->getDirectory(); + if (Relative.consume_front(CompDir)) + Relative.consume_front(llvm::sys::path::get_separator()); + + return Relative.str(); + }; + if (CreateSkeletonCU && IsRootModule && !Mod.getASTFile().empty()) { // PCH files don't have a signature field in the control block, // but LLVM detects skeleton CUs by looking for a non-zero DWO id. @@ -2437,12 +2466,16 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod, ? (uint64_t)Mod.getSignature()[1] << 32 | Mod.getSignature()[0] : ~1ULL; llvm::DIBuilder DIB(CGM.getModule()); - DIB.createCompileUnit(TheCU->getSourceLanguage(), - // TODO: Support "Source" from external AST providers? - DIB.createFile(Mod.getModuleName(), Mod.getPath()), - TheCU->getProducer(), true, StringRef(), 0, - Mod.getASTFile(), llvm::DICompileUnit::FullDebug, - Signature); + SmallString<0> PCM; + if (!llvm::sys::path::is_absolute(Mod.getASTFile())) + PCM = Mod.getPath(); + llvm::sys::path::append(PCM, Mod.getASTFile()); + DIB.createCompileUnit( + TheCU->getSourceLanguage(), + // TODO: Support "Source" from external AST providers? + DIB.createFile(Mod.getModuleName(), TheCU->getDirectory()), + TheCU->getProducer(), false, StringRef(), 0, RemapPath(PCM), + llvm::DICompileUnit::FullDebug, Signature); DIB.finalize(); } @@ -2451,9 +2484,10 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod, : getOrCreateModuleRef( ExternalASTSource::ASTSourceDescriptor(*M->Parent), CreateSkeletonCU); + std::string IncludePath = Mod.getPath().str(); llvm::DIModule *DIMod = DBuilder.createModule(Parent, Mod.getModuleName(), ConfigMacros, - Mod.getPath(), CGM.getHeaderSearchOpts().Sysroot); + RemapPath(IncludePath), M ? M->APINotesFile : ""); ModuleCache[M].reset(DIMod); return DIMod; } @@ -3633,8 +3667,11 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc, Name = getDynamicInitializerName(cast(D), GD.getDynamicInitKind(), Fn); } else { - // Use llvm function name. Name = Fn->getName(); + + if (const auto *BD = dyn_cast(D)) + LinkageName = Name; + Flags |= llvm::DINode::FlagPrototyped; } if (Name.startswith("\01")) @@ -4826,8 +4863,7 @@ llvm::DINode::DIFlags CGDebugInfo::getCallSiteRelatedAttrs() const { (CGM.getCodeGenOpts().getDebuggerTuning() == llvm::DebuggerKind::LLDB || 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/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 61fb8fa384cf3..b29519534ed94 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -743,7 +743,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(*this)); + 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 3baa0a080f5db..74aba90182edf 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -227,7 +227,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). @@ -256,7 +256,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 c1a0d5639d125..3914cd23ce573 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -711,7 +711,7 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, if (SanOpts.has(SanitizerKind::ObjectSize) && !SkippedChecks.has(SanitizerKind::ObjectSize) && !Ty->isIncompleteType()) { - uint64_t TySize = getContext().getTypeSizeInChars(Ty).getQuantity(); + uint64_t TySize = CGM.getMinimumObjectSize(Ty).getQuantity(); llvm::Value *Size = llvm::ConstantInt::get(IntPtrTy, TySize); if (ArraySize) Size = Builder.CreateMul(Size, ArraySize); @@ -742,7 +742,9 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, !SkippedChecks.has(SanitizerKind::Alignment)) { AlignVal = Alignment.getQuantity(); if (!Ty->isIncompleteType() && !AlignVal) - AlignVal = getContext().getTypeAlignInChars(Ty).getQuantity(); + AlignVal = + getNaturalTypeAlignment(Ty, nullptr, nullptr, /*ForPointeeType=*/true) + .getQuantity(); // The glvalue must be suitably aligned. if (AlignVal > 1 && @@ -858,8 +860,12 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, static bool isFlexibleArrayMemberExpr(const Expr *E) { // For compatibility with existing code, we treat arrays of length 0 or // 1 as flexible array members. + // FIXME: This is inconsistent with the warning code in SemaChecking. Unify + // the two mechanisms. const ArrayType *AT = E->getType()->castAsArrayTypeUnsafe(); if (const auto *CAT = dyn_cast(AT)) { + // FIXME: Sema doesn't treat [1] as a flexible array member if the bound + // was produced by macro expansion. if (CAT->getSize().ugt(1)) return false; } else if (!isa(AT)) @@ -872,6 +878,10 @@ static bool isFlexibleArrayMemberExpr(const Expr *E) { // FIXME: If the base type of the member expr is not FD->getParent(), // this should not be treated as a flexible array member access. if (const auto *FD = dyn_cast(ME->getMemberDecl())) { + // FIXME: Sema doesn't treat a T[1] union member as a flexible array + // member, only a T[0] or T[] member gets that treatment. + if (FD->getParent()->isUnion()) + return true; RecordDecl::field_iterator FI( DeclContext::decl_iterator(const_cast(FD))); return ++FI == FD->getParent()->field_end(); @@ -1475,8 +1485,11 @@ CodeGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) { return ConstantEmission(); // Emit as a constant. - auto C = ConstantEmitter(*this).emitAbstract(refExpr->getLocation(), - result.Val, resultType); + // Try to emit as a constant. + llvm::Constant *C = + ConstantEmitter(*this).tryEmitAbstract(result.Val, resultType); + if (!C) + return ConstantEmission(); // Make sure we emit a debug reference to the global variable. // This should probably fire even for @@ -1775,6 +1788,16 @@ 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(*this), + /*known nonnull*/ false)); + } + if (LV.isObjCWeak()) { // load of a __weak object. Address AddrWeakObj = LV.getAddress(*this); @@ -1953,6 +1976,14 @@ 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(*this), + /*known nonnull*/ false)); + } + // There's special magic for assigning into an ARC-qualified l-value. if (Qualifiers::ObjCLifetime Lifetime = Dst.getQuals().getObjCLifetime()) { switch (Lifetime) { @@ -2397,14 +2428,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()) { @@ -2412,10 +2443,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; @@ -2423,7 +2453,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); @@ -3934,7 +3964,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); @@ -4249,6 +4280,14 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){ EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(), /*Init*/ true); + // Block-scope compound literals are destroyed at the end of the enclosing + // scope in C. + if (!getLangOpts().CPlusPlus) + if (QualType::DestructionKind DtorKind = E->getType().isDestructedType()) + pushLifetimeExtendedDestroy(getCleanupKind(DtorKind), DeclPtr, + E->getType(), getDestroyer(DtorKind), + DtorKind & EHCleanup); + return Result; } @@ -4626,10 +4665,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(); @@ -4640,6 +4739,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())) { @@ -4658,6 +4778,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. @@ -4671,14 +4821,14 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { calleePtr = EmitLValue(E).getPointer(*this); } 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; } @@ -4701,6 +4851,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(*this)); + 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/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 8de609a2ccd98..df576decd69d6 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -249,7 +249,7 @@ void AggExprEmitter::withReturnValueSlot( const Expr *E, llvm::function_ref EmitCall) { QualType RetTy = E->getType(); bool RequiresDestruction = - Dest.isIgnored() && + !Dest.isExternallyDestructed() && RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct; // If it makes no observable difference, save a memcpy + temporary. @@ -287,10 +287,8 @@ void AggExprEmitter::withReturnValueSlot( } RValue Src = - EmitCall(ReturnValueSlot(RetAddr, Dest.isVolatile(), IsResultUnused)); - - if (RequiresDestruction) - CGF.pushDestroy(RetTy.isDestructedType(), Src.getAggregateAddress(), RetTy); + EmitCall(ReturnValueSlot(RetAddr, Dest.isVolatile(), IsResultUnused, + Dest.isExternallyDestructed())); if (!UseTemp) return; @@ -659,7 +657,21 @@ AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { } AggValueSlot Slot = EnsureSlot(E->getType()); + + // Block-scope compound literals are destroyed at the end of the enclosing + // scope in C. + bool Destruct = + !CGF.getLangOpts().CPlusPlus && !Slot.isExternallyDestructed(); + if (Destruct) + Slot.setExternallyDestructed(); + CGF.EmitAggExpr(E->getInitializer(), Slot); + + if (Destruct) + if (QualType::DestructionKind DtorKind = E->getType().isDestructedType()) + CGF.pushLifetimeExtendedDestroy( + CGF.getCleanupKind(DtorKind), Slot.getAddress(), E->getType(), + CGF.getDestroyer(DtorKind), DtorKind & EHCleanup); } /// Attempt to look through various unimportant expressions to find a @@ -813,8 +825,19 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { // If we're loading from a volatile type, force the destination // into existence. if (E->getSubExpr()->getType().isVolatileQualified()) { + bool Destruct = + !Dest.isExternallyDestructed() && + E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct; + if (Destruct) + Dest.setExternallyDestructed(); EnsureDest(E->getType()); - return Visit(E->getSubExpr()); + Visit(E->getSubExpr()); + + if (Destruct) + CGF.pushDestroy(QualType::DK_nontrivial_c_struct, Dest.getAddress(), + E->getType()); + + return; } LLVM_FALLTHROUGH; diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 46ed90a20264f..3377f7d9b0888 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1167,9 +1167,7 @@ class ConstExprEmitter : } llvm::Constant *VisitExprWithCleanups(ExprWithCleanups *E, QualType T) { - if (!E->cleanupsHaveSideEffects()) - return Visit(E->getSubExpr(), T); - return nullptr; + return Visit(E->getSubExpr(), T); } llvm::Constant *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E, @@ -1429,8 +1427,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); @@ -1488,7 +1515,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(); } @@ -1726,10 +1753,13 @@ namespace { struct ConstantLValue { llvm::Constant *Value; bool HasOffsetApplied; + bool HasDestPointerAuth; /*implicit*/ ConstantLValue(llvm::Constant *value, - bool hasOffsetApplied = false) - : Value(value), HasOffsetApplied(hasOffsetApplied) {} + bool hasOffsetApplied = false, + bool hasDestPointerAuth = false) + : Value(value), HasOffsetApplied(hasOffsetApplied), + HasDestPointerAuth(hasDestPointerAuth) {} /*implicit*/ ConstantLValue(ConstantAddress address) : ConstantLValue(address.getPointer()) {} @@ -1773,6 +1803,14 @@ class ConstantLValueEmitter : public ConstStmtVisitor + emitPointerAuthDiscriminator(const Expr *E); + llvm::Constant *tryEmitConstantSignedPointer(llvm::Constant *ptr, + PointerAuthQualifier auth); + bool hasNonZeroOffset() const { return !Value.getLValueOffset().isZero(); } @@ -1831,6 +1869,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)) @@ -1868,8 +1914,27 @@ 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)) { + llvm::Constant *C = CGM.getRawFunctionPointer(FD); + if (auto pointerAuth = DestType.getPointerAuth()) { + C = applyOffset(C); + C = tryEmitConstantSignedPointer(C, pointerAuth); + return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true); + } + + if (CGPointerAuthInfo AuthInfo = + CGM.getFunctionPointerAuthInfo(DestType)) { + if (hasNonZeroOffset()) + return ConstantLValue(nullptr); + C = CGM.getConstantSignedPointer( + C, AuthInfo.getKey(), + /*storageAddress=*/nullptr, + cast_or_null(AuthInfo.getDiscriminator())); + return ConstantLValue(C, /*AppliedOffset=*/true, /*Signed=*/true); + } + + return ConstantLValue(C); + } if (auto VD = dyn_cast(D)) { // We can never refer to a variable with local storage. @@ -1959,6 +2024,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; @@ -1972,6 +2040,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 4adaca8ae571f..91731c334a40d 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -533,7 +533,7 @@ class ScalarExprEmitter } Value *VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { - VersionTuple Version = E->getVersion(); + VersionTuple Version = E->getVersionAsWritten(); // If we're checking for a platform older than our minimum deployment // target, we can fold the check away. @@ -556,6 +556,11 @@ class ScalarExprEmitter Value *VisitMemberExpr(MemberExpr *E); Value *VisitExtVectorElementExpr(Expr *E) { return EmitLoadOfLValue(E); } Value *VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { + // Strictly speaking, we shouldn't be calling EmitLoadOfLValue, which + // transitively calls EmitCompoundLiteralLValue, here in C++ since compound + // literals aren't l-values in C++. We do so simply because that's the + // cleanest way to handle compound literals in C++. + // See the discussion here: https://reviews.llvm.org/D64464 return EmitLoadOfLValue(E); } @@ -1940,6 +1945,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(); @@ -3985,6 +4042,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)); + 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(CGF), /*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 d5f378c522322..61b8cad76d3ae 100644 --- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp +++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp @@ -254,6 +254,10 @@ struct GenBinaryFuncName : CopyStructVisitor, IsMove>, void visitVolatileTrivial(QualType FT, const FieldDecl *FD, CharUnits CurStructOffset) { + // Zero-length bit-fields don't need to be copied/assigned. + if (FD && FD->isZeroLengthBitField(this->Ctx)) + return; + // Because volatile fields can be bit-fields and are individually copied, // their offset and width are in bits. uint64_t OffsetInBits = @@ -261,6 +265,16 @@ 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"); + PointerAuthQualifier PtrAuth = FT.getPointerAuth(); + this->appendStr(llvm::to_string(PtrAuth.getKey()) + "_"); + this->appendStr(llvm::to_string(PtrAuth.getExtraDiscriminator()) + "_"); + CharUnits FieldOffset = CurStructOffset + this->getFieldOffset(FD); + this->appendStr(llvm::to_string(FieldOffset.getQuantity())); + } }; struct GenDefaultInitializeFuncName @@ -543,6 +557,10 @@ struct GenBinaryFunc : CopyStructVisitor, std::array Addrs) { LValue DstLV, SrcLV; if (FD) { + // No need to copy zero-length bit-fields. + if (FD->isZeroLengthBitField(this->CGF->getContext())) + return; + QualType RT = QualType(FD->getParent()->getTypeForDecl(), 0); llvm::PointerType *PtrTy = this->CGF->ConvertType(RT)->getPointerTo(); Address DstAddr = this->getAddrWithOffset(Addrs[DstIdx], Offset); @@ -563,6 +581,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 3c11aa7f2f42a..8b5a8cbc0c719 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -461,38 +461,39 @@ tryEmitSpecializedAllocInit(CodeGenFunction &CGF, const ObjCMessageExpr *OME) { Sel.getNameForSlot(0) != "init") return None; - // Okay, this is '[receiver init]', check if 'receiver' is '[cls alloc]' or - // we are in an ObjC class method and 'receiver' is '[self alloc]'. + // Okay, this is '[receiver init]', check if 'receiver' is '[cls alloc]' + // with 'cls' a Class. auto *SubOME = dyn_cast(OME->getInstanceReceiver()->IgnoreParenCasts()); if (!SubOME) return None; Selector SubSel = SubOME->getSelector(); - // Check if we are in an ObjC class method and the receiver expression is - // 'self'. - const Expr *SelfInClassMethod = nullptr; - if (const auto *CurMD = dyn_cast_or_null(CGF.CurFuncDecl)) - if (CurMD->isClassMethod()) - if ((SelfInClassMethod = SubOME->getInstanceReceiver())) - if (!SelfInClassMethod->isObjCSelfExpr()) - SelfInClassMethod = nullptr; - - if ((SubOME->getReceiverKind() != ObjCMessageExpr::Class && - !SelfInClassMethod) || !SubOME->getType()->isObjCObjectPointerType() || + if (!SubOME->getType()->isObjCObjectPointerType() || !SubSel.isUnarySelector() || SubSel.getNameForSlot(0) != "alloc") return None; - llvm::Value *Receiver; - if (SelfInClassMethod) { - Receiver = CGF.EmitScalarExpr(SelfInClassMethod); - } else { + llvm::Value *Receiver = nullptr; + switch (SubOME->getReceiverKind()) { + case ObjCMessageExpr::Instance: + if (!SubOME->getInstanceReceiver()->getType()->isObjCClassType()) + return None; + Receiver = CGF.EmitScalarExpr(SubOME->getInstanceReceiver()); + break; + + case ObjCMessageExpr::Class: { QualType ReceiverType = SubOME->getClassReceiver(); const ObjCObjectType *ObjTy = ReceiverType->getAs(); const ObjCInterfaceDecl *ID = ObjTy->getInterface(); assert(ID && "null interface should be impossible here"); Receiver = CGF.CGM.getObjCRuntime().GetClass(CGF, ID); + break; + } + case ObjCMessageExpr::SuperInstance: + case ObjCMessageExpr::SuperClass: + return None; } + return CGF.EmitObjCAllocInit(Receiver, CGF.ConvertType(OME->getType())); } @@ -540,10 +541,7 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, switch (E->getReceiverKind()) { case ObjCMessageExpr::Instance: ReceiverType = E->getInstanceReceiver()->getType(); - if (auto *OMD = dyn_cast_or_null(CurFuncDecl)) - if (OMD->isClassMethod()) - if (E->getInstanceReceiver()->isObjCSelfExpr()) - isClassMessage = true; + isClassMessage = ReceiverType->isObjCClassType(); if (retainSelf) { TryEmitResult ter = tryEmitARCRetainScalarExpr(*this, E->getInstanceReceiver()); @@ -3578,7 +3576,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; } @@ -3683,7 +3682,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 479cd8ec77cec..7caff873fab01 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -2603,7 +2603,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); @@ -2723,7 +2724,8 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF, imp = EnforceType(Builder, imp, MSI.MessengerType); llvm::CallBase *call; - CGCallee callee(CGCalleeInfo(), imp); + CGPointerAuthInfo pointerAuth; // TODO + CGCallee callee(CGCalleeInfo(), imp, pointerAuth); RValue msgRet = CGF.EmitCall(MSI.CallInfo, callee, Return, ActualArgs, &call); call->setMetadata(msgSendMDKind, node); diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index f36c28a85a68f..ea05effd4893b 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2991,7 +2991,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, @@ -3291,6 +3291,8 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, for (auto *PD : ClassExt->properties()) { if (IsClassProperty != PD->isClassProperty()) continue; + if (PD->isDirectProperty()) + continue; PropertySet.insert(PD->getIdentifier()); Properties.push_back(PD); } @@ -3302,6 +3304,8 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, // class extension. if (!PropertySet.insert(PD->getIdentifier()).second) continue; + if (PD->isDirectProperty()) + continue; Properties.push_back(PD); } @@ -3327,8 +3331,6 @@ 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)); @@ -4029,22 +4031,49 @@ llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD, llvm::Function * CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) { - auto I = DirectMethodDefinitions.find(OMD->getCanonicalDecl()); - if (I != DirectMethodDefinitions.end()) - return I->second; + auto *COMD = OMD->getCanonicalDecl(); + auto I = DirectMethodDefinitions.find(COMD); + llvm::Function *OldFn = nullptr, *Fn = nullptr; - SmallString<256> Name; - GetNameForMethod(OMD, CD, Name, /*ignoreCategoryNamespace*/true); + if (I != DirectMethodDefinitions.end()) { + // Objective-C allows for the declaration and implementation types + // to differ slightly. + // + // If we're being asked for the Function associated for a method + // implementation, a previous value might have been cached + // based on the type of the canonical declaration. + // + // If these do not match, then we'll replace this function with + // a new one that has the proper type below. + if (!OMD->getBody() || COMD->getReturnType() == OMD->getReturnType()) + return I->second; + OldFn = I->second; + } CodeGenTypes &Types = CGM.getTypes(); llvm::FunctionType *MethodTy = Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD)); - llvm::Function *Method = - llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage, - Name.str(), &CGM.getModule()); - DirectMethodDefinitions.insert(std::make_pair(OMD->getCanonicalDecl(), Method)); - return Method; + if (OldFn) { + Fn = llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage, + "", &CGM.getModule()); + Fn->takeName(OldFn); + OldFn->replaceAllUsesWith( + llvm::ConstantExpr::getBitCast(Fn, OldFn->getType())); + OldFn->eraseFromParent(); + + // Replace the cached function in the map. + I->second = Fn; + } else { + SmallString<256> Name; + GetNameForMethod(OMD, CD, Name, /*ignoreCategoryNamespace*/ true); + + Fn = llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage, + Name.str(), &CGM.getModule()); + DirectMethodDefinitions.insert(std::make_pair(COMD, Fn)); + } + + return Fn; } void CGObjCCommonMac::GenerateDirectMethodPrologue( @@ -6217,11 +6246,9 @@ void CGObjCNonFragileABIMac::AddModuleClassList( assert((!CGM.getTriple().isOSBinFormatMachO() || SectionName.startswith("__DATA")) && "SectionName expected to start with __DATA on MachO"); - llvm::GlobalValue::LinkageTypes LT = - getLinkageTypeForObjCMetadata(CGM, SectionName); - llvm::GlobalVariable *GV = - new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, LT, Init, - SymbolName); + llvm::GlobalVariable *GV = new llvm::GlobalVariable( + CGM.getModule(), Init->getType(), false, + llvm::GlobalValue::PrivateLinkage, Init, SymbolName); GV->setAlignment( llvm::Align(CGM.getDataLayout().getABITypeAlignment(Init->getType()))); GV->setSection(SectionName); @@ -6778,7 +6805,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); @@ -7048,9 +7082,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); @@ -7351,7 +7384,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, @@ -7417,7 +7451,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 * @@ -7509,10 +7548,9 @@ CGObjCNonFragileABIMac::EmitSuperClassRef(CodeGenFunction &CGF, llvm::Constant *ClassGV = GetClassGlobalForClassRef(ID); std::string SectionName = GetSectionName("__objc_superrefs", "regular,no_dead_strip"); - Entry = new llvm::GlobalVariable( - CGM.getModule(), ClassGV->getType(), false, - getLinkageTypeForObjCMetadata(CGM, SectionName), ClassGV, - "OBJC_CLASSLIST_SUP_REFS_$_"); + Entry = new llvm::GlobalVariable(CGM.getModule(), ClassGV->getType(), false, + llvm::GlobalValue::PrivateLinkage, ClassGV, + "OBJC_CLASSLIST_SUP_REFS_$_"); Entry->setAlignment(CGF.getPointerAlign().getAsAlign()); Entry->setSection(SectionName); CGM.addCompilerUsedGlobal(Entry); @@ -7533,10 +7571,9 @@ llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CodeGenFunction &CGF, auto MetaClassGV = GetClassGlobal(ID, /*metaclass*/ true, NotForDefinition); std::string SectionName = GetSectionName("__objc_superrefs", "regular,no_dead_strip"); - Entry = new llvm::GlobalVariable( - CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, - getLinkageTypeForObjCMetadata(CGM, SectionName), MetaClassGV, - "OBJC_CLASSLIST_SUP_REFS_$_"); + Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, + false, llvm::GlobalValue::PrivateLinkage, + MetaClassGV, "OBJC_CLASSLIST_SUP_REFS_$_"); Entry->setAlignment(Align.getAsAlign()); Entry->setSection(SectionName); CGM.addCompilerUsedGlobal(Entry); @@ -7550,7 +7587,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()); @@ -7853,11 +7891,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/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp new file mode 100644 index 0000000000000..e9ad8d06463ae --- /dev/null +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -0,0 +1,647 @@ +//===--- 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)); + + case PointerAuthSchema::Discrimination::Constant: + return llvm::ConstantInt::get(IntPtrTy, schema.getConstantDiscrimination()); + } + 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(CGF)); + } 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 c22504ce2b13e..2f725fb255cc0 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -364,7 +364,8 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, ReturnValueSlot Slot; if (!ResultType->isVoidType() && CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect) - Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified()); + Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified(), + /*IsUnused=*/false, /*IsExternallyDestructed=*/true); // Now emit our call. llvm::CallBase *CallOrInvoke; @@ -437,7 +438,8 @@ void CodeGenFunction::EmitMustTailThunk(GlobalDecl GD, // Finish the function to maintain CodeGenFunction invariants. // FIXME: Don't emit unreachable code. EmitBlock(createBasicBlock()); - FinishFunction(); + + FinishThunk(); } void CodeGenFunction::generateThunk(llvm::Function *Fn, @@ -564,7 +566,7 @@ llvm::Constant *CodeGenVTables::maybeEmitThunk(GlobalDecl GD, CGM.SetLLVMFunctionAttributesForDefinition(GD.getDecl(), ThunkFn); // Thunks for variadic methods are special because in general variadic - // arguments cannot be perferctly forwarded. In the general case, clang + // arguments cannot be perfectly forwarded. In the general case, clang // implements such thunks by cloning the original function body. However, for // thunks with no return adjustment on targets that support musttail, we can // use musttail to perfectly forward the variadic arguments. @@ -709,14 +711,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; } @@ -809,7 +824,7 @@ CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD, assert(!VTable->isDeclaration() && "Shouldn't set properties on declaration"); CGM.setGVProperties(VTable, RD); - CGM.EmitVTableTypeMetadata(RD, VTable, *VTLayout.get()); + CGM.EmitVTableTypeMetadata(VTable, *VTLayout.get()); return VTable; } @@ -1040,32 +1055,7 @@ bool CodeGenModule::HasHiddenLTOVisibility(const CXXRecordDecl *RD) { return true; } -llvm::GlobalObject::VCallVisibility -CodeGenModule::GetVCallVisibilityLevel(const CXXRecordDecl *RD) { - LinkageInfo LV = RD->getLinkageAndVisibility(); - llvm::GlobalObject::VCallVisibility TypeVis; - if (!isExternallyVisible(LV.getLinkage())) - TypeVis = llvm::GlobalObject::VCallVisibilityTranslationUnit; - else if (HasHiddenLTOVisibility(RD)) - TypeVis = llvm::GlobalObject::VCallVisibilityLinkageUnit; - else - TypeVis = llvm::GlobalObject::VCallVisibilityPublic; - - for (auto B : RD->bases()) - if (B.getType()->getAsCXXRecordDecl()->isDynamicClass()) - TypeVis = std::min(TypeVis, - GetVCallVisibilityLevel(B.getType()->getAsCXXRecordDecl())); - - for (auto B : RD->vbases()) - if (B.getType()->getAsCXXRecordDecl()->isDynamicClass()) - TypeVis = std::min(TypeVis, - GetVCallVisibilityLevel(B.getType()->getAsCXXRecordDecl())); - - return TypeVis; -} - -void CodeGenModule::EmitVTableTypeMetadata(const CXXRecordDecl *RD, - llvm::GlobalVariable *VTable, +void CodeGenModule::EmitVTableTypeMetadata(llvm::GlobalVariable *VTable, const VTableLayout &VTLayout) { if (!getCodeGenOpts().LTOUnit) return; @@ -1125,10 +1115,4 @@ void CodeGenModule::EmitVTableTypeMetadata(const CXXRecordDecl *RD, VTable->addTypeMetadata((PointerWidth * I).getQuantity(), MD); } } - - if (getCodeGenOpts().VirtualFunctionElimination) { - llvm::GlobalObject::VCallVisibility TypeVis = GetVCallVisibilityLevel(RD); - if (TypeVis != llvm::GlobalObject::VCallVisibilityPublic) - VTable->addVCallVisibilityMetadata(TypeVis); - } } diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index d8b3c234a1efd..4145a5f4b0da6 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -71,6 +71,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/CodeGenABITypes.cpp b/clang/lib/CodeGen/CodeGenABITypes.cpp index 6b6a116cf259b..d3a16a1d5accf 100644 --- a/clang/lib/CodeGen/CodeGenABITypes.cpp +++ b/clang/lib/CodeGen/CodeGenABITypes.cpp @@ -16,7 +16,9 @@ //===----------------------------------------------------------------------===// #include "clang/CodeGen/CodeGenABITypes.h" +#include "CGCXXABI.h" #include "CGRecordLayout.h" +#include "CodeGenFunction.h" #include "CodeGenModule.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/Lex/HeaderSearchOptions.h" @@ -25,6 +27,11 @@ using namespace clang; using namespace CodeGen; +void CodeGen::addDefaultFunctionDefinitionAttributes(CodeGenModule &CGM, + llvm::AttrBuilder &attrs) { + CGM.addDefaultFunctionDefinitionAttributes(attrs); +} + const CGFunctionInfo & CodeGen::arrangeObjCMessageSendSignature(CodeGenModule &CGM, const ObjCMethodDecl *MD, @@ -63,6 +70,30 @@ CodeGen::arrangeFreeFunctionCall(CodeGenModule &CGM, info, {}, args); } +ImplicitCXXConstructorArgs +CodeGen::getImplicitCXXConstructorArgs(CodeGenModule &CGM, + const CXXConstructorDecl *D) { + // We have to create a dummy CodeGenFunction here to pass to + // getImplicitConstructorArgs(). In some cases (base and delegating + // constructor calls), getImplicitConstructorArgs() can reach into the + // CodeGenFunction to find parameters of the calling constructor to pass on to + // the called constructor, but that can't happen here because we're asking for + // the args for a complete, non-delegating constructor call. + CodeGenFunction CGF(CGM, /* suppressNewContext= */ true); + CGCXXABI::AddedStructorArgs addedArgs = + CGM.getCXXABI().getImplicitConstructorArgs(CGF, D, Ctor_Complete, + /* ForVirtualBase= */ false, + /* Delegating= */ false); + ImplicitCXXConstructorArgs implicitArgs; + for (const auto &arg : addedArgs.Prefix) { + implicitArgs.Prefix.push_back(arg.Value); + } + for (const auto &arg : addedArgs.Suffix) { + implicitArgs.Suffix.push_back(arg.Value); + } + return implicitArgs; +} + llvm::FunctionType * CodeGen::convertFreeFunctionType(CodeGenModule &CGM, const FunctionDecl *FD) { assert(FD != nullptr && "Expected a non-null function declaration!"); @@ -84,3 +115,16 @@ unsigned CodeGen::getLLVMFieldNumber(CodeGenModule &CGM, const FieldDecl *FD) { return CGM.getTypes().getCGRecordLayout(RD).getLLVMFieldNo(FD); } + +llvm::Value *CodeGen::getCXXDestructorImplicitParam( + CodeGenModule &CGM, llvm::BasicBlock *InsertBlock, + llvm::BasicBlock::iterator InsertPoint, const CXXDestructorDecl *D, + CXXDtorType Type, bool ForVirtualBase, bool Delegating) { + CodeGenFunction CGF(CGM, /*suppressNewContext=*/true); + CGF.CurCodeDecl = D; + CGF.CurFuncDecl = D; + CGF.CurFn = InsertBlock->getParent(); + CGF.Builder.SetInsertPoint(InsertBlock, InsertPoint); + return CGM.getCXXABI().getCXXDestructorImplicitParam( + CGF, D, Type, ForVirtualBase, Delegating); +} diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index 7f3f358d3d988..5bbb9726c8cfd 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -32,8 +32,8 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LLVMRemarkStreamer.h" #include "llvm/IR/Module.h" -#include "llvm/IR/RemarkStreamer.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Linker/Linker.h" #include "llvm/Pass.h" @@ -86,15 +86,15 @@ namespace clang { const CodeGenOptions CodeGenOpts) { handleAllErrors( std::move(E), - [&](const RemarkSetupFileError &E) { + [&](const LLVMRemarkSetupFileError &E) { Diags.Report(diag::err_cannot_open_file) << CodeGenOpts.OptRecordFile << E.message(); }, - [&](const RemarkSetupPatternError &E) { + [&](const LLVMRemarkSetupPatternError &E) { Diags.Report(diag::err_drv_optimization_remark_pattern) << E.message() << CodeGenOpts.OptRecordPasses; }, - [&](const RemarkSetupFormatError &E) { + [&](const LLVMRemarkSetupFormatError &E) { Diags.Report(diag::err_drv_optimization_remark_format) << CodeGenOpts.OptRecordFormat; }); @@ -223,7 +223,7 @@ namespace clang { for (auto &LM : LinkModules) { if (LM.PropagateAttrs) for (Function &F : *LM.Module) - Gen->CGM().AddDefaultFnAttrs(F); + Gen->CGM().addDefaultFunctionDefinitionAttributes(F); CurLinkModule = LM.Module.get(); @@ -286,7 +286,7 @@ namespace clang { CodeGenOpts, this)); Expected> OptRecordFileOrErr = - setupOptimizationRemarks( + setupLLVMOptimizationRemarks( Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses, CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness, CodeGenOpts.DiagnosticsHotnessThreshold); @@ -1089,11 +1089,9 @@ void CodeGenAction::ExecuteAction() { &Diagnostics); Expected> OptRecordFileOrErr = - setupOptimizationRemarks( - Ctx, CodeGenOpts.OptRecordFile, - CodeGenOpts.OptRecordPasses, - CodeGenOpts.OptRecordFormat, - CodeGenOpts.DiagnosticsWithHotness, + setupLLVMOptimizationRemarks( + Ctx, CodeGenOpts.OptRecordFile, CodeGenOpts.OptRecordPasses, + CodeGenOpts.OptRecordFormat, CodeGenOpts.DiagnosticsWithHotness, CodeGenOpts.DiagnosticsHotnessThreshold); if (Error E = OptRecordFileOrErr.takeError()) { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index a717f43e3efdd..1e7764bc43f69 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2479,3 +2479,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 0aff6667840d5..13307d8a4a1a1 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2594,7 +2594,8 @@ class CodeGenFunction : public CodeGenTypeCache { Address EmitCXXUuidofExpr(const CXXUuidofExpr *E); /// Situations in which we might emit a check for the suitability of a - /// pointer or glvalue. + /// pointer or glvalue. Needs to be kept in sync with ubsan_handlers.cpp in + /// compiler-rt. enum TypeCheckKind { /// Checking the operand of a load. Must be suitably sized and aligned. TCK_Load, @@ -3611,7 +3612,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) { @@ -3661,6 +3663,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, @@ -3958,7 +4005,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 963cd82210c44..9b1ca3cd8f9ed 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -176,7 +176,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 @@ -4063,17 +4065,24 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, GV->setAlignment(getContext().getDeclAlign(D).getAsAlign()); - // On Darwin, if the normal linkage of a C++ thread_local variable is - // LinkOnce or Weak, we keep the normal linkage to prevent multiple - // copies within a linkage unit; otherwise, the backing variable has - // internal linkage and all accesses should just be calls to the - // Itanium-specified entry point, which has the normal linkage of the - // variable. This is to preserve the ability to change the implementation - // behind the scenes. - if (!D->isStaticLocal() && D->getTLSKind() == VarDecl::TLS_Dynamic && + // On Darwin, unlike other Itanium C++ ABI platforms, the thread-wrapper + // function is only defined alongside the variable, not also alongside + // callers. Normally, all accesses to a thread_local go through the + // thread-wrapper in order to ensure initialization has occurred, underlying + // variable will never be used other than the thread-wrapper, so it can be + // converted to internal linkage. + // + // However, if the variable has the 'constinit' attribute, it _can_ be + // referenced directly, without calling the thread-wrapper, so the linkage + // must not be changed. + // + // Additionally, if the variable isn't plain external linkage, e.g. if it's + // weak or linkonce, the de-duplication semantics are important to preserve, + // so we don't change the linkage. + if (D->getTLSKind() == VarDecl::TLS_Dynamic && + Linkage == llvm::GlobalValue::ExternalLinkage && Context.getTargetInfo().getTriple().isOSDarwin() && - !llvm::GlobalVariable::isLinkOnceLinkage(Linkage) && - !llvm::GlobalVariable::isWeakLinkage(Linkage)) + !D->hasAttr()) Linkage = llvm::GlobalValue::InternalLinkage; GV->setLinkage(Linkage); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 115e754bb3920..4ebf863170732 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; @@ -409,6 +410,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; @@ -551,6 +557,8 @@ class CodeGenModule : public CodeGenTypeCache { MetadataTypeMap VirtualMetadataIdMap; MetadataTypeMap GeneralizedMetadataIdMap; + llvm::DenseMap PtrAuthDiscriminatorHashes; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, @@ -852,6 +860,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); @@ -868,6 +921,17 @@ class CodeGenModule : public CodeGenTypeCache { /// Returns the assumed alignment of an opaque pointer to the given class. CharUnits getClassPointerAlignment(const CXXRecordDecl *CD); + /// Returns the minimum object size for an object of the given class type + /// (or a class derived from it). + CharUnits getMinimumClassObjectSize(const CXXRecordDecl *CD); + + /// Returns the minimum object size for an object of the given type. + CharUnits getMinimumObjectSize(QualType Ty) { + if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) + return getMinimumClassObjectSize(RD); + return getContext().getTypeSizeInChars(Ty); + } + /// Returns the assumed alignment of a virtual base of a class. CharUnits getVBaseAlignment(CharUnits DerivedAlign, const CXXRecordDecl *Derived, @@ -1155,7 +1219,11 @@ class CodeGenModule : public CodeGenTypeCache { /// on the function more conservative. But it's unsafe to call this on a /// function which relies on particular fast-math attributes for correctness. /// It's up to you to ensure that this is safe. - void AddDefaultFnAttrs(llvm::Function &F); + void addDefaultFunctionDefinitionAttributes(llvm::Function &F); + + /// Like the overload taking a `Function &`, but intended specifically + /// for frontends that want to build on Clang's target-configuration logic. + void addDefaultFunctionDefinitionAttributes(llvm::AttrBuilder &attrs); StringRef getMangledName(GlobalDecl GD); StringRef getBlockMangledName(GlobalDecl GD, const BlockDecl *BD); @@ -1292,16 +1360,8 @@ class CodeGenModule : public CodeGenTypeCache { /// optimization. bool HasHiddenLTOVisibility(const CXXRecordDecl *RD); - /// Returns the vcall visibility of the given type. This is the scope in which - /// a virtual function call could be made which ends up being dispatched to a - /// member function of this class. This scope can be wider than the visibility - /// of the class itself when the class has a more-visible dynamic base class. - llvm::GlobalObject::VCallVisibility - GetVCallVisibilityLevel(const CXXRecordDecl *RD); - /// Emit type metadata for the given vtable using the given layout. - void EmitVTableTypeMetadata(const CXXRecordDecl *RD, - llvm::GlobalVariable *VTable, + void EmitVTableTypeMetadata(llvm::GlobalVariable *VTable, const VTableLayout &VTLayout); /// Generate a cross-DSO type identifier for MD. @@ -1516,11 +1576,14 @@ class CodeGenModule : public CodeGenTypeCache { /// function. void SimplifyPersonality(); - /// Helper function for ConstructAttributeList and AddDefaultFnAttrs. - /// Constructs an AttrList for a function with the given properties. - void ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone, - bool AttrOnCallSite, - llvm::AttrBuilder &FuncAttrs); + void destroyConstantSignedPointerCaches(); + + /// Helper function for ConstructAttributeList and + /// addDefaultFunctionDefinitionAttributes. Builds a set of function + /// attributes to add to a function with the given properties. + void getDefaultFunctionAttributes(StringRef Name, bool HasOptnone, + bool AttrOnCallSite, + llvm::AttrBuilder &FuncAttrs); llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map, StringRef Suffix); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 6ed172bb107e1..a829a6d1deea3 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -30,6 +30,7 @@ #include "clang/AST/Type.h" #include "clang/CodeGen/ConstantInitBuilder.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" @@ -203,7 +204,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { void EmitCXXConstructors(const CXXConstructorDecl *D) override; - AddedStructorArgs + AddedStructorArgCounts buildStructorSignature(GlobalDecl GD, SmallVectorImpl &ArgTys) override; @@ -222,10 +223,17 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { void EmitInstanceFunctionProlog(CodeGenFunction &CGF) override; - AddedStructorArgs - addImplicitConstructorArgs(CodeGenFunction &CGF, const CXXConstructorDecl *D, - CXXCtorType Type, bool ForVirtualBase, - bool Delegating, CallArgList &Args) override; + AddedStructorArgs getImplicitConstructorArgs(CodeGenFunction &CGF, + const CXXConstructorDecl *D, + CXXCtorType Type, + bool ForVirtualBase, + bool Delegating) override; + + llvm::Value *getCXXDestructorImplicitParam(CodeGenFunction &CGF, + const CXXDestructorDecl *DD, + CXXDtorType Type, + bool ForVirtualBase, + bool Delegating) override; void EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, @@ -369,6 +377,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { bool NeedsVTTParameter(GlobalDecl GD) override; + llvm::Constant * + getOrCreateVirtualFunctionPointerThunk(const CXXMethodDecl *MD); + /**************************** RTTI Uniqueness ******************************/ protected: @@ -407,6 +418,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); @@ -661,6 +675,8 @@ CGCallee ItaniumCXXABI::EmitLoadOfMemberFunctionPointer( VTableOffset = Builder.CreateTrunc(VTableOffset, CGF.Int32Ty); VTableOffset = Builder.CreateZExt(VTableOffset, CGM.PtrDiffTy); } + // Compute the address of the virtual function pointer. + llvm::Value *VFPAddr = Builder.CreateGEP(VTable, VTableOffset); // Check the address of the function pointer if CFI on member function // pointers is enabled. @@ -668,81 +684,44 @@ CGCallee ItaniumCXXABI::EmitLoadOfMemberFunctionPointer( llvm::Constant *CheckTypeDesc; bool ShouldEmitCFICheck = CGF.SanOpts.has(SanitizerKind::CFIMFCall) && CGM.HasHiddenLTOVisibility(RD); - bool ShouldEmitVFEInfo = CGM.getCodeGenOpts().VirtualFunctionElimination && - CGM.HasHiddenLTOVisibility(RD); - llvm::Value *VirtualFn = nullptr; - - { + if (ShouldEmitCFICheck) { CodeGenFunction::SanitizerScope SanScope(&CGF); - llvm::Value *TypeId = nullptr; - llvm::Value *CheckResult = nullptr; - - if (ShouldEmitCFICheck || ShouldEmitVFEInfo) { - // If doing CFI or VFE, we will need the metadata node to check against. - llvm::Metadata *MD = - CGM.CreateMetadataIdentifierForVirtualMemPtrType(QualType(MPT, 0)); - TypeId = llvm::MetadataAsValue::get(CGF.getLLVMContext(), MD); - } - llvm::Value *VFPAddr = Builder.CreateGEP(VTable, VTableOffset); - - if (ShouldEmitVFEInfo) { - // If doing VFE, load from the vtable with a type.checked.load intrinsic - // call. Note that we use the GEP to calculate the address to load from - // and pass 0 as the offset to the intrinsic. This is because every - // vtable slot of the correct type is marked with matching metadata, and - // we know that the load must be from one of these slots. - llvm::Value *CheckedLoad = Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::type_checked_load), - {VFPAddr, llvm::ConstantInt::get(CGM.Int32Ty, 0), TypeId}); - CheckResult = Builder.CreateExtractValue(CheckedLoad, 1); - VirtualFn = Builder.CreateExtractValue(CheckedLoad, 0); - VirtualFn = Builder.CreateBitCast(VirtualFn, FTy->getPointerTo(), - "memptr.virtualfn"); - } else { - // When not doing VFE, emit a normal load, as it allows more - // optimisations than type.checked.load. - if (ShouldEmitCFICheck) { - CheckResult = Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::type_test), - {Builder.CreateBitCast(VFPAddr, CGF.Int8PtrTy), TypeId}); - } - VFPAddr = - Builder.CreateBitCast(VFPAddr, FTy->getPointerTo()->getPointerTo()); - VirtualFn = Builder.CreateAlignedLoad(VFPAddr, CGF.getPointerAlign(), - "memptr.virtualfn"); - } - assert(VirtualFn && "Virtual fuction pointer not created!"); - assert((!ShouldEmitCFICheck || !ShouldEmitVFEInfo || CheckResult) && - "Check result required but not created!"); - - if (ShouldEmitCFICheck) { - // If doing CFI, emit the check. - CheckSourceLocation = CGF.EmitCheckSourceLocation(E->getBeginLoc()); - CheckTypeDesc = CGF.EmitCheckTypeDescriptor(QualType(MPT, 0)); - llvm::Constant *StaticData[] = { - llvm::ConstantInt::get(CGF.Int8Ty, CodeGenFunction::CFITCK_VMFCall), - CheckSourceLocation, - CheckTypeDesc, - }; + CheckSourceLocation = CGF.EmitCheckSourceLocation(E->getBeginLoc()); + CheckTypeDesc = CGF.EmitCheckTypeDescriptor(QualType(MPT, 0)); + llvm::Constant *StaticData[] = { + llvm::ConstantInt::get(CGF.Int8Ty, CodeGenFunction::CFITCK_VMFCall), + CheckSourceLocation, + CheckTypeDesc, + }; - if (CGM.getCodeGenOpts().SanitizeTrap.has(SanitizerKind::CFIMFCall)) { - CGF.EmitTrapCheck(CheckResult); - } else { - llvm::Value *AllVtables = llvm::MetadataAsValue::get( - CGM.getLLVMContext(), - llvm::MDString::get(CGM.getLLVMContext(), "all-vtables")); - llvm::Value *ValidVtable = Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::type_test), {VTable, AllVtables}); - CGF.EmitCheck(std::make_pair(CheckResult, SanitizerKind::CFIMFCall), - SanitizerHandler::CFICheckFail, StaticData, - {VTable, ValidVtable}); - } + llvm::Metadata *MD = + CGM.CreateMetadataIdentifierForVirtualMemPtrType(QualType(MPT, 0)); + llvm::Value *TypeId = llvm::MetadataAsValue::get(CGF.getLLVMContext(), MD); - FnVirtual = Builder.GetInsertBlock(); + llvm::Value *TypeTest = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::type_test), {VFPAddr, TypeId}); + + if (CGM.getCodeGenOpts().SanitizeTrap.has(SanitizerKind::CFIMFCall)) { + CGF.EmitTrapCheck(TypeTest); + } else { + llvm::Value *AllVtables = llvm::MetadataAsValue::get( + CGM.getLLVMContext(), + llvm::MDString::get(CGM.getLLVMContext(), "all-vtables")); + llvm::Value *ValidVtable = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::type_test), {VTable, AllVtables}); + CGF.EmitCheck(std::make_pair(TypeTest, SanitizerKind::CFIMFCall), + SanitizerHandler::CFICheckFail, StaticData, + {VTable, ValidVtable}); } - } // End of sanitizer scope + FnVirtual = Builder.GetInsertBlock(); + } + + // Load the virtual function to call. + VFPAddr = Builder.CreateBitCast(VFPAddr, FTy->getPointerTo()->getPointerTo()); + llvm::Value *VirtualFn = Builder.CreateAlignedLoad( + VFPAddr, CGF.getPointerAlign(), "memptr.virtualfn"); CGF.EmitBranch(FnEnd); // In the non-virtual path, the function pointer is actually a @@ -794,7 +773,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; } @@ -821,6 +816,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. /// @@ -848,21 +863,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 = @@ -907,6 +963,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; @@ -997,9 +1069,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 @@ -1021,7 +1117,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, @@ -1284,6 +1380,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); } } @@ -1531,7 +1628,7 @@ void ItaniumCXXABI::EmitCXXConstructors(const CXXConstructorDecl *D) { } } -CGCXXABI::AddedStructorArgs +CGCXXABI::AddedStructorArgCounts ItaniumCXXABI::buildStructorSignature(GlobalDecl GD, SmallVectorImpl &ArgTys) { ASTContext &Context = getContext(); @@ -1545,9 +1642,9 @@ ItaniumCXXABI::buildStructorSignature(GlobalDecl GD, cast(GD.getDecl())->getParent()->getNumVBases() != 0) { ArgTys.insert(ArgTys.begin() + 1, Context.getPointerType(Context.VoidPtrTy)); - return AddedStructorArgs::prefix(1); + return AddedStructorArgCounts::prefix(1); } - return AddedStructorArgs{}; + return AddedStructorArgCounts{}; } void ItaniumCXXABI::EmitCXXDestructors(const CXXDestructorDecl *D) { @@ -1613,9 +1710,9 @@ void ItaniumCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) { CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue); } -CGCXXABI::AddedStructorArgs ItaniumCXXABI::addImplicitConstructorArgs( +CGCXXABI::AddedStructorArgs ItaniumCXXABI::getImplicitConstructorArgs( CodeGenFunction &CGF, const CXXConstructorDecl *D, CXXCtorType Type, - bool ForVirtualBase, bool Delegating, CallArgList &Args) { + bool ForVirtualBase, bool Delegating) { if (!NeedsVTTParameter(GlobalDecl(D, Type))) return AddedStructorArgs{}; @@ -1623,8 +1720,14 @@ CGCXXABI::AddedStructorArgs ItaniumCXXABI::addImplicitConstructorArgs( llvm::Value *VTT = CGF.GetVTTParameter(GlobalDecl(D, Type), ForVirtualBase, Delegating); QualType VTTTy = getContext().getPointerType(getContext().VoidPtrTy); - Args.insert(Args.begin() + 1, CallArg(RValue::get(VTT), VTTTy)); - return AddedStructorArgs::prefix(1); // Added one arg. + return AddedStructorArgs::prefix({{VTT, VTTTy}}); +} + +llvm::Value *ItaniumCXXABI::getCXXDestructorImplicitParam( + CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, + bool ForVirtualBase, bool Delegating) { + GlobalDecl GD(DD, Type); + return CGF.GetVTTParameter(GD, ForVirtualBase, Delegating); } void ItaniumCXXABI::EmitDestructorCall(CodeGenFunction &CGF, @@ -1633,7 +1736,8 @@ void ItaniumCXXABI::EmitDestructorCall(CodeGenFunction &CGF, bool Delegating, Address This, QualType ThisTy) { GlobalDecl GD(DD, Type); - llvm::Value *VTT = CGF.GetVTTParameter(GD, ForVirtualBase, Delegating); + llvm::Value *VTT = + getCXXDestructorImplicitParam(CGF, DD, Type, ForVirtualBase, Delegating); QualType VTTTy = getContext().getPointerType(getContext().VoidPtrTy); CGCallee Callee; @@ -1686,7 +1790,7 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, EmitFundamentalRTTIDescriptors(RD); if (!VTable->isDeclarationForLinker()) - CGM.EmitVTableTypeMetadata(RD, VTable, VTLayout); + CGM.EmitVTableTypeMetadata(VTable, VTLayout); } bool ItaniumCXXABI::isVirtualOffsetNeededForVTableField( @@ -1746,12 +1850,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, @@ -1798,15 +1917,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()); @@ -1826,7 +1946,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; } @@ -1945,6 +2071,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); @@ -2408,6 +2541,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 @@ -2415,8 +2556,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); @@ -2771,6 +2911,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. @@ -3287,6 +3493,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); } @@ -4391,6 +4601,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 aff46135705a4..577980ba71ed7 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -206,7 +206,7 @@ class MicrosoftCXXABI : public CGCXXABI { // lacks a definition for the destructor, non-base destructors must always // delegate to or alias the base destructor. - AddedStructorArgs + AddedStructorArgCounts buildStructorSignature(GlobalDecl GD, SmallVectorImpl &ArgTys) override; @@ -253,10 +253,17 @@ class MicrosoftCXXABI : public CGCXXABI { void EmitInstanceFunctionProlog(CodeGenFunction &CGF) override; - AddedStructorArgs - addImplicitConstructorArgs(CodeGenFunction &CGF, const CXXConstructorDecl *D, - CXXCtorType Type, bool ForVirtualBase, - bool Delegating, CallArgList &Args) override; + AddedStructorArgs getImplicitConstructorArgs(CodeGenFunction &CGF, + const CXXConstructorDecl *D, + CXXCtorType Type, + bool ForVirtualBase, + bool Delegating) override; + + llvm::Value *getCXXDestructorImplicitParam(CodeGenFunction &CGF, + const CXXDestructorDecl *DD, + CXXDtorType Type, + bool ForVirtualBase, + bool Delegating) override; void EmitDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, bool ForVirtualBase, @@ -1261,10 +1268,10 @@ void MicrosoftCXXABI::EmitVBPtrStores(CodeGenFunction &CGF, } } -CGCXXABI::AddedStructorArgs +CGCXXABI::AddedStructorArgCounts MicrosoftCXXABI::buildStructorSignature(GlobalDecl GD, SmallVectorImpl &ArgTys) { - AddedStructorArgs Added; + AddedStructorArgCounts Added; // TODO: 'for base' flag if (isa(GD.getDecl()) && GD.getDtorType() == Dtor_Deleting) { @@ -1553,9 +1560,9 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) { } } -CGCXXABI::AddedStructorArgs MicrosoftCXXABI::addImplicitConstructorArgs( +CGCXXABI::AddedStructorArgs MicrosoftCXXABI::getImplicitConstructorArgs( CodeGenFunction &CGF, const CXXConstructorDecl *D, CXXCtorType Type, - bool ForVirtualBase, bool Delegating, CallArgList &Args) { + bool ForVirtualBase, bool Delegating) { assert(Type == Ctor_Complete || Type == Ctor_Base); // Check if we need a 'most_derived' parameter. @@ -1570,13 +1577,16 @@ CGCXXABI::AddedStructorArgs MicrosoftCXXABI::addImplicitConstructorArgs( } else { MostDerivedArg = llvm::ConstantInt::get(CGM.Int32Ty, Type == Ctor_Complete); } - RValue RV = RValue::get(MostDerivedArg); if (FPT->isVariadic()) { - Args.insert(Args.begin() + 1, CallArg(RV, getContext().IntTy)); - return AddedStructorArgs::prefix(1); + return AddedStructorArgs::prefix({{MostDerivedArg, getContext().IntTy}}); } - Args.add(RV, getContext().IntTy); - return AddedStructorArgs::suffix(1); + return AddedStructorArgs::suffix({{MostDerivedArg, getContext().IntTy}}); +} + +llvm::Value *MicrosoftCXXABI::getCXXDestructorImplicitParam( + CodeGenFunction &CGF, const CXXDestructorDecl *DD, CXXDtorType Type, + bool ForVirtualBase, bool Delegating) { + return nullptr; } void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF, @@ -1605,8 +1615,11 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF, BaseDtorEndBB = EmitDtorCompleteObjectHandler(CGF); } + llvm::Value *Implicit = + getCXXDestructorImplicitParam(CGF, DD, Type, ForVirtualBase, + Delegating); // = nullptr CGF.EmitCXXDestructorCall(GD, Callee, This.getPointer(), ThisTy, - /*ImplicitParam=*/nullptr, + /*ImplicitParam=*/Implicit, /*ImplicitParamTy=*/QualType(), nullptr); if (BaseDtorEndBB) { // Complete object handler should continue to be the remaining @@ -1908,7 +1921,7 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, VFunc = Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); } - CGCallee Callee(GD, VFunc); + CGCallee Callee(GD, VFunc, /*unsigned*/ CGPointerAuthInfo()); return Callee; } @@ -3394,7 +3407,7 @@ CGCallee MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer( FunctionPointer = Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo()); - CGCallee Callee(FPT, FunctionPointer); + CGCallee Callee(FPT, FunctionPointer, /*unsigned*/ CGPointerAuthInfo()); return Callee; } @@ -4000,7 +4013,7 @@ MicrosoftCXXABI::getAddrOfCXXCtorClosure(const CXXConstructorDecl *CD, CGF.EmitCallArgs(Args, FPT, llvm::makeArrayRef(ArgVec), CD, IsCopy ? 1 : 0); // Insert any ABI-specific implicit constructor arguments. - AddedStructorArgs ExtraArgs = + AddedStructorArgCounts ExtraArgs = addImplicitConstructorArgs(CGF, CD, Ctor_Complete, /*ForVirtualBase=*/false, /*Delegating=*/false, Args); diff --git a/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp b/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp index 7a60369a4da0a..cc9b1a848dbc3 100644 --- a/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp +++ b/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp @@ -14,10 +14,13 @@ #include "llvm/Support/Error.h" #include "llvm/Support/Path.h" #include +#include using namespace llvm; using namespace clang; +#if TARGET_OS_OSX + static void stopFSEventStream(FSEventStreamRef); namespace { @@ -43,24 +46,32 @@ namespace { class DirectoryWatcherMac : public clang::DirectoryWatcher { public: DirectoryWatcherMac( - FSEventStreamRef EventStream, + dispatch_queue_t Queue, FSEventStreamRef EventStream, std::function, bool)> Receiver, llvm::StringRef WatchedDirPath) - : EventStream(EventStream), Receiver(Receiver), + : Queue(Queue), EventStream(EventStream), Receiver(Receiver), WatchedDirPath(WatchedDirPath) {} ~DirectoryWatcherMac() override { - stopFSEventStream(EventStream); - EventStream = nullptr; - // Now it's safe to use Receiver as the only other concurrent use would have - // been in EventStream processing. - Receiver(DirectoryWatcher::Event( - DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""), - false); + // FSEventStreamStop and Invalidate must be called after Start and + // SetDispatchQueue to follow FSEvents API contract. The call to Receiver + // also uses Queue to not race with the initial scan. + dispatch_sync(Queue, ^{ + stopFSEventStream(EventStream); + EventStream = nullptr; + Receiver( + DirectoryWatcher::Event( + DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""), + false); + }); + + // Balance initial creation. + dispatch_release(Queue); } private: + dispatch_queue_t Queue; FSEventStreamRef EventStream; std::function, bool)> Receiver; const std::string WatchedDirPath; @@ -138,7 +149,6 @@ static void eventStreamCallback(ConstFSEventStreamRef Stream, // default Events.emplace_back(DirectoryWatcher::Event{ DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""}); - llvm_unreachable("Unknown FSEvent type."); } if (!Events.empty()) { @@ -217,7 +227,7 @@ llvm::Expected> clang::DirectoryWatcher::creat assert(EventStream && "EventStream expected to be non-null"); std::unique_ptr Result = - std::make_unique(EventStream, Receiver, Path); + std::make_unique(Queue, EventStream, Receiver, Path); // We need to copy the data so the lifetime is ok after a const copy is made // for the block. @@ -230,10 +240,6 @@ llvm::Expected> clang::DirectoryWatcher::creat // inital scan and handling events ONLY AFTER the scan finishes. FSEventStreamSetDispatchQueue(EventStream, Queue); FSEventStreamStart(EventStream); - // We need to decrement the ref count for Queue as initialize() will return - // and FSEvents has incremented it. Since we have to wait for FSEvents to - // take ownership it's the easiest to do it here rather than main thread. - dispatch_release(Queue); Receiver(getAsFileEvents(scanDirectory(CopiedPath)), /*IsInitial=*/true); }; @@ -245,3 +251,17 @@ llvm::Expected> clang::DirectoryWatcher::creat return Result; } + +#else // TARGET_OS_OSX + +llvm::Expected> +clang::DirectoryWatcher::create( + StringRef Path, + std::function, bool)> Receiver, + bool WaitForInitialSync) { + return llvm::make_error( + "DirectoryWatcher is not implemented for this platform!", + llvm::inconvertibleErrorCode()); +} + +#endif // TARGET_OS_OSX diff --git a/clang/lib/Driver/DarwinSDKInfo.cpp b/clang/lib/Driver/DarwinSDKInfo.cpp index 761c6717266bf..06e2d438d09c9 100644 --- a/clang/lib/Driver/DarwinSDKInfo.cpp +++ b/clang/lib/Driver/DarwinSDKInfo.cpp @@ -15,6 +15,42 @@ using namespace clang::driver; using namespace clang; +static Optional +parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj) { + auto VersionString = Obj->getString("Version"); + if (!VersionString) + return None; + VersionTuple Version; + if (Version.tryParse(*VersionString)) + return None; + DarwinSDKInfo SDKInfo(Version); + if (const auto *VM = Obj->getObject("VersionMap")) { + auto parseVersionMap = [](const llvm::json::Object &Obj, + llvm::StringMap &Mapping) -> bool { + for (const auto &KV : Obj) { + if (auto Val = KV.getSecond().getAsString()) { + llvm::VersionTuple Version; + if (Version.tryParse(*Val)) + return true; + Mapping[KV.getFirst()] = Version; + } + } + return false; + }; + if (const auto *Mapping = VM->getObject("macOS_iOSMac")) { + if (parseVersionMap(*Mapping, + SDKInfo.getVersionMap().MacOS2iOSMacMapping)) + return None; + } + if (const auto *Mapping = VM->getObject("iOSMac_macOS")) { + if (parseVersionMap(*Mapping, + SDKInfo.getVersionMap().IosMac2macOSMapping)) + return None; + } + } + return std::move(SDKInfo); +} + Expected> driver::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { llvm::SmallString<256> Filepath = SDKRootPath; @@ -31,12 +67,8 @@ driver::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { return Result.takeError(); if (const auto *Obj = Result->getAsObject()) { - auto VersionString = Obj->getString("Version"); - if (VersionString) { - VersionTuple Version; - if (!Version.tryParse(*VersionString)) - return DarwinSDKInfo(Version); - } + if (auto SDKInfo = parseDarwinSDKSettingsJSON(Obj)) + return std::move(SDKInfo); } return llvm::make_error("invalid SDKSettings.json", llvm::inconvertibleErrorCode()); diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index e718b8366df0e..afa79660fd0fd 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1389,7 +1389,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 cab97b1a601a3..469ef62f1a9d0 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 9c27504dccf5b..7c886b192ef55 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 the appropriate Apple CPU if -arch is used or when @@ -140,10 +151,14 @@ getAArch64MicroArchFeaturesFromMtune(const Driver &D, StringRef Mtune, // Handle CPU name is 'native'. if (MtuneLowerCase == "native") MtuneLowerCase = llvm::sys::getHostCPUName(); - if (MtuneLowerCase == "cyclone" || MtuneLowerCase.find("apple") == 0) { + + // 'cyclone' and later have zero-cycle register moves and zeroing. + if (MtuneLowerCase == "cyclone" || MtuneLowerCase == "vortex" || + MtuneLowerCase == "lightning" || MtuneLowerCase.find("apple") == 0) { 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 fbb772bb370ae..5861c3943f987 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1129,6 +1129,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)) { @@ -4660,6 +4661,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. // @@ -5157,6 +5178,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()) || @@ -5473,6 +5507,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, @@ -5756,30 +5814,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString(TargetInfo.str())); } - bool VirtualFunctionElimination = - Args.hasFlag(options::OPT_fvirtual_function_elimination, - options::OPT_fno_virtual_function_elimination, false); - if (VirtualFunctionElimination) { - // VFE requires full LTO (currently, this might be relaxed to allow ThinLTO - // in the future). - if (D.getLTOMode() != LTOK_Full) - D.Diag(diag::err_drv_argument_only_allowed_with) - << "-fvirtual-function-elimination" - << "-flto=full"; - - CmdArgs.push_back("-fvirtual-function-elimination"); - } - - // VFE requires whole-program-vtables, and enables it by default. - bool WholeProgramVTables = Args.hasFlag( - options::OPT_fwhole_program_vtables, - options::OPT_fno_whole_program_vtables, VirtualFunctionElimination); - if (VirtualFunctionElimination && !WholeProgramVTables) { - D.Diag(diag::err_drv_argument_not_allowed_with) - << "-fno-whole-program-vtables" - << "-fvirtual-function-elimination"; - } - + bool WholeProgramVTables = + Args.hasFlag(options::OPT_fwhole_program_vtables, + options::OPT_fno_whole_program_vtables, false); if (WholeProgramVTables) { if (!D.isUsingLTO()) D.Diag(diag::err_drv_argument_only_allowed_with) diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 344a14fe1ea7c..a73e0148a2ddf 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("arm64_32", llvm::Triple::aarch64_32) .Case("r600", llvm::Triple::r600) .Case("amdgcn", llvm::Triple::amdgcn) @@ -73,8 +73,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) { @@ -429,6 +434,75 @@ static bool isObjCRuntimeLinked(const ArgList &Args) { return Args.hasArg(options::OPT_fobjc_link_runtime); } +static bool checkRemarksOptions(const Driver &D, const ArgList &Args, + const llvm::Triple &Triple) { + // When enabling remarks, we need to error if: + // * The remark file is specified but we're targeting multiple architectures, + // which means more than one remark file is being generated. + bool hasMultipleInvocations = + Args.getAllArgValues(options::OPT_arch).size() > 1; + bool hasExplicitOutputFile = + Args.getLastArg(options::OPT_foptimization_record_file_EQ); + if (hasMultipleInvocations && hasExplicitOutputFile) { + D.Diag(diag::err_drv_invalid_output_with_multiple_archs) + << "-foptimization-record-file"; + return false; + } + return true; +} + +static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs, + const llvm::Triple &Triple, + const InputInfo &Output, const JobAction &JA) { + StringRef Format = "yaml"; + if (const Arg *A = Args.getLastArg(options::OPT_fsave_optimization_record_EQ)) + Format = A->getValue(); + + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-lto-pass-remarks-output"); + CmdArgs.push_back("-mllvm"); + + const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ); + if (A) { + CmdArgs.push_back(A->getValue()); + } else { + assert(Output.isFilename() && "Unexpected ld output."); + SmallString<128> F; + F = Output.getFilename(); + F += ".opt."; + F += Format; + + CmdArgs.push_back(Args.MakeArgString(F)); + } + + if (const Arg *A = + Args.getLastArg(options::OPT_foptimization_record_passes_EQ)) { + CmdArgs.push_back("-mllvm"); + std::string Passes = + std::string("-lto-pass-remarks-filter=") + A->getValue(); + CmdArgs.push_back(Args.MakeArgString(Passes)); + } + + if (!Format.empty()) { + CmdArgs.push_back("-mllvm"); + Twine FormatArg = Twine("-lto-pass-remarks-format=") + Format; + CmdArgs.push_back(Args.MakeArgString(FormatArg)); + } + + if (getLastProfileUseArg(Args)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-lto-pass-remarks-with-hotness"); + + if (const Arg *A = + Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) { + CmdArgs.push_back("-mllvm"); + std::string Opt = + std::string("-lto-pass-remarks-hotness-threshold=") + A->getValue(); + CmdArgs.push_back(Args.MakeArgString(Opt)); + } + } +} + void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -447,6 +521,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)) { @@ -463,55 +541,10 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, // we follow suite for ease of comparison. AddLinkArgs(C, Args, CmdArgs, Inputs); - // For LTO, pass the name of the optimization record file and other - // opt-remarks flags. - if (Args.hasFlag(options::OPT_fsave_optimization_record, - options::OPT_fsave_optimization_record_EQ, - options::OPT_fno_save_optimization_record, false)) { - CmdArgs.push_back("-mllvm"); - CmdArgs.push_back("-lto-pass-remarks-output"); - CmdArgs.push_back("-mllvm"); - - SmallString<128> F; - F = Output.getFilename(); - F += ".opt."; - if (const Arg *A = - Args.getLastArg(options::OPT_fsave_optimization_record_EQ)) - F += A->getValue(); - else - F += "yaml"; - - CmdArgs.push_back(Args.MakeArgString(F)); - - if (getLastProfileUseArg(Args)) { - CmdArgs.push_back("-mllvm"); - CmdArgs.push_back("-lto-pass-remarks-with-hotness"); - - if (const Arg *A = - Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) { - CmdArgs.push_back("-mllvm"); - std::string Opt = - std::string("-lto-pass-remarks-hotness-threshold=") + A->getValue(); - CmdArgs.push_back(Args.MakeArgString(Opt)); - } - } - - if (const Arg *A = - Args.getLastArg(options::OPT_foptimization_record_passes_EQ)) { - CmdArgs.push_back("-mllvm"); - std::string Passes = - std::string("-lto-pass-remarks-filter=") + A->getValue(); - CmdArgs.push_back(Args.MakeArgString(Passes)); - } - - if (const Arg *A = - Args.getLastArg(options::OPT_fsave_optimization_record_EQ)) { - CmdArgs.push_back("-mllvm"); - std::string Format = - std::string("-lto-pass-remarks-format=") + A->getValue(); - CmdArgs.push_back(Args.MakeArgString(Format)); - } - } + if (willEmitRemarks(Args) && + checkRemarksOptions(getToolChain().getDriver(), Args, + getToolChain().getTriple())) + renderRemarksOptions(Args, CmdArgs, getToolChain().getTriple(), Output, JA); // Propagate the -moutline flag to the linker in LTO. if (Arg *A = @@ -755,8 +788,8 @@ bool MachO::HasNativeLLVMSupport() const { return true; } ToolChain::CXXStdlibType Darwin::GetDefaultCXXStdlibType() const { // Default to use libc++ on OS X 10.9+ and iOS 7+. if ((isTargetMacOS() && !isMacosxVersionLT(10, 9)) || - (isTargetIOSBased() && !isIPhoneOSVersionLT(7, 0)) || - isTargetWatchOSBased()) + (isTargetIOSBased() && !isIPhoneOSVersionLT(7, 0)) || + isTargetWatchOSBased()) return ToolChain::CST_Libcxx; return ToolChain::CST_Libstdcxx; @@ -775,7 +808,7 @@ ObjCRuntime Darwin::getDefaultObjCRuntime(bool isNonFragile) const { /// Darwin provides a blocks runtime starting in MacOS X 10.6 and iOS 3.2. bool Darwin::hasBlocksRuntime() const { - if (isTargetWatchOSBased()) + if (isTargetWatchOSBased() || isTargetMacABI()) return true; else if (isTargetIOSBased()) return !isIPhoneOSVersionLT(3, 2); @@ -838,8 +871,11 @@ StringRef MachO::getMachOArchName(const ArgList &Args) const { case llvm::Triple::aarch64_32: return "arm64_32"; - case llvm::Triple::aarch64: + case llvm::Triple::aarch64: { + if (getTriple().getArchName() == "arm64e") + return "arm64e"; return "arm64"; + } case llvm::Triple::thumb: case llvm::Triple::arm: @@ -873,13 +909,12 @@ std::string Darwin::ComputeEffectiveClangTriple(const ArgList &Args, Str += "watchos"; else if (isTargetTvOSBased()) Str += "tvos"; - else if (isTargetIOSBased()) + else if (isTargetIOSBased() || isTargetMacABI()) Str += "ios"; else Str += "macosx"; - Str += getTargetVersion().getAsString(); + Str += getOSTargetVersion().getAsString(); Triple.setOSName(Str); - return Triple.getTriple(); } @@ -922,11 +957,42 @@ void DarwinClang::addClangWarningOptions(ArgStringList &CC1Args) const { // For iOS and watchOS, also error about implicit function declarations, // as that can impact calling conventions. - if (!isTargetMacOS()) + if (!isTargetMacOSBased()) CC1Args.push_back("-Werror=implicit-function-declaration"); } } +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. @@ -942,7 +1008,9 @@ static StringRef getXcodeDeveloperPath(StringRef PathIntoXcode) { void DarwinClang::AddLinkARCArgs(const ArgList &Args, ArgStringList &CmdArgs) const { // Avoid linking compatibility stubs on i386 mac. - if (isTargetMacOS() && getArch() == llvm::Triple::x86) + if (isTargetMacOSBased() && getArch() == llvm::Triple::x86) + return; + if (isTargetAppleSiliconMac()) return; ObjCRuntime runtime = getDefaultObjCRuntime(/*nonfragile*/ true); @@ -1068,8 +1136,8 @@ StringRef Darwin::getPlatformFamily() const { StringRef Darwin::getSDKName(StringRef isysroot) { // Assume SDK has path: SOME_PATH/SDKs/PlatformXX.YY.sdk - auto BeginSDK = llvm::sys::path::begin(isysroot); - auto EndSDK = llvm::sys::path::end(isysroot); + auto BeginSDK = llvm::sys::path::rbegin(isysroot); + auto EndSDK = llvm::sys::path::rend(isysroot); for (auto IT = BeginSDK; IT != EndSDK; ++IT) { StringRef SDK = *IT; if (SDK.endswith(".sdk")) @@ -1083,6 +1151,8 @@ StringRef Darwin::getOSLibraryNameSuffix(bool IgnoreSim) const { case DarwinPlatformKind::MacOS: return "osx"; case DarwinPlatformKind::IPhoneOS: + if (TargetEnvironment == MacABI) + return "osx"; return TargetEnvironment == NativeEnvironment || IgnoreSim ? "ios" : "iossim"; case DarwinPlatformKind::TvOS: @@ -1307,6 +1377,12 @@ struct DarwinPlatform { DarwinEnvironmentKind getEnvironment() const { return Environment; } + VersionTuple getNativeTargetVersion() const { + assert(Environment == DarwinEnvironmentKind::MacABI && + "native target version is specified only for macabi"); + return NativeTargetVersion; + } + void setEnvironment(DarwinEnvironmentKind Kind) { Environment = Kind; InferSimulatorFromArch = false; @@ -1371,21 +1447,38 @@ struct DarwinPlatform { llvm_unreachable("Unsupported Darwin Source Kind"); } - static DarwinPlatform createFromTarget(const llvm::Triple &TT, - StringRef OSVersion, Arg *A) { + static DarwinPlatform + createFromTarget(const llvm::Triple &TT, StringRef OSVersion, Arg *A, + const Optional &SDKInfo) { DarwinPlatform Result(TargetArg, getPlatformFromOS(TT.getOS()), OSVersion, A); + unsigned Major, Minor, Micro; + TT.getOSVersion(Major, Minor, Micro); + if (Major == 0) + Result.HasOSVersion = false; switch (TT.getEnvironment()) { case llvm::Triple::Simulator: Result.Environment = DarwinEnvironmentKind::Simulator; break; + case llvm::Triple::MacABI: { + auto NativeTargetVersion = VersionTuple(10, 15); + if (Result.HasOSVersion && SDKInfo) { + const auto &VM = SDKInfo->getVersionMap().IosMac2macOSMapping; + // FIXME: make it a more robust lookup when upstreaming. + VersionTuple VV = Micro ? VersionTuple(Major, Minor, Micro) + : VersionTuple(Major, Minor); + std::string VersionString = VV.getAsString(); + auto It = VM.find(VersionString); + if (It != VM.end()) + NativeTargetVersion = It->getValue(); + } + Result.Environment = DarwinEnvironmentKind::MacABI; + Result.NativeTargetVersion = NativeTargetVersion; + break; + } default: break; } - unsigned Major, Minor, Micro; - TT.getOSVersion(Major, Minor, Micro); - if (Major == 0) - Result.HasOSVersion = false; return Result; } static DarwinPlatform createOSVersionArg(DarwinPlatformKind Platform, @@ -1451,6 +1544,7 @@ struct DarwinPlatform { SourceKind Kind; DarwinPlatformKind Platform; DarwinEnvironmentKind Environment = DarwinEnvironmentKind::NativeEnvironment; + VersionTuple NativeTargetVersion; std::string OSVersion; bool HasOSVersion = true, InferSimulatorFromArch = true; Arg *Argument; @@ -1643,8 +1737,16 @@ inferDeploymentTargetFromArch(DerivedArgList &Args, const Darwin &Toolchain, llvm::Triple::OSType OSTy = llvm::Triple::UnknownOS; StringRef MachOArchName = Toolchain.getMachOArchName(Args); - if (MachOArchName == "armv7" || MachOArchName == "armv7s" || - MachOArchName == "arm64") + if (MachOArchName == "arm64" || MachOArchName == "arm64e") { +#if __arm64__ + // A clang running on an Apple Silicon mac defaults + // to building for mac when building for arm64 rather than + // defaulting to iOS. + OSTy = llvm::Triple::MacOSX; +#else + OSTy = llvm::Triple::IOS; +#endif + } else if (MachOArchName == "armv7" || MachOArchName == "armv7s") OSTy = llvm::Triple::IOS; else if (MachOArchName == "armv7k" || MachOArchName == "arm64_32") OSTy = llvm::Triple::WatchOS; @@ -1660,7 +1762,8 @@ inferDeploymentTargetFromArch(DerivedArgList &Args, const Darwin &Toolchain, /// Returns the deployment target that's specified using the -target option. Optional getDeploymentTargetFromTargetArg( - DerivedArgList &Args, const llvm::Triple &Triple, const Driver &TheDriver) { + DerivedArgList &Args, const llvm::Triple &Triple, const Driver &TheDriver, + const Optional &SDKInfo) { if (!Args.hasArg(options::OPT_target)) return None; if (Triple.getOS() == llvm::Triple::Darwin || @@ -1668,7 +1771,8 @@ Optional getDeploymentTargetFromTargetArg( return None; std::string OSVersion = getOSVersion(Triple.getOS(), Triple, TheDriver); return DarwinPlatform::createFromTarget(Triple, OSVersion, - Args.getLastArg(options::OPT_target)); + Args.getLastArg(options::OPT_target), + SDKInfo); } Optional parseSDKSettings(llvm::vfs::FileSystem &VFS, @@ -1717,7 +1821,7 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { // The OS and the version can be specified using the -target argument. Optional OSTarget = - getDeploymentTargetFromTargetArg(Args, getTriple(), getDriver()); + getDeploymentTargetFromTargetArg(Args, getTriple(), getDriver(), SDKInfo); if (OSTarget) { Optional OSVersionArgTarget = getDeploymentTargetFromOSVersionArg(Args, getDriver()); @@ -1793,7 +1897,7 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { if (Platform == MacOS) { if (!Driver::GetReleaseVersion(OSTarget->getOSVersion(), Major, Minor, Micro, HadExtra) || - HadExtra || Major != 10 || Minor >= 100 || Micro >= 100) + HadExtra || Major < 10 || Major >= 100 || Minor >= 100 || Micro >= 100) getDriver().Diag(diag::err_drv_invalid_version_number) << OSTarget->getAsString(Args, Opts); } else if (Platform == IPhoneOS) { @@ -1808,8 +1912,11 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { if (getTriple().isArch32Bit() && Major >= 11) { // If the deployment target is explicitly specified, print a diagnostic. if (OSTarget->isExplicitlySpecified()) { - getDriver().Diag(diag::warn_invalid_ios_deployment_target) - << OSTarget->getAsString(Args, Opts); + if (OSTarget->getEnvironment() == MacABI) + getDriver().Diag(diag::err_invalid_macos_32bit_deployment_target); + else + getDriver().Diag(diag::warn_invalid_ios_deployment_target) + << OSTarget->getAsString(Args, Opts); // Otherwise, set it to 10.99.99. } else { Major = 10; @@ -1838,14 +1945,19 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { OSTarget->canInferSimulatorFromArch() && getTriple().isX86()) Environment = Simulator; - setTarget(Platform, Environment, Major, Minor, Micro); + VersionTuple NativeTargetVersion; + if (Environment == MacABI) + NativeTargetVersion = OSTarget->getNativeTargetVersion(); + setTarget(Platform, Environment, Major, Minor, Micro, NativeTargetVersion); if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { StringRef SDK = getSDKName(A->getValue()); if (SDK.size() > 0) { size_t StartVer = SDK.find_first_of("0123456789"); StringRef SDKName = SDK.slice(0, StartVer); - if (!SDKName.startswith(getPlatformFamily())) + // Don't warn about the macabi SDK. + // FIXME: Can we warn here? + if (!SDKName.startswith(getPlatformFamily()) && Environment != MacABI) getDriver().Diag(diag::warn_incompatible_sysroot) << SDKName << getPlatformFamily(); } @@ -1870,7 +1982,10 @@ void DarwinClang::AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs bool NoStdInc = DriverArgs.hasArg(options::OPT_nostdinc); bool NoStdlibInc = DriverArgs.hasArg(options::OPT_nostdlibinc); - bool NoBuiltinInc = DriverArgs.hasArg(options::OPT_nobuiltininc); + bool NoBuiltinInc = DriverArgs.hasFlag( + options::OPT_nobuiltininc, options::OPT_ibuiltininc, /*Default=*/false); + bool ForceBuiltinInc = DriverArgs.hasFlag( + options::OPT_ibuiltininc, options::OPT_nobuiltininc, /*Default=*/false); // Add /usr/local/include if (!NoStdInc && !NoStdlibInc) { @@ -1880,7 +1995,7 @@ void DarwinClang::AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs } // Add the Clang builtin headers (/include) - if (!NoStdInc && !NoBuiltinInc) { + if (!(NoStdInc && !ForceBuiltinInc) && !NoBuiltinInc) { SmallString<128> P(D.ResourceDir); llvm::sys::path::append(P, "include"); addSystemInclude(DriverArgs, CC1Args, P); @@ -2333,6 +2448,8 @@ void MachO::AddLinkRuntimeLibArgs(const ArgList &Args, bool Darwin::isAlignedAllocationUnavailable() const { llvm::Triple::OSType OS; + if (isTargetMacABI()) + return TargetVersion < alignedAllocMinVersion(llvm::Triple::MacOSX); switch (TargetPlatform) { case MacOS: // Earlier than 10.13. OS = llvm::Triple::MacOSX; @@ -2362,11 +2479,26 @@ void Darwin::addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, CC1Args.push_back("-faligned-alloc-unavailable"); if (SDKInfo) { + VersionTuple TargetVariantSDKVersion; + bool UseTVSDKVersionAsPrimary = + getTriple().getOS() == llvm::Triple::IOS && + getTriple().getEnvironment() == llvm::Triple::MacABI; + if (UseTVSDKVersionAsPrimary) { + const auto &VM = SDKInfo->getVersionMap().MacOS2iOSMacMapping; + std::string VersionString = SDKInfo->getVersion().getAsString(); + auto It = VM.find(VersionString); + if (It != VM.end()) + TargetVariantSDKVersion = It->getValue(); + } + /// Pass the SDK version to the compiler when the SDK information is /// available. std::string Arg; llvm::raw_string_ostream OS(Arg); - OS << "-target-sdk-version=" << SDKInfo->getVersion(); + VersionTuple SDKVersion = SDKInfo->getVersion(); + if (UseTVSDKVersionAsPrimary) + std::swap(SDKVersion, TargetVariantSDKVersion); + OS << "-target-sdk-version=" << SDKVersion; CC1Args.push_back(DriverArgs.MakeArgString(OS.str())); } } @@ -2492,7 +2624,7 @@ bool MachO::SupportsProfiling() const { void Darwin::addMinVersionArgs(const ArgList &Args, ArgStringList &CmdArgs) const { - VersionTuple TargetVersion = getTargetVersion(); + VersionTuple TargetVersion = getOSTargetVersion(); if (isTargetWatchOS()) CmdArgs.push_back("-watchos_version_min"); @@ -2504,13 +2636,18 @@ void Darwin::addMinVersionArgs(const ArgList &Args, CmdArgs.push_back("-tvos_simulator_version_min"); else if (isTargetIOSSimulator()) CmdArgs.push_back("-ios_simulator_version_min"); + else if (isTargetMacABI()) + CmdArgs.push_back("-maccatalyst_version_min"); else if (isTargetIOSBased()) CmdArgs.push_back("-iphoneos_version_min"); else { - assert(isTargetMacOS() && "unexpected target"); + assert(isTargetMacOSBased() && "unexpected target"); CmdArgs.push_back("-macosx_version_min"); } + VersionTuple MinTgtVers = getEffectiveTriple().getMinimumSupportedOSVersion(); + if (!MinTgtVers.empty() && MinTgtVers > TargetVersion) + TargetVersion = MinTgtVers; CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); } @@ -2520,11 +2657,9 @@ static const char *getPlatformName(Darwin::DarwinPlatformKind Platform, case Darwin::MacOS: return "macos"; case Darwin::IPhoneOS: - if (Environment == Darwin::NativeEnvironment || - Environment == Darwin::Simulator) - return "ios"; - // FIXME: Add macCatalyst support here ("\"mac catalyst\""). - llvm_unreachable("macCatalyst isn't yet supported"); + if (Environment == Darwin::MacABI) + return "mac catalyst"; + return "ios"; case Darwin::TvOS: return "tvos"; case Darwin::WatchOS: @@ -2535,116 +2670,142 @@ static const char *getPlatformName(Darwin::DarwinPlatformKind Platform, void Darwin::addPlatformVersionArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const { - // -platform_version - // Both the target and SDK version support only up to 3 components. - CmdArgs.push_back("-platform_version"); - std::string PlatformName = getPlatformName(TargetPlatform, TargetEnvironment); - if (TargetEnvironment == Darwin::Simulator) - PlatformName += "-simulator"; - CmdArgs.push_back(Args.MakeArgString(PlatformName)); - VersionTuple TargetVersion = getTargetVersion().withoutBuild(); - CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); - if (SDKInfo) { - VersionTuple SDKVersion = SDKInfo->getVersion().withoutBuild(); - CmdArgs.push_back(Args.MakeArgString(SDKVersion.getAsString())); - } else { - // Use a blank SDK version if it's not present. - CmdArgs.push_back("0.0.0"); + auto EmitPlatformVersionArg = [&](const VersionTuple &TV, + Darwin::DarwinPlatformKind Platform, + Darwin::DarwinEnvironmentKind Environment) { + // -platform_version + // Both the target and SDK version support only up to 3 components. + CmdArgs.push_back("-platform_version"); + std::string PlatformName = getPlatformName(Platform, Environment); + if (Environment == Darwin::Simulator) + PlatformName += "-simulator"; + CmdArgs.push_back(Args.MakeArgString(PlatformName)); + VersionTuple TargetVersion = TV.withoutBuild(); + // FIXME: This should be an error. + if (Platform == Darwin::IPhoneOS && Environment == Darwin::MacABI && + TargetVersion.getMajor() < 13) + TargetVersion = VersionTuple(13, 0); + VersionTuple MinTgtVers = getEffectiveTriple().getMinimumSupportedOSVersion(); + if (!MinTgtVers.empty() && MinTgtVers > TargetVersion) + TargetVersion = MinTgtVers; + CmdArgs.push_back(Args.MakeArgString(TargetVersion.getAsString())); + if (SDKInfo) { + VersionTuple SDKVersion = SDKInfo->getVersion().withoutBuild(); + // Use the appropriate SDK version for macCatalyst. + if (Platform == Darwin::IPhoneOS && Environment == Darwin::MacABI) { + const auto &VM = SDKInfo->getVersionMap().MacOS2iOSMacMapping; + auto It = VM.find(SDKVersion.getAsString()); + if (It != VM.end()) + SDKVersion = It->getValue(); + else + SDKVersion = VersionTuple(0, 0, 0); + } + CmdArgs.push_back(Args.MakeArgString(SDKVersion.getAsString())); + } else { + // Use a blank SDK version if it's not present. + CmdArgs.push_back("0.0.0"); + } + }; + EmitPlatformVersionArg(getOSTargetVersion(), TargetPlatform, + TargetEnvironment); +} + +// Add additional link args for the -dynamiclib option. +static void addDynamicLibLinkArgs(const Darwin &D, const ArgList &Args, + ArgStringList &CmdArgs) { + // Derived from darwin_dylib1 spec. + if (D.isTargetIPhoneOS()) { + if (D.isIPhoneOSVersionLT(3, 1)) + CmdArgs.push_back("-ldylib1.o"); + return; } + + if (!D.isTargetMacOS()) + return; + if (D.isMacosxVersionLT(10, 5)) + CmdArgs.push_back("-ldylib1.o"); + else if (D.isMacosxVersionLT(10, 6)) + CmdArgs.push_back("-ldylib1.10.5.o"); } -void Darwin::addStartObjectFileArgs(const ArgList &Args, - ArgStringList &CmdArgs) const { - // Derived from startfile spec. - if (Args.hasArg(options::OPT_dynamiclib)) { - // Derived from darwin_dylib1 spec. - if (isTargetWatchOSBased()) { - ; // watchOS does not need dylib1.o. - } else if (isTargetIOSSimulator()) { - ; // iOS simulator does not need dylib1.o. - } else if (isTargetIPhoneOS()) { - if (isIPhoneOSVersionLT(3, 1)) - CmdArgs.push_back("-ldylib1.o"); +// Add additional link args for the -bundle option. +static void addBundleLinkArgs(const Darwin &D, const ArgList &Args, + ArgStringList &CmdArgs) { + if (Args.hasArg(options::OPT_static)) + return; + // Derived from darwin_bundle1 spec. + if ((D.isTargetIPhoneOS() && D.isIPhoneOSVersionLT(3, 1)) || + (D.isTargetMacOS() && D.isMacosxVersionLT(10, 6))) + CmdArgs.push_back("-lbundle1.o"); +} + +// Add additional link args for the -pg option. +static void addPgProfilingLinkArgs(const Darwin &D, const ArgList &Args, + ArgStringList &CmdArgs) { + if (D.isTargetMacOS() && D.isMacosxVersionLT(10, 9)) { + if (Args.hasArg(options::OPT_static) || Args.hasArg(options::OPT_object) || + Args.hasArg(options::OPT_preload)) { + CmdArgs.push_back("-lgcrt0.o"); } else { - if (isMacosxVersionLT(10, 5)) - CmdArgs.push_back("-ldylib1.o"); - else if (isMacosxVersionLT(10, 6)) - CmdArgs.push_back("-ldylib1.10.5.o"); + CmdArgs.push_back("-lgcrt1.o"); + + // darwin_crt2 spec is empty. } + // By default on OS X 10.8 and later, we don't link with a crt1.o + // file and the linker knows to use _main as the entry point. But, + // when compiling with -pg, we need to link with the gcrt1.o file, + // so pass the -no_new_main option to tell the linker to use the + // "start" symbol as the entry point. + if (!D.isMacosxVersionLT(10, 8)) + CmdArgs.push_back("-no_new_main"); } else { - if (Args.hasArg(options::OPT_bundle)) { - if (!Args.hasArg(options::OPT_static)) { - // Derived from darwin_bundle1 spec. - if (isTargetWatchOSBased()) { - ; // watchOS does not need bundle1.o. - } else if (isTargetIOSSimulator()) { - ; // iOS simulator does not need bundle1.o. - } else if (isTargetIPhoneOS()) { - if (isIPhoneOSVersionLT(3, 1)) - CmdArgs.push_back("-lbundle1.o"); - } else { - if (isMacosxVersionLT(10, 6)) - CmdArgs.push_back("-lbundle1.o"); - } - } - } else { - if (Args.hasArg(options::OPT_pg) && SupportsProfiling()) { - if (isTargetMacOS() && isMacosxVersionLT(10, 9)) { - if (Args.hasArg(options::OPT_static) || - Args.hasArg(options::OPT_object) || - Args.hasArg(options::OPT_preload)) { - CmdArgs.push_back("-lgcrt0.o"); - } else { - CmdArgs.push_back("-lgcrt1.o"); - - // darwin_crt2 spec is empty. - } - // By default on OS X 10.8 and later, we don't link with a crt1.o - // file and the linker knows to use _main as the entry point. But, - // when compiling with -pg, we need to link with the gcrt1.o file, - // so pass the -no_new_main option to tell the linker to use the - // "start" symbol as the entry point. - if (isTargetMacOS() && !isMacosxVersionLT(10, 8)) - CmdArgs.push_back("-no_new_main"); - } else { - getDriver().Diag(diag::err_drv_clang_unsupported_opt_pg_darwin) - << isTargetMacOS(); - } - } else { - if (Args.hasArg(options::OPT_static) || - Args.hasArg(options::OPT_object) || - Args.hasArg(options::OPT_preload)) { - CmdArgs.push_back("-lcrt0.o"); - } else { - // Derived from darwin_crt1 spec. - if (isTargetWatchOSBased()) { - ; // watchOS does not need crt1.o. - } else if (isTargetIOSSimulator()) { - ; // iOS simulator does not need crt1.o. - } else if (isTargetIPhoneOS()) { - if (getArch() == llvm::Triple::aarch64) - ; // iOS does not need any crt1 files for arm64 - else if (isIPhoneOSVersionLT(3, 1)) - CmdArgs.push_back("-lcrt1.o"); - else if (isIPhoneOSVersionLT(6, 0)) - CmdArgs.push_back("-lcrt1.3.1.o"); - } else { - if (isMacosxVersionLT(10, 5)) - CmdArgs.push_back("-lcrt1.o"); - else if (isMacosxVersionLT(10, 6)) - CmdArgs.push_back("-lcrt1.10.5.o"); - else if (isMacosxVersionLT(10, 8)) - CmdArgs.push_back("-lcrt1.10.6.o"); - - // darwin_crt2 spec is empty. - } - } - } - } + D.getDriver().Diag(diag::err_drv_clang_unsupported_opt_pg_darwin) + << D.isTargetMacOS(); + } +} + +static void addDefaultCRTLinkArgs(const Darwin &D, const ArgList &Args, + ArgStringList &CmdArgs) { + // Derived from darwin_crt1 spec. + if (D.isTargetIPhoneOS()) { + if (D.getArch() == llvm::Triple::aarch64) + ; // iOS does not need any crt1 files for arm64 + else if (D.isIPhoneOSVersionLT(3, 1)) + CmdArgs.push_back("-lcrt1.o"); + else if (D.isIPhoneOSVersionLT(6, 0)) + CmdArgs.push_back("-lcrt1.3.1.o"); + return; } - if (!isTargetIPhoneOS() && Args.hasArg(options::OPT_shared_libgcc) && - !isTargetWatchOS() && isMacosxVersionLT(10, 5)) { + if (!D.isTargetMacOS()) + return; + if (D.isMacosxVersionLT(10, 5)) + CmdArgs.push_back("-lcrt1.o"); + else if (D.isMacosxVersionLT(10, 6)) + CmdArgs.push_back("-lcrt1.10.5.o"); + else if (D.isMacosxVersionLT(10, 8)) + CmdArgs.push_back("-lcrt1.10.6.o"); + // darwin_crt2 spec is empty. +} + +void Darwin::addStartObjectFileArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + // Derived from startfile spec. + if (Args.hasArg(options::OPT_dynamiclib)) + addDynamicLibLinkArgs(*this, Args, CmdArgs); + else if (Args.hasArg(options::OPT_bundle)) + addBundleLinkArgs(*this, Args, CmdArgs); + else if (Args.hasArg(options::OPT_pg) && SupportsProfiling()) + addPgProfilingLinkArgs(*this, Args, CmdArgs); + else if (Args.hasArg(options::OPT_static) || + Args.hasArg(options::OPT_object) || + Args.hasArg(options::OPT_preload)) + CmdArgs.push_back("-lcrt0.o"); + else + addDefaultCRTLinkArgs(*this, Args, CmdArgs); + + if (isTargetMacOS() && Args.hasArg(options::OPT_shared_libgcc) && + isMacosxVersionLT(10, 5)) { const char *Str = Args.MakeArgString(GetFilePath("crt3.o")); CmdArgs.push_back(Str); } @@ -2668,19 +2829,19 @@ 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. - if (!(isTargetMacOS() && isMacosxVersionLT(10, 9)) - && !(isTargetIPhoneOS() && isIPhoneOSVersionLT(5, 0))) + if (!(isTargetMacOS() && isMacosxVersionLT(10, 9)) && + !(isTargetIPhoneOS() && isIPhoneOSVersionLT(5, 0))) Res |= SanitizerKind::Vptr; - if (isTargetMacOS()) { - if (IsX86_64) - Res |= SanitizerKind::Thread; - } else if (isTargetIOSSimulator() || isTargetTvOSSimulator()) { - if (IsX86_64) - Res |= SanitizerKind::Thread; + if (IsX86_64 && (isTargetMacOSBased() || isTargetIOSSimulator() || + isTargetTvOSSimulator())) { + Res |= SanitizerKind::Thread; } return Res; } diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h index 1b193a4c4eb96..37dee33781b4d 100644 --- a/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -288,13 +288,16 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { enum DarwinEnvironmentKind { NativeEnvironment, Simulator, + MacABI, }; mutable DarwinPlatformKind TargetPlatform; mutable DarwinEnvironmentKind TargetEnvironment; - /// The OS version we are targeting. + /// The native OS version we are targeting. mutable VersionTuple TargetVersion; + /// The OS version we are targeting as specified in the triple. + mutable VersionTuple OSTargetVersion; /// The information about the darwin SDK that was used. mutable Optional SDKInfo; @@ -340,12 +343,14 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { // FIXME: Eliminate these ...Target functions and derive separate tool chains // for these targets and put version in constructor. void setTarget(DarwinPlatformKind Platform, DarwinEnvironmentKind Environment, - unsigned Major, unsigned Minor, unsigned Micro) const { + unsigned Major, unsigned Minor, unsigned Micro, + VersionTuple NativeTargetVersion) const { // FIXME: For now, allow reinitialization as long as values don't // change. This will go away when we move away from argument translation. if (TargetInitialized && TargetPlatform == Platform && TargetEnvironment == Environment && - TargetVersion == VersionTuple(Major, Minor, Micro)) + (Environment == MacABI ? OSTargetVersion : TargetVersion) == + VersionTuple(Major, Minor, Micro)) return; assert(!TargetInitialized && "Target already initialized!"); @@ -355,8 +360,14 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { TargetVersion = VersionTuple(Major, Minor, Micro); if (Environment == Simulator) const_cast(this)->setTripleEnvironment(llvm::Triple::Simulator); + else if (Environment == MacABI) { + const_cast(this)->setTripleEnvironment(llvm::Triple::MacABI); + TargetVersion = NativeTargetVersion; + OSTargetVersion = VersionTuple(Major, Minor, Micro); + } } +public: bool isTargetIPhoneOS() const { assert(TargetInitialized && "Target not initialized!"); return (TargetPlatform == IPhoneOS || TargetPlatform == TvOS) && @@ -404,16 +415,33 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { return TargetPlatform == WatchOS; } + bool isTargetMacABI() const { + assert(TargetInitialized && "Target not initialized!"); + return TargetPlatform == IPhoneOS && TargetEnvironment == MacABI; + } + bool isTargetMacOS() const { assert(TargetInitialized && "Target not initialized!"); return TargetPlatform == MacOS; } + bool isTargetMacOSBased() const { + assert(TargetInitialized && "Target not initialized!"); + return TargetPlatform == MacOS || isTargetMacABI(); + } + + bool isTargetAppleSiliconMac() const { + assert(TargetInitialized && "Target not initialized!"); + return isTargetMacOSBased() && getArch() == llvm::Triple::aarch64; + } + bool isTargetInitialized() const { return TargetInitialized; } - VersionTuple getTargetVersion() const { + /// The version of the OS that's used by the OS specified in the target + /// triple. + VersionTuple getOSTargetVersion() const { assert(TargetInitialized && "Target not initialized!"); - return TargetVersion; + return isTargetMacABI() ? OSTargetVersion : TargetVersion; } bool isIPhoneOSVersionLT(unsigned V0, unsigned V1 = 0, @@ -422,11 +450,20 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { return TargetVersion < VersionTuple(V0, V1, V2); } + /// Returns true if the minimum supported macOS version for the slice that's + /// being built is less than the specified version. If there's no minimum + /// supported macOS version, the deployment target version is compared to the + /// specifed version instead. bool isMacosxVersionLT(unsigned V0, unsigned V1 = 0, unsigned V2 = 0) const { - assert(isTargetMacOS() && "Unexpected call for non OS X target!"); - return TargetVersion < VersionTuple(V0, V1, V2); + assert(isTargetMacOS() && getTriple().isMacOSX() && + "Unexpected call for non OS X target!"); + VersionTuple MinVers = getTriple().getMinimumSupportedOSVersion(); + return (!MinVers.empty() && MinVers > TargetVersion + ? MinVers + : TargetVersion) < VersionTuple(V0, V1, V2); } +protected: /// Return true if c++17 aligned allocation/deallocation functions are not /// implemented in the c++ standard library of the deployment target we are /// targeting. @@ -474,6 +511,8 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public MachO { // and for everything in 10.6 and beyond if (isTargetIOSBased() || isTargetWatchOSBased()) return 1; + if (isTargetMacABI()) + return 1; else if (isTargetMacOS() && !isMacosxVersionLT(10, 6)) return 1; else if (isTargetMacOS() && !isMacosxVersionLT(10, 5) && !KernelOrKext) @@ -524,6 +563,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 e9cd327754efe..255c1edaa3df2 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -450,6 +450,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 ead6b47432073..d04beb12c60a6 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 b3264952ff47b..00bd3dec6f9a2 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 688f21dd0908b..18c61276c5c21 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()); @@ -1102,8 +1125,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, @@ -1128,6 +1153,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. @@ -1145,8 +1174,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 6f6f43ca284ba..25fe251622997 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; @@ -758,8 +844,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.CodeViewGHash = Args.hasArg(OPT_gcodeview_ghash); Opts.MacroDebugInfo = Args.hasArg(OPT_debug_info_macro); Opts.WholeProgramVTables = Args.hasArg(OPT_fwhole_program_vtables); - Opts.VirtualFunctionElimination = - Args.hasArg(OPT_fvirtual_function_elimination); Opts.LTOVisibilityPublicStd = Args.hasArg(OPT_flto_visibility_public_std); Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); Opts.SplitDwarfOutput = Args.getLastArgValue(OPT_split_dwarf_output); @@ -781,17 +865,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.DisableLLVMPasses = Args.hasArg(OPT_disable_llvm_passes); Opts.DisableLifetimeMarkers = Args.hasArg(OPT_disable_lifetimemarkers); - - const llvm::Triple::ArchType DebugEntryValueArchs[] = { - llvm::Triple::x86, llvm::Triple::x86_64, llvm::Triple::aarch64, - llvm::Triple::arm, llvm::Triple::armeb}; - - llvm::Triple T(TargetOpts.Triple); - if (Opts.OptimizationLevel > 0 && - Opts.getDebugInfo() >= codegenoptions::LimitedDebugInfo && - llvm::is_contained(DebugEntryValueArchs, T.getArch())) - Opts.EnableDebugEntryValues = Args.hasArg(OPT_femit_debug_entry_values); - Opts.DisableO0ImplyOptNone = Args.hasArg(OPT_disable_O0_optnone); Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); Opts.IndirectTlsSegRefs = Args.hasArg(OPT_mno_tls_direct_seg_refs); @@ -1368,6 +1441,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)) { @@ -1408,6 +1483,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); @@ -1421,6 +1505,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); @@ -1970,6 +2055,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(); @@ -2093,7 +2182,8 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, Opts.ModulesStrictContextHash = Args.hasArg(OPT_fmodules_strict_context_hash); Opts.ModulesValidateDiagnosticOptions = !Args.hasArg(OPT_fmodules_disable_diagnostic_validation); - Opts.ImplicitModuleMaps = Args.hasArg(OPT_fimplicit_module_maps); + Opts.ImplicitModuleMaps = Args.hasFlag(OPT_fimplicit_module_maps, + OPT_fno_implicit_module_maps, false); Opts.ModuleMapFileHomeIsCwd = Args.hasArg(OPT_fmodule_map_file_home_is_cwd); Opts.ModuleCachePruneInterval = getLastArgIntValue(Args, OPT_fmodules_prune_interval, 7 * 24 * 60 * 60); @@ -2199,6 +2289,27 @@ 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()); +} + +static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args) { + Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics); + Opts.PointerAuthCalls = Args.hasArg(OPT_fptrauth_calls); + Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns); + Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); + Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps); + Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, const llvm::Triple &T, PreprocessorOptions &PPOpts, @@ -2806,8 +2917,17 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.ModulesLocalVisibility = Args.hasArg(OPT_fmodules_local_submodule_visibility) || Opts.ModulesTS || Opts.CPlusPlusModules; + Opts.ODRCheckAttributes = Args.hasArg(OPT_fodr_hash_attributes); + Opts.ODRCheckCategories = !Args.hasArg(OPT_fno_odr_hash_categories); + Opts.ODRCheckInterfaces = !Args.hasArg(OPT_fno_odr_hash_interfaces); + Opts.ODRCheckProtocols = !Args.hasArg(OPT_fno_odr_hash_protocols); + Opts.ODRCheckRecords = !Args.hasArg(OPT_fno_odr_hash_records); + Opts.ODRCheckProperties = !Args.hasArg(OPT_fno_odr_hash_properties); + Opts.ODRCheckIvars = !Args.hasArg(OPT_fno_odr_hash_ivars); + Opts.ODRCheckMethods = !Args.hasArg(OPT_fno_odr_hash_methods); 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); @@ -2913,6 +3033,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 @@ -3492,6 +3614,28 @@ static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args, } } +static void removeExplicitModuleBuildIncompatibleOptions(InputArgList &Args) { + auto REMBIO = llvm::find_if(Args, [](const Arg *A){ + return A->getOption().getID() == + OPT_remove_preceeding_explicit_module_build_incompatible_options; + }); + if (REMBIO == Args.end()) + return; + + llvm::SmallPtrSet BeforeREMBIO; + for (auto I = Args.begin(); I != REMBIO; ++I) + BeforeREMBIO.insert(*I); + + Args.eraseArgIf([&](const Arg *A) { + if (!BeforeREMBIO.count(A)) + return false; + const Option &O = A->getOption(); + return O.matches(OPT_INPUT) || + O.matches(OPT_Action_Group) || + O.matches(OPT__output); + }); +} + bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ArrayRef CommandLineArgs, DiagnosticsEngine &Diags) { @@ -3503,6 +3647,9 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, unsigned MissingArgIndex, MissingArgCount; InputArgList Args = Opts.ParseArgs(CommandLineArgs, MissingArgIndex, MissingArgCount, IncludedFlagsBitmask); + + removeExplicitModuleBuildIncompatibleOptions(Args); + LangOptions &LangOpts = *Res.getLangOpts(); // Check for missing argument error. @@ -3541,10 +3688,12 @@ 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); + + ParsePointerAuthArgs(LangOpts, Args); + llvm::Triple T(Res.getTargetOpts().Triple); if (DashX.getFormat() == InputKind::Precompiled || DashX.getLanguage() == Language::LLVM_IR) { @@ -3580,6 +3729,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. @@ -3605,6 +3758,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"); @@ -3618,7 +3775,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; @@ -3703,7 +3869,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 @@ -3719,6 +3897,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 934d17b3c9251..31988d5155be8 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -345,7 +345,8 @@ static std::error_code collectModuleHeaderIncludes( // file relative to the module build directory (the directory containing // the module map file) so this will find the same file that we found // while parsing the module map. - addHeaderInclude(H.NameAsWritten, Includes, LangOpts, Module->IsExternC); + addHeaderInclude(H.PathRelativeToRootModuleDirectory, Includes, LangOpts, + Module->IsExternC); } } // Note that Module->PrivateHeaders will not be a TopHeader. @@ -354,8 +355,8 @@ static std::error_code collectModuleHeaderIncludes( Module->addTopHeader(UmbrellaHeader.Entry); if (Module->Parent) // Include the umbrella header for submodules. - addHeaderInclude(UmbrellaHeader.NameAsWritten, Includes, LangOpts, - Module->IsExternC); + addHeaderInclude(UmbrellaHeader.PathRelativeToRootModuleDirectory, + Includes, LangOpts, Module->IsExternC); } else if (Module::DirectoryName UmbrellaDir = Module->getUmbrellaDir()) { // Add all of the headers we find in this subdirectory. std::error_code EC; @@ -363,6 +364,7 @@ static std::error_code collectModuleHeaderIncludes( llvm::sys::path::native(UmbrellaDir.Entry->getName(), DirNative); llvm::vfs::FileSystem &FS = FileMgr.getVirtualFileSystem(); + SmallVector, 8> Headers; for (llvm::vfs::recursive_directory_iterator Dir(FS, DirNative, EC), End; Dir != End && !EC; Dir.increment(EC)) { // Check whether this entry has an extension typically associated with @@ -388,18 +390,31 @@ static std::error_code collectModuleHeaderIncludes( auto PathIt = llvm::sys::path::rbegin(Dir->path()); for (int I = 0; I != Dir.level() + 1; ++I, ++PathIt) Components.push_back(*PathIt); - SmallString<128> RelativeHeader(UmbrellaDir.NameAsWritten); + SmallString<128> RelativeHeader( + UmbrellaDir.PathRelativeToRootModuleDirectory); for (auto It = Components.rbegin(), End = Components.rend(); It != End; ++It) llvm::sys::path::append(RelativeHeader, *It); - // Include this header as part of the umbrella directory. - Module->addTopHeader(*Header); - addHeaderInclude(RelativeHeader, Includes, LangOpts, Module->IsExternC); + std::string RelName = RelativeHeader.c_str(); + Headers.push_back(std::make_pair(RelName, *Header)); } if (EC) return EC; + + // Sort header paths and make the header inclusion order deterministic + // across different OSs and filesystems. + llvm::sort(Headers.begin(), Headers.end(), []( + const std::pair &LHS, + const std::pair &RHS) { + return LHS.first > RHS.first; + }); + for (auto &H : Headers) { + // Include this header as part of the umbrella directory. + Module->addTopHeader(H.second); + addHeaderInclude(H.first, Includes, LangOpts, Module->IsExternC); + } } // Recurse into submodules. @@ -460,7 +475,7 @@ static Module *prepareToBuildModule(CompilerInstance &CI, // Dig out the module definition. HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); Module *M = HS.lookupModule(CI.getLangOpts().CurrentModule, - /*AllowSearch=*/false); + /*AllowSearch=*/true); if (!M) { CI.getDiagnostics().Report(diag::err_missing_module) << CI.getLangOpts().CurrentModule << ModuleMapFilename; @@ -518,8 +533,8 @@ getInputBufferForModule(CompilerInstance &CI, Module *M) { SmallString<256> HeaderContents; std::error_code Err = std::error_code(); if (Module::Header UmbrellaHeader = M->getUmbrellaHeader()) - addHeaderInclude(UmbrellaHeader.NameAsWritten, HeaderContents, - CI.getLangOpts(), M->IsExternC); + addHeaderInclude(UmbrellaHeader.PathRelativeToRootModuleDirectory, + HeaderContents, CI.getLangOpts(), M->IsExternC); Err = collectModuleHeaderIncludes( CI.getLangOpts(), FileMgr, CI.getDiagnostics(), CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M, @@ -710,6 +725,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; @@ -738,6 +754,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()) @@ -1077,6 +1096,9 @@ void WrapperFrontendAction::ExecuteAction() { void WrapperFrontendAction::EndSourceFileAction() { WrappedAction->EndSourceFileAction(); } +bool WrapperFrontendAction::shouldEraseOutputFiles() { + return WrappedAction->shouldEraseOutputFiles(); +} bool WrapperFrontendAction::usesPreprocessorOnly() const { return WrappedAction->usesPreprocessorOnly(); diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 8574d0a7e8132..09efb64b53ebc 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -295,7 +295,7 @@ bool GenerateHeaderModuleAction::BeginSourceFileAction( << Name; continue; } - Headers.push_back({Name, &FE->getFileEntry()}); + Headers.push_back({std::string(Name), std::string(Name), &FE->getFileEntry()}); } HS.getModuleMap().createHeaderModule(CI.getLangOpts().CurrentModule, Headers); diff --git a/clang/lib/Frontend/LogDiagnosticPrinter.cpp b/clang/lib/Frontend/LogDiagnosticPrinter.cpp index 4bac17553999d..4ae77788b4a5b 100644 --- a/clang/lib/Frontend/LogDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -11,6 +11,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/PlistSupport.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -35,6 +36,27 @@ static StringRef getLevelName(DiagnosticsEngine::Level Level) { llvm_unreachable("Invalid DiagnosticsEngine level!"); } +void LogDiagnosticPrinter::EmitDiagEntryLocation( + llvm::raw_ostream &OS, StringRef Indent, + const LogDiagnosticPrinter::DiagEntryLocation &Del) { + OS << Indent << "\n"; + if (!Del.Filename.empty()) { + OS << Indent << " filename\n"; + OS << Indent << " "; + EmitString(OS, Del.Filename) << '\n'; + } + OS << Indent << " line\n"; + OS << Indent << " "; + EmitInteger(OS, Del.Line) << '\n'; + OS << Indent << " column\n"; + OS << Indent << " "; + EmitInteger(OS, Del.Column) << '\n'; + OS << Indent << " offset\n"; + OS << Indent << " "; + EmitInteger(OS, Del.Offset) << '\n'; + OS << Indent << "\n"; +} + void LogDiagnosticPrinter::EmitDiagEntry(llvm::raw_ostream &OS, const LogDiagnosticPrinter::DiagEntry &DE) { @@ -70,6 +92,41 @@ LogDiagnosticPrinter::EmitDiagEntry(llvm::raw_ostream &OS, << " "; EmitString(OS, DE.WarningOption) << '\n'; } + if (!DE.SourceRanges.empty()) { + OS << " source-ranges\n" + << " \n"; + for (auto R = DE.SourceRanges.begin(), E = DE.SourceRanges.end(); R != E; + ++R) { + OS << " \n"; + OS << " start-at\n"; + EmitDiagEntryLocation(OS, " ", R->Start); + OS << " end-before\n"; + EmitDiagEntryLocation(OS, " ", R->End); + OS << " \n"; + } + OS << " \n"; + } + if (!DE.FixIts.empty()) { + OS << " fixits\n" + << " \n"; + for (auto F = DE.FixIts.begin(), E = DE.FixIts.end(); F != E; ++F) { + OS << " \n"; + OS << " start-at\n"; + EmitDiagEntryLocation(OS, " ", F->RemoveRange.Start); + OS << " end-before\n"; + EmitDiagEntryLocation(OS, " ", F->RemoveRange.End); + // Always issue a replacement key/value, even if CodeToInsert is empty. + OS << " replacement\n" + << " "; + if (F->CodeToInsert.empty()) { + OS << "\n"; + } else { + EmitString(OS, F->CodeToInsert) << '\n'; + } + OS << " \n"; + } + OS << " \n"; + } OS << " \n"; } @@ -158,7 +215,72 @@ void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, } } + auto InitDer = [](DiagEntryRange &Der, const CharSourceRange &R, + const DiagEntry &DE, const Diagnostic &Info, + const LangOptions *LangOpts) -> bool { + if (!R.isValid() || !Info.hasSourceManager()) + return false; + + SourceManager &SM = Info.getSourceManager(); + FullSourceLoc StartLoc = FullSourceLoc(R.getBegin(), SM); + FullSourceLoc EndLoc = FullSourceLoc(R.getEnd(), SM); + if (StartLoc.isInvalid() || EndLoc.isInvalid()) + return false; + + PresumedLoc StartPLoc = + StartLoc.hasManager() ? StartLoc.getPresumedLoc() : PresumedLoc(); + StringRef StartFilename = StartPLoc.getFilename(); + if (!DE.Filename.empty() && DE.Filename == StartFilename) + StartFilename = ""; + + PresumedLoc EndPLoc = + EndLoc.hasManager() ? EndLoc.getPresumedLoc() : PresumedLoc(); + StringRef EndFilename = EndPLoc.getFilename(); + if (!DE.Filename.empty() && DE.Filename == EndFilename) + EndFilename = ""; + + unsigned TokSize = 0; + if (R.isTokenRange()) + TokSize = Lexer::MeasureTokenLength(R.getEnd(), SM, *LangOpts); + Der = {{StartFilename, StartPLoc.getLine(), StartPLoc.getColumn(), + StartLoc.getFileOffset()}, + {EndFilename, EndPLoc.getLine(), EndPLoc.getColumn() + TokSize, + EndLoc.getFileOffset() + TokSize}}; + return true; + }; + + ArrayRef ranges = Info.getRanges(); + for (ArrayRef::iterator R = ranges.begin(), + E = ranges.end(); R != E; ++R) { + DiagEntryRange Der; + bool Success = InitDer(Der, *R, DE, Info, LangOpts); + if (Success) + DE.SourceRanges.push_back(Der); + } + + ArrayRef fixits = Info.getFixItHints(); + for (ArrayRef::iterator F = fixits.begin(), E = fixits.end(); + F != E; ++F) { + // We follow FixItRewriter's example in not (yet) handling + // fix-its in macros. + if (F->RemoveRange.getBegin().isMacroID() || + F->RemoveRange.getEnd().isMacroID()) { + // If any bad FixItHint, skip all of them; the rest + // might not make sense independent of the skipped ones. + DE.FixIts.clear(); + break; + } + if (F->isNull()) + continue; + + DiagEntryFixIt FI; + bool Success = InitDer(FI.RemoveRange, F->RemoveRange, DE, Info, LangOpts); + if (Success) { + FI.CodeToInsert = F->CodeToInsert; + DE.FixIts.push_back(FI); + } + } + // Record the diagnostic entry. Entries.push_back(DE); } - diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp index 82c2af87706eb..56e05242f7c99 100644 --- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -89,9 +89,10 @@ namespace { class StandardDirective : public Directive { public: StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - bool MatchAnyLine, StringRef Text, unsigned Min, - unsigned Max) - : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) {} + bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine, + MatchAnyLine, Text, Min, Max) {} bool isValid(std::string &Error) override { // all strings are considered valid; even empty ones @@ -107,9 +108,10 @@ class StandardDirective : public Directive { class RegexDirective : public Directive { public: RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, - bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max, - StringRef RegexStr) - : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max), + bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max, StringRef RegexStr) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine, + MatchAnyLine, Text, Min, Max), Regex(RegexStr) {} bool isValid(std::string &Error) override { @@ -294,11 +296,13 @@ struct UnattachedDirective { // Attach the specified directive to the line of code indicated by // \p ExpectedLoc. void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD, - SourceLocation ExpectedLoc, bool MatchAnyLine = false) { + SourceLocation ExpectedLoc, + bool MatchAnyFileAndLine = false, + bool MatchAnyLine = false) { // Construct new directive. - std::unique_ptr D = - Directive::create(UD.RegexKind, UD.DirectivePos, ExpectedLoc, - MatchAnyLine, UD.Text, UD.Min, UD.Max); + std::unique_ptr D = Directive::create( + UD.RegexKind, UD.DirectivePos, ExpectedLoc, MatchAnyFileAndLine, + MatchAnyLine, UD.Text, UD.Min, UD.Max); std::string Error; if (!D->isValid(Error)) { @@ -498,6 +502,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, // Next optional token: @ SourceLocation ExpectedLoc; StringRef Marker; + bool MatchAnyFileAndLine = false; bool MatchAnyLine = false; if (!PH.Next("@")) { ExpectedLoc = Pos; @@ -526,26 +531,39 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, StringRef Filename(PH.C, PH.P-PH.C); PH.Advance(); - // Lookup file via Preprocessor, like a #include. - const DirectoryLookup *CurDir; - Optional File = - PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, - nullptr, nullptr, nullptr, nullptr, nullptr); - if (!File) { - Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), - diag::err_verify_missing_file) << Filename << KindStr; - continue; - } - - const FileEntry *FE = &File->getFileEntry(); - if (SM.translateFile(FE).isInvalid()) - SM.createFileID(FE, Pos, SrcMgr::C_User); - - if (PH.Next(Line) && Line > 0) - ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); - else if (PH.Next("*")) { + if (Filename == "*") { + MatchAnyFileAndLine = true; + if (!PH.Next("*")) { + Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin), + diag::err_verify_missing_line) + << "'*'"; + continue; + } MatchAnyLine = true; - ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); + ExpectedLoc = SourceLocation(); + } else { + // Lookup file via Preprocessor, like a #include. + const DirectoryLookup *CurDir; + Optional File = + PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, + nullptr, nullptr, nullptr, nullptr, nullptr); + if (!File) { + Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin), + diag::err_verify_missing_file) + << Filename << KindStr; + continue; + } + + const FileEntry *FE = &File->getFileEntry(); + if (SM.translateFile(FE).isInvalid()) + SM.createFileID(FE, Pos, SrcMgr::C_User); + + if (PH.Next(Line) && Line > 0) + ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); + else if (PH.Next("*")) { + MatchAnyLine = true; + ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); + } } } else if (PH.Next("*")) { MatchAnyLine = true; @@ -631,7 +649,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, } if (Marker.empty()) - attachDirective(Diags, D, ExpectedLoc, MatchAnyLine); + attachDirective(Diags, D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine); else Markers.addDirective(Marker, D); FoundDirective = true; @@ -877,7 +895,7 @@ static unsigned PrintExpected(DiagnosticsEngine &Diags, SmallString<256> Fmt; llvm::raw_svector_ostream OS(Fmt); for (const auto *D : DL) { - if (D->DiagnosticLoc.isInvalid()) + if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine) OS << "\n File *"; else OS << "\n File " << SourceMgr.getFilename(D->DiagnosticLoc); @@ -937,7 +955,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, continue; } - if (!D.DiagnosticLoc.isInvalid() && + if (!D.DiagnosticLoc.isInvalid() && !D.MatchAnyFileAndLine && !IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) continue; @@ -1114,11 +1132,13 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() { std::unique_ptr Directive::create(bool RegexKind, SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max) { if (!RegexKind) return std::make_unique(DirectiveLoc, DiagnosticLoc, - MatchAnyLine, Text, Min, Max); + MatchAnyFileAndLine, + MatchAnyLine, Text, Min, Max); // Parse the directive into a regular expression. std::string RegexStr; @@ -1143,6 +1163,7 @@ std::unique_ptr Directive::create(bool RegexKind, } } - return std::make_unique( - DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); + return std::make_unique(DirectiveLoc, DiagnosticLoc, + MatchAnyFileAndLine, MatchAnyLine, + Text, Min, Max, RegexStr); } 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 85c3124234adc..fff9b9245d25c 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -84,6 +84,7 @@ set(files pconfigintrin.h popcntintrin.h prfchwintrin.h + ptrauth.h ptwriteintrin.h rdseedintrin.h rtmintrin.h diff --git a/clang/lib/Headers/arm_acle.h b/clang/lib/Headers/arm_acle.h index 596ea03cff2f4..de568b4ff9c51 100644 --- a/clang/lib/Headers/arm_acle.h +++ b/clang/lib/Headers/arm_acle.h @@ -22,31 +22,43 @@ extern "C" { /* 8 SYNCHRONIZATION, BARRIER AND HINT INTRINSICS */ /* 8.3 Memory barriers */ -#if !defined(_MSC_VER) +#if !__has_builtin(__dmb) #define __dmb(i) __builtin_arm_dmb(i) +#endif +#if !__has_builtin(__dsb) #define __dsb(i) __builtin_arm_dsb(i) +#endif +#if !__has_builtin(__isb) #define __isb(i) __builtin_arm_isb(i) #endif /* 8.4 Hints */ -#if !defined(_MSC_VER) +#if !__has_builtin(__wfi) static __inline__ void __attribute__((__always_inline__, __nodebug__)) __wfi(void) { __builtin_arm_wfi(); } +#endif +#if !__has_builtin(__wfe) static __inline__ void __attribute__((__always_inline__, __nodebug__)) __wfe(void) { __builtin_arm_wfe(); } +#endif +#if !__has_builtin(__sev) static __inline__ void __attribute__((__always_inline__, __nodebug__)) __sev(void) { __builtin_arm_sev(); } +#endif +#if !__has_builtin(__sevl) static __inline__ void __attribute__((__always_inline__, __nodebug__)) __sevl(void) { __builtin_arm_sevl(); } +#endif +#if !__has_builtin(__yield) static __inline__ void __attribute__((__always_inline__, __nodebug__)) __yield(void) { __builtin_arm_yield(); } diff --git a/clang/lib/Headers/module.modulemap b/clang/lib/Headers/module.modulemap index 7954a77a41258..d6887bc63997f 100644 --- a/clang/lib/Headers/module.modulemap +++ b/clang/lib/Headers/module.modulemap @@ -40,6 +40,47 @@ module _Builtin_intrinsics [system] [extern_c] { textual header "avx512fintrin.h" textual header "avx512erintrin.h" textual header "fmaintrin.h" + textual header "adxintrin.h" + textual header "avx512bf16intrin.h" + textual header "avx512bitalgintrin.h" + textual header "avx512bwintrin.h" + textual header "avx512cdintrin.h" + textual header "avx512dqintrin.h" + textual header "avx512ifmaintrin.h" + textual header "avx512ifmavlintrin.h" + textual header "avx512pfintrin.h" + textual header "avx512vbmi2intrin.h" + textual header "avx512vbmiintrin.h" + textual header "avx512vbmivlintrin.h" + textual header "avx512vlbf16intrin.h" + textual header "avx512vlbitalgintrin.h" + textual header "avx512vlbwintrin.h" + textual header "avx512vlcdintrin.h" + textual header "avx512vldqintrin.h" + textual header "avx512vlintrin.h" + textual header "avx512vlvbmi2intrin.h" + textual header "avx512vlvnniintrin.h" + textual header "avx512vlvp2intersectintrin.h" + textual header "avx512vnniintrin.h" + textual header "avx512vp2intersectintrin.h" + textual header "avx512vpopcntdqintrin.h" + textual header "avx512vpopcntdqvlintrin.h" + textual header "cetintrin.h" + textual header "clflushoptintrin.h" + textual header "clwbintrin.h" + textual header "enqcmdintrin.h" + textual header "fxsrintrin.h" + textual header "gfniintrin.h" + textual header "pkuintrin.h" + textual header "rtmintrin.h" + textual header "shaintrin.h" + textual header "vaesintrin.h" + textual header "vpclmulqdqintrin.h" + textual header "xsavecintrin.h" + textual header "xsaveintrin.h" + textual header "xsaveoptintrin.h" + textual header "xsavesintrin.h" + textual header "xtestintrin.h" header "x86intrin.h" textual header "bmiintrin.h" @@ -57,6 +98,15 @@ module _Builtin_intrinsics [system] [extern_c] { textual header "sgxintrin.h" textual header "ptwriteintrin.h" textual header "invpcidintrin.h" + textual header "ia32intrin.h" + textual header "lwpintrin.h" + textual header "prfchwintrin.h" + textual header "rdseedintrin.h" + textual header "tbmintrin.h" + textual header "lwpintrin.h" + textual header "prfchwintrin.h" + textual header "rdseedintrin.h" + textual header "tbmintrin.h" textual header "__wmmintrin_aes.h" textual header "__wmmintrin_pclmul.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..7bb53a5f967ba 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -1,17 +1,26 @@ set(LLVM_LINK_COMPONENTS + BitReader + BitstreamReader 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/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp new file mode 100644 index 0000000000000..1d1556f4a52c6 --- /dev/null +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -0,0 +1,482 @@ +//===--- 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/Attr.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 ae9134bf11826..c2c278c9ac1ce 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -517,6 +517,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"); } @@ -530,6 +531,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 4f402135672c3..d9c4f97809771 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 handleDeclOccurrence(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.getASTReader()) { + 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.getASTReader(); + 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.getASTReader()->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.getASTReader(); + 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 a7c37e8528d1b..b2ddb81e1847d 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/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp index 394daf94c4b2d..f3eb653f10fa4 100644 --- a/clang/lib/Index/USRGeneration.cpp +++ b/clang/lib/Index/USRGeneration.cpp @@ -382,6 +382,14 @@ void USRGenerator::VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { Out << "@NA@" << D->getName(); } +static const ObjCCategoryDecl *getCategoryContext(const NamedDecl *D) { + if (auto *CD = dyn_cast(D->getDeclContext())) + return CD; + if (auto *ICD = dyn_cast(D->getDeclContext())) + return ICD->getCategoryDecl(); + return nullptr; +}; + void USRGenerator::VisitObjCMethodDecl(const ObjCMethodDecl *D) { const DeclContext *container = D->getDeclContext(); if (const ObjCProtocolDecl *pd = dyn_cast(container)) { @@ -395,14 +403,6 @@ void USRGenerator::VisitObjCMethodDecl(const ObjCMethodDecl *D) { IgnoreResults = true; return; } - auto getCategoryContext = [](const ObjCMethodDecl *D) -> - const ObjCCategoryDecl * { - if (auto *CD = dyn_cast(D->getDeclContext())) - return CD; - if (auto *ICD = dyn_cast(D->getDeclContext())) - return ICD->getCategoryDecl(); - return nullptr; - }; auto *CD = getCategoryContext(D); VisitObjCContainerDecl(ID, CD); } @@ -475,7 +475,7 @@ void USRGenerator::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { // The USR for a property declared in a class extension or category is based // on the ObjCInterfaceDecl, not the ObjCCategoryDecl. if (const ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) - Visit(ID); + VisitObjCContainerDecl(ID, getCategoryContext(D)); else Visit(cast(D->getDeclContext())); GenObjCProperty(D->getName(), D->isClassProperty()); 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..49725ea1eeadc --- /dev/null +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -0,0 +1,236 @@ +//===--- 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); + } + }; + + // Create the unit path if necessary so that the directory watcher can start + // even if the data has not been populated yet. + if (std::error_code EC = llvm::sys::fs::create_directories(UnitPath)) { + Error = EC.message(); + return true; + } + + 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..c93c02e496fe6 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -1273,20 +1273,18 @@ bool HeaderSearch::ShouldEnterIncludeFile(Preprocessor &PP, // // It's common that libc++ and system modules will both define such // submodules. Make sure cached results for a builtin header won't - // prevent other builtin modules to potentially enter the builtin header. - // Note that builtins are header guarded and the decision to actually - // enter them is postponed to the controlling macros logic below. + // prevent other builtin modules from potentially entering the builtin + // header. Note that builtins are header guarded and the decision to + // actually enter them is postponed to the controlling macros logic below. bool TryEnterHdr = false; if (FileInfo.isCompilingModuleHeader && FileInfo.isModuleHeader) - TryEnterHdr = File->getDir() == ModMap.getBuiltinDir() && - ModuleMap::isBuiltinHeader( - llvm::sys::path::filename(File->getName())); + TryEnterHdr = ModMap.isBuiltinHeader(File); // Textual headers can be #imported from different modules. Since ObjC // 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; @@ -1403,20 +1401,31 @@ static bool suggestModule(HeaderSearch &HS, const FileEntry *File, ModuleMap::KnownHeader *SuggestedModule) { ModuleMap::KnownHeader Module = HS.findModuleForHeader(File, /*AllowTextual*/true); - if (SuggestedModule) - *SuggestedModule = (Module.getRole() & ModuleMap::TextualHeader) - ? ModuleMap::KnownHeader() - : Module; // If this module specifies [no_undeclared_includes], we cannot find any // file that's in a non-dependency module. if (RequestingModule && Module && RequestingModule->NoUndeclaredIncludes) { - HS.getModuleMap().resolveUses(RequestingModule, /*Complain*/false); + HS.getModuleMap().resolveUses(RequestingModule, /*Complain*/ false); if (!RequestingModule->directlyUses(Module.getModule())) { + // Builtin headers are a special case. Multiple modules can use the same + // builtin as a modular header (see also comment in + // ShouldEnterIncludeFile()), so the builtin header may have been + // "claimed" by an unrelated module. This shouldn't prevent us from + // including the builtin header textually in this module. + if (HS.getModuleMap().isBuiltinHeader(File)) { + if (SuggestedModule) + *SuggestedModule = ModuleMap::KnownHeader(); + return true; + } return false; } } + if (SuggestedModule) + *SuggestedModule = (Module.getRole() & ModuleMap::TextualHeader) + ? ModuleMap::KnownHeader() + : Module; + return true; } @@ -1567,6 +1576,16 @@ HeaderSearch::lookupModuleMapFile(const DirectoryEntry *Dir, bool IsFramework) { llvm::sys::path::append(ModuleMapFileName, "module.map"); if (auto F = FileMgr.getFile(ModuleMapFileName)) return *F; + + // For frameworks, allow to have a private module map with a preferred + // spelling when a public module map is absent. + if (IsFramework) { + ModuleMapFileName = Dir->getName(); + llvm::sys::path::append(ModuleMapFileName, "Modules", + "module.private.modulemap"); + if (auto F = FileMgr.getFile(ModuleMapFileName)) + return *F; + } return nullptr; } 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..88d0f6a576804 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -260,9 +260,9 @@ void ModuleMap::resolveHeader(Module *Mod, << UmbrellaMod->getFullModuleName(); else // Record this umbrella header. - setUmbrellaHeader(Mod, File, RelativePathName.str()); + setUmbrellaHeader(Mod, File, Header.FileName, RelativePathName.str()); } else { - Module::Header H = {RelativePathName.str(), File}; + Module::Header H = {Header.FileName, std::string(RelativePathName.str()), File}; if (Header.Kind == Module::HK_Excluded) excludeHeader(Mod, H); else @@ -305,7 +305,7 @@ bool ModuleMap::resolveAsBuiltinHeader( return false; auto Role = headerKindToRole(Header.Kind); - Module::Header H = {Path.str(), *File}; + Module::Header H = {Header.FileName, std::string(Path.str()), *File}; addHeader(Mod, H, Role); return true; } @@ -387,13 +387,17 @@ bool ModuleMap::isBuiltinHeader(StringRef FileName) { .Default(false); } +bool ModuleMap::isBuiltinHeader(const FileEntry *File) { + return File->getDir() == BuiltinIncludeDir && + ModuleMap::isBuiltinHeader(llvm::sys::path::filename(File->getName())); +} + ModuleMap::HeadersMap::iterator ModuleMap::findKnownHeader(const FileEntry *File) { resolveHeaderDirectives(File); HeadersMap::iterator Known = Headers.find(File); if (HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps && - Known == Headers.end() && File->getDir() == BuiltinIncludeDir && - ModuleMap::isBuiltinHeader(llvm::sys::path::filename(File->getName()))) { + Known == Headers.end() && ModuleMap::isBuiltinHeader(File)) { HeaderInfo.loadTopLevelSystemModules(); return Headers.find(File); } @@ -769,8 +773,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; } @@ -1018,11 +1031,14 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir, Result->NoUndeclaredIncludes |= Attrs.NoUndeclaredIncludes; Result->Directory = FrameworkDir; + // Chop off the first framework bit, as that is implied. + StringRef RelativePath = + UmbrellaName.str().substr( + Result->getTopLevelModule()->Directory->getName().size()); + RelativePath = llvm::sys::path::relative_path(RelativePath); + // umbrella header "umbrella-header-name" - // - // The "Headers/" component of the name is implied because this is - // a framework module. - setUmbrellaHeader(Result, *UmbrellaHeader, ModuleName + ".h"); + setUmbrellaHeader(Result, *UmbrellaHeader, ModuleName + ".h", RelativePath); // export * Result->Exports.push_back(Module::ExportDecl(nullptr, true)); @@ -1102,10 +1118,13 @@ Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework, } void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader, - Twine NameAsWritten) { + Twine NameAsWritten, + Twine PathRelativeToRootModuleDirectory) { Headers[UmbrellaHeader].push_back(KnownHeader(Mod, NormalHeader)); Mod->Umbrella = UmbrellaHeader; Mod->UmbrellaAsWritten = NameAsWritten.str(); + Mod->UmbrellaRelativeToRootModuleDirectory = + PathRelativeToRootModuleDirectory.str(); UmbrellaDirs[UmbrellaHeader->getDir()] = Mod; // Notify callbacks that we just added a new header. @@ -1114,9 +1133,12 @@ void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader, } void ModuleMap::setUmbrellaDir(Module *Mod, const DirectoryEntry *UmbrellaDir, - Twine NameAsWritten) { + Twine NameAsWritten, + Twine PathRelativeToRootModuleDirectory) { Mod->Umbrella = UmbrellaDir; Mod->UmbrellaAsWritten = NameAsWritten.str(); + Mod->UmbrellaRelativeToRootModuleDirectory = + PathRelativeToRootModuleDirectory.str(); UmbrellaDirs[UmbrellaDir] = Mod; } @@ -1713,6 +1735,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 }; @@ -2381,6 +2406,7 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { } std::string DirName = Tok.getString(); + std::string DirNameAsWritten = DirName; SourceLocation DirNameLoc = consumeToken(); // Check whether we already have an umbrella. @@ -2422,8 +2448,7 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { for (llvm::vfs::recursive_directory_iterator I(FS, Dir->getName(), EC), E; I != E && !EC; I.increment(EC)) { if (auto FE = SourceMgr.getFileManager().getFile(I->path())) { - - Module::Header Header = {I->path(), *FE}; + Module::Header Header = {"", std::string(I->path()), *FE}; Headers.push_back(std::move(Header)); } } @@ -2444,7 +2469,7 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { } // Record this umbrella directory. - Map.setUmbrellaDir(ActiveModule, Dir, DirName); + Map.setUmbrellaDir(ActiveModule, Dir, DirNameAsWritten, DirName); } /// Parse a module export declaration. @@ -2873,6 +2898,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 +2914,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..e61c1e85d211e 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()); @@ -1939,8 +1937,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( // some directives (e.g. #endif of a header guard) will never be seen. // Since this will lead to confusing errors, avoid the inclusion. if (File && PreambleConditionalStack.isRecording() && - SourceMgr.translateFile(&File->getFileEntry()) == - SourceMgr.getMainFileID()) { + SourceMgr.isMainFile(*File)) { Diag(FilenameTok.getLocation(), diag::err_pp_including_mainfile_in_preamble); return {ImportAction::None}; 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 f8b5fec438007..8b0f29c3b38be 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 a90147ca46928..76a73dc4d1c24 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: @@ -2197,7 +2234,7 @@ bool Parser::ParseAsmAttributesAfterDeclarator(Declarator &D) { // If a simple-asm-expr is present, parse it. if (Tok.is(tok::kw_asm)) { SourceLocation Loc; - ExprResult AsmLabel(ParseSimpleAsm(&Loc)); + ExprResult AsmLabel(ParseSimpleAsm(/*ForAsmLabel*/ true, &Loc)); if (AsmLabel.isInvalid()) { SkipUntil(tok::semi, StopBeforeMatch); return true; @@ -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: @@ -4930,6 +5005,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: @@ -5127,6 +5203,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: @@ -5366,6 +5443,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()); @@ -7134,3 +7217,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 ca2497ef6121d..4e070adb38ba6 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2325,7 +2325,7 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer( // If a simple-asm-expr is present, parse it. if (Tok.is(tok::kw_asm)) { SourceLocation Loc; - ExprResult AsmLabel(ParseSimpleAsm(&Loc)); + ExprResult AsmLabel(ParseSimpleAsm(/*ForAsmLabel*/ true, &Loc)); if (AsmLabel.isInvalid()) SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch); @@ -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/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index fecd70b17e5ba..a39998482e956 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2017,7 +2017,7 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt, // simple-asm-expr[opt] if (Tok.is(tok::kw_asm)) { SourceLocation Loc; - ExprResult AsmLabel(ParseSimpleAsm(&Loc)); + ExprResult AsmLabel(ParseSimpleAsm(/*ForAsmLabel*/ true, &Loc)); if (AsmLabel.isInvalid()) { SkipUntil(tok::semi, StopAtSemi); return Sema::ConditionError(); diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index efcef6d3b1235..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) { diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp index d35973df921b1..ea2c871d6a829 100644 --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -743,7 +743,7 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); - ExprResult AsmString(ParseAsmStringLiteral()); + ExprResult AsmString(ParseAsmStringLiteral(/*ForAsmLabel*/ false)); // Check if GNU-style InlineAsm is disabled. // Error on anything other than empty string. @@ -823,7 +823,7 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { // Parse the asm-string list for clobbers if present. if (!AteExtraColon && isTokenStringLiteral()) { while (1) { - ExprResult Clobber(ParseAsmStringLiteral()); + ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false)); if (Clobber.isInvalid()) break; @@ -920,7 +920,7 @@ bool Parser::ParseAsmOperandsOpt(SmallVectorImpl &Names, } else Names.push_back(nullptr); - ExprResult Constraint(ParseAsmStringLiteral()); + ExprResult Constraint(ParseAsmStringLiteral(/*ForAsmLabel*/ false)); if (Constraint.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return true; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index ed4e6ff0fc538..3b81464694f9f 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) { @@ -418,6 +423,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; @@ -803,7 +811,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, SourceLocation StartLoc = Tok.getLocation(); SourceLocation EndLoc; - ExprResult Result(ParseSimpleAsm(&EndLoc)); + ExprResult Result(ParseSimpleAsm(/*ForAsmLabel*/ false, &EndLoc)); // Check if GNU-style InlineAsm is disabled. // Empty asm string is allowed because it will not introduce @@ -1467,11 +1475,14 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { /// ParseAsmStringLiteral - This is just a normal string-literal, but is not /// allowed to be a wide string, and is not subject to character translation. +/// Unlike GCC, we also diagnose an empty string literal when parsing for an +/// asm label as opposed to an asm statement, because such a construct does not +/// behave well. /// /// [GNU] asm-string-literal: /// string-literal /// -ExprResult Parser::ParseAsmStringLiteral() { +ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) { if (!isTokenStringLiteral()) { Diag(Tok, diag::err_expected_string_literal) << /*Source='in...'*/0 << "'asm'"; @@ -1487,6 +1498,11 @@ ExprResult Parser::ParseAsmStringLiteral() { << SL->getSourceRange(); return ExprError(); } + if (ForAsmLabel && SL->getString().empty()) { + Diag(Tok, diag::err_asm_operand_wide_string_literal) + << 2 /* an empty */ << SL->getSourceRange(); + return ExprError(); + } } return AsmString; } @@ -1496,7 +1512,7 @@ ExprResult Parser::ParseAsmStringLiteral() { /// [GNU] simple-asm-expr: /// 'asm' '(' asm-string-literal ')' /// -ExprResult Parser::ParseSimpleAsm(SourceLocation *EndLoc) { +ExprResult Parser::ParseSimpleAsm(bool ForAsmLabel, SourceLocation *EndLoc) { assert(Tok.is(tok::kw_asm) && "Not an asm!"); SourceLocation Loc = ConsumeToken(); @@ -1516,7 +1532,7 @@ ExprResult Parser::ParseSimpleAsm(SourceLocation *EndLoc) { return ExprError(); } - ExprResult Result(ParseAsmStringLiteral()); + ExprResult Result(ParseAsmStringLiteral(ForAsmLabel)); if (!Result.isInvalid()) { // Close the paren and get the location of the end bracket diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 844abc7ce598f..3ca1461b50419 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -29,6 +29,7 @@ add_clang_library(clangSema Sema.cpp SemaAccess.cpp SemaAttr.cpp + SemaAPINotes.cpp SemaCXXScopeSpec.cpp SemaCast.cpp SemaChecking.cpp @@ -75,4 +76,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..a6555ffa88f6c 100644 --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -217,7 +217,11 @@ void IdentifierResolver::RemoveDecl(NamedDecl *D) { assert(Ptr && "Didn't find this decl on its identifier's chain!"); if (isDeclPtr(Ptr)) { - assert(D == Ptr && "Didn't find this decl on its identifier's chain!"); + // FIXME: The following assert fires for ObjectiveC id, SEL, and Class + // declarations when the module is explicitly built. For implicit builds + // it works fine. rdar://58552906 + + // assert(D == Ptr && "Didn't find this decl on its identifier's chain!"); Name.setFETokenInfo(nullptr); return; } @@ -385,7 +389,7 @@ void IdentifierResolver::updatingIdentifier(IdentifierInfo &II) { PP.getExternalSource()->updateOutOfDateIdentifier(II); if (II.isFromAST()) - II.setFETokenInfoChangedSinceDeserialization(); + II.setChangedSinceDeserialization(); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp index 960e62d4a2db8..b34243edea35e 100644 --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/clang/lib/Sema/JumpDiagnostics.cpp @@ -75,6 +75,7 @@ class JumpScopeChecker { void BuildScopeInformation(Decl *D, unsigned &ParentScope); void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl, unsigned &ParentScope); + void BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope); void BuildScopeInformation(Stmt *S, unsigned &origParentScope); void VerifyJumps(); @@ -276,6 +277,16 @@ void JumpScopeChecker::BuildScopeInformation(VarDecl *D, } } +/// Build scope information for compound literals of C struct types that are +/// non-trivial to destruct. +void JumpScopeChecker::BuildScopeInformation(CompoundLiteralExpr *CLE, + unsigned &ParentScope) { + unsigned InDiag = diag::note_enters_compound_literal_scope; + unsigned OutDiag = diag::note_exits_compound_literal_scope; + Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, CLE->getExprLoc())); + ParentScope = Scopes.size() - 1; +} + /// BuildScopeInformation - The statements from CI to CE are known to form a /// coherent VLA scope with a specified parent node. Walk through the /// statements, adding any labels or gotos to LabelAndGotoScopes and recursively @@ -529,11 +540,15 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, // implementable but a lot of work which we haven't felt up to doing. ExprWithCleanups *EWC = cast(S); for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) { - const BlockDecl *BDecl = EWC->getObject(i); - for (const auto &CI : BDecl->captures()) { - VarDecl *variable = CI.getVariable(); - BuildScopeInformation(variable, BDecl, origParentScope); - } + if (auto *BDecl = EWC->getObject(i).dyn_cast()) + for (const auto &CI : BDecl->captures()) { + VarDecl *variable = CI.getVariable(); + BuildScopeInformation(variable, BDecl, origParentScope); + } + else if (auto *CLE = EWC->getObject(i).dyn_cast()) + BuildScopeInformation(CLE, origParentScope); + else + llvm_unreachable("unexpected cleanup object type"); } break; } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 2cd158a8b43c1..f1a3203de38bf 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 6216206690b0c..104c393d5bda5 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!"); @@ -2899,6 +2909,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)); @@ -2918,6 +2930,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 d8711fb6bcabb..2bd1daa0743b4 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -30,6 +30,7 @@ #include "clang/AST/NSAPI.h" #include "clang/AST/NonTrivialTypeVisitor.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/RecordLayout.h" #include "clang/AST/Stmt.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" @@ -125,6 +126,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 +1009,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 +1765,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: @@ -1518,6 +1845,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return ExprError(); break; case Builtin::BI__builtin_os_log_format: + Cleanup.setExprNeedsCleanups(true); + LLVM_FALLTHROUGH; case Builtin::BI__builtin_os_log_format_buffer_size: if (SemaBuiltinOSLogFormat(TheCall)) return ExprError(); @@ -9488,6 +9817,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()); @@ -12047,7 +12379,16 @@ static void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC, if (E->isTypeDependent() || E->isValueDependent()) return; - if (const auto *UO = dyn_cast(E)) + Expr *SourceExpr = E; + // Examine, but don't traverse into the source expression of an + // OpaqueValueExpr, since it may have multiple parents and we don't want to + // emit duplicate diagnostics. Its fine to examine the form or attempt to + // evaluate it in the context of checking the specific conversion to T though. + if (auto *OVE = dyn_cast(E)) + if (auto *Src = OVE->getSourceExpr()) + SourceExpr = Src; + + if (const auto *UO = dyn_cast(SourceExpr)) if (UO->getOpcode() == UO_Not && UO->getSubExpr()->isKnownToHaveBooleanValue()) S.Diag(UO->getBeginLoc(), diag::warn_bitwise_negation_bool) @@ -12056,21 +12397,20 @@ static void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC, // For conditional operators, we analyze the arguments as if they // were being fed directly into the output. - if (isa(E)) { - ConditionalOperator *CO = cast(E); + if (auto *CO = dyn_cast(SourceExpr)) { CheckConditionalOperator(S, CO, CC, T); return; } // Check implicit argument conversions for function calls. - if (CallExpr *Call = dyn_cast(E)) + if (CallExpr *Call = dyn_cast(SourceExpr)) CheckImplicitArgumentConversions(S, Call, CC); // Go ahead and check any implicit conversions we might have skipped. // The non-canonical typecheck is just an optimization; // CheckImplicitConversion will filter out dead implicit conversions. - if (E->getType() != T) - CheckImplicitConversion(S, E, T, CC, nullptr, IsListInit); + if (SourceExpr->getType() != T) + CheckImplicitConversion(S, SourceExpr, T, CC, nullptr, IsListInit); // Now continue drilling into this expression. @@ -13290,17 +13630,233 @@ bool Sema::CheckParmsForFunctionDef(ArrayRef Parameters, return HasInvalidParm; } -/// A helper function to get the alignment of a Decl referred to by DeclRefExpr -/// or MemberExpr. -static CharUnits getDeclAlign(Expr *E, CharUnits TypeAlign, - ASTContext &Context) { - if (const auto *DRE = dyn_cast(E)) - return Context.getDeclAlign(DRE->getDecl()); +Optional> +static getBaseAlignmentAndOffsetFromPtr(const Expr *E, ASTContext &Ctx); + +/// Compute the alignment and offset of the base class object given the +/// derived-to-base cast expression and the alignment and offset of the derived +/// class object. +static std::pair +getDerivedToBaseAlignmentAndOffset(const CastExpr *CE, QualType DerivedType, + CharUnits BaseAlignment, CharUnits Offset, + ASTContext &Ctx) { + for (auto PathI = CE->path_begin(), PathE = CE->path_end(); PathI != PathE; + ++PathI) { + const CXXBaseSpecifier *Base = *PathI; + const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl(); + if (Base->isVirtual()) { + // The complete object may have a lower alignment than the non-virtual + // alignment of the base, in which case the base may be misaligned. Choose + // the smaller of the non-virtual alignment and BaseAlignment, which is a + // conservative lower bound of the complete object alignment. + CharUnits NonVirtualAlignment = + Ctx.getASTRecordLayout(BaseDecl).getNonVirtualAlignment(); + BaseAlignment = std::min(BaseAlignment, NonVirtualAlignment); + Offset = CharUnits::Zero(); + } else { + const ASTRecordLayout &RL = + Ctx.getASTRecordLayout(DerivedType->getAsCXXRecordDecl()); + Offset += RL.getBaseClassOffset(BaseDecl); + } + DerivedType = Base->getType(); + } + + return std::make_pair(BaseAlignment, Offset); +} - if (const auto *ME = dyn_cast(E)) - return Context.getDeclAlign(ME->getMemberDecl()); +/// Compute the alignment and offset of a binary additive operator. +static Optional> +getAlignmentAndOffsetFromBinAddOrSub(const Expr *PtrE, const Expr *IntE, + bool IsSub, ASTContext &Ctx) { + QualType PointeeType = PtrE->getType()->getPointeeType(); - return TypeAlign; + if (!PointeeType->isConstantSizeType()) + return llvm::None; + + auto P = getBaseAlignmentAndOffsetFromPtr(PtrE, Ctx); + + if (!P) + return llvm::None; + + llvm::APSInt IdxRes; + CharUnits EltSize = Ctx.getTypeSizeInChars(PointeeType); + if (IntE->isIntegerConstantExpr(IdxRes, Ctx)) { + CharUnits Offset = EltSize * IdxRes.getExtValue(); + if (IsSub) + Offset = -Offset; + return std::make_pair(P->first, P->second + Offset); + } + + // If the integer expression isn't a constant expression, compute the lower + // bound of the alignment using the alignment and offset of the pointer + // expression and the element size. + return std::make_pair( + P->first.alignmentAtOffset(P->second).alignmentAtOffset(EltSize), + CharUnits::Zero()); +} + +/// This helper function takes an lvalue expression and returns the alignment of +/// a VarDecl and a constant offset from the VarDecl. +Optional> +static getBaseAlignmentAndOffsetFromLValue(const Expr *E, ASTContext &Ctx) { + E = E->IgnoreParens(); + switch (E->getStmtClass()) { + default: + break; + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::ImplicitCastExprClass: { + auto *CE = cast(E); + const Expr *From = CE->getSubExpr(); + switch (CE->getCastKind()) { + default: + break; + case CK_NoOp: + return getBaseAlignmentAndOffsetFromLValue(From, Ctx); + case CK_UncheckedDerivedToBase: + case CK_DerivedToBase: { + auto P = getBaseAlignmentAndOffsetFromLValue(From, Ctx); + if (!P) + break; + return getDerivedToBaseAlignmentAndOffset(CE, From->getType(), P->first, + P->second, Ctx); + } + } + break; + } + case Stmt::ArraySubscriptExprClass: { + auto *ASE = cast(E); + return getAlignmentAndOffsetFromBinAddOrSub(ASE->getBase(), ASE->getIdx(), + false, Ctx); + } + case Stmt::DeclRefExprClass: { + if (auto *VD = dyn_cast(cast(E)->getDecl())) { + // FIXME: If VD is captured by copy or is an escaping __block variable, + // use the alignment of VD's type. + if (!VD->getType()->isReferenceType()) + return std::make_pair(Ctx.getDeclAlign(VD), CharUnits::Zero()); + if (VD->hasInit()) + return getBaseAlignmentAndOffsetFromLValue(VD->getInit(), Ctx); + } + break; + } + case Stmt::MemberExprClass: { + auto *ME = cast(E); + auto *FD = dyn_cast(ME->getMemberDecl()); + if (!FD || FD->getType()->isReferenceType()) + break; + Optional> P; + if (ME->isArrow()) + P = getBaseAlignmentAndOffsetFromPtr(ME->getBase(), Ctx); + else + P = getBaseAlignmentAndOffsetFromLValue(ME->getBase(), Ctx); + if (!P) + break; + const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(FD->getParent()); + uint64_t Offset = Layout.getFieldOffset(FD->getFieldIndex()); + return std::make_pair(P->first, + P->second + CharUnits::fromQuantity(Offset)); + } + case Stmt::UnaryOperatorClass: { + auto *UO = cast(E); + switch (UO->getOpcode()) { + default: + break; + case UO_Deref: + return getBaseAlignmentAndOffsetFromPtr(UO->getSubExpr(), Ctx); + } + break; + } + case Stmt::BinaryOperatorClass: { + auto *BO = cast(E); + auto Opcode = BO->getOpcode(); + switch (Opcode) { + default: + break; + case BO_Comma: + return getBaseAlignmentAndOffsetFromLValue(BO->getRHS(), Ctx); + } + break; + } + } + return llvm::None; +} + +/// This helper function takes a pointer expression and returns the alignment of +/// a VarDecl and a constant offset from the VarDecl. +Optional> +static getBaseAlignmentAndOffsetFromPtr(const Expr *E, ASTContext &Ctx) { + E = E->IgnoreParens(); + switch (E->getStmtClass()) { + default: + break; + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::ImplicitCastExprClass: { + auto *CE = cast(E); + const Expr *From = CE->getSubExpr(); + switch (CE->getCastKind()) { + default: + break; + case CK_NoOp: + return getBaseAlignmentAndOffsetFromPtr(From, Ctx); + case CK_ArrayToPointerDecay: + return getBaseAlignmentAndOffsetFromLValue(From, Ctx); + case CK_UncheckedDerivedToBase: + case CK_DerivedToBase: { + auto P = getBaseAlignmentAndOffsetFromPtr(From, Ctx); + if (!P) + break; + return getDerivedToBaseAlignmentAndOffset( + CE, From->getType()->getPointeeType(), P->first, P->second, Ctx); + } + } + break; + } + case Stmt::CXXThisExprClass: { + auto *RD = E->getType()->getPointeeType()->getAsCXXRecordDecl(); + CharUnits Alignment = Ctx.getASTRecordLayout(RD).getNonVirtualAlignment(); + return std::make_pair(Alignment, CharUnits::Zero()); + } + case Stmt::UnaryOperatorClass: { + auto *UO = cast(E); + if (UO->getOpcode() == UO_AddrOf) + return getBaseAlignmentAndOffsetFromLValue(UO->getSubExpr(), Ctx); + break; + } + case Stmt::BinaryOperatorClass: { + auto *BO = cast(E); + auto Opcode = BO->getOpcode(); + switch (Opcode) { + default: + break; + case BO_Add: + case BO_Sub: { + const Expr *LHS = BO->getLHS(), *RHS = BO->getRHS(); + if (Opcode == BO_Add && !RHS->getType()->isIntegralOrEnumerationType()) + std::swap(LHS, RHS); + return getAlignmentAndOffsetFromBinAddOrSub(LHS, RHS, Opcode == BO_Sub, + Ctx); + } + case BO_Comma: + return getBaseAlignmentAndOffsetFromPtr(BO->getRHS(), Ctx); + } + break; + } + } + return llvm::None; +} + +static CharUnits getPresumedAlignmentOfPointer(const Expr *E, Sema &S) { + // See if we can compute the alignment of a VarDecl and an offset from it. + Optional> P = + getBaseAlignmentAndOffsetFromPtr(E, S.Context); + + if (P) + return P->first.alignmentAtOffset(P->second); + + // If that failed, return the type's alignment. + return S.Context.getTypeAlignInChars(E->getType()->getPointeeType()); } /// CheckCastAlign - Implements -Wcast-align, which warns when a @@ -13336,15 +13892,7 @@ void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) { // includes 'void'. if (SrcPointee->isIncompleteType()) return; - CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee); - - if (auto *CE = dyn_cast(Op)) { - if (CE->getCastKind() == CK_ArrayToPointerDecay) - SrcAlign = getDeclAlign(CE->getSubExpr(), SrcAlign, Context); - } else if (auto *UO = dyn_cast(Op)) { - if (UO->getOpcode() == UO_AddrOf) - SrcAlign = getDeclAlign(UO->getSubExpr(), SrcAlign, Context); - } + CharUnits SrcAlign = getPresumedAlignmentOfPointer(Op, *this); if (SrcAlign >= DestAlign) return; diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index b4299b615a61d..a9746d7552a8f 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; -} // Some declarations have reserved names that we don't want to ever show. // Filter out names reserved for the implementation if they come from a @@ -789,8 +746,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; } @@ -3989,7 +3946,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; @@ -4260,7 +4217,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 92b115c8a3f3c..c96f3764dad1d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2573,6 +2573,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)) @@ -2585,6 +2588,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)) @@ -11375,6 +11380,9 @@ bool Sema::DeduceVariableDeclarationType(VarDecl *VDecl, bool DirectInit, void Sema::checkNonTrivialCUnionInInitializer(const Expr *Init, SourceLocation Loc) { + if (auto *EWC = dyn_cast(Init)) + Init = EWC->getSubExpr(); + if (auto *CE = dyn_cast(Init)) Init = CE->getSubExpr(); @@ -11606,6 +11614,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) {} @@ -13250,10 +13264,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 && @@ -13281,6 +13294,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); @@ -13317,6 +13340,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 @@ -13572,13 +13601,12 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator, VarDecl *VD = C.getCapturedVar(); if (VD->isInitCapture()) S.CurrentInstantiationScope->InstantiatedLocal(VD, VD); - QualType CaptureType = VD->getType(); const bool ByRef = C.getCaptureKind() == LCK_ByRef; LSI->addCapture(VD, /*IsBlock*/false, ByRef, /*RefersToEnclosingVariableOrCapture*/true, C.getLocation(), /*EllipsisLoc*/C.isPackExpansion() ? C.getEllipsisLoc() : SourceLocation(), - CaptureType, /*Invalid*/false); + I->getType(), /*Invalid*/false); } else if (C.capturesThis()) { LSI->addThisCapture(/*Nested*/ false, C.getLocation(), I->getType(), @@ -14192,7 +14220,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()) @@ -16750,8 +16780,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()) @@ -16957,6 +16991,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, CDecl->setIvarRBraceLoc(RBrac); } } + ProcessAPINotes(Record); } /// Determine whether the given integral value is representable within @@ -17267,6 +17302,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); @@ -17462,6 +17499,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 02aebbea4a8bd..7a841c7e4d497 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2414,6 +2414,15 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { AvailabilityChange Introduced = AL.getAvailabilityIntroduced(); AvailabilityChange Deprecated = AL.getAvailabilityDeprecated(); AvailabilityChange Obsoleted = AL.getAvailabilityObsoleted(); + if (II->getName() == "macos" || II->getName() == "macos_app_extension") { + // Canonicalize macOS availability versions. + Introduced.Version = llvm::Triple::getCanonicalVersionForOS( + llvm::Triple::MacOSX, Introduced.Version); + Deprecated.Version = llvm::Triple::getCanonicalVersionForOS( + llvm::Triple::MacOSX, Deprecated.Version); + Obsoleted.Version = llvm::Triple::getCanonicalVersionForOS( + llvm::Triple::MacOSX, Obsoleted.Version); + } bool IsUnavailable = AL.getUnavailableLoc().isValid(); bool IsStrict = AL.getStrictLoc().isValid(); StringRef Str; @@ -4248,6 +4257,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)) @@ -5222,6 +5250,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; @@ -5383,6 +5444,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. //===----------------------------------------------------------------------===// @@ -6548,7 +7017,9 @@ static void handleObjCExternallyRetainedAttr(Sema &S, Decl *D, // If D is a function-like declaration (method, block, or function), then we // make every parameter psuedo-strong. - for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E; ++I) { + unsigned NumParams = + hasFunctionProto(D) ? getFunctionOrMethodNumParams(D) : 0; + for (unsigned I = 0; I != NumParams; ++I) { auto *PVD = const_cast(getFunctionOrMethodParam(D, I)); QualType Ty = PVD->getType(); @@ -6966,6 +7437,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); @@ -7074,6 +7548,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; @@ -7366,6 +7843,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); @@ -7671,6 +8170,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 5dba1c1d2c121..11696359fc467 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -8385,6 +8385,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); } @@ -8534,12 +8536,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) { @@ -8578,6 +8604,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. @@ -8642,6 +8671,9 @@ bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) { if (shouldDeleteForVariantObjCPtrMember(&*UI, UnionFieldType)) return true; + if (shouldDeleteForVariantPtrAuthMember(&*UI, UnionFieldType)) + return true; + if (!UnionFieldType.isConstQualified()) AllVariantFieldsAreConst = false; @@ -9437,6 +9469,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()) { @@ -10518,6 +10556,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()) @@ -11049,6 +11088,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, if (UDir) ProcessDeclAttributeList(S, UDir, AttrList); + ProcessAPINotes(UDir); return UDir; } @@ -12155,6 +12195,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 5fdf6aeed5b40..d1e5189015ec4 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) @@ -1801,7 +1806,7 @@ Decl *Sema::ActOnStartCategoryInterface( Decl *const *ProtoRefs, unsigned NumProtoRefs, const SourceLocation *ProtoLocs, SourceLocation EndProtoLoc, const ParsedAttributesView &AttrList) { - ObjCCategoryDecl *CDecl; + ObjCCategoryDecl *CDecl = nullptr; ObjCInterfaceDecl *IDecl = getObjCInterfaceDecl(ClassName, ClassLoc, true); /// Check that class of this category is already completely declared. @@ -1814,10 +1819,11 @@ Decl *Sema::ActOnStartCategoryInterface( // the enclosing method declarations. We mark the decl invalid // to make it clear that this isn't a valid AST. CDecl = ObjCCategoryDecl::Create(Context, CurContext, AtInterfaceLoc, - ClassLoc, CategoryLoc, CategoryName, - IDecl, typeParamList); + ClassLoc, CategoryLoc, CategoryName, IDecl, + nullptr, typeParamList); CDecl->setInvalidDecl(); CurContext->addDecl(CDecl); + CDecl->startDefinition(); if (!IDecl) Diag(ClassLoc, diag::err_undef_interface) << ClassName; @@ -1830,14 +1836,15 @@ Decl *Sema::ActOnStartCategoryInterface( diag::note_implementation_declared); } + ObjCCategoryDecl *PrevDecl = nullptr; if (CategoryName) { /// Check for duplicate interface declaration for this category - if (ObjCCategoryDecl *Previous - = IDecl->FindCategoryDeclaration(CategoryName)) { + PrevDecl = IDecl->FindCategoryDeclaration(CategoryName); + if (!getLangOpts().Modules && PrevDecl) { // Class extensions can be declared multiple times, categories cannot. Diag(CategoryLoc, diag::warn_dup_category_def) << ClassName << CategoryName; - Diag(Previous->getLocation(), diag::note_previous_definition); + Diag(PrevDecl->getLocation(), diag::note_previous_definition); } } @@ -1862,16 +1869,22 @@ Decl *Sema::ActOnStartCategoryInterface( CDecl = ObjCCategoryDecl::Create(Context, CurContext, AtInterfaceLoc, ClassLoc, CategoryLoc, CategoryName, IDecl, - typeParamList); - // FIXME: PushOnScopeChains? + PrevDecl, typeParamList); CurContext->addDecl(CDecl); + if (!PrevDecl) + CDecl->startDefinition(); + // Process the attributes before looking at protocols to ensure that the // availability attribute is attached to the category to provide availability // checking for protocol uses. ProcessDeclAttributeList(TUScope, CDecl, AttrList); AddPragmaAttributes(TUScope, CDecl); + // Merge attributes from previous declarations. + if (PrevDecl) + mergeDeclAttributes(CDecl, PrevDecl); + if (NumProtoRefs) { diagnoseUseOfProtocols(*this, CDecl, (ObjCProtocolDecl*const*)ProtoRefs, NumProtoRefs, ProtoLocs); @@ -1903,10 +1916,11 @@ Decl *Sema::ActOnStartCategoryImplementation( // Category @implementation with no corresponding @interface. // Create and install one. CatIDecl = ObjCCategoryDecl::Create(Context, CurContext, AtCatImplLoc, - ClassLoc, CatLoc, - CatName, IDecl, + ClassLoc, CatLoc, CatName, IDecl, + /*PrevDecl=*/nullptr, /*typeParamList=*/nullptr); CatIDecl->setImplicit(); + CatIDecl->startDefinition(); } } @@ -2202,13 +2216,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 +2686,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 +2751,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 +2781,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 +2809,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 @@ -3013,23 +3043,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 @@ -4055,7 +4111,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) @@ -4580,6 +4636,62 @@ static void checkObjCMethodX86VectorTypes(Sema &SemaRef, << (Triple.isMacOSX() ? "macOS 10.11" : "iOS 9"); } +static void mergeObjCDirectMembers(Sema &S, Decl *CD, ObjCMethodDecl *Method) { + if (!Method->isDirectMethod() && !Method->hasAttr() && + CD->hasAttr()) { + Method->addAttr( + ObjCDirectAttr::CreateImplicit(S.Context, Method->getLocation())); + } +} + +static void checkObjCDirectMethodClashes(Sema &S, ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *Method, + ObjCImplDecl *ImpDecl = nullptr) { + auto Sel = Method->getSelector(); + bool isInstance = Method->isInstanceMethod(); + bool diagnosed = false; + + auto diagClash = [&](const ObjCMethodDecl *IMD) { + if (diagnosed || IMD->isImplicit()) + return; + if (Method->isDirectMethod() || IMD->isDirectMethod()) { + S.Diag(Method->getLocation(), diag::err_objc_direct_duplicate_decl) + << Method->isDirectMethod() << /* method */ 0 << IMD->isDirectMethod() + << Method->getDeclName(); + S.Diag(IMD->getLocation(), diag::note_previous_declaration); + diagnosed = true; + } + }; + + // Look for any other declaration of this method anywhere we can see in this + // compilation unit. + // + // We do not use IDecl->lookupMethod() because we have specific needs: + // + // - we absolutely do not need to walk protocols, because + // diag::err_objc_direct_on_protocol has already been emitted + // during parsing if there's a conflict, + // + // - when we do not find a match in a given @interface container, + // we need to attempt looking it up in the @implementation block if the + // translation unit sees it to find more clashes. + + if (auto *IMD = IDecl->getMethod(Sel, isInstance)) + diagClash(IMD); + else if (auto *Impl = IDecl->getImplementation()) + if (Impl != ImpDecl) + if (auto *IMD = IDecl->getImplementation()->getMethod(Sel, isInstance)) + diagClash(IMD); + + for (const auto *Cat : IDecl->visible_categories()) + if (auto *IMD = Cat->getMethod(Sel, isInstance)) + diagClash(IMD); + else if (auto CatImpl = Cat->getImplementation()) + if (CatImpl != ImpDecl) + if (auto *IMD = Cat->getMethod(Sel, isInstance)) + diagClash(IMD); +} + Decl *Sema::ActOnMethodDeclaration( Scope *S, SourceLocation MethodLoc, SourceLocation EndLoc, tok::TokenKind MethodType, ObjCDeclSpec &ReturnQT, ParsedType ReturnType, @@ -4669,6 +4781,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); @@ -4699,6 +4812,7 @@ Decl *Sema::ActOnMethodDeclaration( ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList); AddPragmaAttributes(TUScope, ObjCMethod); + ProcessAPINotes(ObjCMethod); // Add the method now. const ObjCMethodDecl *PrevMethod = nullptr; @@ -4808,9 +4922,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())); + } else { + mergeObjCDirectMembers(*this, ClassDecl, ObjCMethod); + checkObjCDirectMethodClashes(*this, IDecl, ObjCMethod, ImpDecl); } // Warn if a method declared in a protocol to which a category or @@ -4831,39 +4945,16 @@ Decl *Sema::ActOnMethodDeclaration( } } else { if (!isa(ClassDecl)) { - if (!ObjCMethod->isDirectMethod() && - ClassDecl->hasAttr()) { - ObjCMethod->addAttr( - ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); - } + mergeObjCDirectMembers(*this, ClassDecl, ObjCMethod); - // There can be a single declaration in any @interface container - // for a given direct method, look for clashes as we add them. - // - // For valid code, we should always know the primary interface - // declaration by now, however for invalid code we'll keep parsing - // but we won't find the primary interface and IDecl will be nil. ObjCInterfaceDecl *IDecl = dyn_cast(ClassDecl); if (!IDecl) IDecl = cast(ClassDecl)->getClassInterface(); - + // For valid code, we should always know the primary interface + // declaration by now, however for invalid code we'll keep parsing + // but we won't find the primary interface and IDecl will be nil. if (IDecl) - if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(), - ObjCMethod->isInstanceMethod(), - /*shallowCategoryLookup=*/false, - /*followSuper=*/false)) { - if (isa(IMD->getDeclContext())) { - // Do not emit a diagnostic for the Protocol case: - // diag::err_objc_direct_on_protocol has already been emitted - // during parsing for these with a nicer diagnostic. - } else if (ObjCMethod->isDirectMethod() || IMD->isDirectMethod()) { - Diag(ObjCMethod->getLocation(), - diag::err_objc_direct_duplicate_decl) - << ObjCMethod->isDirectMethod() << IMD->isDirectMethod() - << ObjCMethod->getDeclName(); - Diag(IMD->getLocation(), diag::note_previous_declaration); - } - } + checkObjCDirectMethodClashes(*this, IDecl, ObjCMethod); } cast(ClassDecl)->addDecl(ObjCMethod); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 61cd629646f60..141e124238bd0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -569,6 +569,10 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { QualType T = E->getType(); assert(!T.isNull() && "r-value conversion on typeless expression?"); + // lvalue-to-rvalue conversion cannot be applied to function or array types. + if (T->isFunctionType() || T->isArrayType()) + return E; + // We don't want to throw lvalue-to-rvalue casts on top of // expressions of certain types in C++. if (getLangOpts().CPlusPlus && @@ -637,6 +641,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { if (E->getType().getObjCLifetime() == Qualifiers::OCL_Weak) Cleanup.setExprNeedsCleanups(true); + if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) + Cleanup.setExprNeedsCleanups(true); + // C++ [conv.lval]p3: // If T is cv std::nullptr_t, the result is a null pointer constant. CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue; @@ -3834,6 +3841,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, @@ -4033,6 +4051,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)) @@ -6216,14 +6238,24 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, return ExprError(); } - // Compound literals that have automatic storage duration are destroyed at - // the end of the scope. Emit diagnostics if it is or contains a C union type - // that is non-trivial to destruct. - if (!isFileScope) + if (!isFileScope && !getLangOpts().CPlusPlus) { + // Compound literals that have automatic storage duration are destroyed at + // the end of the scope in C; in C++, they're just temporaries. + + // Emit diagnostics if it is or contains a C union type that is non-trivial + // to destruct. if (E->getType().hasNonTrivialToPrimitiveDestructCUnion()) checkNonTrivialCUnion(E->getType(), E->getExprLoc(), NTCUC_CompoundLiteral, NTCUK_Destruct); + // Diagnose jumps that enter or exit the lifetime of the compound literal. + if (literalType.isDestructedType()) { + Cleanup.setExprNeedsCleanups(true); + ExprCleanupObjects.push_back(E); + getCurFunction()->setHasBranchProtectedScope(); + } + } + if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() || E->getType().hasNonTrivialToPrimitiveCopyCUnion()) checkNonTrivialCUnionInInitializer(E->getInitializer(), @@ -7064,6 +7096,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 @@ -8027,6 +8067,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; @@ -8224,7 +8268,7 @@ Sema::CheckAssignmentConstraints(SourceLocation Loc, /// type ElementType. static bool isVector(QualType QT, QualType ElementType) { if (const VectorType *VT = QT->getAs()) - return VT->getElementType() == ElementType; + return VT->getElementType().getCanonicalType() == ElementType; return false; } @@ -8736,6 +8780,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); @@ -12492,6 +12546,39 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { QualType MPTy = Context.getMemberPointerType( op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr()); + + if (getLangOpts().PointerAuthCalls && MD->isVirtual() && + !isUnevaluatedContext() && !MPTy->isDependentType()) { + // When pointer authentication is enabled, argument and return types of + // vitual member functions must be complete. This is because vitrual + // member function pointers are implemented using virtual dispatch + // thunks and the thunks cannot be emitted if the argument or return + // types are incomplete. + auto ReturnOrParamTypeIsIncomplete = [&](QualType T, + SourceLocation DeclRefLoc, + SourceLocation RetArgTypeLoc) { + if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) { + Diag(DeclRefLoc, + diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret); + Diag(RetArgTypeLoc, + diag::note_ptrauth_virtual_function_incomplete_arg_ret_type) + << T; + return true; + } + return false; + }; + QualType RetTy = MD->getReturnType(); + bool IsIncomplete = + !RetTy->isVoidType() && + ReturnOrParamTypeIsIncomplete( + RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin()); + for (auto *PVD : MD->parameters()) + IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc, + PVD->getBeginLoc()); + if (IsIncomplete) + return QualType(); + } + // Under the MS ABI, lock down the inheritance model now. if (Context.getTargetInfo().getCXXABI().isMicrosoft()) (void)isCompleteType(OpLoc, MPTy); @@ -12871,10 +12958,27 @@ CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, /// Returns true if conversion between vectors of halfs and vectors of floats /// is needed. static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx, - QualType SrcType) { - return OpRequiresConversion && !Ctx.getLangOpts().NativeHalfType && - !Ctx.getTargetInfo().useFP16ConversionIntrinsics() && - isVector(SrcType, Ctx.HalfTy); + Expr *E0, Expr *E1 = nullptr) { + if (!OpRequiresConversion || Ctx.getLangOpts().NativeHalfType || + Ctx.getTargetInfo().useFP16ConversionIntrinsics()) + return false; + + auto HasVectorOfHalfType = [&Ctx](Expr *E) { + QualType Ty = E->IgnoreImplicit()->getType(); + + // Don't promote half precision neon vectors like float16x4_t in arm_neon.h + // to vectors of floats. Although the element type of the vectors is __fp16, + // the vectors shouldn't be treated as storage-only types. See the + // discussion here: https://reviews.llvm.org/rG825235c140e7 + if (const VectorType *VT = Ty->getAs()) { + if (VT->getVectorKind() == VectorType::NeonVector) + return false; + return VT->getElementType().getCanonicalType() == Ctx.HalfTy; + } + return false; + }; + + return HasVectorOfHalfType(E0) && (!E1 || HasVectorOfHalfType(E1)); } /// CreateBuiltinBinOp - Creates a new built-in binary operation with @@ -13109,8 +13213,8 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, assert(isVector(RHS.get()->getType(), Context.HalfTy) == isVector(LHS.get()->getType(), Context.HalfTy) && "both sides are half vectors or neither sides are"); - ConvertHalfVec = needsConversionOfHalfVec(ConvertHalfVec, Context, - LHS.get()->getType()); + ConvertHalfVec = + needsConversionOfHalfVec(ConvertHalfVec, Context, LHS.get(), RHS.get()); // Check for array bounds violations for both sides of the BinaryOperator CheckArrayAccess(LHS.get()); @@ -13602,8 +13706,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, // float vector and truncating the result back to a half vector. For now, we // do this only when HalfArgsAndReturns is set (that is, when the target is // arm or arm64). - ConvertHalfVec = - needsConversionOfHalfVec(true, Context, Input.get()->getType()); + ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get()); // If the operand is a half vector, promote it to a float vector. if (ConvertHalfVec) @@ -14788,7 +14891,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; @@ -18092,15 +18197,28 @@ ExprResult Sema::ActOnObjCAvailabilityCheckExpr( llvm::ArrayRef AvailSpecs, SourceLocation AtLoc, SourceLocation RParen) { - StringRef Platform = getASTContext().getTargetInfo().getPlatformName(); - - auto Spec = llvm::find_if(AvailSpecs, [&](const AvailabilitySpec &Spec) { - return Spec.getPlatform() == Platform; - }); + auto FindSpecVersion = [&](StringRef Platform) + -> Optional { + auto Spec = llvm::find_if(AvailSpecs, [&](const AvailabilitySpec &Spec) { + return Spec.getPlatform() == Platform; + }); + if (Spec == AvailSpecs.end()) + return None; + if (Platform == "macos") { + return ObjCAvailabilityCheckExpr::VersionAsWritten{ + llvm::Triple::getCanonicalVersionForOS(llvm::Triple::MacOSX, + Spec->getVersion()), + Spec->getVersion()}; + } + return ObjCAvailabilityCheckExpr::VersionAsWritten{Spec->getVersion(), + Spec->getVersion()}; + }; - VersionTuple Version; - if (Spec != AvailSpecs.end()) - Version = Spec->getVersion(); + auto MaybeVersion = + FindSpecVersion(Context.getTargetInfo().getPlatformName()); + ObjCAvailabilityCheckExpr::VersionAsWritten Version; + if (MaybeVersion) + Version = *MaybeVersion; // The use of `@available` in the enclosing function should be analyzed to // warn when it's used inappropriately (i.e. not if(@available)). diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index cfb3a05e9c146..5ccf9fe73a348 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -6208,6 +6208,7 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // exists. SmallVector QualifierUnion; SmallVector, 4> MemberOfClass; + SmallVector PtrAuthQualifier; QualType Composite1 = T1; QualType Composite2 = T2; unsigned NeedConstBefore = 0; @@ -6226,6 +6227,9 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, QualifierUnion.push_back( Composite1.getCVRQualifiers() | Composite2.getCVRQualifiers()); MemberOfClass.push_back(std::make_pair(nullptr, nullptr)); + PtrAuthQualifier.resize(PtrAuthQualifier.size() + 1); + if (Composite1.getPointerAuth() == Composite2.getPointerAuth()) + PtrAuthQualifier.back() = Composite1.getPointerAuth(); continue; } @@ -6244,6 +6248,9 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, Composite1.getCVRQualifiers() | Composite2.getCVRQualifiers()); MemberOfClass.push_back(std::make_pair(MemPtr1->getClass(), MemPtr2->getClass())); + PtrAuthQualifier.resize(PtrAuthQualifier.size() + 1); + if (Composite1.getPointerAuth() == Composite2.getPointerAuth()) + PtrAuthQualifier.back() = Composite1.getPointerAuth(); continue; } @@ -6299,8 +6306,11 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // Rewrap the composites as pointers or member pointers with the union CVRs. auto MOC = MemberOfClass.rbegin(); + auto PtrAuthQualIt = PtrAuthQualifier.rbegin(); for (unsigned CVR : llvm::reverse(QualifierUnion)) { Qualifiers Quals = Qualifiers::fromCVRMask(CVR); + if (PointerAuthQualifier PtrAuthQual = *PtrAuthQualIt++) + Quals.setPointerAuth(PtrAuthQual); auto Classes = *MOC++; if (Classes.first && Classes.second) { // Rebuild member pointer type @@ -6475,6 +6485,9 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { VK_RValue); } + if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) + Cleanup.setExprNeedsCleanups(true); + if (!getLangOpts().CPlusPlus) return E; diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 687348c90a8c5..eb21b94a88673 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -1280,12 +1280,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()) @@ -2572,6 +2568,16 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, diag::err_illegal_message_expr_incomplete_type)) return ExprError(); + if (Method && Method->isDirectMethod() && SuperLoc.isValid()) { + Diag(SuperLoc, diag::err_messaging_super_with_direct_method) + << FixItHint::CreateReplacement( + SuperLoc, getLangOpts().ObjCAutoRefCount + ? "self" + : Method->getClassInterface()->getName()); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + // Warn about explicit call of +initialize on its own class. But not on 'super'. if (Method && Method->getMethodFamily() == OMF_initialize) { if (!SuperLoc.isValid()) { @@ -2776,9 +2782,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, ReceiverType->isIntegerType())) { // Implicitly convert integers and pointers to 'id' but emit a warning. // But not in ARC. - Diag(Loc, diag::warn_bad_receiver_type) - << ReceiverType - << Receiver->getSourceRange(); + Diag(Loc, diag::warn_bad_receiver_type) << ReceiverType << RecRange; if (ReceiverType->isPointerType()) { Receiver = ImpCastExprToType(Receiver, Context.getObjCIdType(), CK_CPointerToObjCPointerCast).get(); @@ -2929,11 +2933,10 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // definition is found in a module that's not visible. const ObjCInterfaceDecl *forwardClass = nullptr; if (RequireCompleteType(Loc, OCIType->getPointeeType(), - getLangOpts().ObjCAutoRefCount - ? diag::err_arc_receiver_forward_instance - : diag::warn_receiver_forward_instance, - Receiver? Receiver->getSourceRange() - : SourceRange(SuperLoc))) { + getLangOpts().ObjCAutoRefCount + ? diag::err_arc_receiver_forward_instance + : diag::warn_receiver_forward_instance, + RecRange)) { if (getLangOpts().ObjCAutoRefCount) return ExprError(); @@ -2995,8 +2998,7 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, return ExprError(); } else { // Reject other random receiver types (e.g. structs). - Diag(Loc, diag::err_bad_receiver_type) - << ReceiverType << Receiver->getSourceRange(); + Diag(Loc, diag::err_bad_receiver_type) << ReceiverType << RecRange; return ExprError(); } } @@ -3014,15 +3016,35 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, << Method->getDeclName(); } - if (ReceiverType->isObjCClassType() && !isImplicit) { - Diag(Receiver->getExprLoc(), - diag::err_messaging_class_with_direct_method); + // Under ARC, self can't be assigned, and doing a direct call to `self` + // when it's a Class is hence safe. For other cases, we can't trust `self` + // is what we think it is, so we reject it. + if (ReceiverType->isObjCClassType() && !isImplicit && + !(Receiver->isObjCSelfExpr() && getLangOpts().ObjCAutoRefCount)) { + { + DiagnosticBuilder Builder = + Diag(Receiver->getExprLoc(), + diag::err_messaging_class_with_direct_method); + if (Receiver->isObjCSelfExpr()) { + Builder.AddFixItHint(FixItHint::CreateReplacement( + RecRange, Method->getClassInterface()->getName())); + } + } Diag(Method->getLocation(), diag::note_direct_method_declared_at) << Method->getDeclName(); } if (SuperLoc.isValid()) { - Diag(SuperLoc, diag::err_messaging_super_with_direct_method); + { + DiagnosticBuilder Builder = + Diag(SuperLoc, diag::err_messaging_super_with_direct_method); + if (ReceiverType->isObjCClassType()) { + Builder.AddFixItHint(FixItHint::CreateReplacement( + SuperLoc, Method->getClassInterface()->getName())); + } else { + Builder.AddFixItHint(FixItHint::CreateReplacement(SuperLoc, "self")); + } + } Diag(Method->getLocation(), diag::note_direct_method_declared_at) << Method->getDeclName(); } diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index f6717f4cbe5e2..c49f5b08ce504 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -645,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); @@ -654,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); @@ -1627,6 +1627,15 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, CatImplClass->addPropertyImplementation(PIDecl); } + if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic && + PIDecl->getPropertyDecl() && + PIDecl->getPropertyDecl()->isDirectProperty()) { + Diag(PropertyLoc, diag::err_objc_direct_dynamic_property); + Diag(PIDecl->getPropertyDecl()->getLocation(), + diag::note_previous_declaration); + return nullptr; + } + return PIDecl; } @@ -2421,6 +2430,40 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { DiagnosePropertyAccessorMismatch(property, GetterMethod, property->getLocation()); + // synthesizing accessors must not result in a direct method that is not + // monomorphic + if (!GetterMethod) { + if (const ObjCCategoryDecl *CatDecl = dyn_cast(CD)) { + auto *ExistingGetter = CatDecl->getClassInterface()->lookupMethod( + property->getGetterName(), !IsClassProperty, true, false, CatDecl); + if (ExistingGetter) { + if (ExistingGetter->isDirectMethod() || property->isDirectProperty()) { + Diag(property->getLocation(), diag::err_objc_direct_duplicate_decl) + << property->isDirectProperty() << 1 /* property */ + << ExistingGetter->isDirectMethod() + << ExistingGetter->getDeclName(); + Diag(ExistingGetter->getLocation(), diag::note_previous_declaration); + } + } + } + } + + if (!property->isReadOnly() && !SetterMethod) { + if (const ObjCCategoryDecl *CatDecl = dyn_cast(CD)) { + auto *ExistingSetter = CatDecl->getClassInterface()->lookupMethod( + property->getSetterName(), !IsClassProperty, true, false, CatDecl); + if (ExistingSetter) { + if (ExistingSetter->isDirectMethod() || property->isDirectProperty()) { + Diag(property->getLocation(), diag::err_objc_direct_duplicate_decl) + << property->isDirectProperty() << 1 /* property */ + << ExistingSetter->isDirectMethod() + << ExistingSetter->getDeclName(); + Diag(ExistingSetter->getLocation(), diag::note_previous_declaration); + } + } + } + } + if (!property->isReadOnly() && SetterMethod) { if (Context.getCanonicalType(SetterMethod->getReturnType()) != Context.VoidTy) @@ -2492,6 +2535,8 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { Context, SA->getName(), Loc, AttributeCommonInfo::AS_GNU, SectionAttr::GNU_section)); + ProcessAPINotes(GetterMethod); + if (getLangOpts().ObjCAutoRefCount) CheckARCMethodDecl(GetterMethod); } else @@ -2564,6 +2609,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { 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) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 83b7f497f99d9..6a066a3f17de1 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10041,6 +10041,17 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, return; } + if (FromQs.getPointerAuth() != ToQs.getPointerAuth()) { + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_ptrauth) + << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc + << FromTy + << !!FromQs.getPointerAuth() << FromQs.getPointerAuth().getAsString() + << !!ToQs.getPointerAuth() << ToQs.getPointerAuth().getAsString() + << I + 1 << (FromExpr ? FromExpr->getSourceRange() : SourceRange()); + MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl); + return; + } + unsigned CVR = FromQs.getCVRQualifiers() & ~ToQs.getCVRQualifiers(); assert(CVR && "unexpected qualifiers mismatch"); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 1e7cc503d8c21..e85fa34596f7a 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" @@ -365,7 +367,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { } } - if (E->isGLValue() && E->getType().isVolatileQualified()) { + // Tell the user to assign it into a variable to force a volatile load if this + // isn't an array. + if (E->isGLValue() && E->getType().isVolatileQualified() && + !E->getType()->isArrayType()) { Diag(Loc, diag::warn_unused_volatile) << R1 << R2; return; } @@ -1229,14 +1234,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 ade8a5a6ac148..2ff61ed004f4c 100755 --- 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. @@ -9143,6 +9145,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 @@ -9547,6 +9550,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); } @@ -9722,6 +9728,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 d77542be53fb9..0a264148d4f7e 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. @@ -2489,6 +2475,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, @@ -2577,6 +2569,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: @@ -3731,32 +3727,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; @@ -3780,6 +3753,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 @@ -4655,6 +4655,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. @@ -6567,6 +6572,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) { @@ -6584,30 +6608,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; @@ -6616,6 +6628,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); @@ -6623,11 +6638,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(); @@ -6638,7 +6658,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); @@ -6663,15 +6683,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(); @@ -6694,11 +6715,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, @@ -6712,7 +6764,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(); @@ -7359,6 +7410,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) { @@ -7547,6 +7682,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/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index cdb5b17022c2f..96b5a39478388 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -352,6 +352,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::TypeAliasTemplate: case Decl::ObjCProtocol: case Decl::ObjCInterface: + case Decl::ObjCCategory: case Decl::Empty: return true; @@ -372,7 +373,6 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Using: case Decl::UsingPack: case Decl::ObjCMethod: - case Decl::ObjCCategory: case Decl::ObjCCategoryImpl: case Decl::ObjCImplementation: case Decl::ObjCProperty: diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index a9f433b50074f..97c6990f4be0c 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1919,7 +1919,7 @@ HeaderFileInfoTrait::ReadData(internal_key_ref key, const unsigned char *d, // FIXME: This is not always the right filename-as-written, but we're not // going to use this information to rebuild the module, so it doesn't make // a lot of difference. - Module::Header H = { key.Filename, *FileMgr.getFile(Filename) }; + Module::Header H = {std::string(key.Filename), "", *FileMgr.getFile(Filename)}; ModMap.addHeader(Mod, H, HeaderRole, /*Imported*/true); HFI.isModuleHeader |= !(HeaderRole & ModuleMap::TextualHeader); } @@ -2531,14 +2531,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 && @@ -3512,24 +3504,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"); @@ -4502,7 +4476,7 @@ ASTReader::ReadASTCore(StringRef FileName, if (ShouldFinalizePCM) MC.finalizePCM(FileName); else - MC.tryToDropPCM(FileName); + MC.tryToRemovePCM(FileName); }); ModuleFile &F = *M; BitstreamCursor &Stream = F.Stream; @@ -4620,6 +4594,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. @@ -4711,6 +4690,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; @@ -5446,7 +5440,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; } @@ -5456,6 +5453,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++]; @@ -5513,6 +5515,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); @@ -5542,7 +5549,8 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { ResolveImportedPath(F, Filename); if (auto Umbrella = PP.getFileManager().getFile(Filename)) { if (!CurrentModule->getUmbrellaHeader()) - ModMap.setUmbrellaHeader(CurrentModule, *Umbrella, Blob); + // FIXME: NameAsWritten + ModMap.setUmbrellaHeader(CurrentModule, *Umbrella, Blob, ""); else if (CurrentModule->getUmbrellaHeader().Entry != *Umbrella) { if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) Error("mismatched umbrella headers in submodule"); @@ -5575,7 +5583,8 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { ResolveImportedPath(F, Dirname); if (auto Umbrella = PP.getFileManager().getDirectory(Dirname)) { if (!CurrentModule->getUmbrellaDir()) - ModMap.setUmbrellaDir(CurrentModule, *Umbrella, Blob); + // FIXME: NameAsWritten + ModMap.setUmbrellaDir(CurrentModule, *Umbrella, Blob, ""); else if (CurrentModule->getUmbrellaDir().Entry != *Umbrella) { if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) Error("mismatched umbrella directories in submodule"); @@ -5777,6 +5786,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) { @@ -5796,20 +5826,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, @@ -5875,20 +5893,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); @@ -8484,7 +8488,7 @@ unsigned ASTReader::getModuleFileID(ModuleFile *F) { llvm::Optional ASTReader::getSourceDescriptor(unsigned ID) { - if (const Module *M = getSubmodule(ID)) + if (Module *M = getSubmodule(ID)) return ExternalASTSource::ASTSourceDescriptor(*M); // If there is only a single PCH, return it instead. @@ -9267,7 +9271,11 @@ void ASTReader::finishPendingActions() { void ASTReader::diagnoseOdrViolations() { if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && PendingFunctionOdrMergeFailures.empty() && - PendingEnumOdrMergeFailures.empty()) + PendingEnumOdrMergeFailures.empty() && + PendingRecordOdrMergeFailures.empty() && + PendingObjCInterfaceOdrMergeFailures.empty() && + PendingObjCProtocolOdrMergeFailures.empty() && + PendingObjCCategoryOdrMergeFailures.empty()) return; // Trigger the import of the full definition of each class that had any @@ -9289,6 +9297,42 @@ void ASTReader::diagnoseOdrViolations() { } } + // Trigger the import of the full definition of each record in C/ObjC. + auto RecordOdrMergeFailures = std::move(PendingRecordOdrMergeFailures); + PendingRecordOdrMergeFailures.clear(); + for (auto &Merge : RecordOdrMergeFailures) { + Merge.first->decls_begin(); + for (auto &D : Merge.second) + D->decls_begin(); + } + + // Trigger the import of the full interface & protocol definition. + auto ObjCInterfaceOdrMergeFailures = + std::move(PendingObjCInterfaceOdrMergeFailures); + PendingObjCInterfaceOdrMergeFailures.clear(); + for (auto &Merge : ObjCInterfaceOdrMergeFailures) { + Merge.first->decls_begin(); + for (auto &InterfacePair : Merge.second) + InterfacePair.first->decls_begin(); + } + auto ObjCProtocolOdrMergeFailures = + std::move(PendingObjCProtocolOdrMergeFailures); + PendingObjCProtocolOdrMergeFailures.clear(); + for (auto &Merge : ObjCProtocolOdrMergeFailures) { + Merge.first->decls_begin(); + for (auto &ProtocolPair : Merge.second) + ProtocolPair.first->decls_begin(); + } + + auto ObjCCategoryOdrMergeFailures = + std::move(PendingObjCCategoryOdrMergeFailures); + PendingObjCCategoryOdrMergeFailures.clear(); + for (auto &Merge : ObjCCategoryOdrMergeFailures) { + Merge.first->decls_begin(); + for (auto &CategoryPair : Merge.second) + CategoryPair.first->decls_begin(); + } + // Trigger the import of functions. auto FunctionOdrMergeFailures = std::move(PendingFunctionOdrMergeFailures); PendingFunctionOdrMergeFailures.clear(); @@ -9396,7 +9440,10 @@ void ASTReader::diagnoseOdrViolations() { } if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty() && - EnumOdrMergeFailures.empty()) + EnumOdrMergeFailures.empty() && RecordOdrMergeFailures.empty() && + ObjCInterfaceOdrMergeFailures.empty() && + ObjCCategoryOdrMergeFailures.empty() && + ObjCProtocolOdrMergeFailures.empty()) return; // Ensure we don't accidentally recursively enter deserialization while @@ -9439,6 +9486,649 @@ void ASTReader::diagnoseOdrViolations() { return Hash.CalculateHash(); }; + auto ComputeAttrODRHash = [&Hash](const Attr *A) { + Hash.clear(); + Hash.AddAttr(A); + return Hash.CalculateHash(); + }; + + // Used with err_module_odr_violation_mismatch_decl and + // note_module_odr_violation_mismatch_decl + // This list should be the same Decl's as in ODRHash::isWhiteListedDecl + enum ODRMismatchDecl { + EndOfClass, + PublicSpecifer, + PrivateSpecifer, + ProtectedSpecifer, + StaticAssert, + Field, + CXXMethod, + TypeAlias, + TypeDef, + Var, + Friend, + FunctionTemplate, + ObjCMethod, + ObjCIvar, + ObjCProperty, + Other + }; + + // Used with err_module_odr_violation_mismatch_decl_diff and + // note_module_odr_violation_mismatch_decl_diff + enum ODRMismatchDeclDifference { + StaticAssertCondition, + StaticAssertMessage, + StaticAssertOnlyMessage, + FieldName, + FieldTypeName, + FieldSingleBitField, + FieldDifferentWidthBitField, + FieldSingleMutable, + FieldSingleInitializer, + FieldDifferentInitializers, + MethodName, + MethodDeleted, + MethodDefaulted, + MethodVirtual, + MethodStatic, + MethodVolatile, + MethodConst, + MethodInline, + MethodNumberParameters, + MethodParameterType, + MethodParameterName, + MethodParameterSingleDefaultArgument, + MethodParameterDifferentDefaultArgument, + MethodNoTemplateArguments, + MethodDifferentNumberTemplateArguments, + MethodDifferentTemplateArgument, + MethodSingleBody, + MethodDifferentBody, + TypedefName, + TypedefType, + VarName, + VarType, + VarSingleInitializer, + VarDifferentInitializer, + VarConstexpr, + FriendTypeFunction, + FriendType, + FriendFunction, + FunctionTemplateDifferentNumberParameters, + FunctionTemplateParameterDifferentKind, + FunctionTemplateParameterName, + FunctionTemplateParameterSingleDefaultArgument, + FunctionTemplateParameterDifferentDefaultArgument, + FunctionTemplateParameterDifferentType, + FunctionTemplatePackParameter, + ObjCMethodReturnType, + ObjCMethodName, + ObjCMethodInstanceOrClass, + ObjCDesignatedInitializer, + ObjCDirectMethod, + ObjCIvarAccess, + ObjCPropertyName, + ObjCPropertyType, + ObjCPropertyAttributes, + ObjCImplementationControl, + ObjCReferencedProtocolName, + AttributeKind + }; + + // These lambdas have the common portions of the ODR diagnostics. This + // has the same return as Diag(), so addition parameters can be passed + // in with operator<< + auto ODRDiagDeclError = [this](const NamedDecl *FirstRecord, + StringRef FirstModule, SourceLocation Loc, + SourceRange Range, + ODRMismatchDeclDifference DiffType) { + std::string Name; + llvm::raw_string_ostream OS(Name); + if (auto *C = dyn_cast(FirstRecord)) + OS << "category '" << C->getName() << "' on interface '" + << C->getClassInterface()->getName() << "'"; + else + OS << "'" << FirstRecord->getQualifiedNameAsString() << "'"; + + return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff) + << OS.str() << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto ODRDiagDeclNote = [this](StringRef SecondModule, SourceLocation Loc, + SourceRange Range, ODRMismatchDeclDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff) + << SecondModule << Range << DiffType; + }; + + enum ODRDiagMethodTypes { + DiagMethod, + DiagConstructor, + DiagDestructor, + }; + + auto ODRDiagCommonMethodChecks = [&ComputeQualTypeODRHash, &ODRDiagDeclError, + &ODRDiagDeclNote, + &ComputeODRHash](NamedDecl *FirstContainer, + StringRef FirstModule, + StringRef SecondModule, + unsigned FirstMethodType, + unsigned SecondMethodType, + const auto *FirstMethod, + const auto *SecondMethod) { + const unsigned FirstNumParameters = FirstMethod->param_size(); + const unsigned SecondNumParameters = SecondMethod->param_size(); + auto FirstName = FirstMethod->getDeclName(); + auto SecondName = SecondMethod->getDeclName(); + if (FirstNumParameters != SecondNumParameters) { + ODRDiagDeclError(FirstContainer, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodNumberParameters) + << FirstMethodType << FirstName << FirstNumParameters; + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodNumberParameters) + << SecondMethodType << SecondName << SecondNumParameters; + return true; + } + + for (unsigned I = 0; I < FirstNumParameters; ++I) { + const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); + + QualType FirstParamType = FirstParam->getType(); + QualType SecondParamType = SecondParam->getType(); + if (FirstParamType != SecondParamType && + ComputeQualTypeODRHash(FirstParamType) != + ComputeQualTypeODRHash(SecondParamType)) { + if (const DecayedType *ParamDecayedType = + FirstParamType->getAs()) { + ODRDiagDeclError(FirstContainer, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterType) + << FirstMethodType << FirstName << (I + 1) << FirstParamType + << true << ParamDecayedType->getOriginalType(); + } else { + ODRDiagDeclError(FirstContainer, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterType) + << FirstMethodType << FirstName << (I + 1) << FirstParamType + << false; + } + + if (const DecayedType *ParamDecayedType = + SecondParamType->getAs()) { + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodParameterType) + << SecondMethodType << SecondName << (I + 1) << SecondParamType + << true << ParamDecayedType->getOriginalType(); + } else { + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodParameterType) + << SecondMethodType << SecondName << (I + 1) << SecondParamType + << false; + } + return true; + } + + DeclarationName FirstParamName = FirstParam->getDeclName(); + DeclarationName SecondParamName = SecondParam->getDeclName(); + if (FirstParamName != SecondParamName) { + ODRDiagDeclError(FirstContainer, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterName) + << FirstMethodType << FirstName << (I + 1) << FirstParamName; + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodParameterName) + << SecondMethodType << SecondName << (I + 1) << SecondParamName; + return true; + } + + if (!isa(FirstMethod)) + continue; + + const Expr *FirstInit = FirstParam->getInit(); + const Expr *SecondInit = SecondParam->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + ODRDiagDeclError( + FirstContainer, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterSingleDefaultArgument) + << FirstMethodType << FirstName << (I + 1) << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodParameterSingleDefaultArgument) + << SecondMethodType << SecondName << (I + 1) + << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; + } + + if (FirstInit && SecondInit && + ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { + ODRDiagDeclError(FirstContainer, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodParameterDifferentDefaultArgument) + << FirstMethodType << FirstName << (I + 1) + << FirstInit->getSourceRange(); + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodParameterDifferentDefaultArgument) + << SecondMethodType << SecondName << (I + 1) + << SecondInit->getSourceRange(); + return true; + } + } + return false; + }; + + auto ODRDiagField = [this, &ODRDiagDeclError, &ODRDiagDeclNote, + &ComputeQualTypeODRHash, &ComputeODRHash]( + NamedDecl *FirstRecord, StringRef FirstModule, + StringRef SecondModule, FieldDecl *FirstField, + FieldDecl *SecondField) { + IdentifierInfo *FirstII = FirstField->getIdentifier(); + IdentifierInfo *SecondII = SecondField->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldName) + << FirstII; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldName) + << SecondII; + + return true; + } + + QualType FirstType = FirstField->getType(); + QualType SecondType = SecondField->getType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldTypeName) + << FirstII << FirstType; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldTypeName) + << SecondII << SecondType; + + return true; + } + + assert(getContext().hasSameType(FirstField->getType(), + SecondField->getType())); + + const bool IsFirstBitField = FirstField->isBitField(); + const bool IsSecondBitField = SecondField->isBitField(); + if (IsFirstBitField != IsSecondBitField) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldSingleBitField) + << FirstII << IsFirstBitField; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldSingleBitField) + << SecondII << IsSecondBitField; + return true; + } + + if (IsFirstBitField && IsSecondBitField) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), + FieldDifferentWidthBitField) + << FirstII << FirstField->getBitWidth()->getSourceRange(); + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), + FieldDifferentWidthBitField) + << SecondII << SecondField->getBitWidth()->getSourceRange(); + return true; + } + + if (!PP.getLangOpts().CPlusPlus) + return false; + + const bool IsFirstMutable = FirstField->isMutable(); + const bool IsSecondMutable = SecondField->isMutable(); + if (IsFirstMutable != IsSecondMutable) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldSingleMutable) + << FirstII << IsFirstMutable; + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldSingleMutable) + << SecondII << IsSecondMutable; + return true; + } + + const Expr *FirstInitializer = FirstField->getInClassInitializer(); + const Expr *SecondInitializer = SecondField->getInClassInitializer(); + if ((!FirstInitializer && SecondInitializer) || + (FirstInitializer && !SecondInitializer)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), FieldSingleInitializer) + << FirstII << (FirstInitializer != nullptr); + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), FieldSingleInitializer) + << SecondII << (SecondInitializer != nullptr); + return true; + } + + if (FirstInitializer && SecondInitializer) { + unsigned FirstInitHash = ComputeODRHash(FirstInitializer); + unsigned SecondInitHash = ComputeODRHash(SecondInitializer); + if (FirstInitHash != SecondInitHash) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstField->getLocation(), + FirstField->getSourceRange(), + FieldDifferentInitializers) + << FirstII << FirstInitializer->getSourceRange(); + ODRDiagDeclNote(SecondModule, SecondField->getLocation(), + SecondField->getSourceRange(), + FieldDifferentInitializers) + << SecondII << SecondInitializer->getSourceRange(); + return true; + } + } + + return false; + }; + + auto ODRDiagTypeDefOrAlias = + [&ODRDiagDeclError, &ODRDiagDeclNote, &ComputeQualTypeODRHash]( + NamedDecl *FirstRecord, StringRef FirstModule, StringRef SecondModule, + TypedefNameDecl *FirstTD, TypedefNameDecl *SecondTD, + bool IsTypeAlias) { + auto FirstName = FirstTD->getDeclName(); + auto SecondName = SecondTD->getDeclName(); + if (FirstName != SecondName) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstTD->getLocation(), + FirstTD->getSourceRange(), TypedefName) + << IsTypeAlias << FirstName; + ODRDiagDeclNote(SecondModule, SecondTD->getLocation(), + SecondTD->getSourceRange(), TypedefName) + << IsTypeAlias << SecondName; + return true; + } + + QualType FirstType = FirstTD->getUnderlyingType(); + QualType SecondType = SecondTD->getUnderlyingType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstTD->getLocation(), + FirstTD->getSourceRange(), TypedefType) + << IsTypeAlias << FirstName << FirstType; + ODRDiagDeclNote(SecondModule, SecondTD->getLocation(), + SecondTD->getSourceRange(), TypedefType) + << IsTypeAlias << SecondName << SecondType; + return true; + } + + return false; + }; + + auto ODRDiagVar = [&ODRDiagDeclError, &ODRDiagDeclNote, + &ComputeQualTypeODRHash, &ComputeODRHash, + this](NamedDecl *FirstRecord, StringRef FirstModule, + StringRef SecondModule, VarDecl *FirstVD, + VarDecl *SecondVD) { + auto FirstName = FirstVD->getDeclName(); + auto SecondName = SecondVD->getDeclName(); + if (FirstName != SecondName) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarName) + << FirstName; + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarName) + << SecondName; + return true; + } + + QualType FirstType = FirstVD->getType(); + QualType SecondType = SecondVD->getType(); + if (ComputeQualTypeODRHash(FirstType) != + ComputeQualTypeODRHash(SecondType)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarType) + << FirstName << FirstType; + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarType) + << SecondName << SecondType; + return true; + } + + if (!PP.getLangOpts().CPlusPlus) + return false; + + const Expr *FirstInit = FirstVD->getInit(); + const Expr *SecondInit = SecondVD->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarSingleInitializer) + << FirstName << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarSingleInitializer) + << SecondName << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + return true; + } + + if (FirstInit && SecondInit && + ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarDifferentInitializer) + << FirstName << FirstInit->getSourceRange(); + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarDifferentInitializer) + << SecondName << SecondInit->getSourceRange(); + return true; + } + + const bool FirstIsConstexpr = FirstVD->isConstexpr(); + const bool SecondIsConstexpr = SecondVD->isConstexpr(); + if (FirstIsConstexpr != SecondIsConstexpr) { + ODRDiagDeclError(FirstRecord, FirstModule, FirstVD->getLocation(), + FirstVD->getSourceRange(), VarConstexpr) + << FirstName << FirstIsConstexpr; + ODRDiagDeclNote(SecondModule, SecondVD->getLocation(), + SecondVD->getSourceRange(), VarConstexpr) + << SecondName << SecondIsConstexpr; + return true; + } + return false; + }; + + auto ODRDiagAttrs = [&ODRDiagDeclError, &ODRDiagDeclNote, + &ComputeAttrODRHash, this]( + NamedDecl *FirstContainer, NamedDecl *SecondContainer, + StringRef FirstModule, StringRef SecondModule, + llvm::SmallVectorImpl &FirstAttrs, + llvm::SmallVectorImpl &SecondAttrs) { + unsigned NumFirstAttrs = FirstAttrs.size(); + unsigned NumSecondAttrs = SecondAttrs.size(); + if (!NumFirstAttrs && !NumSecondAttrs) + return false; + + const Attr *LHS = nullptr; + const Attr *RHS = nullptr; + auto MaxNumAttrs = std::max(NumFirstAttrs, NumSecondAttrs); + for (unsigned I = 0; I < MaxNumAttrs; ++I) { + if (I < NumFirstAttrs) + LHS = FirstAttrs[I]; + if (I < NumSecondAttrs) + RHS = SecondAttrs[I]; + if (LHS && RHS && ComputeAttrODRHash(LHS) == ComputeAttrODRHash(RHS)) + continue; + + std::string LHSName, RHSName; + llvm::raw_string_ostream OSL(LHSName), OSR(RHSName); + if (LHS) + LHS->printPretty(OSL, this->getContext().getPrintingPolicy()); + if (RHS) + RHS->printPretty(OSR, this->getContext().getPrintingPolicy()); + + SourceLocation LHSLoc = + LHS ? LHS->getLocation() : FirstContainer->getLocation(); + SourceLocation RHSLoc = + RHS ? RHS->getLocation() : SecondContainer->getLocation(); + SourceRange LHSRange = + LHS ? LHS->getRange() : FirstContainer->getSourceRange(); + SourceRange RHSRange = + RHS ? RHS->getRange() : SecondContainer->getSourceRange(); + + ODRDiagDeclError(FirstContainer, FirstModule, LHSLoc, + LHSRange, AttributeKind) + << (LHS != nullptr) << OSL.str(); + ODRDiagDeclNote(SecondModule, RHSLoc, RHSRange, + AttributeKind) + << (RHS != nullptr) << OSR.str(); + return true; + } + return false; + }; + + auto DifferenceSelector = [](Decl *D) { + assert(D && "valid Decl required"); + switch (D->getKind()) { + default: + return Other; + case Decl::AccessSpec: + switch (D->getAccess()) { + case AS_public: + return PublicSpecifer; + case AS_private: + return PrivateSpecifer; + case AS_protected: + return ProtectedSpecifer; + case AS_none: + break; + } + llvm_unreachable("Invalid access specifier"); + case Decl::StaticAssert: + return StaticAssert; + case Decl::Field: + return Field; + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + return CXXMethod; + case Decl::TypeAlias: + return TypeAlias; + case Decl::Typedef: + return TypeDef; + case Decl::Var: + return Var; + case Decl::Friend: + return Friend; + case Decl::FunctionTemplate: + return FunctionTemplate; + case Decl::ObjCMethod: + return ObjCMethod; + case Decl::ObjCIvar: + return ObjCIvar; + case Decl::ObjCProperty: + return ObjCProperty; + } + }; + + using DeclHashes = llvm::SmallVector, 4>; + auto PopulateHashes = [&ComputeSubDeclODRHash](DeclHashes &Hashes, + RecordDecl *Record, + const DeclContext *DC) { + for (auto *D : Record->decls()) { + if (!ODRHash::isWhitelistedDecl(D, DC)) + continue; + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); + } + }; + + struct DiffResult { + Decl *FirstDecl = nullptr, *SecondDecl = nullptr; + ODRMismatchDecl FirstDiffType = Other, SecondDiffType = Other; + }; + + // If there is a diagnoseable difference, FirstDiffType and + // SecondDiffType will not be Other and FirstDecl and SecondDecl will be + // filled in if not EndOfClass. + auto FindTypeDiffs = [&DifferenceSelector](DeclHashes &FirstHashes, + DeclHashes &SecondHashes) { + DiffResult DR; + auto FirstIt = FirstHashes.begin(); + auto SecondIt = SecondHashes.begin(); + while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { + if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && + FirstIt->second == SecondIt->second) { + ++FirstIt; + ++SecondIt; + continue; + } + + DR.FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; + DR.SecondDecl = + SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; + + DR.FirstDiffType = + DR.FirstDecl ? DifferenceSelector(DR.FirstDecl) : EndOfClass; + DR.SecondDiffType = + DR.SecondDecl ? DifferenceSelector(DR.SecondDecl) : EndOfClass; + return DR; + } + return DR; + }; + + // Use this to diagnose that an unexpected Decl was encountered + // or no difference was detected. This causes a generic error + // message to be emitted. + auto DiagnoseODRUnexpected = [this](DiffResult &DR, NamedDecl *FirstRecord, + StringRef FirstModule, + NamedDecl *SecondRecord, + StringRef SecondModule) { + Diag(FirstRecord->getLocation(), + diag::err_module_odr_violation_different_definitions) + << FirstRecord << FirstModule.empty() << FirstModule; + + if (DR.FirstDecl) { + Diag(DR.FirstDecl->getLocation(), diag::note_first_module_difference) + << FirstRecord << DR.FirstDecl->getSourceRange(); + } + + Diag(SecondRecord->getLocation(), + diag::note_module_odr_violation_different_definitions) + << SecondModule; + + if (DR.SecondDecl) { + Diag(DR.SecondDecl->getLocation(), diag::note_second_module_difference) + << DR.SecondDecl->getSourceRange(); + } + }; + + auto DiagnoseODRMismatchGetLoc = [](NamedDecl *Container, auto DiffType, + auto *DiffDecl) { + SourceLocation Loc = Container->getEndLoc(); + SourceRange Range; + if (DiffType != EndOfClass) { + if (DiffDecl) + return std::make_pair(DiffDecl->getLocation(), + DiffDecl->getSourceRange()); + return std::make_pair(Loc, Range); + } + + if (auto *T = dyn_cast(Container)) + return std::make_pair(T->getBraceRange().getEnd(), Range); + + return std::make_pair(Loc, Range); + }; + + auto DiagnoseODRMismatch = [this, &DiagnoseODRMismatchGetLoc]( + DiffResult &DR, NamedDecl *FirstRecord, + StringRef FirstModule, NamedDecl *SecondRecord, + StringRef SecondModule) { + auto FirstDiagInfo = + DiagnoseODRMismatchGetLoc(FirstRecord, DR.FirstDiffType, DR.FirstDecl); + Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstDiagInfo.second << DR.FirstDiffType; + + auto SecondDiagInfo = DiagnoseODRMismatchGetLoc( + SecondRecord, DR.SecondDiffType, DR.SecondDecl); + Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) + << SecondModule << SecondDiagInfo.second << DR.SecondDiffType; + }; + // Issue any pending ODR-failure diagnostics. for (auto &Merge : OdrMergeFailures) { // If we've already pointed out a specific problem with this class, don't @@ -9472,16 +10162,16 @@ void ASTReader::diagnoseOdrViolations() { BaseVirtual, BaseAccess, }; - auto ODRDiagError = [FirstRecord, &FirstModule, - this](SourceLocation Loc, SourceRange Range, - ODRDefinitionDataDifference DiffType) { + auto ODRDiagBaseError = [FirstRecord, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRDefinitionDataDifference DiffType) { return Diag(Loc, diag::err_module_odr_violation_definition_data) << FirstRecord << FirstModule.empty() << FirstModule << Range << DiffType; }; - auto ODRDiagNote = [&SecondModule, - this](SourceLocation Loc, SourceRange Range, - ODRDefinitionDataDifference DiffType) { + auto ODRDiagBaseNote = [&SecondModule, + this](SourceLocation Loc, SourceRange Range, + ODRDefinitionDataDifference DiffType) { return Diag(Loc, diag::note_module_odr_violation_definition_data) << SecondModule << Range << DiffType; }; @@ -9500,22 +10190,22 @@ void ASTReader::diagnoseOdrViolations() { }; if (FirstNumBases != SecondNumBases) { - ODRDiagError(FirstRecord->getLocation(), GetSourceRange(FirstDD), - NumBases) + ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumBases) << FirstNumBases; - ODRDiagNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), - NumBases) + ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumBases) << SecondNumBases; Diagnosed = true; break; } if (FirstNumVBases != SecondNumVBases) { - ODRDiagError(FirstRecord->getLocation(), GetSourceRange(FirstDD), - NumVBases) + ODRDiagBaseError(FirstRecord->getLocation(), GetSourceRange(FirstDD), + NumVBases) << FirstNumVBases; - ODRDiagNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), - NumVBases) + ODRDiagBaseNote(SecondRecord->getLocation(), GetSourceRange(SecondDD), + NumVBases) << SecondNumVBases; Diagnosed = true; break; @@ -9529,33 +10219,33 @@ void ASTReader::diagnoseOdrViolations() { auto SecondBase = SecondBases[i]; if (ComputeQualTypeODRHash(FirstBase.getType()) != ComputeQualTypeODRHash(SecondBase.getType())) { - ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(), - BaseType) + ODRDiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseType) << (i + 1) << FirstBase.getType(); - ODRDiagNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseType) + ODRDiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseType) << (i + 1) << SecondBase.getType(); break; } if (FirstBase.isVirtual() != SecondBase.isVirtual()) { - ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(), - BaseVirtual) + ODRDiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseVirtual) << (i + 1) << FirstBase.isVirtual() << FirstBase.getType(); - ODRDiagNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseVirtual) + ODRDiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseVirtual) << (i + 1) << SecondBase.isVirtual() << SecondBase.getType(); break; } if (FirstBase.getAccessSpecifierAsWritten() != SecondBase.getAccessSpecifierAsWritten()) { - ODRDiagError(FirstRecord->getLocation(), FirstBase.getSourceRange(), - BaseAccess) + ODRDiagBaseError(FirstRecord->getLocation(), + FirstBase.getSourceRange(), BaseAccess) << (i + 1) << FirstBase.getType() << (int)FirstBase.getAccessSpecifierAsWritten(); - ODRDiagNote(SecondRecord->getLocation(), - SecondBase.getSourceRange(), BaseAccess) + ODRDiagBaseNote(SecondRecord->getLocation(), + SecondBase.getSourceRange(), BaseAccess) << (i + 1) << SecondBase.getType() << (int)SecondBase.getAccessSpecifierAsWritten(); break; @@ -9568,8 +10258,6 @@ void ASTReader::diagnoseOdrViolations() { } } - using DeclHashes = llvm::SmallVector, 4>; - const ClassTemplateDecl *FirstTemplate = FirstRecord->getDescribedClassTemplate(); const ClassTemplateDecl *SecondTemplate = @@ -9610,16 +10298,16 @@ void ASTReader::diagnoseOdrViolations() { if (FirstIt->second == SecondIt->second) continue; - auto ODRDiagError = [FirstRecord, &FirstModule, - this](SourceLocation Loc, SourceRange Range, - ODRTemplateDifference DiffType) { + auto ODRDiagTemplateError = [FirstRecord, &FirstModule, this]( + SourceLocation Loc, SourceRange Range, + ODRTemplateDifference DiffType) { return Diag(Loc, diag::err_module_odr_violation_template_parameter) << FirstRecord << FirstModule.empty() << FirstModule << Range << DiffType; }; - auto ODRDiagNote = [&SecondModule, - this](SourceLocation Loc, SourceRange Range, - ODRTemplateDifference DiffType) { + auto ODRDiagTemplateNote = [&SecondModule, this]( + SourceLocation Loc, SourceRange Range, + ODRTemplateDifference DiffType) { return Diag(Loc, diag::note_module_odr_violation_template_parameter) << SecondModule << Range << DiffType; }; @@ -9640,11 +10328,13 @@ void ASTReader::diagnoseOdrViolations() { SecondName.isIdentifier() && !SecondName.getAsIdentifierInfo(); assert((!FirstNameEmpty || !SecondNameEmpty) && "Both template parameters cannot be unnamed."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - FirstNameEmpty ? ParamEmptyName : ParamName) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + FirstNameEmpty ? ParamEmptyName : ParamName) << FirstName; - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - SecondNameEmpty ? ParamEmptyName : ParamName) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + SecondNameEmpty ? ParamEmptyName : ParamName) << SecondName; break; } @@ -9663,13 +10353,13 @@ void ASTReader::diagnoseOdrViolations() { !SecondParam->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstDecl->getLocation(), - FirstDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasFirstDefaultArgument; - ODRDiagNote(SecondDecl->getLocation(), - SecondDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasSecondDefaultArgument; break; } @@ -9677,10 +10367,12 @@ void ASTReader::diagnoseOdrViolations() { assert(HasFirstDefaultArgument && HasSecondDefaultArgument && "Expecting default arguments."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - ParamDifferentDefaultArgument); - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - ParamDifferentDefaultArgument); + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamDifferentDefaultArgument); + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamDifferentDefaultArgument); break; } @@ -9695,13 +10387,13 @@ void ASTReader::diagnoseOdrViolations() { !SecondParam->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstDecl->getLocation(), - FirstDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasFirstDefaultArgument; - ODRDiagNote(SecondDecl->getLocation(), - SecondDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasSecondDefaultArgument; break; } @@ -9709,10 +10401,12 @@ void ASTReader::diagnoseOdrViolations() { assert(HasFirstDefaultArgument && HasSecondDefaultArgument && "Expecting default arguments."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - ParamDifferentDefaultArgument); - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - ParamDifferentDefaultArgument); + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamDifferentDefaultArgument); + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamDifferentDefaultArgument); break; } @@ -9728,13 +10422,13 @@ void ASTReader::diagnoseOdrViolations() { !SecondParam->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstDecl->getLocation(), - FirstDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasFirstDefaultArgument; - ODRDiagNote(SecondDecl->getLocation(), - SecondDecl->getSourceRange(), - ParamSingleDefaultArgument) + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamSingleDefaultArgument) << HasSecondDefaultArgument; break; } @@ -9742,10 +10436,12 @@ void ASTReader::diagnoseOdrViolations() { assert(HasFirstDefaultArgument && HasSecondDefaultArgument && "Expecting default arguments."); - ODRDiagError(FirstDecl->getLocation(), FirstDecl->getSourceRange(), - ParamDifferentDefaultArgument); - ODRDiagNote(SecondDecl->getLocation(), SecondDecl->getSourceRange(), - ParamDifferentDefaultArgument); + ODRDiagTemplateError(FirstDecl->getLocation(), + FirstDecl->getSourceRange(), + ParamDifferentDefaultArgument); + ODRDiagTemplateNote(SecondDecl->getLocation(), + SecondDecl->getSourceRange(), + ParamDifferentDefaultArgument); break; } @@ -9762,230 +10458,41 @@ void ASTReader::diagnoseOdrViolations() { DeclHashes FirstHashes; DeclHashes SecondHashes; + const DeclContext *DC = FirstRecord; + PopulateHashes(FirstHashes, FirstRecord, DC); + PopulateHashes(SecondHashes, SecondRecord, DC); - auto PopulateHashes = [&ComputeSubDeclODRHash, FirstRecord]( - DeclHashes &Hashes, CXXRecordDecl *Record) { - for (auto *D : Record->decls()) { - // Due to decl merging, the first CXXRecordDecl is the parent of - // Decls in both records. - if (!ODRHash::isWhitelistedDecl(D, FirstRecord)) - continue; - Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); - } - }; - PopulateHashes(FirstHashes, FirstRecord); - PopulateHashes(SecondHashes, SecondRecord); - - // Used with err_module_odr_violation_mismatch_decl and - // note_module_odr_violation_mismatch_decl - // This list should be the same Decl's as in ODRHash::isWhiteListedDecl - enum { - EndOfClass, - PublicSpecifer, - PrivateSpecifer, - ProtectedSpecifer, - StaticAssert, - Field, - CXXMethod, - TypeAlias, - TypeDef, - Var, - Friend, - FunctionTemplate, - Other - } FirstDiffType = Other, - SecondDiffType = Other; - - auto DifferenceSelector = [](Decl *D) { - assert(D && "valid Decl required"); - switch (D->getKind()) { - default: - return Other; - case Decl::AccessSpec: - switch (D->getAccess()) { - case AS_public: - return PublicSpecifer; - case AS_private: - return PrivateSpecifer; - case AS_protected: - return ProtectedSpecifer; - case AS_none: - break; - } - llvm_unreachable("Invalid access specifier"); - case Decl::StaticAssert: - return StaticAssert; - case Decl::Field: - return Field; - case Decl::CXXMethod: - case Decl::CXXConstructor: - case Decl::CXXDestructor: - return CXXMethod; - case Decl::TypeAlias: - return TypeAlias; - case Decl::Typedef: - return TypeDef; - case Decl::Var: - return Var; - case Decl::Friend: - return Friend; - case Decl::FunctionTemplate: - return FunctionTemplate; - } - }; - - Decl *FirstDecl = nullptr; - Decl *SecondDecl = nullptr; - auto FirstIt = FirstHashes.begin(); - auto SecondIt = SecondHashes.begin(); - - // If there is a diagnoseable difference, FirstDiffType and - // SecondDiffType will not be Other and FirstDecl and SecondDecl will be - // filled in if not EndOfClass. - while (FirstIt != FirstHashes.end() || SecondIt != SecondHashes.end()) { - if (FirstIt != FirstHashes.end() && SecondIt != SecondHashes.end() && - FirstIt->second == SecondIt->second) { - ++FirstIt; - ++SecondIt; - continue; - } - - FirstDecl = FirstIt == FirstHashes.end() ? nullptr : FirstIt->first; - SecondDecl = SecondIt == SecondHashes.end() ? nullptr : SecondIt->first; - - FirstDiffType = FirstDecl ? DifferenceSelector(FirstDecl) : EndOfClass; - SecondDiffType = - SecondDecl ? DifferenceSelector(SecondDecl) : EndOfClass; - - break; - } + auto DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + Decl *FirstDecl = DR.FirstDecl; + Decl *SecondDecl = DR.SecondDecl; if (FirstDiffType == Other || SecondDiffType == Other) { - // Reaching this point means an unexpected Decl was encountered - // or no difference was detected. This causes a generic error - // message to be emitted. - Diag(FirstRecord->getLocation(), - diag::err_module_odr_violation_different_definitions) - << FirstRecord << FirstModule.empty() << FirstModule; - - if (FirstDecl) { - Diag(FirstDecl->getLocation(), diag::note_first_module_difference) - << FirstRecord << FirstDecl->getSourceRange(); - } - - Diag(SecondRecord->getLocation(), - diag::note_module_odr_violation_different_definitions) - << SecondModule; - - if (SecondDecl) { - Diag(SecondDecl->getLocation(), diag::note_second_module_difference) - << SecondDecl->getSourceRange(); - } - + DiagnoseODRUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); Diagnosed = true; break; } if (FirstDiffType != SecondDiffType) { - SourceLocation FirstLoc; - SourceRange FirstRange; - if (FirstDiffType == EndOfClass) { - FirstLoc = FirstRecord->getBraceRange().getEnd(); - } else { - FirstLoc = FirstIt->first->getLocation(); - FirstRange = FirstIt->first->getSourceRange(); - } - Diag(FirstLoc, diag::err_module_odr_violation_mismatch_decl) - << FirstRecord << FirstModule.empty() << FirstModule << FirstRange - << FirstDiffType; - - SourceLocation SecondLoc; - SourceRange SecondRange; - if (SecondDiffType == EndOfClass) { - SecondLoc = SecondRecord->getBraceRange().getEnd(); - } else { - SecondLoc = SecondDecl->getLocation(); - SecondRange = SecondDecl->getSourceRange(); - } - Diag(SecondLoc, diag::note_module_odr_violation_mismatch_decl) - << SecondModule << SecondRange << SecondDiffType; + DiagnoseODRMismatch(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); Diagnosed = true; break; } assert(FirstDiffType == SecondDiffType); - // Used with err_module_odr_violation_mismatch_decl_diff and - // note_module_odr_violation_mismatch_decl_diff - enum ODRDeclDifference { - StaticAssertCondition, - StaticAssertMessage, - StaticAssertOnlyMessage, - FieldName, - FieldTypeName, - FieldSingleBitField, - FieldDifferentWidthBitField, - FieldSingleMutable, - FieldSingleInitializer, - FieldDifferentInitializers, - MethodName, - MethodDeleted, - MethodDefaulted, - MethodVirtual, - MethodStatic, - MethodVolatile, - MethodConst, - MethodInline, - MethodNumberParameters, - MethodParameterType, - MethodParameterName, - MethodParameterSingleDefaultArgument, - MethodParameterDifferentDefaultArgument, - MethodNoTemplateArguments, - MethodDifferentNumberTemplateArguments, - MethodDifferentTemplateArgument, - MethodSingleBody, - MethodDifferentBody, - TypedefName, - TypedefType, - VarName, - VarType, - VarSingleInitializer, - VarDifferentInitializer, - VarConstexpr, - FriendTypeFunction, - FriendType, - FriendFunction, - FunctionTemplateDifferentNumberParameters, - FunctionTemplateParameterDifferentKind, - FunctionTemplateParameterName, - FunctionTemplateParameterSingleDefaultArgument, - FunctionTemplateParameterDifferentDefaultArgument, - FunctionTemplateParameterDifferentType, - FunctionTemplatePackParameter, - }; - - // These lambdas have the common portions of the ODR diagnostics. This - // has the same return as Diag(), so addition parameters can be passed - // in with operator<< - auto ODRDiagError = [FirstRecord, &FirstModule, this]( - SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) { - return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff) - << FirstRecord << FirstModule.empty() << FirstModule << Range - << DiffType; - }; - auto ODRDiagNote = [&SecondModule, this]( - SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) { - return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff) - << SecondModule << Range << DiffType; - }; - switch (FirstDiffType) { case Other: case EndOfClass: case PublicSpecifer: case PrivateSpecifer: case ProtectedSpecifer: + case ObjCMethod: + case ObjCIvar: + case ObjCProperty: llvm_unreachable("Invalid diff type"); case StaticAssert: { @@ -9997,10 +10504,10 @@ void ASTReader::diagnoseOdrViolations() { unsigned FirstODRHash = ComputeODRHash(FirstExpr); unsigned SecondODRHash = ComputeODRHash(SecondExpr); if (FirstODRHash != SecondODRHash) { - ODRDiagError(FirstExpr->getBeginLoc(), FirstExpr->getSourceRange(), - StaticAssertCondition); - ODRDiagNote(SecondExpr->getBeginLoc(), SecondExpr->getSourceRange(), - StaticAssertCondition); + ODRDiagDeclError(FirstRecord, FirstModule, FirstExpr->getBeginLoc(), + FirstExpr->getSourceRange(), StaticAssertCondition); + ODRDiagDeclNote(SecondModule, SecondExpr->getBeginLoc(), + SecondExpr->getSourceRange(), StaticAssertCondition); Diagnosed = true; break; } @@ -10025,9 +10532,11 @@ void ASTReader::diagnoseOdrViolations() { SecondLoc = SecondSA->getBeginLoc(); SecondRange = SecondSA->getSourceRange(); } - ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage) + ODRDiagDeclError(FirstRecord, FirstModule, FirstLoc, FirstRange, + StaticAssertOnlyMessage) << (FirstStr == nullptr); - ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage) + ODRDiagDeclNote(SecondModule, SecondLoc, SecondRange, + StaticAssertOnlyMessage) << (SecondStr == nullptr); Diagnosed = true; break; @@ -10035,127 +10544,23 @@ void ASTReader::diagnoseOdrViolations() { if (FirstStr && SecondStr && FirstStr->getString() != SecondStr->getString()) { - ODRDiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(), - StaticAssertMessage); - ODRDiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(), - StaticAssertMessage); + ODRDiagDeclError(FirstRecord, FirstModule, FirstStr->getBeginLoc(), + FirstStr->getSourceRange(), StaticAssertMessage); + ODRDiagDeclNote(SecondModule, SecondStr->getBeginLoc(), + SecondStr->getSourceRange(), StaticAssertMessage); Diagnosed = true; break; } break; } case Field: { - FieldDecl *FirstField = cast(FirstDecl); - FieldDecl *SecondField = cast(SecondDecl); - IdentifierInfo *FirstII = FirstField->getIdentifier(); - IdentifierInfo *SecondII = SecondField->getIdentifier(); - if (FirstII->getName() != SecondII->getName()) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldName) - << FirstII; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldName) - << SecondII; - - Diagnosed = true; - break; - } - - assert(getContext().hasSameType(FirstField->getType(), - SecondField->getType())); - - QualType FirstType = FirstField->getType(); - QualType SecondType = SecondField->getType(); - if (ComputeQualTypeODRHash(FirstType) != - ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldTypeName) - << FirstII << FirstType; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldTypeName) - << SecondII << SecondType; - - Diagnosed = true; - break; - } - - const bool IsFirstBitField = FirstField->isBitField(); - const bool IsSecondBitField = SecondField->isBitField(); - if (IsFirstBitField != IsSecondBitField) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldSingleBitField) - << FirstII << IsFirstBitField; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldSingleBitField) - << SecondII << IsSecondBitField; - Diagnosed = true; - break; - } - - if (IsFirstBitField && IsSecondBitField) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldDifferentWidthBitField) - << FirstII << FirstField->getBitWidth()->getSourceRange(); - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldDifferentWidthBitField) - << SecondII << SecondField->getBitWidth()->getSourceRange(); - Diagnosed = true; - break; - } - - const bool IsFirstMutable = FirstField->isMutable(); - const bool IsSecondMutable = SecondField->isMutable(); - if (IsFirstMutable != IsSecondMutable) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldSingleMutable) - << FirstII << IsFirstMutable; - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldSingleMutable) - << SecondII << IsSecondMutable; - Diagnosed = true; - break; - } - - const Expr *FirstInitializer = FirstField->getInClassInitializer(); - const Expr *SecondInitializer = SecondField->getInClassInitializer(); - if ((!FirstInitializer && SecondInitializer) || - (FirstInitializer && !SecondInitializer)) { - ODRDiagError(FirstField->getLocation(), FirstField->getSourceRange(), - FieldSingleInitializer) - << FirstII << (FirstInitializer != nullptr); - ODRDiagNote(SecondField->getLocation(), SecondField->getSourceRange(), - FieldSingleInitializer) - << SecondII << (SecondInitializer != nullptr); - Diagnosed = true; - break; - } - - if (FirstInitializer && SecondInitializer) { - unsigned FirstInitHash = ComputeODRHash(FirstInitializer); - unsigned SecondInitHash = ComputeODRHash(SecondInitializer); - if (FirstInitHash != SecondInitHash) { - ODRDiagError(FirstField->getLocation(), - FirstField->getSourceRange(), - FieldDifferentInitializers) - << FirstII << FirstInitializer->getSourceRange(); - ODRDiagNote(SecondField->getLocation(), - SecondField->getSourceRange(), - FieldDifferentInitializers) - << SecondII << SecondInitializer->getSourceRange(); - Diagnosed = true; - break; - } - } - + Diagnosed = ODRDiagField(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); break; } case CXXMethod: { - enum { - DiagMethod, - DiagConstructor, - DiagDestructor, - } FirstMethodType, - SecondMethodType; + ODRDiagMethodTypes FirstMethodType, SecondMethodType; auto GetMethodTypeForDiagnostics = [](const CXXMethodDecl* D) { if (isa(D)) return DiagConstructor; if (isa(D)) return DiagDestructor; @@ -10168,11 +10573,11 @@ void ASTReader::diagnoseOdrViolations() { auto FirstName = FirstMethod->getDeclName(); auto SecondName = SecondMethod->getDeclName(); if (FirstMethodType != SecondMethodType || FirstName != SecondName) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodName) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodName) << FirstMethodType << FirstName; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodName) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodName) << SecondMethodType << SecondName; Diagnosed = true; @@ -10182,12 +10587,12 @@ void ASTReader::diagnoseOdrViolations() { const bool FirstDeleted = FirstMethod->isDeletedAsWritten(); const bool SecondDeleted = SecondMethod->isDeletedAsWritten(); if (FirstDeleted != SecondDeleted) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodDeleted) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDeleted) << FirstMethodType << FirstName << FirstDeleted; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodDeleted) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodDeleted) << SecondMethodType << SecondName << SecondDeleted; Diagnosed = true; break; @@ -10196,12 +10601,12 @@ void ASTReader::diagnoseOdrViolations() { const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted(); const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted(); if (FirstDefaulted != SecondDefaulted) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodDefaulted) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDefaulted) << FirstMethodType << FirstName << FirstDefaulted; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodDefaulted) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodDefaulted) << SecondMethodType << SecondName << SecondDefaulted; Diagnosed = true; break; @@ -10213,11 +10618,11 @@ void ASTReader::diagnoseOdrViolations() { const bool SecondPure = SecondMethod->isPure(); if ((FirstVirtual || SecondVirtual) && (FirstVirtual != SecondVirtual || FirstPure != SecondPure)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodVirtual) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodVirtual) << FirstMethodType << FirstName << FirstPure << FirstVirtual; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodVirtual) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodVirtual) << SecondMethodType << SecondName << SecondPure << SecondVirtual; Diagnosed = true; break; @@ -10231,11 +10636,11 @@ void ASTReader::diagnoseOdrViolations() { const bool FirstStatic = FirstStorage == SC_Static; const bool SecondStatic = SecondStorage == SC_Static; if (FirstStatic != SecondStatic) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodStatic) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodStatic) << FirstMethodType << FirstName << FirstStatic; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodStatic) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodStatic) << SecondMethodType << SecondName << SecondStatic; Diagnosed = true; break; @@ -10244,11 +10649,11 @@ void ASTReader::diagnoseOdrViolations() { const bool FirstVolatile = FirstMethod->isVolatile(); const bool SecondVolatile = SecondMethod->isVolatile(); if (FirstVolatile != SecondVolatile) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodVolatile) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodVolatile) << FirstMethodType << FirstName << FirstVolatile; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodVolatile) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodVolatile) << SecondMethodType << SecondName << SecondVolatile; Diagnosed = true; break; @@ -10257,11 +10662,11 @@ void ASTReader::diagnoseOdrViolations() { const bool FirstConst = FirstMethod->isConst(); const bool SecondConst = SecondMethod->isConst(); if (FirstConst != SecondConst) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodConst) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodConst) << FirstMethodType << FirstName << FirstConst; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodConst) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodConst) << SecondMethodType << SecondName << SecondConst; Diagnosed = true; break; @@ -10270,124 +10675,21 @@ void ASTReader::diagnoseOdrViolations() { const bool FirstInline = FirstMethod->isInlineSpecified(); const bool SecondInline = SecondMethod->isInlineSpecified(); if (FirstInline != SecondInline) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodInline) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodInline) << FirstMethodType << FirstName << FirstInline; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodInline) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodInline) << SecondMethodType << SecondName << SecondInline; Diagnosed = true; break; } - const unsigned FirstNumParameters = FirstMethod->param_size(); - const unsigned SecondNumParameters = SecondMethod->param_size(); - if (FirstNumParameters != SecondNumParameters) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodNumberParameters) - << FirstMethodType << FirstName << FirstNumParameters; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodNumberParameters) - << SecondMethodType << SecondName << SecondNumParameters; - Diagnosed = true; - break; - } - - // Need this status boolean to know when break out of the switch. - bool ParameterMismatch = false; - for (unsigned I = 0; I < FirstNumParameters; ++I) { - const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); - const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); - - QualType FirstParamType = FirstParam->getType(); - QualType SecondParamType = SecondParam->getType(); - if (FirstParamType != SecondParamType && - ComputeQualTypeODRHash(FirstParamType) != - ComputeQualTypeODRHash(SecondParamType)) { - if (const DecayedType *ParamDecayedType = - FirstParamType->getAs()) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodParameterType) - << FirstMethodType << FirstName << (I + 1) << FirstParamType - << true << ParamDecayedType->getOriginalType(); - } else { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodParameterType) - << FirstMethodType << FirstName << (I + 1) << FirstParamType - << false; - } - - if (const DecayedType *ParamDecayedType = - SecondParamType->getAs()) { - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodParameterType) - << SecondMethodType << SecondName << (I + 1) - << SecondParamType << true - << ParamDecayedType->getOriginalType(); - } else { - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodParameterType) - << SecondMethodType << SecondName << (I + 1) - << SecondParamType << false; - } - ParameterMismatch = true; - break; - } - - DeclarationName FirstParamName = FirstParam->getDeclName(); - DeclarationName SecondParamName = SecondParam->getDeclName(); - if (FirstParamName != SecondParamName) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodParameterName) - << FirstMethodType << FirstName << (I + 1) << FirstParamName; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodParameterName) - << SecondMethodType << SecondName << (I + 1) << SecondParamName; - ParameterMismatch = true; - break; - } - - const Expr *FirstInit = FirstParam->getInit(); - const Expr *SecondInit = SecondParam->getInit(); - if ((FirstInit == nullptr) != (SecondInit == nullptr)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodParameterSingleDefaultArgument) - << FirstMethodType << FirstName << (I + 1) - << (FirstInit == nullptr) - << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodParameterSingleDefaultArgument) - << SecondMethodType << SecondName << (I + 1) - << (SecondInit == nullptr) - << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); - ParameterMismatch = true; - break; - } - - if (FirstInit && SecondInit && - ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodParameterDifferentDefaultArgument) - << FirstMethodType << FirstName << (I + 1) - << FirstInit->getSourceRange(); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodParameterDifferentDefaultArgument) - << SecondMethodType << SecondName << (I + 1) - << SecondInit->getSourceRange(); - ParameterMismatch = true; - break; - - } - } - - if (ParameterMismatch) { - Diagnosed = true; + Diagnosed = ODRDiagCommonMethodChecks( + FirstRecord, FirstModule, SecondModule, FirstMethodType, + SecondMethodType, FirstMethod, SecondMethod); + if (Diagnosed) break; - } const auto *FirstTemplateArgs = FirstMethod->getTemplateSpecializationArgs(); @@ -10396,11 +10698,13 @@ void ASTReader::diagnoseOdrViolations() { if ((FirstTemplateArgs && !SecondTemplateArgs) || (!FirstTemplateArgs && SecondTemplateArgs)) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodNoTemplateArguments) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodNoTemplateArguments) << FirstMethodType << FirstName << (FirstTemplateArgs != nullptr); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodNoTemplateArguments) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodNoTemplateArguments) << SecondMethodType << SecondName << (SecondTemplateArgs != nullptr); @@ -10430,14 +10734,15 @@ void ASTReader::diagnoseOdrViolations() { ExpandTemplateArgumentList(SecondTemplateArgs); if (FirstExpandedList.size() != SecondExpandedList.size()) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodDifferentNumberTemplateArguments) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), + MethodDifferentNumberTemplateArguments) << FirstMethodType << FirstName << (unsigned)FirstExpandedList.size(); - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodDifferentNumberTemplateArguments) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodDifferentNumberTemplateArguments) << SecondMethodType << SecondName << (unsigned)SecondExpandedList.size(); @@ -10454,13 +10759,13 @@ void ASTReader::diagnoseOdrViolations() { continue; } - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), - MethodDifferentTemplateArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDifferentTemplateArgument) << FirstMethodType << FirstName << FirstTA << i + 1; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), - MethodDifferentTemplateArgument) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + MethodDifferentTemplateArgument) << SecondMethodType << SecondName << SecondTA << i + 1; TemplateArgumentMismatch = true; @@ -10489,22 +10794,22 @@ void ASTReader::diagnoseOdrViolations() { ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash(); if (HasFirstBody != HasSecondBody) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodSingleBody) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodSingleBody) << FirstMethodType << FirstName << HasFirstBody; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodSingleBody) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodSingleBody) << SecondMethodType << SecondName << HasSecondBody; Diagnosed = true; break; } if (HasFirstBody && HasSecondBody) { - ODRDiagError(FirstMethod->getLocation(), - FirstMethod->getSourceRange(), MethodDifferentBody) + ODRDiagDeclError(FirstRecord, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodDifferentBody) << FirstMethodType << FirstName; - ODRDiagNote(SecondMethod->getLocation(), - SecondMethod->getSourceRange(), MethodDifferentBody) + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodDifferentBody) << SecondMethodType << SecondName; Diagnosed = true; break; @@ -10514,105 +10819,16 @@ void ASTReader::diagnoseOdrViolations() { } case TypeAlias: case TypeDef: { - TypedefNameDecl *FirstTD = cast(FirstDecl); - TypedefNameDecl *SecondTD = cast(SecondDecl); - auto FirstName = FirstTD->getDeclName(); - auto SecondName = SecondTD->getDeclName(); - if (FirstName != SecondName) { - ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(), - TypedefName) - << (FirstDiffType == TypeAlias) << FirstName; - ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(), - TypedefName) - << (FirstDiffType == TypeAlias) << SecondName; - Diagnosed = true; - break; - } - - QualType FirstType = FirstTD->getUnderlyingType(); - QualType SecondType = SecondTD->getUnderlyingType(); - if (ComputeQualTypeODRHash(FirstType) != - ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstTD->getLocation(), FirstTD->getSourceRange(), - TypedefType) - << (FirstDiffType == TypeAlias) << FirstName << FirstType; - ODRDiagNote(SecondTD->getLocation(), SecondTD->getSourceRange(), - TypedefType) - << (FirstDiffType == TypeAlias) << SecondName << SecondType; - Diagnosed = true; - break; - } + Diagnosed = ODRDiagTypeDefOrAlias( + FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl), + FirstDiffType == TypeAlias); break; } case Var: { - VarDecl *FirstVD = cast(FirstDecl); - VarDecl *SecondVD = cast(SecondDecl); - auto FirstName = FirstVD->getDeclName(); - auto SecondName = SecondVD->getDeclName(); - if (FirstName != SecondName) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarName) - << FirstName; - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarName) - << SecondName; - Diagnosed = true; - break; - } - - QualType FirstType = FirstVD->getType(); - QualType SecondType = SecondVD->getType(); - if (ComputeQualTypeODRHash(FirstType) != - ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarType) - << FirstName << FirstType; - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarType) - << SecondName << SecondType; - Diagnosed = true; - break; - } - - const Expr *FirstInit = FirstVD->getInit(); - const Expr *SecondInit = SecondVD->getInit(); - if ((FirstInit == nullptr) != (SecondInit == nullptr)) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarSingleInitializer) - << FirstName << (FirstInit == nullptr) - << (FirstInit ? FirstInit->getSourceRange(): SourceRange()); - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarSingleInitializer) - << SecondName << (SecondInit == nullptr) - << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); - Diagnosed = true; - break; - } - - if (FirstInit && SecondInit && - ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarDifferentInitializer) - << FirstName << FirstInit->getSourceRange(); - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarDifferentInitializer) - << SecondName << SecondInit->getSourceRange(); - Diagnosed = true; - break; - } - - const bool FirstIsConstexpr = FirstVD->isConstexpr(); - const bool SecondIsConstexpr = SecondVD->isConstexpr(); - if (FirstIsConstexpr != SecondIsConstexpr) { - ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(), - VarConstexpr) - << FirstName << FirstIsConstexpr; - ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(), - VarConstexpr) - << SecondName << SecondIsConstexpr; - Diagnosed = true; - break; - } + Diagnosed = + ODRDiagVar(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl)); break; } case Friend: { @@ -10626,11 +10842,12 @@ void ASTReader::diagnoseOdrViolations() { TypeSourceInfo *SecondTSI = SecondFriend->getFriendType(); if (FirstND && SecondND) { - ODRDiagError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendFunction) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendFunction) << FirstND; - ODRDiagNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendFunction) + ODRDiagDeclNote(SecondModule, SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendFunction) << SecondND; Diagnosed = true; @@ -10642,21 +10859,22 @@ void ASTReader::diagnoseOdrViolations() { QualType SecondFriendType = SecondTSI->getType(); assert(ComputeQualTypeODRHash(FirstFriendType) != ComputeQualTypeODRHash(SecondFriendType)); - ODRDiagError(FirstFriend->getFriendLoc(), - FirstFriend->getSourceRange(), FriendType) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendType) << FirstFriendType; - ODRDiagNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendType) + ODRDiagDeclNote(SecondModule, SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendType) << SecondFriendType; Diagnosed = true; break; } - ODRDiagError(FirstFriend->getFriendLoc(), FirstFriend->getSourceRange(), - FriendTypeFunction) + ODRDiagDeclError(FirstRecord, FirstModule, FirstFriend->getFriendLoc(), + FirstFriend->getSourceRange(), FriendTypeFunction) << (FirstTSI == nullptr); - ODRDiagNote(SecondFriend->getFriendLoc(), - SecondFriend->getSourceRange(), FriendTypeFunction) + ODRDiagDeclNote(SecondModule, SecondFriend->getFriendLoc(), + SecondFriend->getSourceRange(), FriendTypeFunction) << (SecondTSI == nullptr); Diagnosed = true; @@ -10674,14 +10892,15 @@ void ASTReader::diagnoseOdrViolations() { SecondTemplate->getTemplateParameters(); if (FirstTPL->size() != SecondTPL->size()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateDifferentNumberParameters) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateDifferentNumberParameters) << FirstTemplate << FirstTPL->size(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateDifferentNumberParameters) - << SecondTemplate << SecondTPL->size(); + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateDifferentNumberParameters) + << SecondTemplate << SecondTPL->size(); Diagnosed = true; break; @@ -10711,13 +10930,14 @@ void ASTReader::diagnoseOdrViolations() { } }; - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentKind) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentKind) << FirstTemplate << (i + 1) << GetParamType(FirstParam); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentKind) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentKind) << SecondTemplate << (i + 1) << GetParamType(SecondParam); ParameterMismatch = true; @@ -10725,14 +10945,14 @@ void ASTReader::diagnoseOdrViolations() { } if (FirstParam->getName() != SecondParam->getName()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterName) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), FunctionTemplateParameterName) << FirstTemplate << (i + 1) << (bool)FirstParam->getIdentifier() << FirstParam; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterName) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterName) << SecondTemplate << (i + 1) << (bool)SecondParam->getIdentifier() << SecondParam; ParameterMismatch = true; @@ -10752,13 +10972,14 @@ void ASTReader::diagnoseOdrViolations() { SecondTTPD->hasDefaultArgument() && !SecondTTPD->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << FirstTemplate << (i + 1) << HasFirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << SecondTemplate << (i + 1) << HasSecondDefaultArgument; ParameterMismatch = true; break; @@ -10769,13 +10990,15 @@ void ASTReader::diagnoseOdrViolations() { QualType SecondType = SecondTTPD->getDefaultArgument(); if (ComputeQualTypeODRHash(FirstType) != ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << FirstTemplate << (i + 1) << FirstType; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclNote( + SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << SecondTemplate << (i + 1) << SecondType; ParameterMismatch = true; break; @@ -10784,13 +11007,14 @@ void ASTReader::diagnoseOdrViolations() { if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplatePackParameter) << FirstTemplate << (i + 1) << FirstTTPD->isParameterPack(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplatePackParameter) << SecondTemplate << (i + 1) << SecondTTPD->isParameterPack(); ParameterMismatch = true; break; @@ -10811,13 +11035,14 @@ void ASTReader::diagnoseOdrViolations() { if (ComputeTemplateParameterListODRHash(FirstTPL) != ComputeTemplateParameterListODRHash(SecondTPL)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << FirstTemplate << (i + 1); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << SecondTemplate << (i + 1); ParameterMismatch = true; break; @@ -10830,13 +11055,14 @@ void ASTReader::diagnoseOdrViolations() { SecondTTPD->hasDefaultArgument() && !SecondTTPD->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << FirstTemplate << (i + 1) << HasFirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << SecondTemplate << (i + 1) << HasSecondDefaultArgument; ParameterMismatch = true; break; @@ -10849,13 +11075,15 @@ void ASTReader::diagnoseOdrViolations() { SecondTTPD->getDefaultArgument().getArgument(); if (ComputeTemplateArgumentODRHash(FirstTA) != ComputeTemplateArgumentODRHash(SecondTA)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << FirstTemplate << (i + 1) << FirstTA; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclNote( + SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << SecondTemplate << (i + 1) << SecondTA; ParameterMismatch = true; break; @@ -10864,13 +11092,14 @@ void ASTReader::diagnoseOdrViolations() { if (FirstTTPD->isParameterPack() != SecondTTPD->isParameterPack()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplatePackParameter) << FirstTemplate << (i + 1) << FirstTTPD->isParameterPack(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplatePackParameter) << SecondTemplate << (i + 1) << SecondTTPD->isParameterPack(); ParameterMismatch = true; break; @@ -10888,13 +11117,14 @@ void ASTReader::diagnoseOdrViolations() { QualType SecondType = SecondNTTPD->getType(); if (ComputeQualTypeODRHash(FirstType) != ComputeQualTypeODRHash(SecondType)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << FirstTemplate << (i + 1); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentType) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentType) << SecondTemplate << (i + 1); ParameterMismatch = true; break; @@ -10907,13 +11137,14 @@ void ASTReader::diagnoseOdrViolations() { SecondNTTPD->hasDefaultArgument() && !SecondNTTPD->defaultArgumentWasInherited(); if (HasFirstDefaultArgument != HasSecondDefaultArgument) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << FirstTemplate << (i + 1) << HasFirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterSingleDefaultArgument) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterSingleDefaultArgument) << SecondTemplate << (i + 1) << HasSecondDefaultArgument; ParameterMismatch = true; break; @@ -10924,13 +11155,15 @@ void ASTReader::diagnoseOdrViolations() { Expr *SecondDefaultArgument = SecondNTTPD->getDefaultArgument(); if (ComputeODRHash(FirstDefaultArgument) != ComputeODRHash(SecondDefaultArgument)) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclError( + FirstRecord, FirstModule, FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << FirstTemplate << (i + 1) << FirstDefaultArgument; - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplateParameterDifferentDefaultArgument) + ODRDiagDeclNote( + SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplateParameterDifferentDefaultArgument) << SecondTemplate << (i + 1) << SecondDefaultArgument; ParameterMismatch = true; break; @@ -10939,13 +11172,14 @@ void ASTReader::diagnoseOdrViolations() { if (FirstNTTPD->isParameterPack() != SecondNTTPD->isParameterPack()) { - ODRDiagError(FirstTemplate->getLocation(), - FirstTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclError(FirstRecord, FirstModule, + FirstTemplate->getLocation(), + FirstTemplate->getSourceRange(), + FunctionTemplatePackParameter) << FirstTemplate << (i + 1) << FirstNTTPD->isParameterPack(); - ODRDiagNote(SecondTemplate->getLocation(), - SecondTemplate->getSourceRange(), - FunctionTemplatePackParameter) + ODRDiagDeclNote(SecondModule, SecondTemplate->getLocation(), + SecondTemplate->getSourceRange(), + FunctionTemplatePackParameter) << SecondTemplate << (i + 1) << SecondNTTPD->isParameterPack(); ParameterMismatch = true; @@ -10989,6 +11223,849 @@ void ASTReader::diagnoseOdrViolations() { } } + auto PopulateRecordHashes = [&ComputeSubDeclODRHash](DeclHashes &Hashes, + RecordDecl *Record, + const DeclContext *DC) { + std::deque> WorkList; + for (auto *D : Record->decls()) + WorkList.push_back(std::make_pair(D, DC)); + + while (!WorkList.empty()) { + auto &P = WorkList.front(); + WorkList.pop_front(); + auto *SubRec = dyn_cast(P.first); + if (!SubRec) { + if (!ODRHash::isWhitelistedDecl(P.first, P.second)) + continue; + Hashes.emplace_back(P.first, ComputeSubDeclODRHash(P.first)); + continue; + } + if (!SubRec->isAnonymousStructOrUnion()) + continue; + for (auto *SubD : SubRec->decls()) + WorkList.push_front(std::make_pair(SubD, SubRec)); + } + }; + + // Issue any pending ODR-failure (for structural equivalence checks) + // diagnostics for RecordDecl in C/ObjC, note that in C++ this is + // done as paert of CXXRecordDecl ODR checking. + for (auto &Merge : RecordOdrMergeFailures) { + // If we've already pointed out a specific problem with this class, don't + // bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + bool Diagnosed = false; + RecordDecl *FirstRecord = Merge.first; + + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstRecord); + for (auto *SecondRecord : Merge.second) { + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstRecord == SecondRecord) + continue; + + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondRecord); + DeclHashes FirstHashes; + DeclHashes SecondHashes; + + const DeclContext *DC = FirstRecord; + PopulateRecordHashes(FirstHashes, FirstRecord, DC); + PopulateRecordHashes(SecondHashes, SecondRecord, DC); + + auto DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + Decl *FirstDecl = DR.FirstDecl; + Decl *SecondDecl = DR.SecondDecl; + + auto GetAttrList = [](llvm::SmallVectorImpl &Attrs, + NamedDecl *D) { + if (!D->hasAttrs()) + return; + for (const Attr *A : D->getAttrs()) { + if (ODRHash::isWhitelistedAttr(A)) + Attrs.push_back(A); + } + llvm::sort(Attrs, [](const Attr *A, const Attr *B) { + return Attr::compare(A, B); + }); + }; + + if (FirstDiffType == Other || SecondDiffType == Other) { + // The difference might be in the attributes, check for that. + if (FirstRecord->hasAttrs() || SecondRecord->hasAttrs()) { + llvm::SmallVector FirstAttrs; + llvm::SmallVector SecondAttrs; + GetAttrList(FirstAttrs, FirstRecord); + GetAttrList(SecondAttrs, SecondRecord); + Diagnosed = ODRDiagAttrs(FirstRecord, SecondRecord, FirstModule, + SecondModule, FirstAttrs, SecondAttrs); + if (Diagnosed) + break; + } + + DiagnoseODRUnexpected(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + Diagnosed = true; + break; + } + + if (FirstDiffType != SecondDiffType) { + DiagnoseODRMismatch(DR, FirstRecord, FirstModule, SecondRecord, + SecondModule); + Diagnosed = true; + break; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + case EndOfClass: + case Other: + case ObjCMethod: + case ObjCIvar: + case ObjCProperty: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + llvm_unreachable("Invalid diff type"); + + case Field: { + Diagnosed = ODRDiagField(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + case TypeDef: { + Diagnosed = ODRDiagTypeDefOrAlias( + FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl), + false /* IsTypeAlias */); + break; + } + case Var: { + Diagnosed = + ODRDiagVar(FirstRecord, FirstModule, SecondModule, + cast(FirstDecl), cast(SecondDecl)); + break; + } + } + + if (Diagnosed) + continue; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstRecord << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + Diagnosed = true; + } + } + + // Diagnose property implementation mismatches (@required/@optional), + // this used by @protocol's but not for @interface's. + auto ODRDiagObjCImplControl = + [&ODRDiagDeclError, &ODRDiagDeclNote](NamedDecl *Container, StringRef FirstModule, + StringRef SecondModule, auto *FirstProp, + auto *SecondProp, unsigned FirstImplControl, unsigned SecondImplControl) { + bool IsMethod = isa(FirstProp); + if (FirstImplControl != SecondImplControl) { + ODRDiagDeclError(Container, FirstModule, + FirstProp->getLocation(), + FirstProp->getSourceRange(), ObjCImplementationControl) + << FirstImplControl << IsMethod; + ODRDiagDeclNote(SecondModule, SecondProp->getLocation(), + SecondProp->getSourceRange(), ObjCImplementationControl) + << SecondImplControl << IsMethod; + return true; + } + return false; + }; + + auto ODRDiagObjCProperty = + [&ComputeQualTypeODRHash, &ODRDiagDeclError, &ODRDiagObjCImplControl, + &ODRDiagDeclNote](NamedDecl *FirstObjCContainer, StringRef FirstModule, + StringRef SecondModule, ObjCPropertyDecl *FirstProp, + ObjCPropertyDecl *SecondProp) { + IdentifierInfo *FirstII = FirstProp->getIdentifier(); + IdentifierInfo *SecondII = SecondProp->getIdentifier(); + if (FirstII->getName() != SecondII->getName()) { + ODRDiagDeclError(FirstObjCContainer, FirstModule, + FirstProp->getLocation(), + FirstProp->getSourceRange(), ObjCPropertyName) + << FirstII; + ODRDiagDeclNote(SecondModule, SecondProp->getLocation(), + SecondProp->getSourceRange(), ObjCPropertyName) + << SecondII; + + return true; + } + if (ComputeQualTypeODRHash(FirstProp->getType()) != + ComputeQualTypeODRHash(SecondProp->getType())) { + ODRDiagDeclError(FirstObjCContainer, FirstModule, + FirstProp->getLocation(), + FirstProp->getSourceRange(), ObjCPropertyType) + << FirstII << FirstProp->getType(); + ODRDiagDeclNote(SecondModule, SecondProp->getLocation(), + SecondProp->getSourceRange(), ObjCPropertyType) + << SecondII << SecondProp->getType(); + return true; + } + + if (ODRDiagObjCImplControl(FirstObjCContainer, FirstModule, SecondModule, + FirstProp, SecondProp, FirstProp->getPropertyImplementation(), + SecondProp->getPropertyImplementation())) + return true; + + // Go over the attributes and stop at the first mismatch + unsigned FirstAttrs = (unsigned)FirstProp->getPropertyAttributes(); + unsigned SecondAttrs = (unsigned)SecondProp->getPropertyAttributes(); + unsigned FirstAttrsAsWritten = + (unsigned)FirstProp->getPropertyAttributesAsWritten(); + unsigned SecondAttrsAsWritten = + (unsigned)SecondProp->getPropertyAttributesAsWritten(); + if (FirstAttrs == SecondAttrs) // Nothing left to check + return false; + + for (unsigned i = 0; i < ObjCPropertyDecl::NumPropertyAttrsBits; ++i) { + if ((FirstAttrs & 0x1) == (SecondAttrs & 0x1)) { + FirstAttrs >>= 1; + SecondAttrs >>= 1; + continue; + } + + // If a attribute is found first in the second property, swap the + // diagnostics order and start there. + if ((FirstAttrs & 0x1) == 0) { + std::swap(FirstProp, SecondProp); + std::swap(FirstAttrsAsWritten, SecondAttrsAsWritten); + std::swap(FirstModule, SecondModule); + } + + bool isFirstWritten = (FirstAttrsAsWritten >> i) & 0x1; + ODRDiagDeclError(FirstObjCContainer, FirstModule, + FirstProp->getLParenLoc(), + FirstProp->getSourceRange(), ObjCPropertyAttributes) + << (i + 1) << isFirstWritten; + ODRDiagDeclNote(SecondModule, SecondProp->getLParenLoc(), + SecondProp->getSourceRange(), ObjCPropertyAttributes); + return true; + } + + return false; + }; + + auto ODRDiagObjCMethod = + [&ComputeQualTypeODRHash, &ODRDiagDeclError, &ODRDiagDeclNote, + &ODRDiagCommonMethodChecks, &ODRDiagObjCImplControl]( + NamedDecl *FirstObjCContainer, StringRef FirstModule, + StringRef SecondModule, ObjCMethodDecl *FirstMethod, + ObjCMethodDecl *SecondMethod) { + if (ComputeQualTypeODRHash(FirstMethod->getReturnType()) != + ComputeQualTypeODRHash(SecondMethod->getReturnType())) { + ODRDiagDeclError(FirstObjCContainer, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), ObjCMethodReturnType) + << FirstMethod->getReturnType(); + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), ObjCMethodReturnType) + << SecondMethod->getReturnType(); + return true; + } + if (FirstMethod->isInstanceMethod() != SecondMethod->isInstanceMethod()) { + ODRDiagDeclError(FirstObjCContainer, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), ObjCMethodInstanceOrClass) + << FirstMethod->isInstanceMethod(); + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), ObjCMethodInstanceOrClass) + << SecondMethod->isInstanceMethod(); + return true; + + } + if (ODRDiagObjCImplControl(FirstObjCContainer, FirstModule, SecondModule, + FirstMethod, SecondMethod, FirstMethod->getImplementationControl(), + SecondMethod->getImplementationControl())) + return true; + + if (FirstMethod->isThisDeclarationADesignatedInitializer() != + SecondMethod->isThisDeclarationADesignatedInitializer()) { + ODRDiagDeclError( + FirstObjCContainer, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), ObjCDesignatedInitializer) + << FirstMethod->isThisDeclarationADesignatedInitializer(); + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), + ObjCDesignatedInitializer) + << SecondMethod->isThisDeclarationADesignatedInitializer(); + return true; + } + + if (FirstMethod->isDirectMethod() != SecondMethod->isDirectMethod()) { + ODRDiagDeclError(FirstObjCContainer, FirstModule, + FirstMethod->getLocation(), + FirstMethod->getSourceRange(), ObjCDirectMethod) + << FirstMethod->isDirectMethod(); + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), ObjCDirectMethod) + << SecondMethod->isDirectMethod(); + return true; + } + + if (ODRDiagCommonMethodChecks(FirstObjCContainer, FirstModule, + SecondModule, DiagMethod, DiagMethod, + FirstMethod, SecondMethod)) + return true; + + // Check method name *after* looking at the parameters otherwise we get a + // less ideal diagnostics: a ObjCMethodName mismatch given that selectors + // embed the argument name. + auto FirstName = FirstMethod->getDeclName(); + auto SecondName = SecondMethod->getDeclName(); + if (FirstName != SecondName) { + ODRDiagDeclError(FirstObjCContainer, FirstModule, FirstMethod->getLocation(), + FirstMethod->getSourceRange(), ObjCMethodName) + << FirstName; + ODRDiagDeclNote(SecondModule, SecondMethod->getLocation(), + SecondMethod->getSourceRange(), ObjCMethodName) + << SecondName; + return true; + } + + // TODO: check method bodies whenever we get ODR hash support and diagnostics + // for @implementation. + return false; + }; + + auto DiagnoseODRMismatchObjCGetLoc = [](NamedDecl *Container, auto DiffType, + auto *DiffDecl, + SourceLocation DefinitionDataLoc) { + SourceLocation Loc = Container->getEndLoc(); + SourceRange Range; + if (DiffType != EndOfClass) { + if (DiffDecl) + return std::make_pair(DiffDecl->getLocation(), + DiffDecl->getSourceRange()); + return std::make_pair(Loc, Range); + } + + if (auto *I = dyn_cast(Container)) + if (I->hasDefinition()) + return std::make_pair(DefinitionDataLoc, Range); + + return std::make_pair(Loc, Range); + }; + + auto DiagnoseODRMismatchObjC = [this, &DiagnoseODRMismatchObjCGetLoc]( + DiffResult &DR, NamedDecl *FirstRecord, + StringRef FirstModule, + NamedDecl *SecondRecord, + StringRef SecondModule, + SourceLocation FirstDDataLoc, + SourceLocation SecondDDataLoc) { + auto FirstDiagInfo = DiagnoseODRMismatchObjCGetLoc( + FirstRecord, DR.FirstDiffType, DR.FirstDecl, FirstDDataLoc); + Diag(FirstDiagInfo.first, diag::err_module_odr_violation_mismatch_decl) + << FirstRecord << FirstModule.empty() << FirstModule + << FirstDiagInfo.second << DR.FirstDiffType; + + auto SecondDiagInfo = DiagnoseODRMismatchObjCGetLoc( + SecondRecord, DR.SecondDiffType, DR.SecondDecl, SecondDDataLoc); + Diag(SecondDiagInfo.first, diag::note_module_odr_violation_mismatch_decl) + << SecondModule << SecondDiagInfo.second << DR.SecondDiffType; + }; + + // Keep this in sync with + // err_module_odr_violation_objc_container_definition_data and + // note_module_odr_violation_objc_container_definition_data. + enum ODRObjCDefDataDifference { + SuperClassType, + ReferencedProtocols + }; + auto ODRDiagObjCDDataError = [this](const ObjCContainerDecl *C, StringRef FirstModule, + SourceLocation Loc, SourceRange Range, + ODRObjCDefDataDifference DiffType) { + using namespace diag; + return Diag(Loc, + err_module_odr_violation_objc_container_definition_data) + << C << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto ODRDiagObjCDDataNote = [this](StringRef SecondModule, SourceLocation Loc, + SourceRange Range, + ODRObjCDefDataDifference DiffType) { + using namespace diag; + return Diag(Loc, + note_module_odr_violation_objc_container_definition_data) + << SecondModule << Range << DiffType; + }; + auto GetProtoListSourceRange = [](const ObjCContainerDecl *C, + const ObjCProtocolList &PL) { + if (!PL.size()) + return C->getSourceRange(); + return SourceRange(*PL.loc_begin(), *std::prev(PL.loc_end())); + }; + + auto ODRDiagObjCProtocolList = [&ODRDiagObjCDDataError, &ODRDiagObjCDDataNote, &GetProtoListSourceRange, &ODRDiagDeclError, &ODRDiagDeclNote]( + const ObjCProtocolList &FirstProtos, const ObjCContainerDecl *FirstC, StringRef FirstModule, + const ObjCProtocolList &SecondProtos, const ObjCContainerDecl *SecondC, StringRef SecondModule) { + if (FirstProtos.size() != SecondProtos.size()) { + ODRDiagObjCDDataError(FirstC, FirstModule, FirstC->getLocation(), + GetProtoListSourceRange(FirstC, FirstProtos), + ReferencedProtocols) + << FirstProtos.size(); + ODRDiagObjCDDataNote(SecondModule, SecondC->getLocation(), + GetProtoListSourceRange(SecondC, SecondProtos), + ReferencedProtocols) + << SecondProtos.size(); + return true; + } + + auto *FirstProtosLocIt = FirstProtos.loc_begin(); + auto *SecondProtosLocIt = SecondProtos.loc_begin(); + for (unsigned I = 0, E = FirstProtos.size(); I != E; ++I) { + auto *FirstParam = FirstProtos[I]; + auto *SecondParam = SecondProtos[I]; + + DeclarationName FirstParamName = FirstParam->getDeclName(); + DeclarationName SecondParamName = SecondParam->getDeclName(); + if (FirstParamName != SecondParamName) { + SourceLocation FirstLoc = *(FirstProtosLocIt + I); + SourceLocation SecondLoc = *(SecondProtosLocIt + I); + SourceRange Range; + ODRDiagDeclError(FirstC, FirstModule, + FirstLoc, Range, + ObjCReferencedProtocolName) + << (I + 1) << FirstParamName; + ODRDiagDeclNote(SecondModule, SecondLoc, Range, + ObjCReferencedProtocolName) + << (I + 1) << SecondParamName; + return true; + } + } + return false; + }; + + for (auto &Merge : ObjCInterfaceOdrMergeFailures) { + // If we've already pointed out a specific problem with this interface, + // don't bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + bool Diagnosed = false; + ObjCInterfaceDecl *FirstID = Merge.first; + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstID); + for (auto &InterfacePair : Merge.second) { + ObjCInterfaceDecl *SecondID = InterfacePair.first; + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstID == SecondID) + continue; + + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondID); + + auto *FirstDD = &FirstID->data(); + auto *SecondDD = InterfacePair.second; + assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + // Diagnostics from ObjCInterfaces DefinitionData are emitted here. + // FIXME: as part of definition data handling, add support for checking + // matching protocols, ivars and categories + if (FirstDD != SecondDD) { + // Check for matching super class. + auto GetSuperClassSourceRange = [](TypeSourceInfo *SuperInfo, + ObjCInterfaceDecl *ID) { + if (!SuperInfo) + return ID->getSourceRange(); + TypeLoc Loc = SuperInfo->getTypeLoc(); + return SourceRange(Loc.getBeginLoc(), Loc.getEndLoc()); + }; + + ObjCInterfaceDecl *FirstSuperClass = FirstID->getSuperClass(); + ObjCInterfaceDecl *SecondSuperClass = nullptr; + auto *FirstSuperInfo = FirstID->getSuperClassTInfo(); + auto *SecondSuperInfo = SecondDD->SuperClassTInfo; + if (SecondSuperInfo) + SecondSuperClass = SecondSuperInfo->getType() + ->castAs() + ->getInterface(); + + if ((FirstSuperClass && SecondSuperClass && + FirstSuperClass->getODRHash() != SecondSuperClass->getODRHash()) || + (FirstSuperClass && !SecondSuperClass) || + (!FirstSuperClass && SecondSuperClass)) { + QualType FirstType; + if (FirstSuperInfo) + FirstType = FirstSuperInfo->getType(); + + ODRDiagObjCDDataError(FirstID, FirstModule, FirstID->getLocation(), + GetSuperClassSourceRange(FirstSuperInfo, FirstID), + SuperClassType) + << (bool)FirstSuperInfo << FirstType; + + QualType SecondType; + if (SecondSuperInfo) + SecondType = SecondSuperInfo->getType(); + + ODRDiagObjCDDataNote(SecondModule, SecondID->getLocation(), + GetSuperClassSourceRange(SecondSuperInfo, SecondID), + SuperClassType) + << (bool)SecondSuperInfo << SecondType; + Diagnosed = true; + break; + } + + // Check both interfaces reference the same protocols. + auto &FirstProtos = FirstID->getReferencedProtocols(); + auto &SecondProtos = SecondDD->ReferencedProtocols; + Diagnosed = ODRDiagObjCProtocolList(FirstProtos, FirstID, FirstModule, + SecondProtos, SecondID, SecondModule); + if (Diagnosed) + break; + } + + // FIXME: Improve PopulateHashes above and only have one version. + auto PopulateHashes = [&ComputeSubDeclODRHash](DeclHashes &Hashes, + ObjCInterfaceDecl *ID, + const DeclContext *DC) { + for (auto *D : ID->decls()) { + if (!ODRHash::isWhitelistedDecl(D, DC)) + continue; + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + PopulateHashes(FirstHashes, FirstID, FirstID); + PopulateHashes(SecondHashes, SecondID, SecondID); + + auto DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + Decl *FirstDecl = DR.FirstDecl; + Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + DiagnoseODRUnexpected(DR, FirstID, FirstModule, SecondID, SecondModule); + Diagnosed = true; + break; + } + + if (FirstDiffType != SecondDiffType) { + DiagnoseODRMismatchObjC(DR, FirstID, FirstModule, SecondID, + SecondModule, FirstDD->EndLoc, SecondDD->EndLoc); + Diagnosed = true; + break; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + case EndOfClass: + case Other: + case Field: + case TypeDef: + case Var: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + llvm_unreachable("Invalid diff type"); + + case ObjCIvar: { + Diagnosed = ODRDiagField(FirstID, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + if (Diagnosed) + break; + + // Check if the access match. + ObjCIvarDecl *FirstIvar = cast(FirstDecl); + ObjCIvarDecl *SecondIvar = cast(SecondDecl); + if (FirstIvar->getCanonicalAccessControl() != + SecondIvar->getCanonicalAccessControl()) { + ODRDiagDeclError(FirstID, FirstModule, FirstIvar->getLocation(), + FirstIvar->getSourceRange(), ObjCIvarAccess) + << FirstIvar->getName() + << (int)FirstIvar->getCanonicalAccessControl(); + ODRDiagDeclNote(SecondModule, SecondIvar->getLocation(), + SecondIvar->getSourceRange(), ObjCIvarAccess) + << SecondIvar->getName() + << (int)SecondIvar->getCanonicalAccessControl(); + Diagnosed = true; + } + break; + } + case ObjCProperty: { + Diagnosed = ODRDiagObjCProperty(FirstID, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + case ObjCMethod: { + Diagnosed = ODRDiagObjCMethod(FirstID, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + } + + if (Diagnosed) + continue; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstID << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + Diagnosed = true; + } + } + + for (auto &Merge : ObjCProtocolOdrMergeFailures) { + // If we've already pointed out a specific problem with this protocol, + // don't bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + bool Diagnosed = false; + ObjCProtocolDecl *FirstProto = Merge.first; + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstProto); + for (auto &ProtocolPair : Merge.second) { + ObjCProtocolDecl *SecondProto = ProtocolPair.first; + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstProto == SecondProto) + continue; + + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondProto); + + auto *FirstDD = &FirstProto->data(); + auto *SecondDD = ProtocolPair.second; + assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + // Diagnostics from ObjCProtocol DefinitionData are emitted here. + if (FirstDD != SecondDD) { + // Check both protocols reference the same protocols. + auto &FirstProtos = FirstProto->getReferencedProtocols(); + auto &SecondProtos = SecondDD->ReferencedProtocols; + Diagnosed = ODRDiagObjCProtocolList(FirstProtos, FirstProto, FirstModule, + SecondProtos, SecondProto, SecondModule); + if (Diagnosed) + break; + } + + auto PopulateHashes = [&ComputeSubDeclODRHash](DeclHashes &Hashes, + ObjCProtocolDecl *ID, + const DeclContext *DC) { + for (auto *D : ID->decls()) { + if (!ODRHash::isWhitelistedDecl(D, DC)) + continue; + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + PopulateHashes(FirstHashes, FirstProto, FirstProto); + PopulateHashes(SecondHashes, SecondProto, SecondProto); + + auto DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + Decl *FirstDecl = DR.FirstDecl; + Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + DiagnoseODRUnexpected(DR, FirstProto, FirstModule, SecondProto, SecondModule); + Diagnosed = true; + break; + } + + if (FirstDiffType != SecondDiffType) { + SourceLocation Loc; + DiagnoseODRMismatchObjC(DR, FirstProto, FirstModule, SecondProto, + SecondModule, Loc, Loc); + Diagnosed = true; + break; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + case EndOfClass: + case Other: + case Field: + case TypeDef: + case Var: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + case ObjCIvar: + llvm_unreachable("Invalid diff type"); + case ObjCProperty: { + Diagnosed = ODRDiagObjCProperty(FirstProto, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + case ObjCMethod: { + Diagnosed = ODRDiagObjCMethod(FirstProto, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + } + + if (Diagnosed) + continue; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstProto << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + Diagnosed = true; + } + } + + for (auto &Merge : ObjCCategoryOdrMergeFailures) { + // If we've already pointed out a specific problem with this protocol, + // don't bother issuing a general "something's different" diagnostic. + if (!DiagnosedOdrMergeFailures.insert(Merge.first).second) + continue; + + bool Diagnosed = false; + ObjCCategoryDecl *FirstCat = Merge.first; + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstCat); + for (auto &CategoryPair : Merge.second) { + ObjCCategoryDecl *SecondCat = CategoryPair.first; + // Multiple different declarations got merged together; tell the user + // where they came from. + if (FirstCat == SecondCat) + continue; + + std::string SecondModule = getOwningModuleNameForDiagnostic(SecondCat); + + auto *FirstDD = &FirstCat->data(); + auto *SecondDD = CategoryPair.second; + assert(FirstDD && SecondDD && "Definitions without DefinitionData"); + // Diagnostics from ObjCCategory DefinitionData are emitted here. + if (FirstDD != SecondDD) { + // Check both protocols reference the same protocols. + auto &FirstCats = FirstCat->getReferencedProtocols(); + auto &SecondCats = SecondDD->ReferencedProtocols; + Diagnosed = + ODRDiagObjCProtocolList(FirstCats, FirstCat, FirstModule, + SecondCats, SecondCat, SecondModule); + if (Diagnosed) + break; + } + + auto PopulateHashes = [&ComputeSubDeclODRHash](DeclHashes &Hashes, + ObjCCategoryDecl *ID, + const DeclContext *DC) { + for (auto *D : ID->decls()) { + if (!ODRHash::isWhitelistedDecl(D, DC)) + continue; + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); + } + }; + + DeclHashes FirstHashes; + DeclHashes SecondHashes; + PopulateHashes(FirstHashes, FirstCat, FirstCat); + PopulateHashes(SecondHashes, SecondCat, SecondCat); + + auto DR = FindTypeDiffs(FirstHashes, SecondHashes); + ODRMismatchDecl FirstDiffType = DR.FirstDiffType; + ODRMismatchDecl SecondDiffType = DR.SecondDiffType; + Decl *FirstDecl = DR.FirstDecl; + Decl *SecondDecl = DR.SecondDecl; + + if (FirstDiffType == Other || SecondDiffType == Other) { + DiagnoseODRUnexpected(DR, FirstCat, FirstModule, SecondCat, + SecondModule); + Diagnosed = true; + break; + } + + if (FirstDiffType != SecondDiffType) { + SourceLocation Loc; + DiagnoseODRMismatchObjC(DR, FirstCat, FirstModule, SecondCat, + SecondModule, Loc, Loc); + Diagnosed = true; + break; + } + + assert(FirstDiffType == SecondDiffType); + switch (FirstDiffType) { + case EndOfClass: + case Other: + case Field: + case TypeDef: + case Var: + // C++ only, invalid in this context. + case PublicSpecifer: + case PrivateSpecifer: + case ProtectedSpecifer: + case StaticAssert: + case CXXMethod: + case TypeAlias: + case Friend: + case FunctionTemplate: + case ObjCIvar: + llvm_unreachable("Invalid diff type"); + case ObjCProperty: { + Diagnosed = ODRDiagObjCProperty(FirstCat, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + case ObjCMethod: { + Diagnosed = ODRDiagObjCMethod(FirstCat, FirstModule, SecondModule, + cast(FirstDecl), + cast(SecondDecl)); + break; + } + } + + if (Diagnosed) + continue; + + Diag(FirstDecl->getLocation(), + diag::err_module_odr_violation_mismatch_decl_unknown) + << FirstCat << FirstModule.empty() << FirstModule << FirstDiffType + << FirstDecl->getSourceRange(); + Diag(SecondDecl->getLocation(), + diag::note_module_odr_violation_mismatch_decl_unknown) + << SecondModule << FirstDiffType << SecondDecl->getSourceRange(); + Diagnosed = true; + } + } + // Issue ODR failures diagnostics for functions. for (auto &Merge : FunctionOdrMergeFailures) { enum ODRFunctionDifference { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 3351f76151e35..3828691a3b20a 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -167,6 +167,10 @@ namespace clang { void ReadObjCDefinitionData(struct ObjCProtocolDecl::DefinitionData &Data); void MergeDefinitionData(ObjCProtocolDecl *D, struct ObjCProtocolDecl::DefinitionData &&NewDD); + void ReadObjCDefinitionData(ObjCCategoryDecl *CD, + struct ObjCCategoryDecl::DefinitionData &Data); + void MergeDefinitionData(ObjCCategoryDecl *D, + struct ObjCCategoryDecl::DefinitionData &&NewDD); static DeclContext *getPrimaryDCForAnonymousDecl(DeclContext *LexicalDC); @@ -550,7 +554,7 @@ void ASTDeclReader::Visit(Decl *D) { void ASTDeclReader::VisitDecl(Decl *D) { if (D->isTemplateParameter() || D->isTemplateParameterPack() || - isa(D)) { + isa(D) || isa(D)) { // We don't want to deserialize the DeclContext of a template // parameter or of a parameter of a function template immediately. These // entities might be used in the formulation of its DeclContext (for @@ -723,8 +727,11 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitTagDecl(TagDecl *TD) { llvm_unreachable("unexpected tag info kind"); } - if (!isa(TD)) - mergeRedeclarable(TD, Redecl); + if (isa(TD)) + return Redecl; + + // Handle merging in C and Objective-C + mergeRedeclarable(TD, Redecl); return Redecl; } @@ -794,6 +801,29 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) { RD->setHasNonTrivialToPrimitiveCopyCUnion(Record.readInt()); RD->setParamDestroyedInCallee(Record.readInt()); RD->setArgPassingRestrictions((RecordDecl::ArgPassingKind)Record.readInt()); + RD->setODRHash(Record.readInt()); + + // C++ applies ODR checking in VisitCXXRecordDecl instead. Note that + // structural equivalence is the usual way to check for ODR-like semantics + // in ObjC/C, but using ODRHash is prefered if possible because of better + // performance. + if (!Reader.getContext().getLangOpts().CPlusPlus && + Reader.getContext().getLangOpts().ODRCheckRecords) { + RecordDecl *Canon = static_cast(RD->getCanonicalDecl()); + if (RD == Canon || Canon->getODRHash() == RD->getODRHash()) + return Redecl; + // No point in checking equivalence between types that don't match in + // presence of definition. Note that we might still wanna check when + // both types don't have a definition (e.g. when adding checks for + // mismtaches in their __attribute__ lists) + if (RD->isThisDeclarationADefinition() != + Canon->isThisDeclarationADefinition()) + return Redecl; + Reader.PendingRecordOdrMergeFailures[Canon].push_back(RD); + // Track that we merged the definitions. + Reader.MergedDeclContexts.insert(std::make_pair(RD, Canon)); + } + return Redecl; } @@ -1059,6 +1089,8 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { SelLocs.push_back(readSourceLocation()); MD->setParamsAndSelLocs(Reader.getContext(), Params, SelLocs); + MD->ODRHash = Record.readInt(); + MD->setHasODRHash(); } void ASTDeclReader::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) { @@ -1105,6 +1137,8 @@ void ASTDeclReader::ReadObjCDefinitionData( Data.EndLoc = readSourceLocation(); Data.HasDesignatedInitializers = Record.readInt(); + Data.ODRHash = Record.readInt(); + Data.HasODRHash = true; // Read the directly referenced protocols and their SourceLocations. unsigned NumProtocols = Record.readInt(); @@ -1129,9 +1163,49 @@ void ASTDeclReader::ReadObjCDefinitionData( Reader.getContext()); } +static bool MergeCheckProtocolList(const ObjCProtocolList &FirstProtos, + const ObjCProtocolList &SecondProtos) { + if (FirstProtos.size() != SecondProtos.size()) + return true; + + for (unsigned I = 0, E = FirstProtos.size(); I != E; ++I) { + auto *FirstParam = FirstProtos[I]; + auto *SecondParam = SecondProtos[I]; + DeclarationName FirstParamName = FirstParam->getDeclName(); + DeclarationName SecondParamName = SecondParam->getDeclName(); + + // If parameters match name but do not both have a definition, we + // can't disambiguate it during ODR diagnostic time, skip. + if (FirstParamName == SecondParamName && + (FirstParam->hasDefinition() != SecondParam->hasDefinition())) + return false; + + if (FirstParamName != SecondParamName) + return true; + } + + return false; +} + void ASTDeclReader::MergeDefinitionData(ObjCInterfaceDecl *D, struct ObjCInterfaceDecl::DefinitionData &&NewDD) { - // FIXME: odr checking? + bool DetectedOdrViolation = false; + auto &DD = D->data(); + + if (!Reader.getContext().getLangOpts().ODRCheckInterfaces) + return; + + auto &FirstProtos = D->getReferencedProtocols(); + auto &SecondProtos = NewDD.ReferencedProtocols; + if (Reader.getContext().getLangOpts().ODRCheckProtocols && + MergeCheckProtocolList(FirstProtos, SecondProtos)) + DetectedOdrViolation = true; + if (D->getODRHash() != NewDD.ODRHash) + DetectedOdrViolation = true; + + if (DetectedOdrViolation) + Reader.PendingObjCInterfaceOdrMergeFailures[DD.Definition].push_back( + {NewDD.Definition, &NewDD}); } void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) { @@ -1193,11 +1267,28 @@ void ASTDeclReader::ReadObjCDefinitionData( ProtoLocs.push_back(readSourceLocation()); Data.ReferencedProtocols.set(ProtoRefs.data(), NumProtoRefs, ProtoLocs.data(), Reader.getContext()); + Data.ODRHash = Record.readInt(); + Data.HasODRHash = true; } void ASTDeclReader::MergeDefinitionData(ObjCProtocolDecl *D, struct ObjCProtocolDecl::DefinitionData &&NewDD) { - // FIXME: odr checking? + bool DetectedOdrViolation = false; + auto &DD = D->data(); + + auto &FirstProtos = D->getReferencedProtocols(); + auto &SecondProtos = NewDD.ReferencedProtocols; + if (!Reader.getContext().getLangOpts().ODRCheckProtocols) + return; + + if (MergeCheckProtocolList(FirstProtos, SecondProtos)) + DetectedOdrViolation = true; + if (D->getODRHash() != NewDD.ODRHash) + DetectedOdrViolation = true; + + if (DetectedOdrViolation) + Reader.PendingObjCProtocolOdrMergeFailures[DD.Definition].push_back( + {NewDD.Definition, &NewDD}); } void ASTDeclReader::VisitObjCProtocolDecl(ObjCProtocolDecl *PD) { @@ -1233,19 +1324,10 @@ void ASTDeclReader::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *FD) { VisitFieldDecl(FD); } -void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { - VisitObjCContainerDecl(CD); - CD->setCategoryNameLoc(readSourceLocation()); - CD->setIvarLBraceLoc(readSourceLocation()); - CD->setIvarRBraceLoc(readSourceLocation()); - - // Note that this category has been deserialized. We do this before - // deserializing the interface declaration, so that it will consider this - /// category. - Reader.CategoriesDeserialized.insert(CD); - - CD->ClassInterface = readDeclAs(); - CD->TypeParamList = ReadObjCTypeParamList(); +void ASTDeclReader::ReadObjCDefinitionData( + ObjCCategoryDecl *CD, struct ObjCCategoryDecl::DefinitionData &Data) { + Data.ODRHash = Record.readInt(); + Data.HasODRHash = true; unsigned NumProtoRefs = Record.readInt(); SmallVector ProtoRefs; ProtoRefs.reserve(NumProtoRefs); @@ -1255,8 +1337,8 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { ProtoLocs.reserve(NumProtoRefs); for (unsigned I = 0; I != NumProtoRefs; ++I) ProtoLocs.push_back(readSourceLocation()); - CD->setProtocolList(ProtoRefs.data(), NumProtoRefs, ProtoLocs.data(), - Reader.getContext()); + Data.ReferencedProtocols.set(ProtoRefs.data(), NumProtoRefs, ProtoLocs.data(), + Reader.getContext()); // Protocols in the class extension belong to the class. if (NumProtoRefs > 0 && CD->ClassInterface && CD->IsClassExtension()) @@ -1265,9 +1347,65 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { Reader.getContext()); } +void ASTDeclReader::MergeDefinitionData( + ObjCCategoryDecl *D, struct ObjCCategoryDecl::DefinitionData &&NewDD) { + assert(!D->IsClassExtension() && "Class extensions are not to be merged"); + + bool DetectedOdrViolation = false; + auto &DD = D->data(); + + auto &FirstProtos = D->getReferencedProtocols(); + auto &SecondProtos = NewDD.ReferencedProtocols; + + if (!Reader.getContext().getLangOpts().ODRCheckCategories) + return; + + if (Reader.getContext().getLangOpts().ODRCheckProtocols && + MergeCheckProtocolList(FirstProtos, SecondProtos)) + DetectedOdrViolation = true; + if (D->getODRHash() != NewDD.ODRHash) + DetectedOdrViolation = true; + + if (DetectedOdrViolation) + Reader.PendingObjCCategoryOdrMergeFailures[DD.Definition].push_back( + {NewDD.Definition, &NewDD}); +} + +void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { + RedeclarableResult Redecl = VisitRedeclarable(CD); + VisitObjCContainerDecl(CD); + CD->setCategoryNameLoc(readSourceLocation()); + CD->setIvarLBraceLoc(readSourceLocation()); + CD->setIvarRBraceLoc(readSourceLocation()); + + // Note that this category has been deserialized. We do this before + // deserializing the interface declaration, so that it will consider this + // category. + Reader.CategoriesDeserialized.insert(CD); + + CD->ClassInterface = readDeclAs(); + CD->TypeParamList = ReadObjCTypeParamList(); + mergeRedeclarable(CD, Redecl); + + CD->allocateDefinitionData(); + ReadObjCDefinitionData(CD, CD->data()); + + // Class extensions are additive and not supposed to + // be merged, nothing to check here. + if (CD->IsClassExtension()) + return; + + ObjCCategoryDecl *Canon = CD->getCanonicalDecl(); + assert(Canon->Data.getPointer() && "Always expected to have a defintion"); + MergeDefinitionData(Canon, std::move(CD->data())); + CD->Data = Canon->Data; +} + void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) { VisitNamedDecl(CAD); CAD->setClassInterface(readDeclAs()); + CAD->setClassInterfaceLoc(readSourceLocation()); + CAD->setAtLoc(readSourceLocation()); } void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { @@ -2561,7 +2699,8 @@ static bool allowODRLikeMergeInC(NamedDecl *ND) { if (!ND) return false; // TODO: implement merge for other necessary decls. - if (isa(ND)) + if (isa(ND) || isa(ND) || + isa(ND)) return true; return false; } @@ -2708,6 +2847,8 @@ class AttrReader { Expr *readExpr() { return Reader.readExpr(); } + Attr *readAttr() { return Reader.readAttr(); } + std::string readString() { return Reader.readString(); } @@ -3000,6 +3141,20 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { if (isa(X) || isa(X)) return true; + // Objective-C categories match if: + // - Not a class extensions. + // - Declared on top of the same class interface. + if (ObjCCategoryDecl *CX = dyn_cast(X)) { + if (ObjCCategoryDecl *CY = dyn_cast(Y)) { + if (CX->IsClassExtension() || CY->IsClassExtension()) + return false; + ObjCInterfaceDecl *IX = CX->getClassInterface(); + ObjCInterfaceDecl *IY = CY->getClassInterface(); + return IX->getDeclName() == IY->getDeclName(); + } + return false; + } + if (isa(X)) { // No need to handle these here: we merge them when adding them to the // template. @@ -3197,6 +3352,10 @@ DeclContext *ASTDeclReader::getPrimaryContextForMerging(ASTReader &Reader, return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition() : nullptr; + if (auto *RD = dyn_cast(DC)) + if (!RD->getASTContext().getLangOpts().CPlusPlus) + return RD->getCanonicalDecl()->getDefinition(); + // We can see the TU here only if we have no Sema object. In that case, // there's no TU scope to look in, so using the DC alone is sufficient. if (auto *TU = dyn_cast(DC)) @@ -4156,28 +4315,12 @@ namespace { // Check for duplicate categories. if (Cat->getDeclName()) { + // ObjCCategoryDecl are redeclarable and ODR diagnosed in case of + // redefinition, no need to check for multiple copies here. ObjCCategoryDecl *&Existing = NameCategoryMap[Cat->getDeclName()]; - if (Existing && - Reader.getOwningModuleFile(Existing) - != Reader.getOwningModuleFile(Cat)) { - // FIXME: We should not warn for duplicates in diamond: - // - // MT // - // / \ // - // ML MR // - // \ / // - // MB // - // - // If there are duplicates in ML/MR, there will be warning when - // creating MB *and* when importing MB. We should not warn when - // importing. - Reader.Diag(Cat->getLocation(), diag::warn_dup_category_def) - << Interface->getDeclName() << Cat->getDeclName(); - Reader.Diag(Existing->getLocation(), diag::note_previous_definition); - } else if (!Existing) { - // Record this category. + // Record this category. + if (!Existing) Existing = Cat; - } } // Add this category to the end of the chain. diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 533502116ed76..d08befca83a9b 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1393,7 +1393,8 @@ void ASTStmtReader::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) SourceRange R = Record.readSourceRange(); E->AtLoc = R.getBegin(); E->RParen = R.getEnd(); - E->VersionToCheck = Record.readVersionTuple(); + E->VersionToCheck.Version = Record.readVersionTuple(); + E->VersionToCheck.SourceVersion = Record.readVersionTuple(); } //===----------------------------------------------------------------------===// @@ -1689,9 +1690,17 @@ void ASTStmtReader::VisitExprWithCleanups(ExprWithCleanups *E) { unsigned NumObjects = Record.readInt(); assert(NumObjects == E->getNumObjects()); - for (unsigned i = 0; i != NumObjects; ++i) - E->getTrailingObjects()[i] = - readDeclAs(); + for (unsigned i = 0; i != NumObjects; ++i) { + unsigned CleanupKind = Record.readInt(); + ExprWithCleanups::CleanupObject Obj; + if (CleanupKind == COK_Block) + Obj = readDeclAs(); + else if (CleanupKind == COK_CompoundLiteral) + Obj = cast(Record.readSubExpr()); + else + llvm_unreachable("unexpected cleanup object type"); + E->getTrailingObjects()[i] = Obj; + } E->ExprWithCleanupsBits.CleanupsHaveSideEffects = Record.readInt(); E->SubExpr = Record.readSubExpr(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index aebdfa907066e..26383740c8afa 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -682,7 +682,6 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(OPTIONS_BLOCK); RECORD(LANGUAGE_OPTIONS); RECORD(TARGET_OPTIONS); - RECORD(FILE_SYSTEM_OPTIONS); RECORD(HEADER_SEARCH_OPTIONS); RECORD(PREPROCESSOR_OPTIONS); @@ -710,7 +709,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); @@ -918,6 +916,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 @@ -1037,6 +1037,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); @@ -1229,36 +1259,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); @@ -2379,26 +2385,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) { @@ -2451,6 +2437,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 @@ -2557,6 +2548,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, @@ -3232,8 +3229,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()); } @@ -3386,10 +3381,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); } @@ -4304,7 +4297,7 @@ ASTFileSignature ASTWriter::WriteAST(Sema &SemaRef, WritingAST = false; if (ShouldCacheASTInMemory) { // Construct MemoryBuffer and update buffer manager. - ModuleCache.addBuiltPCM(OutputFile, + ModuleCache.addFinalPCM(OutputFile, llvm::MemoryBuffer::getMemBufferCopy( StringRef(Buffer.begin(), Buffer.size()))); } diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 66f4db855a3e9..a98006989552b 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -483,6 +483,9 @@ void ASTDeclWriter::VisitRecordDecl(RecordDecl *D) { Record.push_back(D->hasNonTrivialToPrimitiveCopyCUnion()); Record.push_back(D->isParamDestroyedInCallee()); Record.push_back(D->getArgPassingRestrictions()); + // Only compute this for C/Objective-C, in C++ this is computed as part + // of CXXRecordDecl. + Record.push_back(Writer.getLangOpts().CPlusPlus ? 0UL : D->getODRHash()); if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() && @@ -717,6 +720,7 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) { Record.push_back(NumStoredSelLocs); for (unsigned i = 0; i != NumStoredSelLocs; ++i) Record.AddSourceLocation(SelLocs[i]); + Record.push_back(D->getODRHash()); Code = serialization::DECL_OBJC_METHOD; } @@ -753,6 +757,8 @@ void ASTDeclWriter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { Record.AddSourceLocation(D->getEndOfDefinitionLoc()); Record.push_back(Data.HasDesignatedInitializers); + Record.push_back(D->getODRHash()); + // Write out the protocols that are directly referenced by the @interface. Record.push_back(Data.ReferencedProtocols.size()); for (const auto *P : D->protocols()) @@ -814,6 +820,7 @@ void ASTDeclWriter::VisitObjCProtocolDecl(ObjCProtocolDecl *D) { Record.AddDeclRef(I); for (const auto &PL : D->protocol_locs()) Record.AddSourceLocation(PL); + Record.push_back(D->getODRHash()); } Code = serialization::DECL_OBJC_PROTOCOL; @@ -825,12 +832,14 @@ void ASTDeclWriter::VisitObjCAtDefsFieldDecl(ObjCAtDefsFieldDecl *D) { } void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { + VisitRedeclarable(D); VisitObjCContainerDecl(D); Record.AddSourceLocation(D->getCategoryNameLoc()); Record.AddSourceLocation(D->getIvarLBraceLoc()); Record.AddSourceLocation(D->getIvarRBraceLoc()); Record.AddDeclRef(D->getClassInterface()); AddObjCTypeParamList(D->TypeParamList); + Record.push_back(D->getODRHash()); Record.push_back(D->protocol_size()); for (const auto *I : D->protocols()) Record.AddDeclRef(I); @@ -842,6 +851,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; } @@ -1084,8 +1095,6 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) { Record.AddStmt(D->getUninstantiatedDefaultArg()); Code = serialization::DECL_PARM_VAR; - assert(!D->isARCPseudoStrong()); // can be true of ImplicitParamDecl - // If the assumptions about the DECL_PARM_VAR abbrev are true, use it. Here // we dynamically check for the properties that we optimize for, but don't // know are true of all PARM_VAR_DECLs. @@ -2046,6 +2055,8 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // getArgPassingRestrictions Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); + // ODRHash + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 28)); // DC Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LexicalOffset @@ -2083,7 +2094,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // SClass Abv->Add(BitCodeAbbrevOp(0)); // TSCSpec Abv->Add(BitCodeAbbrevOp(0)); // InitStyle - Abv->Add(BitCodeAbbrevOp(0)); // ARCPseudoStrong + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong Abv->Add(BitCodeAbbrevOp(0)); // Linkage Abv->Add(BitCodeAbbrevOp(0)); // HasInit Abv->Add(BitCodeAbbrevOp(0)); // HasMemberSpecializationInfo diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index e671c78fe97b8..a75e577ee59f8 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1328,6 +1328,7 @@ void ASTStmtWriter::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) VisitExpr(E); Record.AddSourceRange(E->getSourceRange()); Record.AddVersionTuple(E->getVersion()); + Record.AddVersionTuple(E->getVersionAsWritten()); Code = serialization::EXPR_OBJC_AVAILABILITY_CHECK; } @@ -1642,8 +1643,15 @@ void ASTStmtWriter::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E) { void ASTStmtWriter::VisitExprWithCleanups(ExprWithCleanups *E) { VisitExpr(E); Record.push_back(E->getNumObjects()); - for (unsigned i = 0, e = E->getNumObjects(); i != e; ++i) - Record.AddDeclRef(E->getObject(i)); + for (auto &Obj : E->getObjects()) { + if (auto *BD = Obj.dyn_cast()) { + Record.push_back(serialization::COK_Block); + Record.AddDeclRef(BD); + } else if (auto *CLE = Obj.dyn_cast()) { + Record.push_back(serialization::COK_CompoundLiteral); + Record.AddStmt(CLE); + } + } Record.push_back(E->cleanupsHaveSideEffects()); Record.AddStmt(E->getSubExpr()); diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp index 002233e49bb06..d869796b82c12 100644 --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -57,6 +57,11 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) { } } + // Errors that do not prevent the PCH from being written should not cause the + // overall compilation to fail either. + if (AllowASTWithErrors) + PP.getDiagnostics().getClient()->clear(); + // Emit the PCH file to the Buffer. assert(SemaPtr && "No Sema?"); Buffer->Signature = diff --git a/clang/lib/Serialization/InMemoryModuleCache.cpp b/clang/lib/Serialization/InMemoryModuleCache.cpp index d35fa2a807f4d..68d941140a94a 100644 --- a/clang/lib/Serialization/InMemoryModuleCache.cpp +++ b/clang/lib/Serialization/InMemoryModuleCache.cpp @@ -11,16 +11,6 @@ using namespace clang; -InMemoryModuleCache::State -InMemoryModuleCache::getPCMState(llvm::StringRef Filename) const { - auto I = PCMs.find(Filename); - if (I == PCMs.end()) - return Unknown; - if (I->second.IsFinal) - return Final; - return I->second.Buffer ? Tentative : ToBuild; -} - llvm::MemoryBuffer & InMemoryModuleCache::addPCM(llvm::StringRef Filename, std::unique_ptr Buffer) { @@ -30,11 +20,11 @@ InMemoryModuleCache::addPCM(llvm::StringRef Filename, } llvm::MemoryBuffer & -InMemoryModuleCache::addBuiltPCM(llvm::StringRef Filename, +InMemoryModuleCache::addFinalPCM(llvm::StringRef Filename, std::unique_ptr Buffer) { auto &PCM = PCMs[Filename]; assert(!PCM.IsFinal && "Trying to override finalized PCM?"); - assert(!PCM.Buffer && "Trying to override tentative PCM?"); + assert(!PCM.Buffer && "Already has a non-final PCM"); PCM.Buffer = std::move(Buffer); PCM.IsFinal = true; return *PCM.Buffer; @@ -49,24 +39,21 @@ InMemoryModuleCache::lookupPCM(llvm::StringRef Filename) const { } bool InMemoryModuleCache::isPCMFinal(llvm::StringRef Filename) const { - return getPCMState(Filename) == Final; -} - -bool InMemoryModuleCache::shouldBuildPCM(llvm::StringRef Filename) const { - return getPCMState(Filename) == ToBuild; + auto I = PCMs.find(Filename); + if (I == PCMs.end()) + return false; + return I->second.IsFinal; } -bool InMemoryModuleCache::tryToDropPCM(llvm::StringRef Filename) { +bool InMemoryModuleCache::tryToRemovePCM(llvm::StringRef Filename) { auto I = PCMs.find(Filename); assert(I != PCMs.end() && "PCM to remove is unknown..."); auto &PCM = I->second; - assert(PCM.Buffer && "PCM to remove is scheduled to be built..."); - if (PCM.IsFinal) return true; - PCM.Buffer.reset(); + PCMs.erase(I); return false; } diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index daef502cdcb5e..94d64c125db3e 100644 --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -163,7 +163,7 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, // Load the contents of the module if (std::unique_ptr Buffer = lookupBuffer(FileName)) { // The buffer was already provided for us. - NewModule->Buffer = &ModuleCache->addBuiltPCM(FileName, std::move(Buffer)); + NewModule->Buffer = &ModuleCache->addFinalPCM(FileName, std::move(Buffer)); // Since the cached buffer is reused, it is safe to close the file // descriptor that was opened while stat()ing the PCM in // lookupModuleFile() above, it won't be needed any longer. @@ -173,11 +173,6 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, NewModule->Buffer = Buffer; // As above, the file descriptor is no longer needed. Entry->closeFile(); - } else if (getModuleCache().shouldBuildPCM(FileName)) { - // Report that the module is out of date, since we tried (and failed) to - // import it earlier. - Entry->closeFile(); - return OutOfDate; } else { // Open the AST file. llvm::ErrorOr> Buf((std::error_code())); @@ -185,7 +180,14 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type, 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); + // The file is volatile because in a parallel build we expect multiple + // compiler processes to use the same module file rebuilding it if needed. + // + // RequiresNullTerminator is false because module files don't need it, and + // this allows the file to still be mmapped. + Buf = FileMgr.getBufferForFile(NewModule->File, + /*IsVolatile=*/true, + /*RequiresNullTerminator=*/false); } if (!Buf) { diff --git a/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp index 01f5b9c889e32..dc6f7722a8e62 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp @@ -53,7 +53,7 @@ void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const { ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx); const NoteTag *SelfAssignTag = - C.getNoteTag([MD](BugReport &BR) -> std::string { + C.getNoteTag([MD](PathSensitiveBugReport &BR) -> std::string { SmallString<256> Msg; llvm::raw_svector_ostream Out(Msg); Out << "Assuming " << MD->getParamDecl(0)->getName() << " == *this"; @@ -63,7 +63,7 @@ void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const { ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx); const NoteTag *NonSelfAssignTag = - C.getNoteTag([MD](BugReport &BR) -> std::string { + C.getNoteTag([MD](PathSensitiveBugReport &BR) -> std::string { SmallString<256> Msg; llvm::raw_svector_ostream Out(Msg); Out << "Assuming " << MD->getParamDecl(0)->getName() << " != *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/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 17c813962a234..c65cbf2767159 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/SValExplainer.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -45,6 +46,7 @@ class ExprInspectionChecker : public Checker(C.getCalleeName(CE)) - .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) - .Case("clang_analyzer_checkInlined", - &ExprInspectionChecker::analyzerCheckInlined) - .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) - .Case("clang_analyzer_warnIfReached", - &ExprInspectionChecker::analyzerWarnIfReached) - .Case("clang_analyzer_warnOnDeadSymbol", - &ExprInspectionChecker::analyzerWarnOnDeadSymbol) - .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) - .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) - .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) - .Case("clang_analyzer_printState", - &ExprInspectionChecker::analyzerPrintState) - .Case("clang_analyzer_numTimesReached", - &ExprInspectionChecker::analyzerNumTimesReached) - .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump) - .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) - .Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress) - .Default(nullptr); + FnCheck Handler = + llvm::StringSwitch(C.getCalleeName(CE)) + .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) + .Case("clang_analyzer_checkInlined", + &ExprInspectionChecker::analyzerCheckInlined) + .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) + .Case("clang_analyzer_warnIfReached", + &ExprInspectionChecker::analyzerWarnIfReached) + .Case("clang_analyzer_warnOnDeadSymbol", + &ExprInspectionChecker::analyzerWarnOnDeadSymbol) + .StartsWith("clang_analyzer_explain", + &ExprInspectionChecker::analyzerExplain) + .StartsWith("clang_analyzer_dump", + &ExprInspectionChecker::analyzerDump) + .Case("clang_analyzer_getExtent", + &ExprInspectionChecker::analyzerGetExtent) + .Case("clang_analyzer_printState", + &ExprInspectionChecker::analyzerPrintState) + .Case("clang_analyzer_numTimesReached", + &ExprInspectionChecker::analyzerNumTimesReached) + .Case("clang_analyzer_hashDump", + &ExprInspectionChecker::analyzerHashDump) + .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) + .Case("clang_analyzer_express", + &ExprInspectionChecker::analyzerExpress) + .StartsWith("clang_analyzer_isTainted", + &ExprInspectionChecker::analyzerIsTainted) + .Default(nullptr); if (!Handler) return false; @@ -410,6 +420,17 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, reportBug(*Str, C); } +void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() != 1) { + reportBug("clang_analyzer_isTainted() requires exactly one argument", C); + return; + } + const bool IsTainted = + taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext()); + reportBug(IsTainted ? "YES" : "NO", C); +} + void ento::registerExprInspectionChecker(CheckerManager &Mgr) { Mgr.registerChecker(); } diff --git a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp index 861dfef02393a..67a4c7fb82d05 100644 --- a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp @@ -357,8 +357,8 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, } const NoteTag *T = nullptr; if (!Notes.empty()) { - T = C.getNoteTag( - [this, Notes{std::move(Notes)}](BugReport &BR) -> std::string { + T = C.getNoteTag([this, Notes{std::move(Notes)}]( + PathSensitiveBugReport &BR) -> std::string { if (&BR.getBugType() != &UseAfterReleaseBugType && &BR.getBugType() != &LeakBugType && &BR.getBugType() != &DoubleReleaseBugType) diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 302d5bb1bea86..c1b7774d313b7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -22,11 +22,14 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/YAMLTraits.h" + #include #include +#include #include #include @@ -35,17 +38,15 @@ using namespace ento; using namespace taint; namespace { -class GenericTaintChecker - : public Checker, check::PreStmt> { +class GenericTaintChecker : public Checker { public: static void *getTag() { static int Tag; return &Tag; } - void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; @@ -81,7 +82,7 @@ class GenericTaintChecker /// Convert SignedArgVector to ArgVector. ArgVector convertToArgVector(CheckerManager &Mgr, const std::string &Option, - SignedArgVector Args); + const SignedArgVector &Args); /// Parse the config. void parseConfiguration(CheckerManager &Mgr, const std::string &Option, @@ -96,7 +97,8 @@ class GenericTaintChecker mutable std::unique_ptr BT; void initBugType() const { if (!BT) - BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data")); + BT = std::make_unique(this, "Use of Untrusted Data", + "Untrusted Data"); } struct FunctionData { @@ -106,9 +108,12 @@ class GenericTaintChecker FunctionData &operator=(const FunctionData &) = delete; FunctionData &operator=(FunctionData &&) = delete; - static Optional create(const CallExpr *CE, + static Optional create(const CallEvent &Call, const CheckerContext &C) { - const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!Call.getDecl()) + return None; + + const FunctionDecl *FDecl = Call.getDecl()->getAsFunction(); if (!FDecl || (FDecl->getKind() != Decl::Function && FDecl->getKind() != Decl::CXXMethod)) return None; @@ -132,33 +137,33 @@ class GenericTaintChecker /// Catch taint related bugs. Check if tainted data is passed to a /// system call etc. Returns true on matching. - bool checkPre(const CallExpr *CE, const FunctionData &FData, + bool checkPre(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const; /// Add taint sources on a pre-visit. Returns true on matching. - bool addSourcesPre(const CallExpr *CE, const FunctionData &FData, + bool addSourcesPre(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const; /// Mark filter's arguments not tainted on a pre-visit. Returns true on /// matching. - bool addFiltersPre(const CallExpr *CE, const FunctionData &FData, + bool addFiltersPre(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const; /// Propagate taint generated at pre-visit. Returns true on matching. - bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const; + static bool propagateFromPre(const CallEvent &Call, CheckerContext &C); /// Check if the region the expression evaluates to is the standard input, /// and thus, is tainted. static bool isStdin(const Expr *E, CheckerContext &C); /// Given a pointer argument, return the value it points to. - static Optional getPointedToSVal(CheckerContext &C, const Expr *Arg); + static Optional getPointeeOf(CheckerContext &C, const Expr *Arg); /// Check for CWE-134: Uncontrolled Format String. static constexpr llvm::StringLiteral MsgUncontrolledFormatString = "Untrusted data is used as a format string " "(CWE-134: Uncontrolled Format String)"; - bool checkUncontrolledFormatString(const CallExpr *CE, + bool checkUncontrolledFormatString(const CallEvent &Call, CheckerContext &C) const; /// Check for: @@ -167,7 +172,7 @@ class GenericTaintChecker static constexpr llvm::StringLiteral MsgSanitizeSystemArgs = "Untrusted data is passed to a system call " "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; - bool checkSystemCall(const CallExpr *CE, StringRef Name, + bool checkSystemCall(const CallEvent &Call, StringRef Name, CheckerContext &C) const; /// Check if tainted data is used as a buffer size ins strn.. functions, @@ -176,13 +181,12 @@ class GenericTaintChecker "Untrusted data is used to specify the buffer size " "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " "for character data and the null terminator)"; - bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl, - CheckerContext &C) const; + bool checkTaintedBufferSize(const CallEvent &Call, CheckerContext &C) const; /// Check if tainted data is used as a custom sink's parameter. static constexpr llvm::StringLiteral MsgCustomSink = "Untrusted data is passed to a user-defined sink"; - bool checkCustomSinks(const CallExpr *CE, const FunctionData &FData, + bool checkCustomSinks(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const; /// Generate a report if the expression is tainted or points to tainted data. @@ -212,7 +216,7 @@ class GenericTaintChecker /// ReturnValueIndex is added to the dst list, the return value will be /// tainted. struct TaintPropagationRule { - using PropagationFuncType = bool (*)(bool IsTainted, const CallExpr *, + using PropagationFuncType = bool (*)(bool IsTainted, const CallEvent &Call, CheckerContext &C); /// List of arguments which can be taint sources and should be checked. @@ -256,7 +260,8 @@ class GenericTaintChecker return (llvm::find(DstArgs, ArgNum) != DstArgs.end()); } - static bool isTaintedOrPointsToTainted(const Expr *E, ProgramStateRef State, + static bool isTaintedOrPointsToTainted(const Expr *E, + const ProgramStateRef &State, CheckerContext &C) { if (isTainted(State, E, C.getLocationContext()) || isStdin(E, C)) return true; @@ -264,16 +269,16 @@ class GenericTaintChecker if (!E->getType().getTypePtr()->isPointerType()) return false; - Optional V = getPointedToSVal(C, E); + Optional V = getPointeeOf(C, E); return (V && isTainted(State, *V)); } /// Pre-process a function which propagates taint according to the /// taint rule. - ProgramStateRef process(const CallExpr *CE, CheckerContext &C) const; + ProgramStateRef process(const CallEvent &Call, CheckerContext &C) const; // Functions for custom taintedness propagation. - static bool postSocket(bool IsTainted, const CallExpr *CE, + static bool postSocket(bool IsTainted, const CallEvent &Call, CheckerContext &C); }; @@ -351,8 +356,10 @@ template <> struct MappingTraits { /// points to data, which should be tainted on return. REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned) -GenericTaintChecker::ArgVector GenericTaintChecker::convertToArgVector( - CheckerManager &Mgr, const std::string &Option, SignedArgVector Args) { +GenericTaintChecker::ArgVector +GenericTaintChecker::convertToArgVector(CheckerManager &Mgr, + const std::string &Option, + const SignedArgVector &Args) { ArgVector Result; for (int Arg : Args) { if (Arg == -1) @@ -419,125 +426,125 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( llvm::StringSwitch(FData.FullName) // Source functions // TODO: Add support for vfscanf & family. - .Case("fdopen", TaintPropagationRule({}, {ReturnValueIndex})) - .Case("fopen", TaintPropagationRule({}, {ReturnValueIndex})) - .Case("freopen", TaintPropagationRule({}, {ReturnValueIndex})) - .Case("getch", TaintPropagationRule({}, {ReturnValueIndex})) - .Case("getchar", TaintPropagationRule({}, {ReturnValueIndex})) - .Case("getchar_unlocked", - TaintPropagationRule({}, {ReturnValueIndex})) - .Case("getenv", TaintPropagationRule({}, {ReturnValueIndex})) - .Case("gets", TaintPropagationRule({}, {0, ReturnValueIndex})) - .Case("scanf", TaintPropagationRule({}, {}, VariadicType::Dst, 1)) - .Case("socket", - TaintPropagationRule({}, {ReturnValueIndex}, VariadicType::None, - InvalidArgIndex, - &TaintPropagationRule::postSocket)) - .Case("wgetch", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("fdopen", {{}, {ReturnValueIndex}}) + .Case("fopen", {{}, {ReturnValueIndex}}) + .Case("freopen", {{}, {ReturnValueIndex}}) + .Case("getch", {{}, {ReturnValueIndex}}) + .Case("getchar", {{}, {ReturnValueIndex}}) + .Case("getchar_unlocked", {{}, {ReturnValueIndex}}) + .Case("getenv", {{}, {ReturnValueIndex}}) + .Case("gets", {{}, {0, ReturnValueIndex}}) + .Case("scanf", {{}, {}, VariadicType::Dst, 1}) + .Case("socket", {{}, + {ReturnValueIndex}, + VariadicType::None, + InvalidArgIndex, + &TaintPropagationRule::postSocket}) + .Case("wgetch", {{}, {ReturnValueIndex}}) // Propagating functions - .Case("atoi", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("atol", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("atoll", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("fgetc", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("fgetln", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("fgets", TaintPropagationRule({2}, {0, ReturnValueIndex})) - .Case("fscanf", TaintPropagationRule({0}, {}, VariadicType::Dst, 2)) - .Case("getc", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("getc_unlocked", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("getdelim", TaintPropagationRule({3}, {0})) - .Case("getline", TaintPropagationRule({2}, {0})) - .Case("getw", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("pread", - TaintPropagationRule({0, 1, 2, 3}, {1, ReturnValueIndex})) - .Case("read", TaintPropagationRule({0, 2}, {1, ReturnValueIndex})) - .Case("strchr", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("strrchr", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("tolower", TaintPropagationRule({0}, {ReturnValueIndex})) - .Case("toupper", TaintPropagationRule({0}, {ReturnValueIndex})) - .Default(TaintPropagationRule()); + .Case("atoi", {{0}, {ReturnValueIndex}}) + .Case("atol", {{0}, {ReturnValueIndex}}) + .Case("atoll", {{0}, {ReturnValueIndex}}) + .Case("fgetc", {{0}, {ReturnValueIndex}}) + .Case("fgetln", {{0}, {ReturnValueIndex}}) + .Case("fgets", {{2}, {0, ReturnValueIndex}}) + .Case("fscanf", {{0}, {}, VariadicType::Dst, 2}) + .Case("sscanf", {{0}, {}, VariadicType::Dst, 2}) + .Case("getc", {{0}, {ReturnValueIndex}}) + .Case("getc_unlocked", {{0}, {ReturnValueIndex}}) + .Case("getdelim", {{3}, {0}}) + .Case("getline", {{2}, {0}}) + .Case("getw", {{0}, {ReturnValueIndex}}) + .Case("pread", {{0, 1, 2, 3}, {1, ReturnValueIndex}}) + .Case("read", {{0, 2}, {1, ReturnValueIndex}}) + .Case("strchr", {{0}, {ReturnValueIndex}}) + .Case("strrchr", {{0}, {ReturnValueIndex}}) + .Case("tolower", {{0}, {ReturnValueIndex}}) + .Case("toupper", {{0}, {ReturnValueIndex}}) + .Default({}); if (!Rule.isNull()) return Rule; + assert(FData.FDecl); // Check if it's one of the memory setting/copying functions. // This check is specialized but faster then calling isCLibraryFunction. const FunctionDecl *FDecl = FData.FDecl; unsigned BId = 0; - if ((BId = FDecl->getMemoryFunctionKind())) + if ((BId = FDecl->getMemoryFunctionKind())) { switch (BId) { case Builtin::BImemcpy: case Builtin::BImemmove: case Builtin::BIstrncpy: case Builtin::BIstrncat: - return TaintPropagationRule({1, 2}, {0, ReturnValueIndex}); + return {{1, 2}, {0, ReturnValueIndex}}; case Builtin::BIstrlcpy: case Builtin::BIstrlcat: - return TaintPropagationRule({1, 2}, {0}); + return {{1, 2}, {0}}; case Builtin::BIstrndup: - return TaintPropagationRule({0, 1}, {ReturnValueIndex}); + return {{0, 1}, {ReturnValueIndex}}; default: break; - }; + } + } // Process all other functions which could be defined as builtins. if (Rule.isNull()) { - if (C.isCLibraryFunction(FDecl, "snprintf")) - return TaintPropagationRule({1}, {0, ReturnValueIndex}, VariadicType::Src, - 3); - else if (C.isCLibraryFunction(FDecl, "sprintf")) - return TaintPropagationRule({}, {0, ReturnValueIndex}, VariadicType::Src, - 2); - else if (C.isCLibraryFunction(FDecl, "strcpy") || - C.isCLibraryFunction(FDecl, "stpcpy") || - C.isCLibraryFunction(FDecl, "strcat")) - return TaintPropagationRule({1}, {0, ReturnValueIndex}); - else if (C.isCLibraryFunction(FDecl, "bcopy")) - return TaintPropagationRule({0, 2}, {1}); - else if (C.isCLibraryFunction(FDecl, "strdup") || - C.isCLibraryFunction(FDecl, "strdupa")) - return TaintPropagationRule({0}, {ReturnValueIndex}); - else if (C.isCLibraryFunction(FDecl, "wcsdup")) - return TaintPropagationRule({0}, {ReturnValueIndex}); + const auto OneOf = [FDecl](const auto &... Name) { + // FIXME: use fold expression in C++17 + using unused = int[]; + bool ret = false; + static_cast(unused{ + 0, (ret |= CheckerContext::isCLibraryFunction(FDecl, Name), 0)...}); + return ret; + }; + if (OneOf("snprintf")) + return {{1}, {0, ReturnValueIndex}, VariadicType::Src, 3}; + if (OneOf("sprintf")) + return {{}, {0, ReturnValueIndex}, VariadicType::Src, 2}; + if (OneOf("strcpy", "stpcpy", "strcat")) + return {{1}, {0, ReturnValueIndex}}; + if (OneOf("bcopy")) + return {{0, 2}, {1}}; + if (OneOf("strdup", "strdupa", "wcsdup")) + return {{0}, {ReturnValueIndex}}; } - // Skipping the following functions, since they might be used for cleansing - // or smart memory copy: + // Skipping the following functions, since they might be used for cleansing or + // smart memory copy: // - memccpy - copying until hitting a special character. auto It = findFunctionInConfig(CustomPropagations, FData); - if (It != CustomPropagations.end()) { - const auto &Value = It->second; - return Value.second; - } - - return TaintPropagationRule(); + if (It != CustomPropagations.end()) + return It->second.second; + return {}; } -void GenericTaintChecker::checkPreStmt(const CallExpr *CE, +void GenericTaintChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - Optional FData = FunctionData::create(CE, C); + Optional FData = FunctionData::create(Call, C); if (!FData) return; // Check for taintedness related errors first: system call, uncontrolled // format string, tainted buffer size. - if (checkPre(CE, *FData, C)) + if (checkPre(Call, *FData, C)) return; // Marks the function's arguments and/or return value tainted if it present in // the list. - if (addSourcesPre(CE, *FData, C)) + if (addSourcesPre(Call, *FData, C)) return; - addFiltersPre(CE, *FData, C); + addFiltersPre(Call, *FData, C); } -void GenericTaintChecker::checkPostStmt(const CallExpr *CE, +void GenericTaintChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // Set the marked values as tainted. The return value only accessible from // checkPostStmt. - propagateFromPre(CE, C); + propagateFromPre(Call, C); } void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State, @@ -545,14 +552,14 @@ void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State, printTaint(State, Out, NL, Sep); } -bool GenericTaintChecker::addSourcesPre(const CallExpr *CE, +bool GenericTaintChecker::addSourcesPre(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const { // First, try generating a propagation rule for this function. TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule( this->CustomPropagations, FData, C); if (!Rule.isNull()) { - ProgramStateRef State = Rule.process(CE, C); + ProgramStateRef State = Rule.process(Call, C); if (State) { C.addTransition(State); return true; @@ -561,7 +568,7 @@ bool GenericTaintChecker::addSourcesPre(const CallExpr *CE, return false; } -bool GenericTaintChecker::addFiltersPre(const CallExpr *CE, +bool GenericTaintChecker::addFiltersPre(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const { auto It = findFunctionInConfig(CustomFilters, FData); @@ -572,11 +579,11 @@ bool GenericTaintChecker::addFiltersPre(const CallExpr *CE, const auto &Value = It->second; const ArgVector &Args = Value.second; for (unsigned ArgNum : Args) { - if (ArgNum >= CE->getNumArgs()) + if (ArgNum >= Call.getNumArgs()) continue; - const Expr *Arg = CE->getArg(ArgNum); - Optional V = getPointedToSVal(C, Arg); + const Expr *Arg = Call.getArgExpr(ArgNum); + Optional V = getPointeeOf(C, Arg); if (V) State = removeTaint(State, *V); } @@ -588,8 +595,8 @@ bool GenericTaintChecker::addFiltersPre(const CallExpr *CE, return false; } -bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, - CheckerContext &C) const { +bool GenericTaintChecker::propagateFromPre(const CallEvent &Call, + CheckerContext &C) { ProgramStateRef State = C.getState(); // Depending on what was tainted at pre-visit, we determined a set of @@ -602,16 +609,16 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, for (unsigned ArgNum : TaintArgs) { // Special handling for the tainted return value. if (ArgNum == ReturnValueIndex) { - State = addTaint(State, CE, C.getLocationContext()); + State = addTaint(State, Call.getReturnValue()); continue; } // The arguments are pointer arguments. The data they are pointing at is // tainted after the call. - if (CE->getNumArgs() < (ArgNum + 1)) + if (Call.getNumArgs() < (ArgNum + 1)) return false; - const Expr *Arg = CE->getArg(ArgNum); - Optional V = getPointedToSVal(C, Arg); + const Expr *Arg = Call.getArgExpr(ArgNum); + Optional V = getPointeeOf(C, Arg); if (V) State = addTaint(State, *V); } @@ -626,27 +633,23 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, return false; } -bool GenericTaintChecker::checkPre(const CallExpr *CE, +bool GenericTaintChecker::checkPre(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const { - - if (checkUncontrolledFormatString(CE, C)) - return true; - - if (checkSystemCall(CE, FData.Name, C)) + if (checkUncontrolledFormatString(Call, C)) return true; - if (checkTaintedBufferSize(CE, FData.FDecl, C)) + if (checkSystemCall(Call, FData.Name, C)) return true; - if (checkCustomSinks(CE, FData, C)) + if (checkTaintedBufferSize(Call, C)) return true; - return false; + return checkCustomSinks(Call, FData, C); } -Optional GenericTaintChecker::getPointedToSVal(CheckerContext &C, - const Expr *Arg) { +Optional GenericTaintChecker::getPointeeOf(CheckerContext &C, + const Expr *Arg) { ProgramStateRef State = C.getState(); SVal AddrVal = C.getSVal(Arg->IgnoreParens()); if (AddrVal.isUnknownOrUndef()) @@ -671,31 +674,33 @@ Optional GenericTaintChecker::getPointedToSVal(CheckerContext &C, } ProgramStateRef -GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, +GenericTaintChecker::TaintPropagationRule::process(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); // Check for taint in arguments. bool IsTainted = true; for (unsigned ArgNum : SrcArgs) { - if (ArgNum >= CE->getNumArgs()) + if (ArgNum >= Call.getNumArgs()) continue; - if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C))) + if ((IsTainted = + isTaintedOrPointsToTainted(Call.getArgExpr(ArgNum), State, C))) break; } // Check for taint in variadic arguments. if (!IsTainted && VariadicType::Src == VarType) { // Check if any of the arguments is tainted - for (unsigned i = VariadicIndex; i < CE->getNumArgs(); ++i) { - if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C))) + for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) { + if ((IsTainted = + isTaintedOrPointsToTainted(Call.getArgExpr(i), State, C))) break; } } if (PropagationFunc) - IsTainted = PropagationFunc(IsTainted, CE, C); + IsTainted = PropagationFunc(IsTainted, Call, C); if (!IsTainted) return State; @@ -708,7 +713,7 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, continue; } - if (ArgNum >= CE->getNumArgs()) + if (ArgNum >= Call.getNumArgs()) continue; // Mark the given argument. @@ -721,14 +726,15 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, // If they are not pointing to const data, mark data as tainted. // TODO: So far we are just going one level down; ideally we'd need to // recurse here. - for (unsigned i = VariadicIndex; i < CE->getNumArgs(); ++i) { - const Expr *Arg = CE->getArg(i); + for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) { + const Expr *Arg = Call.getArgExpr(i); // Process pointer argument. const Type *ArgTy = Arg->getType().getTypePtr(); QualType PType = ArgTy->getPointeeType(); if ((!PType.isNull() && !PType.isConstQualified()) || - (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) + (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) { State = State->add(i); + } } } @@ -736,16 +742,14 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, } // If argument 0(protocol domain) is network, the return value should get taint. -bool GenericTaintChecker::TaintPropagationRule::postSocket(bool /*IsTainted*/, - const CallExpr *CE, - CheckerContext &C) { - SourceLocation DomLoc = CE->getArg(0)->getExprLoc(); +bool GenericTaintChecker::TaintPropagationRule::postSocket( + bool /*IsTainted*/, const CallEvent &Call, CheckerContext &C) { + SourceLocation DomLoc = Call.getArgExpr(0)->getExprLoc(); StringRef DomName = C.getMacroNameOrSpelling(DomLoc); // White list the internal communication protocols. if (DomName.equals("AF_SYSTEM") || DomName.equals("AF_LOCAL") || DomName.equals("AF_UNIX") || DomName.equals("AF_RESERVED_36")) return false; - return true; } @@ -757,16 +761,15 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { const MemRegion *MemReg = Val.getAsRegion(); // The region should be symbolic, we do not know it's value. - const SymbolicRegion *SymReg = dyn_cast_or_null(MemReg); + const auto *SymReg = dyn_cast_or_null(MemReg); if (!SymReg) return false; // Get it's symbol and find the declaration region it's pointing to. - const SymbolRegionValue *Sm = - dyn_cast(SymReg->getSymbol()); + const auto *Sm = dyn_cast(SymReg->getSymbol()); if (!Sm) return false; - const DeclRegion *DeclReg = dyn_cast_or_null(Sm->getRegion()); + const auto *DeclReg = dyn_cast_or_null(Sm->getRegion()); if (!DeclReg) return false; @@ -784,23 +787,24 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { return false; } -static bool getPrintfFormatArgumentNum(const CallExpr *CE, +static bool getPrintfFormatArgumentNum(const CallEvent &Call, const CheckerContext &C, unsigned &ArgNum) { // Find if the function contains a format string argument. // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf, // vsnprintf, syslog, custom annotated functions. - const FunctionDecl *FDecl = C.getCalleeDecl(CE); + const FunctionDecl *FDecl = Call.getDecl()->getAsFunction(); if (!FDecl) return false; for (const auto *Format : FDecl->specific_attrs()) { ArgNum = Format->getFormatIdx() - 1; - if ((Format->getType()->getName() == "printf") && CE->getNumArgs() > ArgNum) + if ((Format->getType()->getName() == "printf") && + Call.getNumArgs() > ArgNum) return true; } // Or if a function is named setproctitle (this is a heuristic). - if (C.getCalleeName(CE).find("setproctitle") != StringRef::npos) { + if (C.getCalleeName(FDecl).find("setproctitle") != StringRef::npos) { ArgNum = 0; return true; } @@ -814,7 +818,7 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, // Check for taint. ProgramStateRef State = C.getState(); - Optional PointedToSVal = getPointedToSVal(C, E); + Optional PointedToSVal = getPointeeOf(C, E); SVal TaintedSVal; if (PointedToSVal && isTainted(State, *PointedToSVal)) TaintedSVal = *PointedToSVal; @@ -836,19 +840,19 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, } bool GenericTaintChecker::checkUncontrolledFormatString( - const CallExpr *CE, CheckerContext &C) const { + const CallEvent &Call, CheckerContext &C) const { // Check if the function contains a format string argument. unsigned ArgNum = 0; - if (!getPrintfFormatArgumentNum(CE, C, ArgNum)) + if (!getPrintfFormatArgumentNum(Call, C, ArgNum)) return false; // If either the format string content or the pointer itself are tainted, // warn. - return generateReportIfTainted(CE->getArg(ArgNum), + return generateReportIfTainted(Call.getArgExpr(ArgNum), MsgUncontrolledFormatString, C); } -bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name, +bool GenericTaintChecker::checkSystemCall(const CallEvent &Call, StringRef Name, CheckerContext &C) const { // TODO: It might make sense to run this check on demand. In some cases, // we should check if the environment has been cleansed here. We also might @@ -866,21 +870,22 @@ bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name, .Case("dlopen", 0) .Default(InvalidArgIndex); - if (ArgNum == InvalidArgIndex || CE->getNumArgs() < (ArgNum + 1)) + if (ArgNum == InvalidArgIndex || Call.getNumArgs() < (ArgNum + 1)) return false; - return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C); + return generateReportIfTainted(Call.getArgExpr(ArgNum), MsgSanitizeSystemArgs, + C); } // TODO: Should this check be a part of the CString checker? // If yes, should taint be a global setting? -bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE, - const FunctionDecl *FDecl, +bool GenericTaintChecker::checkTaintedBufferSize(const CallEvent &Call, CheckerContext &C) const { + const auto *FDecl = Call.getDecl()->getAsFunction(); // If the function has a buffer size argument, set ArgNum. unsigned ArgNum = InvalidArgIndex; unsigned BId = 0; - if ((BId = FDecl->getMemoryFunctionKind())) + if ((BId = FDecl->getMemoryFunctionKind())) { switch (BId) { case Builtin::BImemcpy: case Builtin::BImemmove: @@ -892,26 +897,29 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE, break; default: break; - }; + } + } if (ArgNum == InvalidArgIndex) { - if (C.isCLibraryFunction(FDecl, "malloc") || - C.isCLibraryFunction(FDecl, "calloc") || - C.isCLibraryFunction(FDecl, "alloca")) + using CCtx = CheckerContext; + if (CCtx::isCLibraryFunction(FDecl, "malloc") || + CCtx::isCLibraryFunction(FDecl, "calloc") || + CCtx::isCLibraryFunction(FDecl, "alloca")) ArgNum = 0; - else if (C.isCLibraryFunction(FDecl, "memccpy")) + else if (CCtx::isCLibraryFunction(FDecl, "memccpy")) ArgNum = 3; - else if (C.isCLibraryFunction(FDecl, "realloc")) + else if (CCtx::isCLibraryFunction(FDecl, "realloc")) ArgNum = 1; - else if (C.isCLibraryFunction(FDecl, "bcopy")) + else if (CCtx::isCLibraryFunction(FDecl, "bcopy")) ArgNum = 2; } - return ArgNum != InvalidArgIndex && CE->getNumArgs() > ArgNum && - generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C); + return ArgNum != InvalidArgIndex && Call.getNumArgs() > ArgNum && + generateReportIfTainted(Call.getArgExpr(ArgNum), MsgTaintedBufferSize, + C); } -bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE, +bool GenericTaintChecker::checkCustomSinks(const CallEvent &Call, const FunctionData &FData, CheckerContext &C) const { auto It = findFunctionInConfig(CustomSinks, FData); @@ -921,10 +929,10 @@ bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE, const auto &Value = It->second; const GenericTaintChecker::ArgVector &Args = Value.second; for (unsigned ArgNum : Args) { - if (ArgNum >= CE->getNumArgs()) + if (ArgNum >= Call.getNumArgs()) continue; - if (generateReportIfTainted(CE->getArg(ArgNum), MsgCustomSink, C)) + if (generateReportIfTainted(Call.getArgExpr(ArgNum), MsgCustomSink, C)) return true; } diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp index d73e2eb92d420..794d74c54b730 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -210,15 +210,17 @@ void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { if (!PVD || State->contains(PVD)) return; - const NoteTag *T = C.getNoteTag([this, PVD](BugReport &BR) -> std::string { - if (&BR.getBugType() != &BT) - return ""; - SmallString<64> Str; - llvm::raw_svector_ostream OS(Str); - OS << "Value passed through parameter '" << PVD->getName() - << "\' is deallocated"; - return OS.str(); - }); + const NoteTag *T = + C.getNoteTag([this, PVD](PathSensitiveBugReport &BR) -> std::string { + if (&BR.getBugType() != &BT) + return ""; + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << "Value passed through parameter '" << PVD->getName() + << "\' is deallocated"; + return std::string(OS.str()); + }); + C.addTransition(State->set(true), T); } diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 85370bf133cd7..fe7ede8101889 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -95,6 +95,15 @@ class CFErrorFunctionChecker }; } +static bool hasReservedReturnType(const FunctionDecl *D) { + if (isa(D)) + return true; + + // operators delete and delete[] are required to have 'void' return type + auto OperatorKind = D->getOverloadedOperator(); + return OperatorKind == OO_Delete || OperatorKind == OO_Array_Delete; +} + void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, AnalysisManager &mgr, BugReporter &BR) const { @@ -102,6 +111,8 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, return; if (!D->getReturnType()->isVoidType()) return; + if (hasReservedReturnType(D)) + return; if (!II) II = &D->getASTContext().Idents.get("CFErrorRef"); diff --git a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 6ffc89745365c..ff7e9caa73abf 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -14,8 +14,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/Attr.h" +#include "clang/Analysis/AnyCall.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -27,44 +28,82 @@ using namespace ento; namespace { class NonNullParamChecker - : public Checker< check::PreCall, EventDispatcher > { + : public Checker> { mutable std::unique_ptr BTAttrNonNull; mutable std::unique_ptr BTNullRefArg; public: - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkBeginFunction(CheckerContext &C) const; std::unique_ptr - genReportNullAttrNonNull(const ExplodedNode *ErrorN, - const Expr *ArgE, + genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE, unsigned IdxOfArg) const; std::unique_ptr genReportReferenceToNullPointer(const ExplodedNode *ErrorN, const Expr *ArgE) const; }; -} // end anonymous namespace -/// \return Bitvector marking non-null attributes. -static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { +template +void setBitsAccordingToFunctionAttributes(const CallType &Call, + llvm::SmallBitVector &AttrNonNull) { const Decl *FD = Call.getDecl(); - unsigned NumArgs = Call.getNumArgs(); - llvm::SmallBitVector AttrNonNull(NumArgs); + for (const auto *NonNull : FD->specific_attrs()) { if (!NonNull->args_size()) { - AttrNonNull.set(0, NumArgs); + // Lack of attribute parameters means that all of the parameters are + // implicitly marked as non-null. + AttrNonNull.set(); break; } + for (const ParamIdx &Idx : NonNull->args()) { + // 'nonnull' attribute's parameters are 1-based and should be adjusted to + // match actual AST parameter/argument indices. unsigned IdxAST = Idx.getASTIndex(); - if (IdxAST >= NumArgs) + if (IdxAST >= AttrNonNull.size()) continue; AttrNonNull.set(IdxAST); } } +} + +template +void setBitsAccordingToParameterAttributes(const CallType &Call, + llvm::SmallBitVector &AttrNonNull) { + for (const ParmVarDecl *Parameter : Call.parameters()) { + unsigned ParameterIndex = Parameter->getFunctionScopeIndex(); + if (ParameterIndex == AttrNonNull.size()) + break; + + if (Parameter->hasAttr()) + AttrNonNull.set(ParameterIndex); + } +} + +template +llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call, + unsigned ExpectedSize) { + llvm::SmallBitVector AttrNonNull(ExpectedSize); + + setBitsAccordingToFunctionAttributes(Call, AttrNonNull); + setBitsAccordingToParameterAttributes(Call, AttrNonNull); + return AttrNonNull; } +/// \return Bitvector marking non-null attributes. +llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { + return getNonNullAttrsImpl(Call, Call.getNumArgs()); +} + +/// \return Bitvector marking non-null attributes. +llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) { + return getNonNullAttrsImpl(Call, Call.param_size()); +} +} // end anonymous namespace + void NonNullParamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) @@ -74,7 +113,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, unsigned NumArgs = Call.getNumArgs(); ProgramStateRef state = C.getState(); - ArrayRef parms = Call.parameters(); + ArrayRef parms = Call.parameters(); for (unsigned idx = 0; idx < NumArgs; ++idx) { // For vararg functions, a corresponding parameter decl may not exist. @@ -82,15 +121,11 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, // Check if the parameter is a reference. We want to report when reference // to a null pointer is passed as a parameter. - bool haveRefTypeParam = + bool HasRefTypeParam = HasParam ? parms[idx]->getType()->isReferenceType() : false; - bool haveAttrNonNull = AttrNonNull[idx]; + bool ExpectedToBeNonNull = AttrNonNull.test(idx); - // Check if the parameter is also marked 'nonnull'. - if (!haveAttrNonNull && HasParam) - haveAttrNonNull = parms[idx]->hasAttr(); - - if (!haveAttrNonNull && !haveRefTypeParam) + if (!ExpectedToBeNonNull && !HasRefTypeParam) continue; // If the value is unknown or undefined, we can't perform this check. @@ -100,10 +135,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (!DV) continue; - assert(!haveRefTypeParam || DV->getAs()); + assert(!HasRefTypeParam || DV->getAs()); // Process the case when the argument is not a location. - if (haveAttrNonNull && !DV->getAs()) { + if (ExpectedToBeNonNull && !DV->getAs()) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) @@ -144,9 +179,9 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { std::unique_ptr R; - if (haveAttrNonNull) + if (ExpectedToBeNonNull) R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1); - else if (haveRefTypeParam) + else if (HasRefTypeParam) R = genReportReferenceToNullPointer(errorNode, ArgE); // Highlight the range of the argument that was null. @@ -163,8 +198,8 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (stateNull) { if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { ImplicitNullDerefEvent event = { - V, false, N, &C.getBugReporter(), - /*IsDirectDereference=*/haveRefTypeParam}; + V, false, N, &C.getBugReporter(), + /*IsDirectDereference=*/HasRefTypeParam}; dispatchEvent(event); } } @@ -179,6 +214,65 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, C.addTransition(state); } +/// We want to trust developer annotations and consider all 'nonnull' parameters +/// as non-null indeed. Each marked parameter will get a corresponding +/// constraint. +/// +/// This approach will not only help us to get rid of some false positives, but +/// remove duplicates and shorten warning traces as well. +/// +/// \code +/// void foo(int *x) [[gnu::nonnull]] { +/// // . . . +/// *x = 42; // we don't want to consider this as an error... +/// // . . . +/// } +/// +/// foo(nullptr); // ...and report here instead +/// \endcode +void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const { + // Planned assumption makes sense only for top-level functions. + // Inlined functions will get similar constraints as part of 'checkPreCall'. + if (!Context.inTopFrame()) + return; + + const LocationContext *LocContext = Context.getLocationContext(); + + const Decl *FD = LocContext->getDecl(); + // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl + // separately and aggregates interfaces of these classes. + auto AbstractCall = AnyCall::forDecl(FD); + if (!AbstractCall) + return; + + ProgramStateRef State = Context.getState(); + llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall); + + for (const ParmVarDecl *Parameter : AbstractCall->parameters()) { + // 1. Check parameter if it is annotated as non-null + if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex())) + continue; + + // 2. Check that parameter is a pointer. + // Nonnull attribute can be applied to non-pointers (by default + // __attribute__(nonnull) implies "all parameters"). + if (!Parameter->getType()->isPointerType()) + continue; + + Loc ParameterLoc = State->getLValue(Parameter, LocContext); + // We never consider top-level function parameters undefined. + auto StoredVal = + State->getSVal(ParameterLoc).castAs(); + + // 3. Assume that it is indeed non-null + if (ProgramStateRef NewState = State->assume(StoredVal, true)) { + State = NewState; + } + } + + Context.addTransition(State); +} + std::unique_ptr NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, const Expr *ArgE, diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 922048733c7c4..e0bda8d067265 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -81,7 +81,7 @@ class NullabilityChecker : public Checker, check::PostCall, check::PostStmt, check::PostObjCMessage, check::DeadSymbols, - check::Event> { + check::Location, check::Event> { mutable std::unique_ptr BT; public: @@ -101,6 +101,8 @@ class NullabilityChecker void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; void checkEvent(ImplicitNullDerefEvent Event) const; + void checkLocation(SVal Location, bool IsLoad, const Stmt *S, + CheckerContext &C) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; @@ -503,18 +505,58 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { } } +// Whenever we see a load from a typed memory region that's been annotated as +// 'nonnull', we want to trust the user on that and assume that it is is indeed +// non-null. +// +// We do so even if the value is known to have been assigned to null. +// The user should be warned on assigning the null value to a non-null pointer +// as opposed to warning on the later dereference of this pointer. +// +// \code +// int * _Nonnull var = 0; // we want to warn the user here... +// // . . . +// *var = 42; // ...and not here +// \endcode +void NullabilityChecker::checkLocation(SVal Location, bool IsLoad, + const Stmt *S, + CheckerContext &Context) const { + // We should care only about loads. + // The main idea is to add a constraint whenever we're loading a value from + // an annotated pointer type. + if (!IsLoad) + return; + + // Annotations that we want to consider make sense only for types. + const auto *Region = + dyn_cast_or_null(Location.getAsRegion()); + if (!Region) + return; + + ProgramStateRef State = Context.getState(); + + auto StoredVal = State->getSVal(Region).getAs(); + if (!StoredVal) + return; + + Nullability NullabilityOfTheLoadedValue = + getNullabilityAnnotation(Region->getValueType()); + + if (NullabilityOfTheLoadedValue == Nullability::Nonnull) { + // It doesn't matter what we think about this particular pointer, it should + // be considered non-null as annotated by the developer. + if (ProgramStateRef NewState = State->assume(*StoredVal, true)) { + Context.addTransition(NewState); + } + } +} + /// Find the outermost subexpression of E that is not an implicit cast. /// This looks through the implicit casts to _Nonnull that ARC adds to /// return expressions of ObjC types when the return type of the function or /// method is non-null but the express is not. static const Expr *lookThroughImplicitCasts(const Expr *E) { - assert(E); - - while (auto *ICE = dyn_cast(E)) { - E = ICE->getSubExpr(); - } - - return E; + return E->IgnoreImpCasts(); } /// This method check when nullable pointer or null value is returned from a diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp index 103208d8b5a51..eec26728347f9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp @@ -99,7 +99,7 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call, std::string Name = getName(Call); const NoteTag *CallTag = C.getNoteTag( - [Name, ExpectedValue](BugReport &) -> std::string { + [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string { SmallString<128> Msg; llvm::raw_svector_ostream Out(Msg); diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 47099f2afb6a4..292a12fd129aa 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -49,6 +49,15 @@ struct StreamState { } }; +class StreamChecker; + +using FnCheck = std::function; + +struct FnDescription { + FnCheck EvalFn; +}; + class StreamChecker : public Checker { mutable std::unique_ptr BT_nullfp, BT_illegalwhence, @@ -59,35 +68,33 @@ class StreamChecker : public Checker; - - CallDescriptionMap Callbacks = { - {{"fopen"}, &StreamChecker::evalFopen}, - {{"freopen", 3}, &StreamChecker::evalFreopen}, - {{"tmpfile"}, &StreamChecker::evalFopen}, - {{"fclose", 1}, &StreamChecker::evalFclose}, + + CallDescriptionMap FnDescriptions = { + {{"fopen"}, {&StreamChecker::evalFopen}}, + {{"freopen", 3}, {&StreamChecker::evalFreopen}}, + {{"tmpfile"}, {&StreamChecker::evalFopen}}, + {{"fclose", 1}, {&StreamChecker::evalFclose}}, {{"fread", 4}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)}}, {{"fwrite", 4}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)}, - {{"fseek", 3}, &StreamChecker::evalFseek}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)}}, + {{"fseek", 3}, {&StreamChecker::evalFseek}}, {{"ftell", 1}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, {{"rewind", 1}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, {{"fgetpos", 2}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, {{"fsetpos", 2}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, {{"clearerr", 1}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, {{"feof", 1}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, {{"ferror", 1}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, {{"fileno", 1}, - std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, + {std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}}, }; void evalFopen(const CallEvent &Call, CheckerContext &C) const; @@ -125,11 +132,11 @@ bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { return false; } - const FnCheck *Callback = Callbacks.lookup(Call); - if (!Callback) + const FnDescription *Description = FnDescriptions.lookup(Call); + if (!Description) return false; - (*Callback)(this, Call, C); + (Description->EvalFn)(this, Call, C); return C.isDifferent(); } diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 01ac2bc83bb6b..99e16752b51a4 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -134,9 +134,9 @@ StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName, CheckerName = CheckerName.substr(0, Pos); } while (!CheckerName.empty() && SearchInParents); - llvm_unreachable("Unknown checker option! Did you call getChecker*Option " - "with incorrect parameters? User input must've been " - "verified by CheckerRegistry."); + assert(false && "Unknown checker option! Did you call getChecker*Option " + "with incorrect parameters? User input must've been " + "verified by CheckerRegistry."); return ""; } diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 168d6fe6ec48f..4fc23d8ac9417 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -889,24 +889,22 @@ void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, Params); } -SVal CXXConstructorCall::getCXXThisVal() const { +SVal AnyCXXConstructorCall::getCXXThisVal() const { if (Data) return loc::MemRegionVal(static_cast(Data)); return UnknownVal(); } -void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, +void AnyCXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const { - if (Data) { - loc::MemRegionVal MV(static_cast(Data)); - if (SymbolRef Sym = MV.getAsSymbol(true)) - ETraits->setTrait(Sym, - RegionAndSymbolInvalidationTraits::TK_SuppressEscape); - Values.push_back(MV); - } + SVal V = getCXXThisVal(); + if (SymbolRef Sym = V.getAsSymbol(true)) + ETraits->setTrait(Sym, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + Values.push_back(V); } -void CXXConstructorCall::getInitialStackFrameContents( +void AnyCXXConstructorCall::getInitialStackFrameContents( const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const { AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); @@ -920,6 +918,14 @@ void CXXConstructorCall::getInitialStackFrameContents( } } +const StackFrameContext * +CXXInheritedConstructorCall::getInheritingStackFrame() const { + const StackFrameContext *SFC = getLocationContext()->getStackFrame(); + while (isa(SFC->getCallSite())) + SFC = SFC->getParent()->getStackFrame(); + return SFC; +} + SVal CXXDestructorCall::getCXXThisVal() const { if (Data) return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer()); @@ -1392,17 +1398,20 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx)) return Out; - // All other cases are handled by getCall. - assert(isa(CallSite) && - "This is not an inlineable statement"); - SValBuilder &SVB = State->getStateManager().getSValBuilder(); const auto *Ctor = cast(CalleeCtx->getDecl()); Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx); SVal ThisVal = State->getSVal(ThisPtr); - return getCXXConstructorCall(cast(CallSite), - ThisVal.getAsRegion(), State, CallerCtx); + if (const auto *CE = dyn_cast(CallSite)) + return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx); + else if (const auto *CIE = dyn_cast(CallSite)) + return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State, + CallerCtx); + else { + // All other cases are handled by getCall. + llvm_unreachable("This is not an inlineable statement"); + } } // Fall back to the CFG. The only thing we haven't handled yet is diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index a9361837cf68b..ce5e4a46d3e2c 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -61,7 +61,8 @@ void CheckerManager::finishedCheckerRegistration() { } void CheckerManager::reportInvalidCheckerOptionValue( - const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) { + const CheckerBase *C, StringRef OptionName, + StringRef ExpectedValueDesc) const { Context.getDiagnostics() .Report(diag::err_analyzer_checker_option_invalid_input) @@ -249,7 +250,7 @@ void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, } const std::vector & -CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) { +CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) const { switch (Kind) { case ObjCMessageVisitKind::Pre: return PreObjCMessageCheckers; diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 94cf74de82931..5a49b18aecf12 100644 --- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -221,7 +221,7 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { if (L.getSrc()->getTerminator().isVirtualBaseBranch() && L.getDst() == *L.getSrc()->succ_begin()) { ProgramPoint P = L.withTag(getNoteTags().makeNoteTag( - [](BugReporterContext &, BugReport &) -> std::string { + [](BugReporterContext &, PathSensitiveBugReport &) -> std::string { // TODO: Just call out the name of the most derived class // when we know it. return "Virtual base initialization skipped because " diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index c4838492271cd..635495e9bf60f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -50,9 +50,8 @@ ExplodedGraph::~ExplodedGraph() = default; bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) { if (!Ex->isLValue()) return false; - return isa(Ex) || - isa(Ex) || - isa(Ex); + return isa(Ex) || isa(Ex) || + isa(Ex) || isa(Ex); } bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index f917a4c8637b6..2017b4844643f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1212,7 +1212,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // C++, OpenMP and ARC stuff we don't support yet. case Expr::ObjCIndirectCopyRestoreExprClass: case Stmt::CXXDependentScopeMemberExprClass: - case Stmt::CXXInheritedCtorInitExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: @@ -1617,6 +1616,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Stmt::CXXInheritedCtorInitExprClass: + Bldr.takeNodes(Pred); + VisitCXXInheritedCtorInitExpr(cast(S), Pred, + Dst); + Bldr.addNodes(Dst); + break; + case Stmt::CXXNewExprClass: { Bldr.takeNodes(Pred); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index b816aab7c18f8..d05b31a64427e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -109,7 +109,7 @@ SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue, return LValue; } -std::pair ExprEngine::prepareForObjectConstruction( +std::pair ExprEngine::handleConstructionContext( const Expr *E, ProgramStateRef State, const LocationContext *LCtx, const ConstructionContext *CC, EvalCallOptions &CallOpts) { SValBuilder &SVB = getSValBuilder(); @@ -202,7 +202,7 @@ std::pair ExprEngine::prepareForObjectConstruction( CallerLCtx = CallerLCtx->getParent(); assert(!isa(CallerLCtx)); } - return prepareForObjectConstruction( + return handleConstructionContext( cast(SFC->getCallSite()), State, CallerLCtx, RTC->getConstructionContext(), CallOpts); } else { @@ -247,7 +247,7 @@ std::pair ExprEngine::prepareForObjectConstruction( ProgramStateRef PreElideState = State; EvalCallOptions PreElideCallOpts = CallOpts; - std::tie(State, V) = prepareForObjectConstruction( + std::tie(State, V) = handleConstructionContext( CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts); // FIXME: This definition of "copy elision has not failed" is unreliable. @@ -392,26 +392,32 @@ std::pair ExprEngine::prepareForObjectConstruction( State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx))); } -void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, - ExplodedNode *Pred, - ExplodedNodeSet &destNodes) { +void ExprEngine::handleConstructor(const Expr *E, + ExplodedNode *Pred, + ExplodedNodeSet &destNodes) { + const auto *CE = dyn_cast(E); + const auto *CIE = dyn_cast(E); + assert(CE || CIE); + const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); SVal Target = UnknownVal(); - if (Optional ElidedTarget = - getObjectUnderConstruction(State, CE, LCtx)) { - // We've previously modeled an elidable constructor by pretending that it in - // fact constructs into the correct target. This constructor can therefore - // be skipped. - Target = *ElidedTarget; - StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); - State = finishObjectConstruction(State, CE, LCtx); - if (auto L = Target.getAs()) - State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); - Bldr.generateNode(CE, Pred, State); - return; + if (CE) { + if (Optional ElidedTarget = + getObjectUnderConstruction(State, CE, LCtx)) { + // We've previously modeled an elidable constructor by pretending that it + // in fact constructs into the correct target. This constructor can + // therefore be skipped. + Target = *ElidedTarget; + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + State = finishObjectConstruction(State, CE, LCtx); + if (auto L = Target.getAs()) + State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); + Bldr.generateNode(CE, Pred, State); + return; + } } // FIXME: Handle arrays, which run the same constructor for every element. @@ -423,10 +429,16 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, assert(C || getCurrentCFGElement().getAs()); const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr; - switch (CE->getConstructionKind()) { + const CXXConstructExpr::ConstructionKind CK = + CE ? CE->getConstructionKind() : CIE->getConstructionKind(); + switch (CK) { case CXXConstructExpr::CK_Complete: { + // Inherited constructors are always base class constructors. + assert(CE && !CIE && "A complete constructor is inherited?!"); + + // The target region is found from construction context. std::tie(State, Target) = - prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts); + handleConstructionContext(CE, State, LCtx, CC, CallOpts); break; } case CXXConstructExpr::CK_VirtualBase: { @@ -455,9 +467,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, // FIXME: Instead of relying on the ParentMap, we should have the // trigger-statement (InitListExpr in this case) passed down from CFG or // otherwise always available during construction. - if (dyn_cast_or_null(LCtx->getParentMap().getParent(CE))) { + if (dyn_cast_or_null(LCtx->getParentMap().getParent(E))) { MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); - Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx)); + Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)); CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; break; } @@ -468,14 +480,13 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); - if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { + if (CK == CXXConstructExpr::CK_Delegating) { Target = ThisVal; } else { // Cast to the base type. - bool IsVirtual = - (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); - SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), - IsVirtual); + bool IsVirtual = (CK == CXXConstructExpr::CK_VirtualBase); + SVal BaseVal = + getStoreManager().evalDerivedToBase(ThisVal, E->getType(), IsVirtual); Target = BaseVal; } break; @@ -487,23 +498,27 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, "Prepare for object construction"); ExplodedNodeSet DstPrepare; StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx); - BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind); + BldrPrepare.generateNode(E, Pred, State, &T, ProgramPoint::PreStmtKind); assert(DstPrepare.size() <= 1); if (DstPrepare.size() == 0) return; Pred = *BldrPrepare.begin(); } + const MemRegion *TargetRegion = Target.getAsRegion(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); - CallEventRef Call = - CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx); + CallEventRef<> Call = + CIE ? (CallEventRef<>)CEMgr.getCXXInheritedConstructorCall( + CIE, TargetRegion, State, LCtx) + : (CallEventRef<>)CEMgr.getCXXConstructorCall( + CE, TargetRegion, State, LCtx); ExplodedNodeSet DstPreVisit; - getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); + getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, E, *this); - // FIXME: Is it possible and/or useful to do this before PreStmt? ExplodedNodeSet PreInitialized; - { + if (CE) { + // FIXME: Is it possible and/or useful to do this before PreStmt? StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx); for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), E = DstPreVisit.end(); @@ -528,6 +543,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, ProgramPoint::PreStmtKind); } + } else { + PreInitialized = DstPreVisit; } ExplodedNodeSet DstPreCall; @@ -537,7 +554,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNodeSet DstEvaluated; StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); - if (CE->getConstructor()->isTrivial() && + if (CE && CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !CallOpts.IsArrayCtorOrDtor) { // FIXME: Handle other kinds of trivial constructors as well. @@ -560,9 +577,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, // paths when no-return temporary destructors are used for assertions. const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext(); if (!ADC->getCFGBuildOptions().AddTemporaryDtors) { - const MemRegion *Target = Call->getCXXThisVal().getAsRegion(); - if (Target && isa(Target) && - Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { + if (TargetRegion && isa(TargetRegion) && + cast(Call->getDecl()) + ->getParent()->isAnyDestructorNoReturn()) { // If we've inlined the constructor, then DstEvaluated would be empty. // In this case we still want a sink, which could be implemented @@ -575,7 +592,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, "We should not have inlined this constructor!"); for (ExplodedNode *N : DstEvaluated) { - Bldr.generateSink(CE, N, N->getState()); + Bldr.generateSink(E, N, N->getState()); } // There is no need to run the PostCall and PostStmt checker @@ -595,7 +612,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, getCheckerManager().runCheckersForPostCall(DstPostCall, DstPostArgumentCleanup, *Call, *this); - getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); + getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, E, *this); +} + +void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + handleConstructor(CE, Pred, Dst); +} + +void ExprEngine::VisitCXXInheritedCtorInitExpr( + const CXXInheritedCtorInitExpr *CE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + handleConstructor(CE, Pred, Dst); } void ExprEngine::VisitCXXDestructor(QualType ObjectType, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 01a371e664b2e..781cc9f7943db 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -668,8 +668,8 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, assert(RTC->getStmt() == Call.getOriginExpr()); EvalCallOptions CallOpts; // FIXME: We won't really need those. std::tie(State, Target) = - prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx, - RTC->getConstructionContext(), CallOpts); + handleConstructionContext(Call.getOriginExpr(), State, LCtx, + RTC->getConstructionContext(), CallOpts); const MemRegion *TargetR = Target.getAsRegion(); assert(TargetR); // Invalidate the region so that it didn't look uninitialized. If this is @@ -789,6 +789,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, break; } + case CE_CXXInheritedConstructor: { + // This doesn't really increase the cost of inlining ever, because + // the stack frame of the inherited constructor is trivial. + return CIP_Allowed; + } case CE_CXXDestructor: { if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) return CIP_DisallowedAlways; diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index a4918d7179ff5..002b6070ddcd1 100644 --- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -607,10 +607,17 @@ window.addEventListener("keydown", function (event) { )<<<"; } +static bool shouldDisplayPopUpRange(const SourceRange &Range) { + return !(Range.getBegin().isMacroID() || Range.getEnd().isMacroID()); +} + static void HandlePopUpPieceStartTag(Rewriter &R, const std::vector &PopUpRanges) { for (const auto &Range : PopUpRanges) { + if (!shouldDisplayPopUpRange(Range)) + continue; + html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", "", /*IsTokenRange=*/true); @@ -626,6 +633,8 @@ static void HandlePopUpPieceEndTag(Rewriter &R, llvm::raw_svector_ostream Out(Buf); SourceRange Range(Piece.getLocation().asRange()); + if (!shouldDisplayPopUpRange(Range)) + return; // Write out the path indices with a right arrow and the message as a row. Out << ""; + << ")\">←"; } os << " +// CHECK-NOT: +// CHECK-SAME: +// CHECK-SAME: MACRO +// CHECK-SAME: +// CHECK-SAME: if (b) +// CHECK-SAME: +// CHECK-SAME: diff --git a/clang/test/Analysis/html_diagnostics/variable-popups-multiple.c b/clang/test/Analysis/html_diagnostics/variable-popups-multiple.c new file mode 100644 index 0000000000000..d7a05b53e4f53 --- /dev/null +++ b/clang/test/Analysis/html_diagnostics/variable-popups-multiple.c @@ -0,0 +1,29 @@ +// RUN: rm -fR %t +// RUN: mkdir %t +// RUN: %clang_analyze_cc1 -analyzer-checker=core \ +// RUN: -analyzer-output=html -o %t -verify %s +// RUN: cat %t/report-*.html | FileCheck %s + +void bar(int); + +void foo() { + int a; + for (unsigned i = 0; i < 3; ++i) + if (i) + bar(a); // expected-warning{{1st function call argument is an uninitialized value}} +} + +// CHECK: i +// CHECK-SAME:
" @@ -870,7 +879,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID, << (num - 1) << "\" title=\"Previous event (" << (num - 1) - << ")\">←
"; diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index a10d7e69ad7e7..1482c627cdd4c 100644 --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -825,8 +825,7 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC, return SFC; } if (const auto *BC = dyn_cast(LC)) { - const auto *BR = - static_cast(BC->getContextData()); + const auto *BR = static_cast(BC->getData()); // FIXME: This can be made more efficient. for (BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp index 675209f6fd7e5..21ead69ab475c 100644 --- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -542,6 +542,11 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ if (!Loc) return true; + // Anonymous parameters of an inheriting constructor are live for the entire + // duration of the constructor. + if (isa(Loc)) + return true; + if (LCtx->getAnalysis()->isLive(Loc, VR->getDecl())) return true; diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index fea8100c3b3bc..c15cee410c74f 100644 --- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -12,7 +12,6 @@ #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" #include "ModelInjector.h" -#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -21,10 +20,12 @@ #include "clang/Analysis/CFG.h" #include "clang/Analysis/CallGraph.h" #include "clang/Analysis/CodeInjector.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/CrossTU/CrossTranslationUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" @@ -33,6 +34,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" +#include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Tooling.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/FileSystem.h" @@ -46,6 +49,7 @@ using namespace clang; using namespace ento; +using namespace tooling; #define DEBUG_TYPE "AnalysisConsumer" @@ -83,11 +87,15 @@ void ento::createTextPathDiagnosticConsumer( namespace { class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { DiagnosticsEngine &Diag; - bool IncludePath = false, ShouldEmitAsError = false, FixitsAsRemarks = false; + LangOptions LO; + + bool IncludePath = false; + bool ShouldEmitAsError = false; + bool ApplyFixIts = false; public: - ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag) - : Diag(Diag) {} + ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag, LangOptions LO) + : Diag(Diag), LO(LO) {} ~ClangDiagPathDiagConsumer() override {} StringRef getName() const override { return "ClangDiags"; } @@ -100,7 +108,7 @@ class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { void enablePaths() { IncludePath = true; } void enableWerror() { ShouldEmitAsError = true; } - void enableFixitsAsRemarks() { FixitsAsRemarks = true; } + void enableApplyFixIts() { ApplyFixIts = true; } void FlushDiagnosticsImpl(std::vector &Diags, FilesMade *filesMade) override { @@ -109,30 +117,27 @@ class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { ? Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0") : Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0"); - unsigned RemarkID = Diag.getCustomDiagID(DiagnosticsEngine::Remark, "%0"); - - auto reportPiece = - [&](unsigned ID, SourceLocation Loc, StringRef String, - ArrayRef Ranges, ArrayRef Fixits) { - if (!FixitsAsRemarks) { - Diag.Report(Loc, ID) << String << Ranges << Fixits; - } else { - Diag.Report(Loc, ID) << String << Ranges; - for (const FixItHint &Hint : Fixits) { - SourceManager &SM = Diag.getSourceManager(); - llvm::SmallString<128> Str; - llvm::raw_svector_ostream OS(Str); - // FIXME: Add support for InsertFromRange and - // BeforePreviousInsertion. - assert(!Hint.InsertFromRange.isValid() && "Not implemented yet!"); - assert(!Hint.BeforePreviousInsertions && "Not implemented yet!"); - OS << SM.getSpellingColumnNumber(Hint.RemoveRange.getBegin()) - << "-" << SM.getSpellingColumnNumber(Hint.RemoveRange.getEnd()) - << ": '" << Hint.CodeToInsert << "'"; - Diag.Report(Loc, RemarkID) << OS.str(); - } - } - }; + SourceManager &SM = Diag.getSourceManager(); + + Replacements Repls; + auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String, + ArrayRef Ranges, + ArrayRef Fixits) { + if (!ApplyFixIts) { + Diag.Report(Loc, ID) << String << Ranges << Fixits; + return; + } + + Diag.Report(Loc, ID) << String << Ranges; + for (const FixItHint &Hint : Fixits) { + Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert); + + if (llvm::Error Err = Repls.add(Repl)) { + llvm::errs() << "Error applying replacement " << Repl.toString() + << ": " << Err << "\n"; + } + } + }; for (std::vector::iterator I = Diags.begin(), E = Diags.end(); @@ -164,6 +169,16 @@ class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { Piece->getString(), Piece->getRanges(), Piece->getFixits()); } } + + if (!ApplyFixIts || Repls.empty()) + return; + + Rewriter Rewrite(SM, LO); + if (!applyAllReplacements(Repls, Rewrite)) { + llvm::errs() << "An error occured during applying fix-it.\n"; + } + + Rewrite.overwriteChangedFiles(); } }; } // end anonymous namespace @@ -256,14 +271,14 @@ class AnalysisConsumer : public AnalysisASTConsumer, if (Opts->AnalysisDiagOpt != PD_NONE) { // Create the PathDiagnosticConsumer. ClangDiagPathDiagConsumer *clangDiags = - new ClangDiagPathDiagConsumer(PP.getDiagnostics()); + new ClangDiagPathDiagConsumer(PP.getDiagnostics(), PP.getLangOpts()); PathConsumers.push_back(clangDiags); if (Opts->AnalyzerWerror) clangDiags->enableWerror(); - if (Opts->ShouldEmitFixItHintsAsRemarks) - clangDiags->enableFixitsAsRemarks(); + if (Opts->ShouldApplyFixIts) + clangDiags->enableApplyFixIts(); if (Opts->AnalysisDiagOpt == PD_TEXT) { clangDiags->enablePaths(); @@ -503,6 +518,13 @@ static bool shouldSkipFunction(const Decl *D, if (VisitedAsTopLevel.count(D)) return true; + // Skip analysis of inheriting constructors as top-level functions. These + // constructors don't even have a body written down in the code, so even if + // we find a bug, we won't be able to display it. + if (const auto *CD = dyn_cast(D)) + if (CD->isInheritingConstructor()) + return true; + // We want to re-analyse the functions as top level in the following cases: // - The 'init' methods should be reanalyzed because // ObjCNonNilReturnValueChecker assumes that '[super init]' never returns diff --git a/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt index 5e7dd8f18cd73..6f1151ab0c111 100644 --- a/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -21,4 +21,6 @@ add_clang_library(clangStaticAnalyzerFrontend clangLex clangStaticAnalyzerCheckers clangStaticAnalyzerCore + clangRewrite + clangToolingCore ) diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt index 59c990daaa29f..6901690f9bc24 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) @@ -42,4 +43,6 @@ add_clang_library(clangTooling clangRewrite clangSerialization clangToolingCore + clangToolingRefactor + clangToolingRefactoring ) diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp index f643c538f8f9a..52ec074e458fb 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -8,24 +8,25 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" #include "clang/Frontend/Utils.h" -#include "llvm/Support/JSON.h" - -static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) { - std::vector Strings; - for (auto &&I : Set) - Strings.push_back(I.getKey()); - std::sort(Strings.begin(), Strings.end()); - return llvm::json::Array(Strings); -} namespace clang{ namespace tooling{ namespace dependencies{ +std::vector FullDependencies::getAdditionalCommandLine( + std::function LookupPCMPath, + std::function LookupModuleDeps) const { + std::vector Ret = AdditionalNonPathCommandLine; + + dependencies::detail::appendCommonModuleArguments( + ClangModuleDeps, LookupPCMPath, LookupModuleDeps, Ret); + + return Ret; +} + DependencyScanningTool::DependencyScanningTool( DependencyScanningService &Service) - : Format(Service.getFormat()), Worker(Service) { -} + : Worker(Service) {} llvm::Expected DependencyScanningTool::getDependencyFile( const tooling::CompilationDatabase &Compilations, StringRef CWD) { @@ -75,8 +76,33 @@ llvm::Expected DependencyScanningTool::getDependencyFile( std::vector Dependencies; }; + // We expect a single command here because if a source file occurs multiple + // times in the original CDB, then `computeDependencies` would run the + // `DependencyScanningAction` once for every time the input occured in the + // CDB. Instead we split up the CDB into single command chunks to avoid this + // behavior. + assert(Compilations.getAllCompileCommands().size() == 1 && + "Expected a compilation database with a single command!"); + std::string Input = Compilations.getAllCompileCommands().front().Filename; + + MakeDependencyPrinterConsumer Consumer; + auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer); + if (Result) + return std::move(Result); + std::string Output; + Consumer.printDependencies(Output); + return Output; +} + +llvm::Expected +DependencyScanningTool::getFullDependencies( + const tooling::CompilationDatabase &Compilations, StringRef CWD, + const llvm::StringSet<> &AlreadySeen) { class FullDependencyPrinterConsumer : public DependencyConsumer { public: + FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen) + : AlreadySeen(AlreadySeen) {} + void handleFileDependency(const DependencyOutputOptions &Opts, StringRef File) override { Dependencies.push_back(File); @@ -90,55 +116,46 @@ llvm::Expected DependencyScanningTool::getDependencyFile( ContextHash = std::move(Hash); } - void printDependencies(std::string &S, StringRef MainFile) { - // Sort the modules by name to get a deterministic order. - std::vector Modules; - for (auto &&Dep : ClangModuleDeps) - Modules.push_back(Dep.first); - std::sort(Modules.begin(), Modules.end()); + FullDependenciesResult getFullDependencies() const { + FullDependencies FD; - llvm::raw_string_ostream OS(S); + FD.ContextHash = std::move(ContextHash); - using namespace llvm::json; + FD.FileDeps.assign(Dependencies.begin(), Dependencies.end()); - Array Imports; - for (auto &&ModName : Modules) { - auto &MD = ClangModuleDeps[ModName]; + for (auto &&M : ClangModuleDeps) { + auto &MD = M.second; if (MD.ImportedByMainFile) - Imports.push_back(MD.ModuleName); + FD.ClangModuleDeps.push_back({MD.ModuleName, ContextHash}); } + + FD.AdditionalNonPathCommandLine = { + "-fno-implicit-modules", + "-fno-implicit-module-maps", + }; - Array Mods; - for (auto &&ModName : Modules) { - auto &MD = ClangModuleDeps[ModName]; - Object Mod{ - {"name", MD.ModuleName}, - {"file-deps", toJSONSorted(MD.FileDeps)}, - {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)}, - {"clang-modulemap-file", MD.ClangModuleMapFile}, - }; - Mods.push_back(std::move(Mod)); - } + FullDependenciesResult FDR; - Object O{ - {"input-file", MainFile}, - {"clang-context-hash", ContextHash}, - {"file-deps", Dependencies}, - {"clang-module-deps", std::move(Imports)}, - {"clang-modules", std::move(Mods)}, - }; + for (auto &&M : ClangModuleDeps) { + // TODO: Avoid handleModuleDependency even being called for modules + // we've already seen. + if (AlreadySeen.count(M.first)) + continue; + FDR.DiscoveredModules.push_back(std::move(M.second)); + } - S = llvm::formatv("{0:2},\n", Value(std::move(O))).str(); - return; + FDR.FullDeps = std::move(FD); + return FDR; } private: std::vector Dependencies; std::unordered_map ClangModuleDeps; std::string ContextHash; + std::vector OutputPaths; + const llvm::StringSet<> &AlreadySeen; }; - // We expect a single command here because if a source file occurs multiple // times in the original CDB, then `computeDependencies` would run the // `DependencyScanningAction` once for every time the input occured in the @@ -147,26 +164,13 @@ llvm::Expected DependencyScanningTool::getDependencyFile( assert(Compilations.getAllCompileCommands().size() == 1 && "Expected a compilation database with a single command!"); std::string Input = Compilations.getAllCompileCommands().front().Filename; - - if (Format == ScanningOutputFormat::Make) { - MakeDependencyPrinterConsumer Consumer; - auto Result = - Worker.computeDependencies(Input, CWD, Compilations, Consumer); - if (Result) - return std::move(Result); - std::string Output; - Consumer.printDependencies(Output); - return Output; - } else { - FullDependencyPrinterConsumer Consumer; - auto Result = - Worker.computeDependencies(Input, CWD, Compilations, Consumer); - if (Result) - return std::move(Result); - std::string Output; - Consumer.printDependencies(Output, Input); - return Output; - } + + FullDependencyPrinterConsumer Consumer(AlreadySeen); + llvm::Error Result = + Worker.computeDependencies(Input, CWD, Compilations, Consumer); + if (Result) + return std::move(Result); + return Consumer.getFullDependencies(); } } // end namespace dependencies diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index edf2cf8bd70f0..6d513b1286ab0 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -142,12 +142,17 @@ class DependencyScanningAction : public tooling::ToolAction { Consumer)); break; case ScanningOutputFormat::Full: - Compiler.addDependencyCollector( - std::make_shared(Compiler, Consumer)); + Compiler.addDependencyCollector(std::make_shared( + std::move(Opts), Compiler, Consumer)); break; } - Consumer.handleContextHash(Compiler.getInvocation().getModuleHash()); + // Consider different header search and diagnostic options to create + // different modules. This avoids the unsound aliasing of module PCMs. + // + // TODO: Implement diagnostic bucketing and header search pruning to reduce + // the impact of strict context hashing. + Compiler.getHeaderSearchOpts().ModulesStrictContextHash = true; auto Action = std::make_unique(); const bool Result = Compiler.ExecuteAction(*Action); @@ -215,3 +220,29 @@ llvm::Error DependencyScanningWorker::computeDependencies( return !Tool.run(&Action); }); } + +llvm::Error DependencyScanningWorker::computeDependenciesForClangInvocation( + StringRef WorkingDirectory, ArrayRef Arguments, + DependencyConsumer &Consumer) { + RealFS->setCurrentWorkingDirectory(WorkingDirectory); + return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) { + IntrusiveRefCntPtr DiagID = new DiagnosticIDs(); + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + DiagnosticsEngine Diags(DiagID, &*DiagOpts, &DC, /*ShouldOwnClient=*/false); + + llvm::opt::ArgStringList CC1Args; + for (const auto &Arg : Arguments) + CC1Args.push_back(Arg.c_str()); + std::unique_ptr Invocation( + newInvocation(&Diags, CC1Args)); + + DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS, + PPSkipMappings.get(), Format); + + llvm::IntrusiveRefCntPtr FM = Files; + if (!FM) + FM = new FileManager(FileSystemOptions(), RealFS); + return Action.runInvocation(std::move(Invocation), FM.get(), + PCHContainerOps, &DC); + }); +} diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 422940047f2db..889872997b949 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -17,12 +17,59 @@ using namespace clang; using namespace tooling; using namespace dependencies; +std::vector ModuleDeps::getFullCommandLine( + std::function LookupPCMPath, + std::function LookupModuleDeps) const { + std::vector Ret = NonPathCommandLine; + + // TODO: Build full command line. That also means capturing the original + // command line into NonPathCommandLine. + + dependencies::detail::appendCommonModuleArguments( + ClangModuleDeps, LookupPCMPath, LookupModuleDeps, Ret); + + return Ret; +} + +void dependencies::detail::appendCommonModuleArguments( + llvm::ArrayRef Modules, + std::function LookupPCMPath, + std::function LookupModuleDeps, + std::vector &Result) { + llvm::StringSet<> AlreadyAdded; + + std::function)> AddArgs = + [&](llvm::ArrayRef Modules) { + for (const ClangModuleDep &CMD : Modules) { + if (!AlreadyAdded.insert(CMD.ModuleName + CMD.ContextHash).second) + continue; + const ModuleDeps &M = LookupModuleDeps(CMD); + // Depth first traversal. + AddArgs(M.ClangModuleDeps); + Result.push_back(("-fmodule-file=" + LookupPCMPath(CMD)).str()); + if (!M.ClangModuleMapFile.empty()) { + Result.push_back("-fmodule-map-file=" + M.ClangModuleMapFile); + } + } + }; + + AddArgs(Modules); +} + void ModuleDepCollectorPP::FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) { if (Reason != PPCallbacks::EnterFile) return; + + // This has to be delayed as the context hash can change at the start of + // `CompilerInstance::ExecuteAction`. + if (MDC.ContextHash.empty()) { + MDC.ContextHash = + Instance.getInvocation().getModuleHash(Instance.getDiagnostics()); + MDC.Consumer.handleContextHash(MDC.ContextHash); + } SourceManager &SM = Instance.getSourceManager(); @@ -50,7 +97,16 @@ void ModuleDepCollectorPP::InclusionDirective( // here as `FileChanged` will never see it. MDC.MainDeps.push_back(FileName); } + handleImport(Imported); +} +void ModuleDepCollectorPP::moduleImport(SourceLocation ImportLoc, + ModuleIdPath Path, + const Module *Imported) { + handleImport(Imported); +} + +void ModuleDepCollectorPP::handleImport(const Module *Imported) { if (!Imported) return; @@ -71,9 +127,8 @@ void ModuleDepCollectorPP::EndOfMainFile() { for (auto &&I : MDC.Deps) MDC.Consumer.handleModuleDependency(I.second); - DependencyOutputOptions Opts; for (auto &&I : MDC.MainDeps) - MDC.Consumer.handleFileDependency(Opts, I); + MDC.Consumer.handleFileDependency(*MDC.Opts, I); } void ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { @@ -90,43 +145,63 @@ void ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { const FileEntry *ModuleMap = Instance.getPreprocessor() .getHeaderSearchInfo() .getModuleMap() - .getContainingModuleMapFile(M); + .getModuleMapFileForUniquing(M); MD.ClangModuleMapFile = ModuleMap ? ModuleMap->getName() : ""; MD.ModuleName = M->getFullModuleName(); - MD.ModulePCMPath = M->getASTFile()->getName(); + MD.ImplicitModulePCMPath = M->getASTFile()->getName(); MD.ContextHash = MDC.ContextHash; serialization::ModuleFile *MF = MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile()); MDC.Instance.getASTReader()->visitInputFiles( *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) { + // __inferred_module.map is the result of the way in which an implicit + // module build handles inferred modules. It adds an overlay VFS with + // this file in the proper directory and relies on the rest of Clang to + // handle it like normal. With explicitly built modules we don't need + // to play VFS tricks, so replace it with the correct module map. + if (IF.getFile()->getName().endswith("__inferred_module.map")) { + MD.FileDeps.insert(ModuleMap->getName()); + return; + } MD.FileDeps.insert(IF.getFile()->getName()); }); + MD.NonPathCommandLine = { + "-remove-preceeding-explicit-module-build-incompatible-options", + "-fno-implicit-modules", "-emit-module", "-fmodule-name=" + MD.ModuleName, + }; - addAllSubmoduleDeps(M, MD); + llvm::DenseSet AddedModules; + addAllSubmoduleDeps(M, MD, AddedModules); } -void ModuleDepCollectorPP::addAllSubmoduleDeps(const Module *M, - ModuleDeps &MD) { - addModuleDep(M, MD); +void ModuleDepCollectorPP::addAllSubmoduleDeps( + const Module *M, ModuleDeps &MD, + llvm::DenseSet &AddedModules) { + addModuleDep(M, MD, AddedModules); for (const Module *SubM : M->submodules()) - addAllSubmoduleDeps(SubM, MD); + addAllSubmoduleDeps(SubM, MD, AddedModules); } -void ModuleDepCollectorPP::addModuleDep(const Module *M, ModuleDeps &MD) { +void ModuleDepCollectorPP::addModuleDep( + const Module *M, ModuleDeps &MD, + llvm::DenseSet &AddedModules) { for (const Module *Import : M->Imports) { if (Import->getTopLevelModule() != M->getTopLevelModule()) { - MD.ClangModuleDeps.insert(Import->getTopLevelModuleName()); + if (AddedModules.insert(Import->getTopLevelModule()).second) + MD.ClangModuleDeps.push_back({Import->getTopLevelModuleName(), + Instance.getInvocation().getModuleHash( + Instance.getDiagnostics())}); handleTopLevelModule(Import->getTopLevelModule()); } } } -ModuleDepCollector::ModuleDepCollector(CompilerInstance &I, - DependencyConsumer &C) - : Instance(I), Consumer(C), ContextHash(I.getInvocation().getModuleHash()) { -} +ModuleDepCollector::ModuleDepCollector( + std::unique_ptr Opts, CompilerInstance &I, + DependencyConsumer &C) + : Instance(I), Consumer(C), Opts(std::move(Opts)) {} void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { PP.addPPCallbacks(std::make_unique(Instance, *this)); diff --git a/clang/lib/Tooling/Refactor/ASTSlice.cpp b/clang/lib/Tooling/Refactor/ASTSlice.cpp new file mode 100644 index 0000000000000..f096876fe97e8 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTSlice.cpp @@ -0,0 +1,639 @@ +//===--- 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 (CurrDecl->getBeginLoc().isInvalid()) + continue; + + 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..23cf6aff3c9e3 --- /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 (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 (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..dc365a6d9ebb5 --- /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]->IgnoreImpCasts(); + 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 d100811c1c246..afbd3d5248b6d 100644 --- a/clang/test/AST/ast-dump-decl-json.m +++ b/clang/test/AST/ast-dump-decl-json.m @@ -1037,11 +1037,11 @@ void f() { // CHECK: "kind": "ObjCCompatibleAliasDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 926, +// CHECK-NEXT: "offset": 947, // CHECK-NEXT: "file": "{{.*}}", // CHECK-NEXT: "line": 60, -// CHECK-NEXT: "col": 1, -// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: "col": 22, +// CHECK-NEXT: "tokLen": 27 // CHECK-NEXT: }, // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { 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-objc-arc-json.m b/clang/test/AST/ast-dump-objc-arc-json.m new file mode 100644 index 0000000000000..bb9268b28ef65 --- /dev/null +++ b/clang/test/AST/ast-dump-objc-arc-json.m @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -ast-dump=json -ast-dump-filter Test %s | FileCheck %s + +typedef struct { + id f; +} S; + +id TestCompoundLiteral(id a) { + return ((S){ .f = a }).f; +} + +// CHECK: "kind": "ExprWithCleanups", +// CHECK-NEXT: "range": { +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 202, +// CHECK-NEXT: "col": 10, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 218, +// CHECK-NEXT: "col": 26, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } +// CHECK-NEXT: }, +// CHECK-NEXT: "type": { +// CHECK-NEXT: "desugaredQualType": "id", +// CHECK-NEXT: "qualType": "id", +// CHECK-NEXT: "typeAliasDeclId": "0x{{.*}}" +// CHECK-NEXT: }, +// CHECK-NEXT: "valueCategory": "rvalue", +// CHECK-NEXT: "cleanupsHaveSideEffects": true, +// CHECK-NEXT: "cleanups": [ +// CHECK-NEXT: { +// CHECK-NEXT: "id": "0x{{.*}}", +// CHECK-NEXT: "kind": "CompoundLiteralExpr" +// CHECK-NEXT: } +// CHECK-NEXT: ], 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/AST/ast-dump-stmt.m b/clang/test/AST/ast-dump-stmt.m index 8c0ca897e5114..9744e7a43c3e8 100644 --- a/clang/test/AST/ast-dump-stmt.m +++ b/clang/test/AST/ast-dump-stmt.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s +// RUN: %clang_cc1 -Wno-unused -fobjc-arc -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s void TestBlockExpr(int x) { ^{ x; }; @@ -34,3 +34,16 @@ void TestObjCAtCatchStmt() { // CHECK-NEXT: ObjCAtCatchStmt{{.*}} catch all // CHECK-NEXT: CompoundStmt // CHECK-NEXT: ObjCAtFinallyStmt + +typedef struct { + id f; +} S; + +id TestCompoundLiteral(id a) { + return ((S){ .f = a }).f; +} + +// CHECK: FunctionDecl{{.*}}TestCompoundLiteral +// CHECK: ExprWithCleanups +// CHECK-NEXT: cleanup CompoundLiteralExpr +// CHECK: CompoundLiteralExpr{{.*}}'S':'S' lvalue diff --git a/clang/test/AST/ast-print-attr.c b/clang/test/AST/ast-print-attr.c index 6448437c5eabe..d223d607ed507 100644 --- a/clang/test/AST/ast-print-attr.c +++ b/clang/test/AST/ast-print-attr.c @@ -11,7 +11,7 @@ using B = int ** __ptr32 *[3]; // CHECK: using C = int ((*))() __attribute__((cdecl)); using C = int (*)() [[gnu::cdecl]]; -// CHECK: int fun_asm() asm(""); -int fun_asm() asm(""); -// CHECK: int var_asm asm(""); -int var_asm asm(""); +// CHECK: int fun_asm() asm("test"); +int fun_asm() asm("test"); +// CHECK: int var_asm asm("test"); +int var_asm asm("test"); diff --git a/clang/test/AST/regression-new-expr-crash.cpp b/clang/test/AST/regression-new-expr-crash.cpp new file mode 100644 index 0000000000000..81dd193b93e88 --- /dev/null +++ b/clang/test/AST/regression-new-expr-crash.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only %s + +struct Bar {int a;}; +const Bar arr[2] = {{1}}; + +struct Foo {}; + +const int b = 2; + +void foo(int a) { + Foo *foo_array; + foo_array = new Foo[arr[0].a]; +} diff --git a/clang/test/Analysis/CheckNSError.m b/clang/test/Analysis/CheckNSError.m index 6de98e85efe30..510f11c055795 100644 --- a/clang/test/Analysis/CheckNSError.m +++ b/clang/test/Analysis/CheckNSError.m @@ -1,5 +1,8 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.NSError,osx.coreFoundation.CFError -analyzer-store=region -verify -Wno-objc-root-class %s - +// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=nullability \ +// RUN: -analyzer-checker=osx.cocoa.NSError \ +// RUN: -analyzer-checker=osx.coreFoundation.CFError typedef signed char BOOL; typedef int NSInteger; @@ -18,6 +21,8 @@ + (id)errorWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictio @interface A - (void)myMethodWhichMayFail:(NSError **)error; - (BOOL)myMethodWhichMayFail2:(NSError **)error; +- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error; +- (BOOL)myMethodWhichMayFail4:(NSError **)error __attribute__((nonnull)); @end @implementation A @@ -29,6 +34,16 @@ - (BOOL)myMethodWhichMayFail2:(NSError **)error { // no-warning if (error) *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning return 0; } + +- (BOOL)myMethodWhichMayFail3:(NSError **_Nonnull)error { // no-warning + *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning + return 0; +} + +- (BOOL)myMethodWhichMayFail4:(NSError **)error { // no-warning + *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // no-warning + return 0; +} @end struct __CFError {}; @@ -53,4 +68,17 @@ int f3(CFErrorRef* error) { return 0; } +int __attribute__((nonnull)) f4(CFErrorRef *error) { + *error = 0; // no-warning + return 0; +} +int __attribute__((nonnull(1))) f5(int *x, CFErrorRef *error) { + *error = 0; // expected-warning {{Potential null dereference}} + return 0; +} + +int __attribute__((nonnull(2))) f6(int *x, CFErrorRef *error) { + *error = 0; // no-warning + return 0; +} diff --git a/clang/test/Analysis/CheckThatArraySubsciptNodeIsNotCollected.cpp b/clang/test/Analysis/CheckThatArraySubsciptNodeIsNotCollected.cpp new file mode 100644 index 0000000000000..3e558f757e6d1 --- /dev/null +++ b/clang/test/Analysis/CheckThatArraySubsciptNodeIsNotCollected.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output=text -verify %s + +class A { +public: + int method(); +}; + +A *foo(); +void bar(A *); + +int index; + +// We want to check here that the notes about the origins of the null pointer +// (array[index] = foo()) will get to the final report. +// +// The analyzer used to drop exploded nodes for array subscripts when it was +// time to collect redundant nodes. This GC-like mechanism kicks in only when +// the exploded graph is large enough (>1K nodes). For this reason, 'index' +// is a global variable, and the sink point is inside of a loop. + +void test() { + A *array[42]; + A *found; + + for (index = 0; (array[index] = foo()); ++index) { // expected-note {{Loop condition is false. Execution continues on line 34}} + // expected-note@-1 {{Value assigned to 'index'}} + // expected-note@-2 {{Assigning value}} + // expected-note@-3 {{Assuming pointer value is null}} + if (array[0]) + break; + } + + do { + found = array[index]; // expected-note {{Null pointer value stored to 'found'}} + + if (found->method()) // expected-warning {{Called C++ object pointer is null}} + // expected-note@-1 {{Called C++ object pointer is null}} + bar(found); + } while (--index); +} diff --git a/clang/test/Analysis/SpecialFunctionsCFError.cpp b/clang/test/Analysis/SpecialFunctionsCFError.cpp new file mode 100644 index 0000000000000..3517933f6bcc6 --- /dev/null +++ b/clang/test/Analysis/SpecialFunctionsCFError.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFError \ +// RUN: -verify %s + +typedef unsigned long size_t; +struct __CFError {}; +typedef struct __CFError *CFErrorRef; +void *malloc(size_t); + +class Foo { +public: + Foo(CFErrorRef *error) {} // no-warning + + void operator delete(void *pointer, CFErrorRef *error) { // no-warning + return; + } + + void operator delete[](void *pointer, CFErrorRef *error) { // no-warning + return; + } + + // Check that we report warnings for operators when it can be useful + void operator()(CFErrorRef *error) {} // expected-warning {{Function accepting CFErrorRef* should have a non-void return value to indicate whether or not an error occurred}} +}; + +// Check that global delete operator is not bothered as well +void operator delete(void *pointer, CFErrorRef *error) { // no-warning + return; +} diff --git a/clang/test/Analysis/UserNullabilityAnnotations.m b/clang/test/Analysis/UserNullabilityAnnotations.m new file mode 100644 index 0000000000000..5e708c7aca588 --- /dev/null +++ b/clang/test/Analysis/UserNullabilityAnnotations.m @@ -0,0 +1,49 @@ +// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \ +// RUN: -Wno-tautological-pointer-compare \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=nullability \ +// RUN: -analyzer-checker=debug.ExprInspection + +void clang_analyzer_eval(int); + +@interface TestFunctionLevelAnnotations +- (void)method1:(int *_Nonnull)x; +- (void)method2:(int *)x __attribute__((nonnull)); +@end + +@implementation TestFunctionLevelAnnotations +- (void)method1:(int *_Nonnull)x { + clang_analyzer_eval(x != 0); // expected-warning{{TRUE}} +} + +- (void)method2:(int *)x { + clang_analyzer_eval(x != 0); // expected-warning{{TRUE}} +} +@end + +typedef struct NestedNonnullMember { + struct NestedNonnullMember *Child; + int *_Nonnull Value; +} NestedNonnullMember; + +NestedNonnullMember *foo(); + +void f1(NestedNonnullMember *Root) { + NestedNonnullMember *Grandson = Root->Child->Child; + + clang_analyzer_eval(Root->Value != 0); // expected-warning{{TRUE}} + clang_analyzer_eval(Grandson->Value != 0); // expected-warning{{TRUE}} + clang_analyzer_eval(foo()->Child->Value != 0); // expected-warning{{TRUE}} +} + +// Check that we correctly process situations when non-pointer parameters +// get nonnul attributes. +// Original problem: rdar://problem/63150074 +typedef struct { + long a; +} B; +__attribute__((nonnull)) void c(B x, int *y); + +void c(B x, int *y) { + clang_analyzer_eval(y != 0); // expected-warning{{TRUE}} +} diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index 4707da3b9a3a3..edd4fac7f8b24 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -10,6 +10,7 @@ // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtExec = 0x04 // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01 // CHECK-NEXT: alpha.security.taint.TaintPropagation:Config = "" +// CHECK-NEXT: apply-fixits = false // CHECK-NEXT: avoid-suppressing-null-argument-paths = false // CHECK-NEXT: c++-allocator-inlining = true // CHECK-NEXT: c++-container-inlining = false @@ -57,7 +58,6 @@ // CHECK-NEXT: experimental-enable-naive-ctu-analysis = false // CHECK-NEXT: exploration_strategy = unexplored_first_queue // CHECK-NEXT: faux-bodies = true -// CHECK-NEXT: fixits-as-remarks = false // CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: inline-lambdas = true // CHECK-NEXT: ipa = dynamic-bifurcate diff --git a/clang/test/Analysis/check-analyzer-fixit.py b/clang/test/Analysis/check-analyzer-fixit.py new file mode 100644 index 0000000000000..6a8f6859f816b --- /dev/null +++ b/clang/test/Analysis/check-analyzer-fixit.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +#===- check-analyzer-fixit.py - Static Analyzer test helper ---*- python -*-===# +# +# 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 copy-pasted mostly from the Clang-Tidy's 'check_clang_tidy.py'. +# +#===------------------------------------------------------------------------===# + +r""" +Clang Static Analyzer test helper +================================= + +This script runs the Analyzer in fix-it mode and verify fixes, warnings, notes. + +Usage: + check-analyzer-fixit.py [analyzer arguments] + +Example: + // RUN: %check-analyzer-fixit %s %t -analyzer-checker=core +""" + +import argparse +import os +import re +import subprocess +import sys + + +def write_file(file_name, text): + with open(file_name, 'w') as f: + f.write(text) + + +def run_test_once(args, extra_args): + input_file_name = args.input_file_name + temp_file_name = args.temp_file_name + clang_analyzer_extra_args = extra_args + + file_name_with_extension = input_file_name + _, extension = os.path.splitext(file_name_with_extension) + if extension not in ['.c', '.hpp', '.m', '.mm']: + extension = '.cpp' + temp_file_name = temp_file_name + extension + + with open(input_file_name, 'r') as input_file: + input_text = input_file.read() + + # Remove the contents of the CHECK lines to avoid CHECKs matching on + # themselves. We need to keep the comments to preserve line numbers while + # avoiding empty lines which could potentially trigger formatting-related + # checks. + cleaned_test = re.sub('// *CHECK-[A-Z0-9\-]*:[^\r\n]*', '//', input_text) + write_file(temp_file_name, cleaned_test) + + original_file_name = temp_file_name + ".orig" + write_file(original_file_name, cleaned_test) + + try: + builtin_include_dir = subprocess.check_output( + ['clang', '-print-file-name=include'], stderr=subprocess.STDOUT).decode() + except subprocess.CalledProcessError as e: + print('Cannot print Clang include directory: ' + e.output.decode()) + + builtin_include_dir = os.path.normpath(builtin_include_dir) + + args = (['clang', '-cc1', '-internal-isystem', builtin_include_dir, + '-nostdsysteminc', '-analyze', '-analyzer-constraints=range', + '-analyzer-config', 'apply-fixits=true'] + + clang_analyzer_extra_args + ['-verify', temp_file_name]) + + print('Running ' + str(args) + '...') + + try: + clang_analyzer_output = \ + subprocess.check_output(args, stderr=subprocess.STDOUT).decode() + except subprocess.CalledProcessError as e: + print('Clang Static Analyzer test failed:\n' + e.output.decode()) + raise + + print('----------------- Clang Static Analyzer output -----------------\n' + + clang_analyzer_output + + '\n--------------------------------------------------------------') + + try: + diff_output = subprocess.check_output( + ['diff', '-u', original_file_name, temp_file_name], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + diff_output = e.output + + print('----------------------------- Fixes ----------------------------\n' + + diff_output.decode() + + '\n--------------------------------------------------------------') + + try: + subprocess.check_output( + ['FileCheck', '-input-file=' + temp_file_name, input_file_name, + '-check-prefixes=CHECK-FIXES', '-strict-whitespace'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print('FileCheck failed:\n' + e.output.decode()) + raise + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('input_file_name') + parser.add_argument('temp_file_name') + + args, extra_args = parser.parse_known_args() + run_test_once(args, extra_args) + + +if __name__ == '__main__': + main() diff --git a/clang/test/Analysis/cxx-inherited-ctor-init-expr.cpp b/clang/test/Analysis/cxx-inherited-ctor-init-expr.cpp new file mode 100644 index 0000000000000..8370ebfbde09b --- /dev/null +++ b/clang/test/Analysis/cxx-inherited-ctor-init-expr.cpp @@ -0,0 +1,75 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_eval(bool); + +namespace basic_tests { +struct A { + int x; + A(int x): x(x) {} +}; + +struct B : A { + using A::A; +}; + +struct C : B { + using B::B; +}; + +void test_B() { + B b(1); + clang_analyzer_eval(b.x == 1); // expected-warning{{TRUE}} +} + +void test_C() { + C c(2); + clang_analyzer_eval(c.x == 2); // expected-warning{{TRUE}} +} +} // namespace basic_tests + +namespace arguments_with_constructors { +struct S { + int x, y; + S(int x, int y): x(x), y(y) {} + ~S() {} +}; + +struct A { + S s; + int z; + A(S s, int z) : s(s), z(z) {} +}; + +struct B : A { + using A::A; +}; + +void test_B() { + B b(S(1, 2), 3); + // FIXME: There should be no execution path on which this is false. + clang_analyzer_eval(b.s.x == 1); // expected-warning{{TRUE}} + // expected-warning@-1{{FALSE}} + + // FIXME: There should be no execution path on which this is false. + clang_analyzer_eval(b.s.y == 2); // expected-warning{{TRUE}} + // expected-warning@-1{{FALSE}} + + clang_analyzer_eval(b.z == 3); // expected-warning{{TRUE}} +} +} // namespace arguments_with_constructors + +namespace inherited_constructor_crash { +class a { +public: + a(int); +}; +struct b : a { + using a::a; // Ihnerited ctor. +}; +void c() { + int d; + // This construct expr utilizes the inherited ctor. + // Note that d must be uninitialized to cause the crash. + (b(d)); // expected-warning{{1st function call argument is an uninitialized value}} +} +} // namespace inherited_constructor_crash diff --git a/clang/test/Analysis/cxx-inherited-ctor-is-skipped-as-top-level.cpp b/clang/test/Analysis/cxx-inherited-ctor-is-skipped-as-top-level.cpp new file mode 100644 index 0000000000000..be7982e641148 --- /dev/null +++ b/clang/test/Analysis/cxx-inherited-ctor-is-skipped-as-top-level.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-display-progress %s 2>&1 | FileCheck %s + +// Test that inheriting constructors are not analyzed as top-level functions. + +// CHECK: ANALYZE (Path, Inline_Regular): {{.*}} c() +// CHECK: ANALYZE (Path, Inline_Regular): {{.*}} a::a(int) +// CHECK-NOT: ANALYZE (Path, Inline_Regular): {{.*}} b::a(int) + +class a { +public: + a(int) {} +}; +struct b : a { + using a::a; // Ihnerited ctor. +}; +void c() { + int d; + (b(d)); + (a(d)); +} diff --git a/clang/test/Analysis/dead-stores.c b/clang/test/Analysis/dead-stores.c index cbfdabdcec632..a17e1692496da 100644 --- a/clang/test/Analysis/dead-stores.c +++ b/clang/test/Analysis/dead-stores.c @@ -1,16 +1,16 @@ -// RUN: %clang_analyze_cc1 -Wunused-variable -fblocks -Wno-unreachable-code \ +// RUN: %check_analyzer_fixit %s %t \ +// RUN: -Wunused-variable -fblocks -Wno-unreachable-code \ // RUN: -analyzer-checker=core,deadcode.DeadStores \ // RUN: -analyzer-config deadcode.DeadStores:ShowFixIts=true \ -// RUN: -analyzer-config fixits-as-remarks=true \ // RUN: -analyzer-config \ // RUN: deadcode.DeadStores:WarnForDeadNestedAssignments=false \ -// RUN: -verify=non-nested %s +// RUN: -verify=non-nested -// RUN: %clang_analyze_cc1 -Wunused-variable -fblocks -Wno-unreachable-code \ +// RUN: %check_analyzer_fixit %s %t \ +// RUN: -Wunused-variable -fblocks -Wno-unreachable-code \ // RUN: -analyzer-checker=core,deadcode.DeadStores \ // RUN: -analyzer-config deadcode.DeadStores:ShowFixIts=true \ -// RUN: -analyzer-config fixits-as-remarks=true \ -// RUN: -verify=non-nested,nested %s +// RUN: -verify=non-nested,nested void f1() { int k, y; // non-nested-warning {{unused variable 'k'}} @@ -18,14 +18,17 @@ void f1() { int abc = 1; long idx = abc + 3 * 5; // non-nested-warning {{never read}} // non-nested-warning@-1 {{unused variable 'idx'}} - // non-nested-remark@-2 {{11-24: ''}} + // CHECK-FIXES: int abc = 1; + // CHECK-FIXES-NEXT: long idx; } void f2(void *b) { char *c = (char *)b; // no-warning char *d = b + 1; // non-nested-warning {{never read}} // non-nested-warning@-1 {{unused variable 'd'}} - // non-nested-remark@-2 {{10-17: ''}} + // CHECK-FIXES: char *c = (char *)b; + // CHECK-FIXES-NEXT: char *d; + printf("%s", c); // non-nested-warning@-1 {{implicitly declaring library function 'printf' with type 'int (const char *, ...)'}} // non-nested-note@-2 {{include the header or explicitly provide a declaration for 'printf'}} @@ -51,7 +54,8 @@ void f5() { int x = 4; // no-warning int *p = &x; // non-nested-warning {{never read}} // non-nested-warning@-1 {{unused variable 'p'}} - // non-nested-remark@-2 {{9-13: ''}} + // CHECK-FIXES: int x = 4; + // CHECK-FIXES-NEXT: int *p; } int f6() { @@ -415,7 +419,8 @@ void f23_pos(int argc, char **argv) { int shouldLog = (argc > 1); // non-nested-warning@-1 {{Value stored to 'shouldLog' during its initialization is never read}} // non-nested-warning@-2 {{unused variable 'shouldLog'}} - // non-nested-remark@-3 {{16-28: ''}} + // CHECK-FIXES: void f23_pos(int argc, char **argv) { + // CHECK-FIXES-NEXT: int shouldLog; ^{ f23_aux("I did too use it!\n"); }(); @@ -428,7 +433,11 @@ void f24_A(int y) { int z = x + y; // non-nested-warning@-1 {{Value stored to 'z' during its initialization is never read}} // non-nested-warning@-2 {{unused variable 'z'}} - // non-nested-remark@-3 {{10-17: ''}} + // CHECK-FIXES: void f24_A(int y) { + // CHECK-FIXES-NEXT: // + // CHECK-FIXES-NEXT: int x = (y > 2); + // CHECK-FIXES-NEXT: ^{ + // CHECK-FIXES-NEXT: int z; }(); } diff --git a/clang/test/Analysis/debug-exprinspection-istainted.c b/clang/test/Analysis/debug-exprinspection-istainted.c new file mode 100644 index 0000000000000..e2f6821e4aa9a --- /dev/null +++ b/clang/test/Analysis/debug-exprinspection-istainted.c @@ -0,0 +1,27 @@ +// RUN: %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=debug.ExprInspection \ +// RUN: -analyzer-checker=alpha.security.taint + +int scanf(const char *restrict format, ...); +void clang_analyzer_isTainted(char); +void clang_analyzer_isTainted_any_suffix(char); +void clang_analyzer_isTainted_many_arguments(char, int, int); + +void foo() { + char buf[32] = ""; + clang_analyzer_isTainted(buf[0]); // expected-warning {{NO}} + clang_analyzer_isTainted_any_suffix(buf[0]); // expected-warning {{NO}} + scanf("%s", buf); + clang_analyzer_isTainted(buf[0]); // expected-warning {{YES}} + clang_analyzer_isTainted_any_suffix(buf[0]); // expected-warning {{YES}} + + int tainted_value = buf[0]; // no-warning +} + +void exactly_one_argument_required() { + char buf[32] = ""; + scanf("%s", buf); + clang_analyzer_isTainted_many_arguments(buf[0], 42, 42); + // expected-warning@-1 {{clang_analyzer_isTainted() requires exactly one argument}} +} diff --git a/clang/test/Analysis/html_diagnostics/td-hotfix.c b/clang/test/Analysis/html_diagnostics/td-hotfix.c new file mode 100644 index 0000000000000..8595642ad0f58 --- /dev/null +++ b/clang/test/Analysis/html_diagnostics/td-hotfix.c @@ -0,0 +1,31 @@ +// RUN: rm -fR %t +// RUN: mkdir %t +// RUN: %clang_analyze_cc1 -analyzer-checker=core \ +// RUN: -analyzer-output=html -o %t -verify %s +// RUN: cat %t/report-*.html | FileCheck %s + +void bar(int); + +void foo() { + int a; + bar(a); // expected-warning{{1st function call argument is an uninitialized value}} +} + +// CHECK-LABEL:
+// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-NOT: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME:
+// CHECK-SAME:
2
+// CHECK-SAME:
+// CHECK-SAME:
+// CHECK-SAME: +// CHECK-SAME:
+// CHECK-SAME:
+// CHECK-SAME: 1st function call argument is an uninitialized value +// CHECK-SAME:
+// CHECK-SAME:
diff --git a/clang/test/Analysis/html_diagnostics/variable-popups-macro.c b/clang/test/Analysis/html_diagnostics/variable-popups-macro.c new file mode 100644 index 0000000000000..83bda14d4f2f7 --- /dev/null +++ b/clang/test/Analysis/html_diagnostics/variable-popups-macro.c @@ -0,0 +1,28 @@ +// RUN: rm -fR %t +// RUN: mkdir %t +// RUN: %clang_analyze_cc1 -analyzer-checker=core \ +// RUN: -analyzer-output=html -o %t -verify %s +// RUN: cat %t/report-*.html | FileCheck %s + +void bar(int); + +#define MACRO if (b) + +void foo2() { + int a; + int b = 1; + MACRO + bar(a); // expected-warning{{1st function call argument is an uninitialized value}} +} + +// For now we don't emit popups inside macros due to UI limitations. +// Once we do, we should test it thoroughly. + +// CHECK-LABEL:
+// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME:
+// CHECK-SAME:
2.1
+// CHECK-SAME:
'i' is 0
+// CHECK-SAME:
4.1
+// CHECK-SAME:
'i' is 1
+// CHECK-SAME: diff --git a/clang/test/Analysis/html_diagnostics/variable-popups-simple.c b/clang/test/Analysis/html_diagnostics/variable-popups-simple.c new file mode 100644 index 0000000000000..cb2f3bf3226d5 --- /dev/null +++ b/clang/test/Analysis/html_diagnostics/variable-popups-simple.c @@ -0,0 +1,23 @@ +// RUN: rm -fR %t +// RUN: mkdir %t +// RUN: %clang_analyze_cc1 -analyzer-checker=core \ +// RUN: -analyzer-output=html -o %t -verify %s +// RUN: cat %t/report-*.html | FileCheck %s + +void bar(int); + +void foo2() { + int a; + int b = 1; + if (b) + bar(a); // expected-warning{{1st function call argument is an uninitialized value}} +} + +// CHECK: b +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME: +// CHECK-SAME:
+// CHECK-SAME:
1.1
+// CHECK-SAME:
'b' is 1
+// CHECK-SAME:
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/nonnull.cpp b/clang/test/Analysis/nonnull.cpp new file mode 100644 index 0000000000000..a3a0568686ae2 --- /dev/null +++ b/clang/test/Analysis/nonnull.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core -verify %s + +void nonnull [[gnu::nonnull]] (int *q); + +void f1(int *p) { + if (p) + return; + nonnull(p); //expected-warning{{nonnull}} +} + +void f2(int *p) { + if (p) + return; + auto lambda = [](int *q) __attribute__((nonnull)){}; + lambda(p); //expected-warning{{nonnull}} +} + +template +void variadicNonnull(ARGS... args) __attribute__((nonnull)); + +void f3(int a, float b, int *p) { + if (p) + return; + variadicNonnull(a, b, p); //expected-warning{{nonnull}} +} + +int globalVar = 15; +void moreParamsThanArgs [[gnu::nonnull(2, 4)]] (int a, int *p, int b = 42, int *q = &globalVar); + +void f4(int a, int *p) { + if (p) + return; + moreParamsThanArgs(a, p); //expected-warning{{nonnull}} +} 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/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp index 42675fc70e785..d88349dcd807e 100644 --- a/clang/test/Analysis/osobject-retain-release.cpp +++ b/clang/test/Analysis/osobject-retain-release.cpp @@ -53,6 +53,9 @@ struct MyArray : public OSArray { OSObject *generateObject(OSObject *input) override; }; +// These are never refcounted. +struct OSSymbol : OSObject {}; + struct OtherStruct { static void doNothingToArray(OSArray *array); OtherStruct(OSArray *arr); @@ -739,3 +742,25 @@ WeirdResult testOutParamWithWeirdResult() { return outParamWithWeirdResult(&obj); // no-warning } } // namespace weird_result + +namespace inherited_constructor_crash { +struct a { + a(int); +}; +struct b : a { + // This is an "inherited constructor". + using a::a; +}; +void test() { + // RetainCountChecker used to crash when looking for a summary + // for the inherited constructor invocation. + b(0); +} +} // namespace inherited_constructor_crash + +namespace ossymbol_suppression { +OSSymbol *createSymbol(); +void test() { + OSSymbol *sym = createSymbol(); // no-warning +} +} // namespace ossymbol_suppression diff --git a/clang/test/Analysis/taint-generic.c b/clang/test/Analysis/taint-generic.c index 60a27c5ce4641..e9d1b5001248e 100644 --- a/clang/test/Analysis/taint-generic.c +++ b/clang/test/Analysis/taint-generic.c @@ -390,3 +390,7 @@ void testConfigurationSinks() { mySink(1, 2, x); // expected-warning@-1 {{Untrusted data is passed to a user-defined sink}} } + +void testUnknownFunction(void (*foo)(void)) { + foo(); // no-crash +} diff --git a/clang/test/Analysis/virtualcall-fixits.cpp b/clang/test/Analysis/virtualcall-fixits.cpp index ea149d52fcd21..b68fcbfea93d5 100644 --- a/clang/test/Analysis/virtualcall-fixits.cpp +++ b/clang/test/Analysis/virtualcall-fixits.cpp @@ -1,10 +1,11 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.VirtualCall \ // RUN: -analyzer-config optin.cplusplus.VirtualCall:ShowFixIts=true \ // RUN: %s 2>&1 | FileCheck -check-prefix=TEXT %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.VirtualCall \ + +// RUN: %check_analyzer_fixit %s %t \ +// RUN: -analyzer-checker=core,optin.cplusplus.VirtualCall \ // RUN: -analyzer-config optin.cplusplus.VirtualCall:ShowFixIts=true \ -// RUN: -analyzer-config fixits-as-remarks=true \ -// RUN: -analyzer-output=plist -o %t.plist -verify %s +// RUN: -analyzer-output=plist -o %t.plist // RUN: cat %t.plist | FileCheck -check-prefix=PLIST %s struct S { @@ -12,7 +13,9 @@ struct S { S() { foo(); // expected-warning@-1{{Call to virtual method 'S::foo' during construction bypasses virtual dispatch}} - // expected-remark@-2{{5-5: 'S::'}} + // CHECK-FIXES: S() { + // CHECK-FIXES-NEXT: S::foo(); + // CHECK-FIXES-NEXT: } } ~S(); }; @@ -30,12 +33,12 @@ struct S { // PLIST-NEXT: remove_range // PLIST-NEXT: // PLIST-NEXT: -// PLIST-NEXT: line13 +// PLIST-NEXT: line14 // PLIST-NEXT: col5 // PLIST-NEXT: file0 // PLIST-NEXT: // PLIST-NEXT: -// PLIST-NEXT: line13 +// PLIST-NEXT: line14 // PLIST-NEXT: col4 // PLIST-NEXT: file0 // PLIST-NEXT: diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index 6f6121f06e56a..8731dc857f882 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -46,6 +46,7 @@ list(APPEND CLANG_TEST_DEPS clang-format clang-tblgen clang-offload-bundler + clang-refactor-test clang-import-test clang-rename clang-refactor @@ -54,7 +55,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/CXX/expr/p10-0x.cpp b/clang/test/CXX/expr/p10-0x.cpp index a42986c85fef2..c1b384ad7a51a 100644 --- a/clang/test/CXX/expr/p10-0x.cpp +++ b/clang/test/CXX/expr/p10-0x.cpp @@ -44,3 +44,12 @@ void f2(volatile int *x) { refcall(); 1 ? refcall() : *x; } + +// CHECK: define void @_Z2f3v() +// CHECK-NOT: load +// CHECK-NOT: memcpy + +void f3(void) { + volatile char a[10]; + a; +} diff --git a/clang/test/Frontend/Inputs/resource_dir_with_cfi_blacklist/share/cfi_blacklist.txt b/clang/test/ClangScanDeps/Inputs/frameworks/Inferred.framework/Frameworks/Sub.framework/Headers/Sub.h similarity index 100% rename from clang/test/Frontend/Inputs/resource_dir_with_cfi_blacklist/share/cfi_blacklist.txt rename to clang/test/ClangScanDeps/Inputs/frameworks/Inferred.framework/Frameworks/Sub.framework/Headers/Sub.h diff --git a/clang/test/ClangScanDeps/Inputs/frameworks/Inferred.framework/Headers/Inferred.h b/clang/test/ClangScanDeps/Inputs/frameworks/Inferred.framework/Headers/Inferred.h new file mode 100644 index 0000000000000..1855e4fad5f8d --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/frameworks/Inferred.framework/Headers/Inferred.h @@ -0,0 +1 @@ +typedef int inferred; diff --git a/clang/test/ClangScanDeps/Inputs/frameworks/module.modulemap b/clang/test/ClangScanDeps/Inputs/frameworks/module.modulemap new file mode 100644 index 0000000000000..e3bad873c7e7b --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/frameworks/module.modulemap @@ -0,0 +1 @@ +framework module * {} diff --git a/clang/test/ClangScanDeps/Inputs/modules_cdb.json b/clang/test/ClangScanDeps/Inputs/modules_cdb.json index da5f9bc6a522f..a0c5123cd2124 100644 --- a/clang/test/ClangScanDeps/Inputs/modules_cdb.json +++ b/clang/test/ClangScanDeps/Inputs/modules_cdb.json @@ -1,13 +1,22 @@ [ { "directory": "DIR", - "command": "clang -E -fsyntax-only DIR/modules_cdb_input2.cpp -IInputs -D INCLUDE_HEADER2 -MD -MF DIR/modules_cdb2.d -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps", + "command": "clang -E DIR/modules_cdb_input2.cpp -IInputs -D INCLUDE_HEADER2 -MD -MF DIR/modules_cdb2.d -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps", "file": "DIR/modules_cdb_input2.cpp" }, { "directory": "DIR", "command": "clang -E DIR/modules_cdb_input.cpp -IInputs -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps", "file": "DIR/modules_cdb_input.cpp" +}, +{ + "directory": "DIR", + "command": "clang -E DIR/modules_cdb_input.cpp -IInputs -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -o a.o", + "file": "DIR/modules_cdb_input.cpp" +}, +{ + "directory": "DIR", + "command": "clang -E DIR/modules_cdb_input.cpp -IInputs -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -o b.o", + "file": "DIR/modules_cdb_input.cpp" } ] - diff --git a/clang/test/ClangScanDeps/Inputs/modules_inferred_cdb.json b/clang/test/ClangScanDeps/Inputs/modules_inferred_cdb.json new file mode 100644 index 0000000000000..c6123e30994ee --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/modules_inferred_cdb.json @@ -0,0 +1,7 @@ +[ +{ + "directory": "DIR", + "command": "clang -E DIR/modules_cdb_input.cpp -FFRAMEWORKS -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps", + "file": "DIR/modules_cdb_input.cpp" +} +] diff --git a/clang/test/ClangScanDeps/module-deps-to-rsp.py b/clang/test/ClangScanDeps/module-deps-to-rsp.py new file mode 100755 index 0000000000000..45a6bcc90af1f --- /dev/null +++ b/clang/test/ClangScanDeps/module-deps-to-rsp.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python + +import argparse +import json +import sys + +class ModuleNotFoundError(Exception): + def __init__(self, module_name): + self.module_name = module_name + +class FullDeps: + def __init__(self): + self.modules = dict() + self.translation_units = str() + +def getModulePathArgs(modules, full_deps): + cmd = [] + for md in modules: + m = full_deps.modules[md['module-name'] + '-' + md['context-hash']] + cmd += [u'-fmodule-map-file=' + m['clang-modulemap-file']] + cmd += [u'-fmodule-file=' + md['module-name'] + '-' + md['context-hash'] + '.pcm'] + return cmd + +def getCommandLineForModule(module_name, full_deps): + for m in full_deps.modules.values(): + if m['name'] == module_name: + module = m + break + else: + raise ModuleNotFoundError(module_name) + + cmd = m['command-line'] + cmd += getModulePathArgs(m['clang-module-deps'], full_deps) + cmd += [u'-o', m['name'] + '-' + m['context-hash'] + '.pcm'] + cmd += [m['clang-modulemap-file']] + + return cmd + +def getCommandLineForTU(tu, full_deps): + cmd = tu['command-line'] + cmd = [a for a in cmd if not (a.startswith('-fmodule-map-file=') or a.startswith('-fmodule-file='))] + cmd += getModulePathArgs(tu['clang-module-deps'], full_deps) + return cmd + +def parseFullDeps(json): + ret = FullDeps() + for m in json['modules']: + ret.modules[m['name'] + '-' + m['context-hash']] = m + ret.translation_units = json['translation-units'] + return ret + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("full_deps_file", help="Path to the full dependencies json file", + type=str) + action = parser.add_mutually_exclusive_group(required=True) + action.add_argument("--module-name", help="The name of the module to get arguments for", + type=str) + action.add_argument("--tu-index", help="The index of the translation unit to get arguments for", + type=int) + args = parser.parse_args() + + full_deps = parseFullDeps(json.load(open(args.full_deps_file, 'r'))) + + try: + if args.module_name: + print(" ".join(getCommandLineForModule(args.module_name, full_deps))) + + elif args.tu_index != None: + print(" ".join(getCommandLineForTU(full_deps.translation_units[args.tu_index], full_deps))) + except: + print("Unexpected error:", sys.exc_info()[0]) + raise + +if __name__ == '__main__': + main() diff --git a/clang/test/ClangScanDeps/modules-full.cpp b/clang/test/ClangScanDeps/modules-full.cpp index 693dffeecbf76..599001743dcdc 100644 --- a/clang/test/ClangScanDeps/modules-full.cpp +++ b/clang/test/ClangScanDeps/modules-full.cpp @@ -1,6 +1,5 @@ // RUN: rm -rf %t.dir // RUN: rm -rf %t.cdb -// RUN: rm -rf %t.module-cache // RUN: mkdir -p %t.dir // RUN: cp %s %t.dir/modules_cdb_input.cpp // RUN: cp %s %t.dir/modules_cdb_input2.cpp @@ -11,67 +10,152 @@ // RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb.json > %t.cdb // // RUN: echo %t.dir > %t.result -// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 \ +// RUN: clang-scan-deps -compilation-database %t.cdb -j 4 -full-command-line \ // RUN: -mode preprocess-minimized-sources -format experimental-full >> %t.result -// RUN: cat %t.result | FileCheck --check-prefixes=CHECK %s +// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck --check-prefixes=CHECK %s // FIXME: Backslash issues. // XFAIL: system-windows #include "header.h" -// CHECK: [[PREFIX:(.*[/\\])+[a-zA-Z0-9.-]+]] +// CHECK: [[PREFIX:.*]] +// CHECK-NEXT: { +// CHECK-NEXT: "modules": [ // CHECK-NEXT: { -// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH:[A-Z0-9]+]]", -// CHECK-NEXT: "clang-module-deps": [ -// CHECK-NEXT: "header1" -// CHECK-NEXT: ], -// CHECK-NEXT: "clang-modules": [ -// CHECK-NEXT: { -// CHECK-NEXT: "clang-module-deps": [ -// CHECK-NEXT: "header2" -// CHECK-NEXT: ], -// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap", -// CHECK-NEXT: "file-deps": [ -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h", -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap" -// CHECK-NEXT: ], -// CHECK-NEXT: "name": "header1" -// CHECK-NEXT: }, -// CHECK-NEXT: { -// CHECK-NEXT: "clang-module-deps": [], -// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap", -// CHECK-NEXT: "file-deps": [ -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header2.h", -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap" -// CHECK-NEXT: ], -// CHECK-NEXT: "name": "header2" -// CHECK-NEXT: } -// CHECK-NEXT: ], -// CHECK-NEXT: "file-deps": [ -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}modules_cdb_input2.cpp" -// CHECK-NEXT: ], -// CHECK-NEXT: "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input2.cpp" -// CHECK-NEXT:}, -// CHECK-NEXT:{ -// CHECK-NOT: "clang-context-hash": "[[CONTEXT_HASH]]", -// CHECK-NEXT: "clang-context-hash": "{{[A-Z0-9]+}}", -// CHECK-NEXT: "clang-module-deps": [ -// CHECK-NEXT: "header1" -// CHECK-NEXT: ], -// CHECK-NEXT: "clang-modules": [ -// CHECK-NEXT: { -// CHECK-NEXT: "clang-module-deps": [], -// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap", -// CHECK-NEXT: "file-deps": [ -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h", -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap" -// CHECK-NEXT: ], -// CHECK-NEXT: "name": "header1" -// CHECK-NEXT: } -// CHECK-NEXT: ], -// CHECK-NEXT: "file-deps": [ -// CHECK-NEXT: "[[PREFIX]]{{[/\\]}}modules_cdb_input.cpp" -// CHECK-NEXT: ], -// CHECK-NEXT: "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input.cpp" -// CHECK-NEXT:}, +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1:[A-Z0-9]+]]", +// CHECK-NEXT: "module-name": "header2" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-remove-preceeding-explicit-module-build-incompatible-options", +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-emit-module", +// CHECK-NEXT: "-fmodule-name=header1", +// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H1]]/header2-{{[A-Z0-9]+}}.pcm", +// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/Inputs/header.h", +// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "header1" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-remove-preceeding-explicit-module-build-incompatible-options", +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-emit-module", +// CHECK-NEXT: "-fmodule-name=header1" +// CHECK-NEXT: ], +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H2:[A-Z0-9]+]]", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/Inputs/header.h", +// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "header1" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/Inputs/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-remove-preceeding-explicit-module-build-incompatible-options", +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-emit-module", +// CHECK-NEXT: "-fmodule-name=header2" +// CHECK-NEXT: ], +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/Inputs/header2.h", +// CHECK-NEXT: "[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "header2" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH_H2]]", +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H2]]", +// CHECK-NEXT: "module-name": "header1" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-fno-implicit-module-maps", +// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H2]]/header1-{{[A-Z0-9]+}}.pcm", +// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH_H2]]", +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H2]]", +// CHECK-NEXT: "module-name": "header1" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-fno-implicit-module-maps", +// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H2]]/header1-{{[A-Z0-9]+}}.pcm", +// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH_H2]]", +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H2]]", +// CHECK-NEXT: "module-name": "header1" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-fno-implicit-module-maps", +// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H2]]/header1-{{[A-Z0-9]+}}.pcm", +// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH_H1]]", +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]", +// CHECK-NEXT: "module-name": "header1" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-fno-implicit-module-maps", +// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H1]]/header2-{{[A-Z0-9]+}}.pcm", +// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap", +// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H1]]/header1-{{[A-Z0-9]+}}.pcm", +// CHECK-NEXT: "-fmodule-map-file=[[PREFIX]]/Inputs/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/modules_cdb_input2.cpp" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/modules_cdb_input2.cpp" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } diff --git a/clang/test/ClangScanDeps/modules-inferred-explicit-build.m b/clang/test/ClangScanDeps/modules-inferred-explicit-build.m new file mode 100644 index 0000000000000..18a0c3bd441c2 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-inferred-explicit-build.m @@ -0,0 +1,20 @@ +// RUN: rm -rf %t.dir +// RUN: rm -rf %t.cdb +// RUN: mkdir -p %t.dir +// RUN: cp %s %t.dir/modules_cdb_input.cpp +// RUN: sed -e "s|DIR|%/t.dir|g" -e "s|FRAMEWORKS|%/S/Inputs/frameworks|g" \ +// RUN: %S/Inputs/modules_inferred_cdb.json > %t.cdb +// +// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -full-command-line \ +// RUN: -mode preprocess-minimized-sources -format experimental-full > %t.db +// RUN: %S/module-deps-to-rsp.py %t.db --module-name=Inferred > %t.inferred.rsp +// RUN: %S/module-deps-to-rsp.py %t.db --tu-index=0 > %t.tu.rsp +// RUN: %clang_cc1 -x objective-c -E %t.dir/modules_cdb_input.cpp \ +// RUN: -F%S/Inputs/frameworks -fmodules -fimplicit-module-maps \ +// RUN: @%t.inferred.rsp +// RUN: %clang_cc1 -x objective-c -E %t.dir/modules_cdb_input.cpp \ +// RUN: -F%S/Inputs/frameworks -fmodules -fimplicit-module-maps @%t.tu.rsp + +#include + +inferred a = 0; diff --git a/clang/test/ClangScanDeps/modules-inferred.m b/clang/test/ClangScanDeps/modules-inferred.m new file mode 100644 index 0000000000000..cca2306b50aca --- /dev/null +++ b/clang/test/ClangScanDeps/modules-inferred.m @@ -0,0 +1,61 @@ +// RUN: rm -rf %t.dir +// RUN: rm -rf %t.cdb +// RUN: mkdir -p %t.dir +// RUN: cp %s %t.dir/modules_cdb_input.cpp +// RUN: sed -e "s|DIR|%/t.dir|g" -e "s|FRAMEWORKS|%S/Inputs/frameworks|g" \ +// RUN: %S/Inputs/modules_inferred_cdb.json > %t.cdb +// +// RUN: echo -%t.dir > %t.result +// RUN: echo -%S >> %t.result +// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -full-command-line \ +// RUN: -mode preprocess-minimized-sources -format experimental-full >> %t.result +// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck --check-prefixes=CHECK %s + +#include + +inferred a = 0; + +// CHECK: -[[PREFIX:.*]] +// CHECK-NEXT: -[[SOURCEDIR:.*]] +// CHECK-NEXT: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[SOURCEDIR]]/Inputs/frameworks/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-remove-preceeding-explicit-module-build-incompatible-options", +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-emit-module", +// CHECK-NEXT: "-fmodule-name=Inferred" +// CHECK-NEXT: ], +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1:[A-Z0-9]+]]", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/Inferred.framework/Frameworks/Sub.framework/Headers/Sub.h", +// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/Inferred.framework/Headers/Inferred.h", +// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "Inferred" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH_H1]]", +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]", +// CHECK-NEXT: "module-name": "Inferred" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-fno-implicit-module-maps", +// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H1]]/Inferred-{{[A-Z0-9]+}}.pcm", +// CHECK-NEXT: "-fmodule-map-file=[[SOURCEDIR]]/Inputs/frameworks/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/modules_cdb_input.cpp" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } diff --git a/clang/test/CodeGen/2006-05-19-SingleEltReturn.c b/clang/test/CodeGen/2006-05-19-SingleEltReturn.c index dfc23f84ab424..d3f9e4e00acde 100644 --- a/clang/test/CodeGen/2006-05-19-SingleEltReturn.c +++ b/clang/test/CodeGen/2006-05-19-SingleEltReturn.c @@ -24,7 +24,7 @@ struct Y bar() { // X86_32: define void @foo(%struct.Y* %P) -// X86_32: call void @bar(%struct.Y* sret %{{[^),]*}}) +// X86_32: call void @bar(%struct.Y* sret align 4 %{{[^),]*}}) -// X86_32: define void @bar(%struct.Y* noalias sret %{{[^,)]*}}) +// X86_32: define void @bar(%struct.Y* noalias sret align 4 %{{[^,)]*}}) // X86_32: ret void diff --git a/clang/test/CodeGen/aarch64-varargs.c b/clang/test/CodeGen/aarch64-varargs.c index c213f5b9375ba..27bb602e75de1 100644 --- a/clang/test/CodeGen/aarch64-varargs.c +++ b/clang/test/CodeGen/aarch64-varargs.c @@ -639,7 +639,7 @@ typedef struct __attribute__((aligned(32))) { __int128 val; } overaligned_int128_struct; overaligned_int128_struct overaligned_int128_struct_test() { -// CHECK-LABEL: define void @overaligned_int128_struct_test(%struct.overaligned_int128_struct* noalias sret %agg.result) +// CHECK-LABEL: define void @overaligned_int128_struct_test(%struct.overaligned_int128_struct* noalias sret align 32 %agg.result) return va_arg(the_list, overaligned_int128_struct); // CHECK: [[GR_OFFS:%[a-z_0-9]+]] = load i32, i32* getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 3) // CHECK: [[EARLY_ONSTACK:%[a-z_0-9]+]] = icmp sge i32 [[GR_OFFS]], 0 @@ -853,7 +853,7 @@ typedef struct { __int128 val __attribute__((aligned(32))); } overaligned_int128_struct_member; overaligned_int128_struct_member overaligned_int128_struct_member_test() { -// CHECK-LABEL: define void @overaligned_int128_struct_member_test(%struct.overaligned_int128_struct_member* noalias sret %agg.result) +// CHECK-LABEL: define void @overaligned_int128_struct_member_test(%struct.overaligned_int128_struct_member* noalias sret align 32 %agg.result) return va_arg(the_list, overaligned_int128_struct_member); // CHECK: [[GR_OFFS:%[a-z_0-9]+]] = load i32, i32* getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 3) // CHECK: [[EARLY_ONSTACK:%[a-z_0-9]+]] = icmp sge i32 [[GR_OFFS]], 0 diff --git a/clang/test/CodeGen/aggregate-assign-call.c b/clang/test/CodeGen/aggregate-assign-call.c index d00cb90c09407..9616e6d22562f 100644 --- a/clang/test/CodeGen/aggregate-assign-call.c +++ b/clang/test/CodeGen/aggregate-assign-call.c @@ -62,8 +62,8 @@ struct S baz(int i, volatile int *j) { // O1-NEWPM: %[[TMP3:.*]] = bitcast %struct.S* %[[TMP2_ALLOCA]] to i8* // O1-NEWPM: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* nonnull %[[P]]) // - // O1-LEGACY: call void @foo_int(%struct.S* sret %[[TMP1_ALLOCA]], - // O1-NEWPM: call void @foo_int(%struct.S* nonnull sret %[[TMP1_ALLOCA]], + // O1-LEGACY: call void @foo_int(%struct.S* sret align 4 %[[TMP1_ALLOCA]], + // O1-NEWPM: call void @foo_int(%struct.S* nonnull sret align 4 %[[TMP1_ALLOCA]], // O1: call void @llvm.memcpy // O1-LEGACY: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP1_ALLOCA]] to i8* // O1-LEGACY: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* %[[P]]) @@ -71,8 +71,8 @@ struct S baz(int i, volatile int *j) { // O1-LEGACY: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP2_ALLOCA]] to i8* // O1-LEGACY: call void @llvm.lifetime.start.p0i8({{[^,]*}}, i8* %[[P]]) // O1-NEWPM: call void @llvm.lifetime.start.p0i8({{[^,]*}}, i8* nonnull %[[TMP3]]) - // O1-LEGACY: call void @foo_int(%struct.S* sret %[[TMP2_ALLOCA]], - // O1-NEWPM: call void @foo_int(%struct.S* nonnull sret %[[TMP2_ALLOCA]], + // O1-LEGACY: call void @foo_int(%struct.S* sret align 4 %[[TMP2_ALLOCA]], + // O1-NEWPM: call void @foo_int(%struct.S* nonnull sret align 4 %[[TMP2_ALLOCA]], // O1: call void @llvm.memcpy // O1-LEGACY: %[[P:[^ ]+]] = bitcast %struct.S* %[[TMP2_ALLOCA]] to i8* // O1-LEGACY: call void @llvm.lifetime.end.p0i8({{[^,]*}}, i8* %[[P]]) diff --git a/clang/test/CodeGen/aligned-sret.c b/clang/test/CodeGen/aligned-sret.c new file mode 100644 index 0000000000000..c459fe730163c --- /dev/null +++ b/clang/test/CodeGen/aligned-sret.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macos %s -S -emit-llvm -o- | FileCheck %s + +typedef __attribute__((__ext_vector_type__(4),__aligned__(16))) double simd_double4; +typedef struct { simd_double4 columns[4]; } simd_double4x4; +typedef simd_double4x4 matrix_double4x4; + +// CHECK: define void @ident(%struct.simd_double4x4* noalias sret align 16 %agg.result +matrix_double4x4 ident(matrix_double4x4 x) { + return x; +} diff --git a/clang/test/CodeGen/arc/arguments.c b/clang/test/CodeGen/arc/arguments.c index fdc6037da730a..9c9b553b1be45 100644 --- a/clang/test/CodeGen/arc/arguments.c +++ b/clang/test/CodeGen/arc/arguments.c @@ -22,7 +22,7 @@ void cf1(cs1 i) {} typedef struct { int cc; } s2; -// CHECK: define void @f2(%struct.s2* noalias sret %agg.result) +// CHECK: define void @f2(%struct.s2* noalias sret align 4 %agg.result) s2 f2() { s2 foo; return foo; @@ -32,7 +32,7 @@ typedef struct { int cc; int dd; } s3; -// CHECK: define void @f3(%struct.s3* noalias sret %agg.result) +// CHECK: define void @f3(%struct.s3* noalias sret align 4 %agg.result) s3 f3() { s3 foo; return foo; @@ -128,8 +128,8 @@ void st3(s16 a, s16 b, s16 c) {} // 1 sret + 1 i32 + 2*(i32 coerce) + 4*(i32 coerce) + 1 byval s16 st4(int x, s8 a, s16 b, s16 c) { return b; } -// CHECK: define void @st4(%struct.s16* noalias sret %agg.result, i32 inreg %x, i32 inreg %a.coerce0, i32 inreg %a.coerce1, i32 inreg %b.coerce0, i32 inreg %b.coerce1, i32 inreg %b.coerce2, i32 inreg %b.coerce3, { i32, i32, i32, i32 } %c.coerce) +// CHECK: define void @st4(%struct.s16* noalias sret align 4 %agg.result, i32 inreg %x, i32 inreg %a.coerce0, i32 inreg %a.coerce1, i32 inreg %b.coerce0, i32 inreg %b.coerce1, i32 inreg %b.coerce2, i32 inreg %b.coerce3, { i32, i32, i32, i32 } %c.coerce) // 1 sret + 2*(i32 coerce) + 4*(i32 coerce) + 4*(i32 coerce) s16 st5(s8 a, s16 b, s16 c) { return b; } -// CHECK: define void @st5(%struct.s16* noalias sret %agg.result, i32 inreg %a.coerce0, i32 inreg %a.coerce1, i32 inreg %b.coerce0, i32 inreg %b.coerce1, i32 inreg %b.coerce2, i32 inreg %b.coerce3, { i32, i32, i32, i32 } %c.coerce) +// CHECK: define void @st5(%struct.s16* noalias sret align 4 %agg.result, i32 inreg %a.coerce0, i32 inreg %a.coerce1, i32 inreg %b.coerce0, i32 inreg %b.coerce1, i32 inreg %b.coerce2, i32 inreg %b.coerce3, { i32, i32, i32, i32 } %c.coerce) diff --git a/clang/test/CodeGen/arm-aapcs-vfp.c b/clang/test/CodeGen/arm-aapcs-vfp.c index 69581fcab2479..486ed6ab94fd4 100644 --- a/clang/test/CodeGen/arm-aapcs-vfp.c +++ b/clang/test/CodeGen/arm-aapcs-vfp.c @@ -125,7 +125,7 @@ void test_vfp_stack_gpr_split_1(double a, double b, double c, double d, double e // CHECK: define arm_aapcs_vfpcc void @test_vfp_stack_gpr_split_2(double %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, double %i, i32 %j, [2 x i64] %k.coerce) void test_vfp_stack_gpr_split_2(double a, double b, double c, double d, double e, double f, double g, double h, double i, int j, struct_long_long_int k) {} -// CHECK: define arm_aapcs_vfpcc void @test_vfp_stack_gpr_split_3(%struct.struct_long_long_int* noalias sret %agg.result, double %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, double %i, [2 x i64] %k.coerce) +// CHECK: define arm_aapcs_vfpcc void @test_vfp_stack_gpr_split_3(%struct.struct_long_long_int* noalias sret align 8 %agg.result, double %a, double %b, double %c, double %d, double %e, double %f, double %g, double %h, double %i, [2 x i64] %k.coerce) struct_long_long_int test_vfp_stack_gpr_split_3(double a, double b, double c, double d, double e, double f, double g, double h, double i, struct_long_long_int k) {} typedef struct { int a; int b:4; int c; } struct_int_bitfield_int; diff --git a/clang/test/CodeGen/arm-homogenous.c b/clang/test/CodeGen/arm-homogenous.c index 42a9bc1c16435..d321fc974c52e 100644 --- a/clang/test/CodeGen/arm-homogenous.c +++ b/clang/test/CodeGen/arm-homogenous.c @@ -27,7 +27,7 @@ void test_union_with_first_floats(void) { void test_return_union_with_first_floats(void) { g_u_f = returns_union_with_first_floats(); } -// CHECK: declare arm_aapcs_vfpcc void @returns_union_with_first_floats(%union.union_with_first_floats* sret) +// CHECK: declare arm_aapcs_vfpcc void @returns_union_with_first_floats(%union.union_with_first_floats* sret align 4) /* This is not a homogenous aggregate - fundamental types are different */ typedef union { @@ -47,7 +47,7 @@ void test_union_with_non_first_floats(void) { void test_return_union_with_non_first_floats(void) { g_u_nf_f = returns_union_with_non_first_floats(); } -// CHECK: declare arm_aapcs_vfpcc void @returns_union_with_non_first_floats(%union.union_with_non_first_floats* sret) +// CHECK: declare arm_aapcs_vfpcc void @returns_union_with_non_first_floats(%union.union_with_non_first_floats* sret align 4) /* This is not a homogenous aggregate - fundamental types are different */ typedef struct { @@ -67,7 +67,7 @@ void test_struct_with_union_with_first_floats(void) { void test_return_struct_with_union_with_first_floats(void) { g_s_f = returns_struct_with_union_with_first_floats(); } -// CHECK: declare arm_aapcs_vfpcc void @returns_struct_with_union_with_first_floats(%struct.struct_with_union_with_first_floats* sret) +// CHECK: declare arm_aapcs_vfpcc void @returns_struct_with_union_with_first_floats(%struct.struct_with_union_with_first_floats* sret align 4) /* This is not a homogenous aggregate - fundamental types are different */ typedef struct { @@ -87,7 +87,7 @@ void test_struct_with_union_with_non_first_floats(void) { void test_return_struct_with_union_with_non_first_floats(void) { g_s_nf_f = returns_struct_with_union_with_non_first_floats(); } -// CHECK: declare arm_aapcs_vfpcc void @returns_struct_with_union_with_non_first_floats(%struct.struct_with_union_with_non_first_floats* sret) +// CHECK: declare arm_aapcs_vfpcc void @returns_struct_with_union_with_non_first_floats(%struct.struct_with_union_with_non_first_floats* sret align 4) /* Plain array is not a homogenous aggregate */ extern void takes_array_of_floats(float a[4]); diff --git a/clang/test/CodeGen/arm-neon-vld.c b/clang/test/CodeGen/arm-neon-vld.c index 2c7af92f4796a..8d3d61c250a92 100644 --- a/clang/test/CodeGen/arm-neon-vld.c +++ b/clang/test/CodeGen/arm-neon-vld.c @@ -9,7 +9,7 @@ // CHECK-LABEL: @test_vld1_f16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float16x4x2_t, align 8 -// CHECK-A32: %struct.float16x4x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float16x4x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float16x4x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.float16x4x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast half* %a to i8* @@ -29,7 +29,7 @@ float16x4x2_t test_vld1_f16_x2(float16_t const *a) { // CHECK-LABEL: @test_vld1_f16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float16x4x3_t, align 8 -// CHECK-A32: %struct.float16x4x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float16x4x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float16x4x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.float16x4x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast half* %a to i8* @@ -49,7 +49,7 @@ float16x4x3_t test_vld1_f16_x3(float16_t const *a) { // CHECK-LABEL: @test_vld1_f16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float16x4x4_t, align 8 -// CHECK-A32: %struct.float16x4x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float16x4x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float16x4x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.float16x4x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast half* %a to i8* @@ -69,7 +69,7 @@ float16x4x4_t test_vld1_f16_x4(float16_t const *a) { // CHECK-LABEL: @test_vld1_f32_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float32x2x2_t, align 8 -// CHECK-A32: %struct.float32x2x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float32x2x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float32x2x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x2x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast float* %a to i8* @@ -89,7 +89,7 @@ float32x2x2_t test_vld1_f32_x2(float32_t const *a) { // CHECK-LABEL: @test_vld1_f32_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float32x2x3_t, align 8 -// CHECK-A32: %struct.float32x2x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float32x2x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float32x2x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x2x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast float* %a to i8* @@ -108,7 +108,7 @@ float32x2x3_t test_vld1_f32_x3(float32_t const *a) { // CHECK-LABEL: @test_vld1_f32_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float32x2x4_t, align 8 -// CHECK-A32: %struct.float32x2x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float32x2x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float32x2x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x2x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast float* %a to i8* @@ -128,7 +128,7 @@ float32x2x4_t test_vld1_f32_x4(float32_t const *a) { // CHECK-LABEL: @test_vld1_p16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly16x4x2_t, align 8 -// CHECK-A32: %struct.poly16x4x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly16x4x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly16x4x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x4x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -148,7 +148,7 @@ poly16x4x2_t test_vld1_p16_x2(poly16_t const *a) { // CHECK-LABEL: @test_vld1_p16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly16x4x3_t, align 8 -// CHECK-A32: %struct.poly16x4x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly16x4x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly16x4x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x4x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -168,7 +168,7 @@ poly16x4x3_t test_vld1_p16_x3(poly16_t const *a) { // CHECK-LABEL: @test_vld1_p16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly16x4x4_t, align 8 -// CHECK-A32: %struct.poly16x4x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly16x4x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly16x4x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x4x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -188,7 +188,7 @@ poly16x4x4_t test_vld1_p16_x4(poly16_t const *a) { // CHECK-LABEL: @test_vld1_p8_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly8x8x2_t, align 8 -// CHECK-A32: %struct.poly8x8x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly8x8x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly8x8x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x8x2_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x2|arm.neon.vld1x2}}.v8i8.p0i8(i8* %a) @@ -206,7 +206,7 @@ poly8x8x2_t test_vld1_p8_x2(poly8_t const *a) { // CHECK-LABEL: @test_vld1_p8_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly8x8x3_t, align 8 -// CHECK-A32: %struct.poly8x8x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly8x8x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly8x8x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x8x3_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x3|arm.neon.vld1x3}}.v8i8.p0i8(i8* %a) @@ -224,7 +224,7 @@ poly8x8x3_t test_vld1_p8_x3(poly8_t const *a) { // CHECK-LABEL: @test_vld1_p8_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly8x8x4_t, align 8 -// CHECK-A32: %struct.poly8x8x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly8x8x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly8x8x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x8x4_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8>, <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x4|arm.neon.vld1x4}}.v8i8.p0i8(i8* %a) @@ -242,7 +242,7 @@ poly8x8x4_t test_vld1_p8_x4(poly8_t const *a) { // CHECK-LABEL: @test_vld1_s16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int16x4x2_t, align 8 -// CHECK-A32: %struct.int16x4x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int16x4x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int16x4x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x4x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -262,7 +262,7 @@ int16x4x2_t test_vld1_s16_x2(int16_t const *a) { // CHECK-LABEL: @test_vld1_s16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int16x4x3_t, align 8 -// CHECK-A32: %struct.int16x4x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int16x4x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int16x4x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x4x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -282,7 +282,7 @@ int16x4x3_t test_vld1_s16_x3(int16_t const *a) { // CHECK-LABEL: @test_vld1_s16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int16x4x4_t, align 8 -// CHECK-A32: %struct.int16x4x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int16x4x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int16x4x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x4x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -302,7 +302,7 @@ int16x4x4_t test_vld1_s16_x4(int16_t const *a) { // CHECK-LABEL: @test_vld1_s32_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int32x2x2_t, align 8 -// CHECK-A32: %struct.int32x2x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int32x2x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int32x2x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x2x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -322,7 +322,7 @@ int32x2x2_t test_vld1_s32_x2(int32_t const *a) { // CHECK-LABEL: @test_vld1_s32_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int32x2x3_t, align 8 -// CHECK-A32: %struct.int32x2x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int32x2x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int32x2x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x2x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -342,7 +342,7 @@ int32x2x3_t test_vld1_s32_x3(int32_t const *a) { // CHECK-LABEL: @test_vld1_s32_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int32x2x4_t, align 8 -// CHECK-A32: %struct.int32x2x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int32x2x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int32x2x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x2x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -362,7 +362,7 @@ int32x2x4_t test_vld1_s32_x4(int32_t const *a) { // CHECK-LABEL: @test_vld1_s64_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int64x1x2_t, align 8 -// CHECK-A32: %struct.int64x1x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int64x1x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int64x1x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int64x1x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -382,7 +382,7 @@ int64x1x2_t test_vld1_s64_x2(int64_t const *a) { // CHECK-LABEL: @test_vld1_s64_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int64x1x3_t, align 8 -// CHECK-A32: %struct.int64x1x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int64x1x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int64x1x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int64x1x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -402,7 +402,7 @@ int64x1x3_t test_vld1_s64_x3(int64_t const *a) { // CHECK-LABEL: @test_vld1_s64_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int64x1x4_t, align 8 -// CHECK-A32: %struct.int64x1x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int64x1x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int64x1x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int64x1x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -422,7 +422,7 @@ int64x1x4_t test_vld1_s64_x4(int64_t const *a) { // CHECK-LABEL: @test_vld1_s8_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int8x8x2_t, align 8 -// CHECK-A32: %struct.int8x8x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int8x8x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int8x8x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x8x2_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x2|arm.neon.vld1x2}}.v8i8.p0i8(i8* %a) @@ -440,7 +440,7 @@ int8x8x2_t test_vld1_s8_x2(int8_t const *a) { // CHECK-LABEL: @test_vld1_s8_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int8x8x3_t, align 8 -// CHECK-A32: %struct.int8x8x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int8x8x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int8x8x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x8x3_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x3|arm.neon.vld1x3}}.v8i8.p0i8(i8* %a) @@ -458,7 +458,7 @@ int8x8x3_t test_vld1_s8_x3(int8_t const *a) { // CHECK-LABEL: @test_vld1_s8_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int8x8x4_t, align 8 -// CHECK-A32: %struct.int8x8x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int8x8x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int8x8x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x8x4_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8>, <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x4|arm.neon.vld1x4}}.v8i8.p0i8(i8* %a) @@ -476,7 +476,7 @@ int8x8x4_t test_vld1_s8_x4(int8_t const *a) { // CHECK-LABEL: @test_vld1_u16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint16x4x2_t, align 8 -// CHECK-A32: %struct.uint16x4x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint16x4x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint16x4x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x4x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -496,7 +496,7 @@ uint16x4x2_t test_vld1_u16_x2(uint16_t const *a) { // CHECK-LABEL: @test_vld1_u16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint16x4x3_t, align 8 -// CHECK-A32: %struct.uint16x4x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint16x4x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint16x4x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x4x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -516,7 +516,7 @@ uint16x4x3_t test_vld1_u16_x3(uint16_t const *a) { // CHECK-LABEL: @test_vld1_u16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint16x4x4_t, align 8 -// CHECK-A32: %struct.uint16x4x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint16x4x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint16x4x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x4x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -536,7 +536,7 @@ uint16x4x4_t test_vld1_u16_x4(uint16_t const *a) { // CHECK-LABEL: @test_vld1_u32_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint32x2x2_t, align 8 -// CHECK-A32: %struct.uint32x2x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint32x2x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint32x2x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x2x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -556,7 +556,7 @@ uint32x2x2_t test_vld1_u32_x2(uint32_t const *a) { // CHECK-LABEL: @test_vld1_u32_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint32x2x3_t, align 8 -// CHECK-A32: %struct.uint32x2x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint32x2x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint32x2x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x2x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -576,7 +576,7 @@ uint32x2x3_t test_vld1_u32_x3(uint32_t const *a) { // CHECK-LABEL: @test_vld1_u32_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint32x2x4_t, align 8 -// CHECK-A32: %struct.uint32x2x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint32x2x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint32x2x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x2x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -596,7 +596,7 @@ uint32x2x4_t test_vld1_u32_x4(uint32_t const *a) { // CHECK-LABEL: @test_vld1_u64_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint64x1x2_t, align 8 -// CHECK-A32: %struct.uint64x1x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint64x1x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint64x1x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint64x1x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -616,7 +616,7 @@ uint64x1x2_t test_vld1_u64_x2(uint64_t const *a) { // CHECK-LABEL: @test_vld1_u64_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint64x1x3_t, align 8 -// CHECK-A32: %struct.uint64x1x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint64x1x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint64x1x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint64x1x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -636,7 +636,7 @@ uint64x1x3_t test_vld1_u64_x3(uint64_t const *a) { // CHECK-LABEL: @test_vld1_u64_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint64x1x4_t, align 8 -// CHECK-A32: %struct.uint64x1x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint64x1x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint64x1x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint64x1x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -656,7 +656,7 @@ uint64x1x4_t test_vld1_u64_x4(uint64_t const *a) { // CHECK-LABEL: @test_vld1_u8_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint8x8x2_t, align 8 -// CHECK-A32: %struct.uint8x8x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint8x8x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint8x8x2_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x8x2_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x2|arm.neon.vld1x2}}.v8i8.p0i8(i8* %a) @@ -674,7 +674,7 @@ uint8x8x2_t test_vld1_u8_x2(uint8_t const *a) { // CHECK-LABEL: @test_vld1_u8_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint8x8x3_t, align 8 -// CHECK-A32: %struct.uint8x8x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint8x8x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint8x8x3_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x8x3_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x3|arm.neon.vld1x3}}.v8i8.p0i8(i8* %a) @@ -692,7 +692,7 @@ uint8x8x3_t test_vld1_u8_x3(uint8_t const *a) { // CHECK-LABEL: @test_vld1_u8_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint8x8x4_t, align 8 -// CHECK-A32: %struct.uint8x8x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint8x8x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint8x8x4_t, align 8 // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x8x4_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <8 x i8>, <8 x i8>, <8 x i8>, <8 x i8> } @llvm.{{aarch64.neon.ld1x4|arm.neon.vld1x4}}.v8i8.p0i8(i8* %a) @@ -710,7 +710,7 @@ uint8x8x4_t test_vld1_u8_x4(uint8_t const *a) { // CHECK-LABEL: @test_vld1q_f16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float16x8x2_t, align 16 -// CHECK-A32: %struct.float16x8x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float16x8x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float16x8x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.float16x8x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast half* %a to i8* @@ -730,7 +730,7 @@ float16x8x2_t test_vld1q_f16_x2(float16_t const *a) { // CHECK-LABEL: @test_vld1q_f16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float16x8x3_t, align 16 -// CHECK-A32: %struct.float16x8x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float16x8x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float16x8x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.float16x8x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast half* %a to i8* @@ -750,7 +750,7 @@ float16x8x3_t test_vld1q_f16_x3(float16_t const *a) { // CHECK-LABEL: @test_vld1q_f16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float16x8x4_t, align 16 -// CHECK-A32: %struct.float16x8x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float16x8x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float16x8x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.float16x8x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast half* %a to i8* @@ -770,7 +770,7 @@ float16x8x4_t test_vld1q_f16_x4(float16_t const *a) { // CHECK-LABEL: @test_vld1q_f32_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float32x4x2_t, align 16 -// CHECK-A32: %struct.float32x4x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float32x4x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float32x4x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x4x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast float* %a to i8* @@ -790,7 +790,7 @@ float32x4x2_t test_vld1q_f32_x2(float32_t const *a) { // CHECK-LABEL: @test_vld1q_f32_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float32x4x3_t, align 16 -// CHECK-A32: %struct.float32x4x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float32x4x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float32x4x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x4x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast float* %a to i8* @@ -810,7 +810,7 @@ float32x4x3_t test_vld1q_f32_x3(float32_t const *a) { // CHECK-LABEL: @test_vld1q_f32_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.float32x4x4_t, align 16 -// CHECK-A32: %struct.float32x4x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.float32x4x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.float32x4x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x4x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast float* %a to i8* @@ -830,7 +830,7 @@ float32x4x4_t test_vld1q_f32_x4(float32_t const *a) { // CHECK-LABEL: @test_vld1q_p16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly16x8x2_t, align 16 -// CHECK-A32: %struct.poly16x8x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly16x8x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly16x8x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x8x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -850,7 +850,7 @@ poly16x8x2_t test_vld1q_p16_x2(poly16_t const *a) { // CHECK-LABEL: @test_vld1q_p16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly16x8x3_t, align 16 -// CHECK-A32: %struct.poly16x8x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly16x8x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly16x8x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x8x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -870,7 +870,7 @@ poly16x8x3_t test_vld1q_p16_x3(poly16_t const *a) { // CHECK-LABEL: @test_vld1q_p16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly16x8x4_t, align 16 -// CHECK-A32: %struct.poly16x8x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly16x8x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly16x8x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x8x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -890,7 +890,7 @@ poly16x8x4_t test_vld1q_p16_x4(poly16_t const *a) { // CHECK-LABEL: @test_vld1q_p8_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly8x16x2_t, align 16 -// CHECK-A32: %struct.poly8x16x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly8x16x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly8x16x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x16x2_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x2|arm.neon.vld1x2}}.v16i8.p0i8(i8* %a) @@ -908,7 +908,7 @@ poly8x16x2_t test_vld1q_p8_x2(poly8_t const *a) { // CHECK-LABEL: @test_vld1q_p8_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly8x16x3_t, align 16 -// CHECK-A32: %struct.poly8x16x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly8x16x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly8x16x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x16x3_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x3|arm.neon.vld1x3}}.v16i8.p0i8(i8* %a) @@ -926,7 +926,7 @@ poly8x16x3_t test_vld1q_p8_x3(poly8_t const *a) { // CHECK-LABEL: @test_vld1q_p8_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.poly8x16x4_t, align 16 -// CHECK-A32: %struct.poly8x16x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.poly8x16x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.poly8x16x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x16x4_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8>, <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x4|arm.neon.vld1x4}}.v16i8.p0i8(i8* %a) @@ -944,7 +944,7 @@ poly8x16x4_t test_vld1q_p8_x4(poly8_t const *a) { // CHECK-LABEL: @test_vld1q_s16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int16x8x2_t, align 16 -// CHECK-A32: %struct.int16x8x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int16x8x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int16x8x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x8x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -964,7 +964,7 @@ int16x8x2_t test_vld1q_s16_x2(int16_t const *a) { // CHECK-LABEL: @test_vld1q_s16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int16x8x3_t, align 16 -// CHECK-A32: %struct.int16x8x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int16x8x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int16x8x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x8x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -984,7 +984,7 @@ int16x8x3_t test_vld1q_s16_x3(int16_t const *a) { // CHECK-LABEL: @test_vld1q_s16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int16x8x4_t, align 16 -// CHECK-A32: %struct.int16x8x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int16x8x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int16x8x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x8x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -1004,7 +1004,7 @@ int16x8x4_t test_vld1q_s16_x4(int16_t const *a) { // CHECK-LABEL: @test_vld1q_s32_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int32x4x2_t, align 16 -// CHECK-A32: %struct.int32x4x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int32x4x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int32x4x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x4x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -1024,7 +1024,7 @@ int32x4x2_t test_vld1q_s32_x2(int32_t const *a) { // CHECK-LABEL: @test_vld1q_s32_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int32x4x3_t, align 16 -// CHECK-A32: %struct.int32x4x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int32x4x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int32x4x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x4x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -1044,7 +1044,7 @@ int32x4x3_t test_vld1q_s32_x3(int32_t const *a) { // CHECK-LABEL: @test_vld1q_s32_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int32x4x4_t, align 16 -// CHECK-A32: %struct.int32x4x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int32x4x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int32x4x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x4x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -1064,7 +1064,7 @@ int32x4x4_t test_vld1q_s32_x4(int32_t const *a) { // CHECK-LABEL: @test_vld1q_s64_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int64x2x2_t, align 16 -// CHECK-A32: %struct.int64x2x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int64x2x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int64x2x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int64x2x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -1084,7 +1084,7 @@ int64x2x2_t test_vld1q_s64_x2(int64_t const *a) { // CHECK-LABEL: @test_vld1q_s64_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int64x2x3_t, align 16 -// CHECK-A32: %struct.int64x2x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int64x2x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int64x2x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int64x2x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -1104,7 +1104,7 @@ int64x2x3_t test_vld1q_s64_x3(int64_t const *a) { // CHECK-LABEL: @test_vld1q_s64_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int64x2x4_t, align 16 -// CHECK-A32: %struct.int64x2x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int64x2x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int64x2x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int64x2x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -1124,7 +1124,7 @@ int64x2x4_t test_vld1q_s64_x4(int64_t const *a) { // CHECK-LABEL: @test_vld1q_s8_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int8x16x2_t, align 16 -// CHECK-A32: %struct.int8x16x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int8x16x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int8x16x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x16x2_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x2|arm.neon.vld1x2}}.v16i8.p0i8(i8* %a) @@ -1142,7 +1142,7 @@ int8x16x2_t test_vld1q_s8_x2(int8_t const *a) { // CHECK-LABEL: @test_vld1q_s8_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int8x16x3_t, align 16 -// CHECK-A32: %struct.int8x16x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int8x16x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int8x16x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x16x3_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x3|arm.neon.vld1x3}}.v16i8.p0i8(i8* %a) @@ -1160,7 +1160,7 @@ int8x16x3_t test_vld1q_s8_x3(int8_t const *a) { // CHECK-LABEL: @test_vld1q_s8_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.int8x16x4_t, align 16 -// CHECK-A32: %struct.int8x16x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.int8x16x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.int8x16x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x16x4_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8>, <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x4|arm.neon.vld1x4}}.v16i8.p0i8(i8* %a) @@ -1178,7 +1178,7 @@ int8x16x4_t test_vld1q_s8_x4(int8_t const *a) { // CHECK-LABEL: @test_vld1q_u16_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint16x8x2_t, align 16 -// CHECK-A32: %struct.uint16x8x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint16x8x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint16x8x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x8x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -1198,7 +1198,7 @@ uint16x8x2_t test_vld1q_u16_x2(uint16_t const *a) { // CHECK-LABEL: @test_vld1q_u16_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint16x8x3_t, align 16 -// CHECK-A32: %struct.uint16x8x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint16x8x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint16x8x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x8x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -1218,7 +1218,7 @@ uint16x8x3_t test_vld1q_u16_x3(uint16_t const *a) { // CHECK-LABEL: @test_vld1q_u16_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint16x8x4_t, align 16 -// CHECK-A32: %struct.uint16x8x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint16x8x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint16x8x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x8x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i16* %a to i8* @@ -1238,7 +1238,7 @@ uint16x8x4_t test_vld1q_u16_x4(uint16_t const *a) { // CHECK-LABEL: @test_vld1q_u32_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint32x4x2_t, align 16 -// CHECK-A32: %struct.uint32x4x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint32x4x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint32x4x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x4x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -1258,7 +1258,7 @@ uint32x4x2_t test_vld1q_u32_x2(uint32_t const *a) { // CHECK-LABEL: @test_vld1q_u32_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint32x4x3_t, align 16 -// CHECK-A32: %struct.uint32x4x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint32x4x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint32x4x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x4x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -1278,7 +1278,7 @@ uint32x4x3_t test_vld1q_u32_x3(uint32_t const *a) { // CHECK-LABEL: @test_vld1q_u32_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint32x4x4_t, align 16 -// CHECK-A32: %struct.uint32x4x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint32x4x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint32x4x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x4x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i32* %a to i8* @@ -1298,7 +1298,7 @@ uint32x4x4_t test_vld1q_u32_x4(uint32_t const *a) { // CHECK-LABEL: @test_vld1q_u64_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint64x2x2_t, align 16 -// CHECK-A32: %struct.uint64x2x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint64x2x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint64x2x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint64x2x2_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -1318,7 +1318,7 @@ uint64x2x2_t test_vld1q_u64_x2(uint64_t const *a) { // CHECK-LABEL: @test_vld1q_u64_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint64x2x3_t, align 16 -// CHECK-A32: %struct.uint64x2x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint64x2x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint64x2x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint64x2x3_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -1338,7 +1338,7 @@ uint64x2x3_t test_vld1q_u64_x3(uint64_t const *a) { // CHECK-LABEL: @test_vld1q_u64_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint64x2x4_t, align 16 -// CHECK-A32: %struct.uint64x2x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint64x2x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint64x2x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint64x2x4_t* [[__RET]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i64* %a to i8* @@ -1358,7 +1358,7 @@ uint64x2x4_t test_vld1q_u64_x4(uint64_t const *a) { // CHECK-LABEL: @test_vld1q_u8_x2( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint8x16x2_t, align 16 -// CHECK-A32: %struct.uint8x16x2_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint8x16x2_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint8x16x2_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x16x2_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x2|arm.neon.vld1x2}}.v16i8.p0i8(i8* %a) @@ -1376,7 +1376,7 @@ uint8x16x2_t test_vld1q_u8_x2(uint8_t const *a) { // CHECK-LABEL: @test_vld1q_u8_x3( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint8x16x3_t, align 16 -// CHECK-A32: %struct.uint8x16x3_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint8x16x3_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint8x16x3_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x16x3_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x3|arm.neon.vld1x3}}.v16i8.p0i8(i8* %a) @@ -1394,7 +1394,7 @@ uint8x16x3_t test_vld1q_u8_x3(uint8_t const *a) { // CHECK-LABEL: @test_vld1q_u8_x4( // CHECK-A64: [[RETVAL:%.*]] = alloca %struct.uint8x16x4_t, align 16 -// CHECK-A32: %struct.uint8x16x4_t* noalias sret [[RETVAL:%.*]], +// CHECK-A32: %struct.uint8x16x4_t* noalias sret align 8 [[RETVAL:%.*]], // CHECK: [[__RET:%.*]] = alloca %struct.uint8x16x4_t, align {{16|8}} // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x16x4_t* [[__RET]] to i8* // CHECK: [[VLD1XN:%.*]] = call { <16 x i8>, <16 x i8>, <16 x i8>, <16 x i8> } @llvm.{{aarch64.neon.ld1x4|arm.neon.vld1x4}}.v16i8.p0i8(i8* %a) diff --git a/clang/test/CodeGen/arm-varargs.c b/clang/test/CodeGen/arm-varargs.c index 1f5c07ef57dad..dff62568b6cae 100644 --- a/clang/test/CodeGen/arm-varargs.c +++ b/clang/test/CodeGen/arm-varargs.c @@ -24,7 +24,7 @@ struct bigstruct { }; struct bigstruct simple_struct(void) { -// CHECK-LABEL: define void @simple_struct(%struct.bigstruct* noalias sret %agg.result) +// CHECK-LABEL: define void @simple_struct(%struct.bigstruct* noalias sret align 4 %agg.result) return va_arg(the_list, struct bigstruct); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, i8* [[CUR]], i32 40 @@ -42,7 +42,7 @@ struct aligned_bigstruct { }; struct aligned_bigstruct simple_aligned_struct(void) { -// CHECK-LABEL: define void @simple_aligned_struct(%struct.aligned_bigstruct* noalias sret %agg.result) +// CHECK-LABEL: define void @simple_aligned_struct(%struct.aligned_bigstruct* noalias sret align 8 %agg.result) return va_arg(the_list, struct aligned_bigstruct); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[CUR_INT:%[a-z0-9._]+]] = ptrtoint i8* [[CUR]] to i32 @@ -78,7 +78,7 @@ struct hfa { }; struct hfa simple_hfa(void) { -// CHECK-LABEL: define void @simple_hfa(%struct.hfa* noalias sret %agg.result) +// CHECK-LABEL: define void @simple_hfa(%struct.hfa* noalias sret align 4 %agg.result) return va_arg(the_list, struct hfa); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, i8* [[CUR]], i32 8 @@ -185,7 +185,7 @@ typedef struct __attribute__((aligned(16))) { int val; } overaligned_int_struct; overaligned_int_struct overaligned_int_struct_test() { -// CHECK-LABEL: define void @overaligned_int_struct_test(%struct.overaligned_int_struct* noalias sret %agg.result) +// CHECK-LABEL: define void @overaligned_int_struct_test(%struct.overaligned_int_struct* noalias sret align 16 %agg.result) return va_arg(the_list, overaligned_int_struct); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, i8* [[CUR]], i32 16 @@ -201,7 +201,7 @@ typedef struct __attribute__((packed,aligned(2))) { long long val; } underaligned_long_long_struct; underaligned_long_long_struct underaligned_long_long_struct_test() { -// CHECK-LABEL: define void @underaligned_long_long_struct_test(%struct.underaligned_long_long_struct* noalias sret %agg.result) +// CHECK-LABEL: define void @underaligned_long_long_struct_test(%struct.underaligned_long_long_struct* noalias sret align 2 %agg.result) return va_arg(the_list, underaligned_long_long_struct); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, i8* [[CUR]], i32 8 @@ -217,7 +217,7 @@ typedef struct __attribute__((aligned(16))) { long long val; } overaligned_long_long_struct; overaligned_long_long_struct overaligned_long_long_struct_test() { -// CHECK-LABEL: define void @overaligned_long_long_struct_test(%struct.overaligned_long_long_struct* noalias sret %agg.result) +// CHECK-LABEL: define void @overaligned_long_long_struct_test(%struct.overaligned_long_long_struct* noalias sret align 16 %agg.result) return va_arg(the_list, overaligned_long_long_struct); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[CUR_INT:%[a-z0-9._]+]] = ptrtoint i8* [[CUR]] to i32 @@ -259,7 +259,7 @@ typedef struct { int val __attribute__((aligned(16))); } overaligned_int_struct_member; overaligned_int_struct_member overaligned_int_struct_member_test() { -// CHECK-LABEL: define void @overaligned_int_struct_member_test(%struct.overaligned_int_struct_member* noalias sret %agg.result) +// CHECK-LABEL: define void @overaligned_int_struct_member_test(%struct.overaligned_int_struct_member* noalias sret align 16 %agg.result) return va_arg(the_list, overaligned_int_struct_member); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[CUR_INT:%[a-z0-9._]+]] = ptrtoint i8* [[CUR]] to i32 @@ -279,7 +279,7 @@ typedef struct { long long val __attribute__((packed,aligned(2))); } underaligned_long_long_struct_member; underaligned_long_long_struct_member underaligned_long_long_struct_member_test() { -// CHECK-LABEL: define void @underaligned_long_long_struct_member_test(%struct.underaligned_long_long_struct_member* noalias sret %agg.result) +// CHECK-LABEL: define void @underaligned_long_long_struct_member_test(%struct.underaligned_long_long_struct_member* noalias sret align 2 %agg.result) return va_arg(the_list, underaligned_long_long_struct_member); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, i8* [[CUR]], i32 8 @@ -295,7 +295,7 @@ typedef struct { long long val __attribute__((aligned(16))); } overaligned_long_long_struct_member; overaligned_long_long_struct_member overaligned_long_long_struct_member_test() { -// CHECK-LABEL: define void @overaligned_long_long_struct_member_test(%struct.overaligned_long_long_struct_member* noalias sret %agg.result) +// CHECK-LABEL: define void @overaligned_long_long_struct_member_test(%struct.overaligned_long_long_struct_member* noalias sret align 16 %agg.result) return va_arg(the_list, overaligned_long_long_struct_member); // CHECK: [[CUR:%[a-z0-9._]+]] = load i8*, i8** getelementptr inbounds (%struct.__va_list, %struct.__va_list* @the_list, i32 0, i32 0), align 4 // CHECK: [[CUR_INT:%[a-z0-9._]+]] = ptrtoint i8* [[CUR]] to i32 diff --git a/clang/test/CodeGen/arm-vector-arguments.c b/clang/test/CodeGen/arm-vector-arguments.c index 9bdddb72696e3..aa8e65ba366f4 100644 --- a/clang/test/CodeGen/arm-vector-arguments.c +++ b/clang/test/CodeGen/arm-vector-arguments.c @@ -9,7 +9,7 @@ #include -// CHECK: define void @f0(%struct.int8x16x2_t* noalias sret %agg.result, <16 x i8> %{{.*}}, <16 x i8> %{{.*}}) +// CHECK: define void @f0(%struct.int8x16x2_t* noalias sret align 16 %agg.result, <16 x i8> %{{.*}}, <16 x i8> %{{.*}}) int8x16x2_t f0(int8x16_t a0, int8x16_t a1) { return vzipq_s8(a0, a1); } @@ -25,7 +25,7 @@ typedef float T_float32x16 __attribute__ ((__vector_size__ (64))); T_float32x2 f1_0(T_float32x2 a0) { return a0; } // CHECK: define <4 x float> @f1_1(<4 x float> %{{.*}}) T_float32x4 f1_1(T_float32x4 a0) { return a0; } -// CHECK: define void @f1_2(<8 x float>* noalias sret %{{.*}}, <8 x float> %{{.*}}) +// CHECK: define void @f1_2(<8 x float>* noalias sret align 32 %{{.*}}, <8 x float> %{{.*}}) T_float32x8 f1_2(T_float32x8 a0) { return a0; } -// CHECK: define void @f1_3(<16 x float>* noalias sret %{{.*}}, <16 x float> %{{.*}}) +// CHECK: define void @f1_3(<16 x float>* noalias sret align 64 %{{.*}}, <16 x float> %{{.*}}) T_float32x16 f1_3(T_float32x16 a0) { return a0; } diff --git a/clang/test/CodeGen/arm-vfp16-arguments.c b/clang/test/CodeGen/arm-vfp16-arguments.c index 32f1bb7b2a775..42d990d970862 100644 --- a/clang/test/CodeGen/arm-vfp16-arguments.c +++ b/clang/test/CodeGen/arm-vfp16-arguments.c @@ -71,6 +71,6 @@ void test_hfa(hfa_t a) {} hfa_t ghfa; hfa_t test_ret_hfa(void) { return ghfa; } -// CHECK-SOFT: define void @test_ret_hfa(%struct.hfa_t* noalias nocapture sret %agg.result) +// CHECK-SOFT: define void @test_ret_hfa(%struct.hfa_t* noalias nocapture sret align 8 %agg.result) // CHECK-HARD: define arm_aapcs_vfpcc [2 x <2 x i32>] @test_ret_hfa() // CHECK-FULL: define arm_aapcs_vfpcc %struct.hfa_t @test_ret_hfa() diff --git a/clang/test/CodeGen/arm-vfp16-arguments2.cpp b/clang/test/CodeGen/arm-vfp16-arguments2.cpp index e436a5ecd6abd..ccc81a3bfdd92 100644 --- a/clang/test/CodeGen/arm-vfp16-arguments2.cpp +++ b/clang/test/CodeGen/arm-vfp16-arguments2.cpp @@ -37,27 +37,27 @@ struct S5 : B1 { B1 M[1]; }; -// CHECK-SOFT: define void @_Z2f12S1(%struct.S1* noalias nocapture sret %agg.result, [2 x i64] %s1.coerce) +// CHECK-SOFT: define void @_Z2f12S1(%struct.S1* noalias nocapture sret align 8 %agg.result, [2 x i64] %s1.coerce) // CHECK-HARD: define arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f12S1([2 x <2 x i32>] returned %s1.coerce) // CHECK-FULL: define arm_aapcs_vfpcc %struct.S1 @_Z2f12S1(%struct.S1 returned %s1.coerce) struct S1 f1(struct S1 s1) { return s1; } -// CHECK-SOFT: define void @_Z2f22S2(%struct.S2* noalias nocapture sret %agg.result, [4 x i32] %s2.coerce) +// CHECK-SOFT: define void @_Z2f22S2(%struct.S2* noalias nocapture sret align 8 %agg.result, [4 x i32] %s2.coerce) // CHECK-HARD: define arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f22S2([2 x <2 x i32>] returned %s2.coerce) // CHECK-FULL: define arm_aapcs_vfpcc %struct.S2 @_Z2f22S2(%struct.S2 returned %s2.coerce) struct S2 f2(struct S2 s2) { return s2; } -// CHECK-SOFT: define void @_Z2f32S3(%struct.S3* noalias nocapture sret %agg.result, [2 x i64] %s3.coerce) +// CHECK-SOFT: define void @_Z2f32S3(%struct.S3* noalias nocapture sret align 8 %agg.result, [2 x i64] %s3.coerce) // CHECK-HARD: define arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f32S3([2 x <2 x i32>] returned %s3.coerce) // CHECK-FULL: define arm_aapcs_vfpcc %struct.S3 @_Z2f32S3(%struct.S3 returned %s3.coerce) struct S3 f3(struct S3 s3) { return s3; } -// CHECK-SOFT: define void @_Z2f42S4(%struct.S4* noalias nocapture sret %agg.result, [2 x i64] %s4.coerce) +// CHECK-SOFT: define void @_Z2f42S4(%struct.S4* noalias nocapture sret align 8 %agg.result, [2 x i64] %s4.coerce) // CHECK-HARD: define arm_aapcs_vfpcc [2 x <2 x i32>] @_Z2f42S4([2 x <2 x i32>] returned %s4.coerce) // CHECK-FULL: define arm_aapcs_vfpcc %struct.S4 @_Z2f42S4(%struct.S4 returned %s4.coerce) struct S4 f4(struct S4 s4) { return s4; } -// CHECK-SOFT: define void @_Z2f52S5(%struct.S5* noalias nocapture sret %agg.result, [2 x i64] %s5.coerce) +// CHECK-SOFT: define void @_Z2f52S5(%struct.S5* noalias nocapture sret align 8 %agg.result, [2 x i64] %s5.coerce) // CHECK-HARD: define arm_aapcs_vfpcc %struct.S5 @_Z2f52S5(%struct.S5 returned %s5.coerce) // CHECK-FULL: define arm_aapcs_vfpcc %struct.S5 @_Z2f52S5(%struct.S5 returned %s5.coerce) struct S5 f5(struct S5 s5) { return s5; } diff --git a/clang/test/CodeGen/arm64-arguments.c b/clang/test/CodeGen/arm64-arguments.c index 5c8474cae7be8..97332deb7c807 100644 --- a/clang/test/CodeGen/arm64-arguments.c +++ b/clang/test/CodeGen/arm64-arguments.c @@ -181,9 +181,9 @@ T_float32x2 f1_0(T_float32x2 a0) { return a0; } // CHECK: define <4 x float> @f1_1(<4 x float> %{{.*}}) T_float32x4 f1_1(T_float32x4 a0) { return a0; } // Vector with length bigger than 16-byte is illegal and is passed indirectly. -// CHECK: define void @f1_2(<8 x float>* noalias sret %{{.*}}, <8 x float>* %0) +// CHECK: define void @f1_2(<8 x float>* noalias sret align 16 %{{.*}}, <8 x float>* %0) T_float32x8 f1_2(T_float32x8 a0) { return a0; } -// CHECK: define void @f1_3(<16 x float>* noalias sret %{{.*}}, <16 x float>* %0) +// CHECK: define void @f1_3(<16 x float>* noalias sret align 16 %{{.*}}, <16 x float>* %0) T_float32x16 f1_3(T_float32x16 a0) { return a0; } // Testing alignment with aggregates: HFA, aggregates with size <= 16 bytes and diff --git a/clang/test/CodeGen/arm64-microsoft-arguments.cpp b/clang/test/CodeGen/arm64-microsoft-arguments.cpp index bca7cc94b3937..f5bcda756b061 100644 --- a/clang/test/CodeGen/arm64-microsoft-arguments.cpp +++ b/clang/test/CodeGen/arm64-microsoft-arguments.cpp @@ -28,8 +28,8 @@ S2 f2() { } // Pass and return for type size > 16 bytes. -// CHECK: define {{.*}} void @{{.*}}f3{{.*}}(%struct.S3* noalias sret %agg.result) -// CHECK: call void {{.*}}func3{{.*}}(%struct.S3* sret %agg.result, %struct.S3* %agg.tmp) +// CHECK: define {{.*}} void @{{.*}}f3{{.*}}(%struct.S3* noalias sret align 4 %agg.result) +// CHECK: call void {{.*}}func3{{.*}}(%struct.S3* sret align 4 %agg.result, %struct.S3* %agg.tmp) struct S3 { int a[5]; }; @@ -42,8 +42,8 @@ S3 f3() { // Pass and return aggregate (of size < 16 bytes) with non-trivial destructor. // Passed directly but returned indirectly. -// CHECK: define {{.*}} void {{.*}}f4{{.*}}(%struct.S4* inreg noalias sret %agg.result) -// CHECK: call void {{.*}}func4{{.*}}(%struct.S4* inreg sret %agg.result, [2 x i64] %5) +// CHECK: define {{.*}} void {{.*}}f4{{.*}}(%struct.S4* inreg noalias sret align 4 %agg.result) +// CHECK: call void {{.*}}func4{{.*}}(%struct.S4* inreg sret align 4 %agg.result, [2 x i64] %5) struct S4 { int a[3]; ~S4(); @@ -56,8 +56,8 @@ S4 f4() { } // Pass and return from instance method called from instance method. -// CHECK: define {{.*}} void @{{.*}}bar@Q1{{.*}}(%class.Q1* %this, %class.P1* inreg noalias sret %agg.result) -// CHECK: call void {{.*}}foo@P1{{.*}}(%class.P1* %ref.tmp, %class.P1* inreg sret %agg.result, i8 %1) +// CHECK: define {{.*}} void @{{.*}}bar@Q1{{.*}}(%class.Q1* %this, %class.P1* inreg noalias sret align 1 %agg.result) +// CHECK: call void {{.*}}foo@P1{{.*}}(%class.P1* %ref.tmp, %class.P1* inreg sret align 1 %agg.result, i8 %1) class P1 { public: @@ -76,7 +76,7 @@ P1 Q1::bar() { // Pass and return from instance method called from free function. // CHECK: define {{.*}} void {{.*}}bar{{.*}}() -// CHECK: call void {{.*}}foo@P2{{.*}}(%class.P2* %ref.tmp, %class.P2* inreg sret %retval, i8 %0) +// CHECK: call void {{.*}}foo@P2{{.*}}(%class.P2* %ref.tmp, %class.P2* inreg sret align 1 %retval, i8 %0) class P2 { public: P2 foo(P2 x); @@ -89,8 +89,8 @@ P2 bar() { // Pass and return an object with a user-provided constructor (passed directly, // returned indirectly) -// CHECK: define {{.*}} void @{{.*}}f5{{.*}}(%struct.S5* inreg noalias sret %agg.result) -// CHECK: call void {{.*}}func5{{.*}}(%struct.S5* inreg sret %agg.result, i64 {{.*}}) +// CHECK: define {{.*}} void @{{.*}}f5{{.*}}(%struct.S5* inreg noalias sret align 4 %agg.result) +// CHECK: call void {{.*}}func5{{.*}}(%struct.S5* inreg sret align 4 %agg.result, i64 {{.*}}) struct S5 { S5(); int x; @@ -146,8 +146,8 @@ struct S8 { int y; }; -// CHECK: define {{.*}} void {{.*}}?f8{{.*}}(%struct.S8* inreg noalias sret {{.*}}) -// CHECK: call void {{.*}}func8{{.*}}(%struct.S8* inreg sret {{.*}}, i64 {{.*}}) +// CHECK: define {{.*}} void {{.*}}?f8{{.*}}(%struct.S8* inreg noalias sret align 4 {{.*}}) +// CHECK: call void {{.*}}func8{{.*}}(%struct.S8* inreg sret align 4 {{.*}}, i64 {{.*}}) S8 func8(S8 x); S8 f8() { S8 x; @@ -157,8 +157,8 @@ S8 f8() { // Pass and return an object with a non-trivial copy-assignment operator and // a trivial copy constructor (passed directly, returned indirectly) -// CHECK: define {{.*}} void @"?f9@@YA?AUS9@@XZ"(%struct.S9* inreg noalias sret {{.*}}) -// CHECK: call void {{.*}}func9{{.*}}(%struct.S9* inreg sret {{.*}}, i64 {{.*}}) +// CHECK: define {{.*}} void @"?f9@@YA?AUS9@@XZ"(%struct.S9* inreg noalias sret align 4 {{.*}}) +// CHECK: call void {{.*}}func9{{.*}}(%struct.S9* inreg sret align 4 {{.*}}, i64 {{.*}}) struct S9 { S9& operator=(const S9&); int x; @@ -174,8 +174,8 @@ S9 f9() { // Pass and return an object with a base class (passed directly, returned // indirectly). -// CHECK: define dso_local void {{.*}}f10{{.*}}(%struct.S10* inreg noalias sret {{.*}}) -// CHECK: call void {{.*}}func10{{.*}}(%struct.S10* inreg sret {{.*}}, [2 x i64] {{.*}}) +// CHECK: define dso_local void {{.*}}f10{{.*}}(%struct.S10* inreg noalias sret align 4 {{.*}}) +// CHECK: call void {{.*}}func10{{.*}}(%struct.S10* inreg sret align 4 {{.*}}, [2 x i64] {{.*}}) struct S10 : public S1 { int x; }; @@ -189,8 +189,8 @@ S10 f10() { // Pass and return a non aggregate object exceeding > 128 bits (passed // indirectly, returned indirectly) -// CHECK: define dso_local void {{.*}}f11{{.*}}(%struct.S11* inreg noalias sret {{.*}}) -// CHECK: call void {{.*}}func11{{.*}}(%struct.S11* inreg sret {{.*}}, %struct.S11* {{.*}}) +// CHECK: define dso_local void {{.*}}f11{{.*}}(%struct.S11* inreg noalias sret align 8 {{.*}}) +// CHECK: call void {{.*}}func11{{.*}}(%struct.S11* inreg sret align 8 {{.*}}, %struct.S11* {{.*}}) struct S11 { virtual void f(); int a[5]; diff --git a/clang/test/CodeGen/arm64_32.c b/clang/test/CodeGen/arm64_32.c index 245dfefc99e3b..1fb121cfcfb14 100644 --- a/clang/test/CodeGen/arm64_32.c +++ b/clang/test/CodeGen/arm64_32.c @@ -27,4 +27,4 @@ long double LongDoubleVar = 0.0; typedef float __attribute__((ext_vector_type(16))) v16f32; v16f32 func(v16f32 in) { return in; } -// CHECK: define void @func(<16 x float>* noalias sret {{%.*}}, <16 x float> {{%.*}}) +// CHECK: define void @func(<16 x float>* noalias sret align 16 {{%.*}}, <16 x float> {{%.*}}) diff --git a/clang/test/CodeGen/arm_neon_intrinsics.c b/clang/test/CodeGen/arm_neon_intrinsics.c index 9f1a64554155c..17aa39fb4ae19 100644 --- a/clang/test/CodeGen/arm_neon_intrinsics.c +++ b/clang/test/CodeGen/arm_neon_intrinsics.c @@ -20079,7 +20079,7 @@ poly8x8_t test_vtbx4_p8(poly8x8_t a, poly8x8x4_t b, uint8x8_t c) { return vtbx4_p8(a, b, c); } -// CHECK: @test_vtrn_s8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_s8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VTRN_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20092,7 +20092,7 @@ int8x8x2_t test_vtrn_s8(int8x8_t a, int8x8_t b) { return vtrn_s8(a, b); } -// CHECK: @test_vtrn_s16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_s16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20107,7 +20107,7 @@ int16x4x2_t test_vtrn_s16(int16x4_t a, int16x4_t b) { return vtrn_s16(a, b); } -// CHECK: @test_vtrn_s32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_s32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x i32> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x i32> %b to <8 x i8> @@ -20122,7 +20122,7 @@ int32x2x2_t test_vtrn_s32(int32x2_t a, int32x2_t b) { return vtrn_s32(a, b); } -// CHECK: @test_vtrn_u8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_u8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VTRN_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20135,7 +20135,7 @@ uint8x8x2_t test_vtrn_u8(uint8x8_t a, uint8x8_t b) { return vtrn_u8(a, b); } -// CHECK: @test_vtrn_u16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_u16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20150,7 +20150,7 @@ uint16x4x2_t test_vtrn_u16(uint16x4_t a, uint16x4_t b) { return vtrn_u16(a, b); } -// CHECK: @test_vtrn_u32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_u32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x i32> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x i32> %b to <8 x i8> @@ -20165,7 +20165,7 @@ uint32x2x2_t test_vtrn_u32(uint32x2_t a, uint32x2_t b) { return vtrn_u32(a, b); } -// CHECK: @test_vtrn_f32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_f32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x float> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x float> %b to <8 x i8> @@ -20180,7 +20180,7 @@ float32x2x2_t test_vtrn_f32(float32x2_t a, float32x2_t b) { return vtrn_f32(a, b); } -// CHECK: @test_vtrn_p8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_p8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VTRN_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20193,7 +20193,7 @@ poly8x8x2_t test_vtrn_p8(poly8x8_t a, poly8x8_t b) { return vtrn_p8(a, b); } -// CHECK: @test_vtrn_p16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrn_p16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20208,7 +20208,7 @@ poly16x4x2_t test_vtrn_p16(poly16x4_t a, poly16x4_t b) { return vtrn_p16(a, b); } -// CHECK: @test_vtrnq_s8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_s8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VTRN_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20221,7 +20221,7 @@ int8x16x2_t test_vtrnq_s8(int8x16_t a, int8x16_t b) { return vtrnq_s8(a, b); } -// CHECK: @test_vtrnq_s16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_s16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20236,7 +20236,7 @@ int16x8x2_t test_vtrnq_s16(int16x8_t a, int16x8_t b) { return vtrnq_s16(a, b); } -// CHECK: @test_vtrnq_s32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_s32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i32> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i32> %b to <16 x i8> @@ -20251,7 +20251,7 @@ int32x4x2_t test_vtrnq_s32(int32x4_t a, int32x4_t b) { return vtrnq_s32(a, b); } -// CHECK: @test_vtrnq_u8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_u8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VTRN_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20264,7 +20264,7 @@ uint8x16x2_t test_vtrnq_u8(uint8x16_t a, uint8x16_t b) { return vtrnq_u8(a, b); } -// CHECK: @test_vtrnq_u16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_u16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20279,7 +20279,7 @@ uint16x8x2_t test_vtrnq_u16(uint16x8_t a, uint16x8_t b) { return vtrnq_u16(a, b); } -// CHECK: @test_vtrnq_u32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_u32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i32> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i32> %b to <16 x i8> @@ -20294,7 +20294,7 @@ uint32x4x2_t test_vtrnq_u32(uint32x4_t a, uint32x4_t b) { return vtrnq_u32(a, b); } -// CHECK: @test_vtrnq_f32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_f32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x float> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x float> %b to <16 x i8> @@ -20309,7 +20309,7 @@ float32x4x2_t test_vtrnq_f32(float32x4_t a, float32x4_t b) { return vtrnq_f32(a, b); } -// CHECK: @test_vtrnq_p8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_p8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VTRN_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20322,7 +20322,7 @@ poly8x16x2_t test_vtrnq_p8(poly8x16_t a, poly8x16_t b) { return vtrnq_p8(a, b); } -// CHECK: @test_vtrnq_p16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vtrnq_p16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20501,7 +20501,7 @@ uint16x8_t test_vtstq_p16(poly16x8_t a, poly16x8_t b) { return vtstq_p16(a, b); } -// CHECK: @test_vuzp_s8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_s8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VUZP_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20514,7 +20514,7 @@ int8x8x2_t test_vuzp_s8(int8x8_t a, int8x8_t b) { return vuzp_s8(a, b); } -// CHECK: @test_vuzp_s16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_s16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20529,7 +20529,7 @@ int16x4x2_t test_vuzp_s16(int16x4_t a, int16x4_t b) { return vuzp_s16(a, b); } -// CHECK: @test_vuzp_s32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_s32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x i32> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x i32> %b to <8 x i8> @@ -20544,7 +20544,7 @@ int32x2x2_t test_vuzp_s32(int32x2_t a, int32x2_t b) { return vuzp_s32(a, b); } -// CHECK: @test_vuzp_u8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_u8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VUZP_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20557,7 +20557,7 @@ uint8x8x2_t test_vuzp_u8(uint8x8_t a, uint8x8_t b) { return vuzp_u8(a, b); } -// CHECK: @test_vuzp_u16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_u16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20572,7 +20572,7 @@ uint16x4x2_t test_vuzp_u16(uint16x4_t a, uint16x4_t b) { return vuzp_u16(a, b); } -// CHECK: @test_vuzp_u32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_u32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x i32> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x i32> %b to <8 x i8> @@ -20587,7 +20587,7 @@ uint32x2x2_t test_vuzp_u32(uint32x2_t a, uint32x2_t b) { return vuzp_u32(a, b); } -// CHECK: @test_vuzp_f32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_f32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x float> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x float> %b to <8 x i8> @@ -20602,7 +20602,7 @@ float32x2x2_t test_vuzp_f32(float32x2_t a, float32x2_t b) { return vuzp_f32(a, b); } -// CHECK: @test_vuzp_p8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_p8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VUZP_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20615,7 +20615,7 @@ poly8x8x2_t test_vuzp_p8(poly8x8_t a, poly8x8_t b) { return vuzp_p8(a, b); } -// CHECK: @test_vuzp_p16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzp_p16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20630,7 +20630,7 @@ poly16x4x2_t test_vuzp_p16(poly16x4_t a, poly16x4_t b) { return vuzp_p16(a, b); } -// CHECK: @test_vuzpq_s8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_s8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VUZP_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20643,7 +20643,7 @@ int8x16x2_t test_vuzpq_s8(int8x16_t a, int8x16_t b) { return vuzpq_s8(a, b); } -// CHECK: @test_vuzpq_s16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_s16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20658,7 +20658,7 @@ int16x8x2_t test_vuzpq_s16(int16x8_t a, int16x8_t b) { return vuzpq_s16(a, b); } -// CHECK: @test_vuzpq_s32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_s32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i32> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i32> %b to <16 x i8> @@ -20673,7 +20673,7 @@ int32x4x2_t test_vuzpq_s32(int32x4_t a, int32x4_t b) { return vuzpq_s32(a, b); } -// CHECK: @test_vuzpq_u8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_u8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VUZP_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20686,7 +20686,7 @@ uint8x16x2_t test_vuzpq_u8(uint8x16_t a, uint8x16_t b) { return vuzpq_u8(a, b); } -// CHECK: @test_vuzpq_u16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_u16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20701,7 +20701,7 @@ uint16x8x2_t test_vuzpq_u16(uint16x8_t a, uint16x8_t b) { return vuzpq_u16(a, b); } -// CHECK: @test_vuzpq_u32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_u32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i32> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i32> %b to <16 x i8> @@ -20716,7 +20716,7 @@ uint32x4x2_t test_vuzpq_u32(uint32x4_t a, uint32x4_t b) { return vuzpq_u32(a, b); } -// CHECK: @test_vuzpq_f32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_f32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x float> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x float> %b to <16 x i8> @@ -20731,7 +20731,7 @@ float32x4x2_t test_vuzpq_f32(float32x4_t a, float32x4_t b) { return vuzpq_f32(a, b); } -// CHECK: @test_vuzpq_p8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_p8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VUZP_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20744,7 +20744,7 @@ poly8x16x2_t test_vuzpq_p8(poly8x16_t a, poly8x16_t b) { return vuzpq_p8(a, b); } -// CHECK: @test_vuzpq_p16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vuzpq_p16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20759,7 +20759,7 @@ poly16x8x2_t test_vuzpq_p16(poly16x8_t a, poly16x8_t b) { return vuzpq_p16(a, b); } -// CHECK: @test_vzip_s8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_s8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VZIP_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20772,7 +20772,7 @@ int8x8x2_t test_vzip_s8(int8x8_t a, int8x8_t b) { return vzip_s8(a, b); } -// CHECK: @test_vzip_s16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_s16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20787,7 +20787,7 @@ int16x4x2_t test_vzip_s16(int16x4_t a, int16x4_t b) { return vzip_s16(a, b); } -// CHECK: @test_vzip_s32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_s32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x i32> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x i32> %b to <8 x i8> @@ -20802,7 +20802,7 @@ int32x2x2_t test_vzip_s32(int32x2_t a, int32x2_t b) { return vzip_s32(a, b); } -// CHECK: @test_vzip_u8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_u8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VZIP_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20815,7 +20815,7 @@ uint8x8x2_t test_vzip_u8(uint8x8_t a, uint8x8_t b) { return vzip_u8(a, b); } -// CHECK: @test_vzip_u16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_u16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20830,7 +20830,7 @@ uint16x4x2_t test_vzip_u16(uint16x4_t a, uint16x4_t b) { return vzip_u16(a, b); } -// CHECK: @test_vzip_u32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_u32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x i32> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x i32> %b to <8 x i8> @@ -20845,7 +20845,7 @@ uint32x2x2_t test_vzip_u32(uint32x2_t a, uint32x2_t b) { return vzip_u32(a, b); } -// CHECK: @test_vzip_f32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_f32({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x2x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <2 x float> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <2 x float> %b to <8 x i8> @@ -20860,7 +20860,7 @@ float32x2x2_t test_vzip_f32(float32x2_t a, float32x2_t b) { return vzip_f32(a, b); } -// CHECK: @test_vzip_p8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_p8({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <8 x i8>* // CHECK: [[VZIP_I:%.*]] = shufflevector <8 x i8> %a, <8 x i8> %b, <8 x i32> @@ -20873,7 +20873,7 @@ poly8x8x2_t test_vzip_p8(poly8x8_t a, poly8x8_t b) { return vzip_p8(a, b); } -// CHECK: @test_vzip_p16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzip_p16({{.*}} sret align 8 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i16> %a to <8 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i16> %b to <8 x i8> @@ -20888,7 +20888,7 @@ poly16x4x2_t test_vzip_p16(poly16x4_t a, poly16x4_t b) { return vzip_p16(a, b); } -// CHECK: @test_vzipq_s8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_s8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VZIP_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20901,7 +20901,7 @@ int8x16x2_t test_vzipq_s8(int8x16_t a, int8x16_t b) { return vzipq_s8(a, b); } -// CHECK: @test_vzipq_s16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_s16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20916,7 +20916,7 @@ int16x8x2_t test_vzipq_s16(int16x8_t a, int16x8_t b) { return vzipq_s16(a, b); } -// CHECK: @test_vzipq_s32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_s32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.int32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i32> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i32> %b to <16 x i8> @@ -20931,7 +20931,7 @@ int32x4x2_t test_vzipq_s32(int32x4_t a, int32x4_t b) { return vzipq_s32(a, b); } -// CHECK: @test_vzipq_u8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_u8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VZIP_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -20944,7 +20944,7 @@ uint8x16x2_t test_vzipq_u8(uint8x16_t a, uint8x16_t b) { return vzipq_u8(a, b); } -// CHECK: @test_vzipq_u16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_u16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> @@ -20959,7 +20959,7 @@ uint16x8x2_t test_vzipq_u16(uint16x8_t a, uint16x8_t b) { return vzipq_u16(a, b); } -// CHECK: @test_vzipq_u32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_u32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.uint32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x i32> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x i32> %b to <16 x i8> @@ -20974,7 +20974,7 @@ uint32x4x2_t test_vzipq_u32(uint32x4_t a, uint32x4_t b) { return vzipq_u32(a, b); } -// CHECK: @test_vzipq_f32({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_f32({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.float32x4x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <4 x float> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <4 x float> %b to <16 x i8> @@ -20989,7 +20989,7 @@ float32x4x2_t test_vzipq_f32(float32x4_t a, float32x4_t b) { return vzipq_f32(a, b); } -// CHECK: @test_vzipq_p8({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_p8({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly8x16x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast i8* [[TMP0]] to <16 x i8>* // CHECK: [[VZIP_I:%.*]] = shufflevector <16 x i8> %a, <16 x i8> %b, <16 x i32> @@ -21002,7 +21002,7 @@ poly8x16x2_t test_vzipq_p8(poly8x16_t a, poly8x16_t b) { return vzipq_p8(a, b); } -// CHECK: @test_vzipq_p16({{.*}} sret [[AGG_RESULT:%[0-9a-zA-Z.]+]], +// CHECK: @test_vzipq_p16({{.*}} sret align 16 [[AGG_RESULT:%[0-9a-zA-Z.]+]], // CHECK: [[TMP0:%.*]] = bitcast %struct.poly16x8x2_t* [[AGG_RESULT]] to i8* // CHECK: [[TMP1:%.*]] = bitcast <8 x i16> %a to <16 x i8> // CHECK: [[TMP2:%.*]] = bitcast <8 x i16> %b to <16 x i8> diff --git a/clang/test/CodeGen/asm-label.c b/clang/test/CodeGen/asm-label.c index f944d368f8b39..c06f11fd2d24d 100644 --- a/clang/test/CodeGen/asm-label.c +++ b/clang/test/CodeGen/asm-label.c @@ -17,15 +17,3 @@ int *test(void) { // DARWIN: @"\01bar" = internal global i32 0 // DARWIN: @"\01foo" = common global i32 0 // DARWIN: declare i8* @"\01alias"(i32) - -// PR7887 -int pr7887_1 asm(""); -extern int pr7887_2 asm(""); -int pr7887_3 () asm(""); - -int pt7887_4 () { - static int y asm(""); - y = pr7887_3(); - pr7887_2 = 1; - return pr7887_1; -} diff --git a/clang/test/CodeGen/attr-availability-new.c b/clang/test/CodeGen/attr-availability-new.c new file mode 100644 index 0000000000000..85f3d5d3f1e83 --- /dev/null +++ b/clang/test/CodeGen/attr-availability-new.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fvisibility hidden "-triple" "x86_64-apple-macos11.0" -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -fvisibility hidden "-triple" "x86_64-apple-macos10.15" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-OLD %s + +__attribute__((availability(macos,introduced=10.16))) +void f0(void); + +__attribute__((availability(macos,introduced=11.0))) +void f1(void); + +__attribute__((availability(macos,introduced=12.0))) +void f2(void); + +// CHECK-OLD: declare extern_weak void @f0 +// CHECK-OLD: declare extern_weak void @f1 +// CHECK-OLD: declare extern_weak void @f2 + +// CHECK: declare void @f0 +// CHECK: declare void @f1 +// CHECK: declare extern_weak void @f2 + +void test() { + f0(); + f1(); + f2(); +} diff --git a/clang/test/CodeGen/blocks.c b/clang/test/CodeGen/blocks.c index fd348c98f65fe..3f1f2502652cd 100644 --- a/clang/test/CodeGen/blocks.c +++ b/clang/test/CodeGen/blocks.c @@ -18,7 +18,7 @@ struct s0 { int a[64]; }; -// CHECK: define internal void @__f2_block_invoke(%struct.s0* noalias sret {{%.*}}, i8* {{%.*}}, %struct.s0* byval(%struct.s0) align 4 {{.*}}) +// CHECK: define internal void @__f2_block_invoke(%struct.s0* noalias sret align 4 {{%.*}}, i8* {{%.*}}, %struct.s0* byval(%struct.s0) align 4 {{.*}}) struct s0 f2(struct s0 a0) { return ^(struct s0 a1){ return a1; }(a0); } diff --git a/clang/test/CodeGen/bounds-checking.c b/clang/test/CodeGen/bounds-checking.c index 2e6a08650dd97..15cef8c007a55 100644 --- a/clang/test/CodeGen/bounds-checking.c +++ b/clang/test/CodeGen/bounds-checking.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsanitize=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s // RUN: %clang_cc1 -fsanitize=local-bounds -fexperimental-new-pass-manager -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s -// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s -// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -fexperimental-new-pass-manager -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s +// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s --check-prefixes=CHECK,NONLOCAL +// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -fexperimental-new-pass-manager -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s --check-prefixes=CHECK,NONLOCAL // // REQUIRES: x86-registered-target @@ -31,3 +31,21 @@ void f3() { // CHECK: call {{.*}} @llvm.trap a[2] = 1; } + +union U { int a[0]; int b[1]; int c[2]; }; + +// CHECK-LABEL: define {{.*}} @f4 +int f4(union U *u, int i) { + // a and b are treated as flexible array members. + // CHECK-NOT: @llvm.trap + return u->a[i] + u->b[i]; + // CHECK: } +} + +// CHECK-LABEL: define {{.*}} @f5 +int f5(union U *u, int i) { + // c is not a flexible array member. + // NONLOCAL: call {{.*}} @llvm.trap + return u->c[i]; + // CHECK: } +} diff --git a/clang/test/CodeGen/c11atomics-ios.c b/clang/test/CodeGen/c11atomics-ios.c index f48e10e4aa430..92d318dac1c35 100644 --- a/clang/test/CodeGen/c11atomics-ios.c +++ b/clang/test/CodeGen/c11atomics-ios.c @@ -203,7 +203,7 @@ void testPromotedStruct(_Atomic(PS) *fp) { } PS test_promoted_load(_Atomic(PS) *addr) { - // CHECK-LABEL: @test_promoted_load(%struct.PS* noalias sret %agg.result, { %struct.PS, [2 x i8] }* %addr) + // CHECK-LABEL: @test_promoted_load(%struct.PS* noalias sret align 2 %agg.result, { %struct.PS, [2 x i8] }* %addr) // CHECK: [[ADDR_ARG:%.*]] = alloca { %struct.PS, [2 x i8] }*, align 4 // CHECK: [[ATOMIC_RES:%.*]] = alloca { %struct.PS, [2 x i8] }, align 8 // CHECK: store { %struct.PS, [2 x i8] }* %addr, { %struct.PS, [2 x i8] }** [[ADDR_ARG]], align 4 @@ -245,7 +245,7 @@ void test_promoted_store(_Atomic(PS) *addr, PS *val) { } PS test_promoted_exchange(_Atomic(PS) *addr, PS *val) { - // CHECK-LABEL: @test_promoted_exchange(%struct.PS* noalias sret %agg.result, { %struct.PS, [2 x i8] }* %addr, %struct.PS* %val) + // CHECK-LABEL: @test_promoted_exchange(%struct.PS* noalias sret align 2 %agg.result, { %struct.PS, [2 x i8] }* %addr, %struct.PS* %val) // CHECK: [[ADDR_ARG:%.*]] = alloca { %struct.PS, [2 x i8] }*, align 4 // CHECK: [[VAL_ARG:%.*]] = alloca %struct.PS*, align 4 // CHECK: [[NONATOMIC_TMP:%.*]] = alloca %struct.PS, align 2 diff --git a/clang/test/CodeGen/c11atomics.c b/clang/test/CodeGen/c11atomics.c index cf251738be55e..cb97c67e485f8 100644 --- a/clang/test/CodeGen/c11atomics.c +++ b/clang/test/CodeGen/c11atomics.c @@ -368,7 +368,7 @@ void testPromotedStruct(_Atomic(PS) *fp) { } PS test_promoted_load(_Atomic(PS) *addr) { - // CHECK-LABEL: @test_promoted_load(%struct.PS* noalias sret %agg.result, { %struct.PS, [2 x i8] }* %addr) + // CHECK-LABEL: @test_promoted_load(%struct.PS* noalias sret align 2 %agg.result, { %struct.PS, [2 x i8] }* %addr) // CHECK: [[ADDR_ARG:%.*]] = alloca { %struct.PS, [2 x i8] }*, align 4 // CHECK: [[ATOMIC_RES:%.*]] = alloca { %struct.PS, [2 x i8] }, align 8 // CHECK: store { %struct.PS, [2 x i8] }* %addr, { %struct.PS, [2 x i8] }** [[ADDR_ARG]], align 4 @@ -411,7 +411,7 @@ void test_promoted_store(_Atomic(PS) *addr, PS *val) { } PS test_promoted_exchange(_Atomic(PS) *addr, PS *val) { - // CHECK-LABEL: @test_promoted_exchange(%struct.PS* noalias sret %agg.result, { %struct.PS, [2 x i8] }* %addr, %struct.PS* %val) + // CHECK-LABEL: @test_promoted_exchange(%struct.PS* noalias sret align 2 %agg.result, { %struct.PS, [2 x i8] }* %addr, %struct.PS* %val) // CHECK: [[ADDR_ARG:%.*]] = alloca { %struct.PS, [2 x i8] }*, align 4 // CHECK: [[VAL_ARG:%.*]] = alloca %struct.PS*, align 4 // CHECK: [[NONATOMIC_TMP:%.*]] = alloca %struct.PS, align 2 diff --git a/clang/test/CodeGen/debug-info-extern-call.c b/clang/test/CodeGen/debug-info-extern-call.c index da3764f7359ea..072e578b58986 100644 --- a/clang/test/CodeGen/debug-info-extern-call.c +++ b/clang/test/CodeGen/debug-info-extern-call.c @@ -1,7 +1,7 @@ // 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 - \ +// RUN: %clang -g -O2 -target x86_64-none-linux-gnu -S -emit-llvm %s -o - \ // RUN: | FileCheck %s -check-prefix=DECLS-FOR-EXTERN // Similarly, when the debugger tuning is gdb, expect a subprogram for extern diff --git a/clang/test/CodeGen/debug-info-oslog.c b/clang/test/CodeGen/debug-info-oslog.c new file mode 100644 index 0000000000000..c32c79eb8a6f4 --- /dev/null +++ b/clang/test/CodeGen/debug-info-oslog.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple x86_64-darwin-apple -debug-info-kind=limited \ +// RUN: %s -emit-llvm -o - | FileCheck %s +void test_builtin_os_log(void *buf, int i, const char *data) { + __builtin_os_log_format(buf, "%d", i); +} + +// CHECK: define linkonce_odr {{.*}}@__os_log_helper_1_0_1_4_0( +// CHECK-SAME: !dbg ![[OS_LOG_HELPER:[0-9]+]] + +// This helper is going to be uniqued, so it should not have a line +// number between file and type. + +// CHECK: distinct !DISubprogram(name: "__os_log_helper_1_0_1_4_0", +// CHECK-SAME: file: !{{[0-9+]}}, type +// CHECK-SAME: flags: DIFlagArtificial diff --git a/clang/test/CodeGen/debug-info-sysroot-sdk.c b/clang/test/CodeGen/debug-info-sysroot-sdk.c new file mode 100644 index 0000000000000..5c4d201d69047 --- /dev/null +++ b/clang/test/CodeGen/debug-info-sysroot-sdk.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -debug-info-kind=limited -triple %itanium_abi_triple \ +// RUN: %s -isysroot /CLANG_SYSROOT/MacOSX.sdk -emit-llvm -o - \ +// RUN: -debugger-tuning=lldb | FileCheck %s --check-prefix=LLDB +// RUN: %clang_cc1 -debug-info-kind=limited -triple %itanium_abi_triple \ +// RUN: %s -isysroot /CLANG_SYSROOT/MacOSX.sdk -emit-llvm -o - \ +// RUN: -debugger-tuning=gdb | FileCheck %s --check-prefix=GDB + +void foo() {} + +// The sysroot and sdk are LLDB-tuning-specific attributes. + +// LLDB: distinct !DICompileUnit({{.*}}sysroot: "/CLANG_SYSROOT/MacOSX.sdk", +// LLDB-SAME: sdk: "MacOSX.sdk" +// GDB: distinct !DICompileUnit( +// GDB-NOT: sysroot: "/CLANG_SYSROOT/MacOSX.sdk" +// GDB-NOT: sdk: "MacOSX.sdk" diff --git a/clang/test/CodeGen/debug-nvptx.c b/clang/test/CodeGen/debug-nvptx.c index ceff300829601..8780c5db6801c 100644 --- a/clang/test/CodeGen/debug-nvptx.c +++ b/clang/test/CodeGen/debug-nvptx.c @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -triple nvptx-unknown-unknown -S -o - -debug-info-kind=limited %s -emit-llvm | FileCheck %s -// CHECK: DICompileUnit({{.*}}, nameTableKind: None) +// CHECK: DICompileUnit({{.*}}, nameTableKind: None void f1(void) { } diff --git a/clang/test/CodeGen/debug-prefix-map.c b/clang/test/CodeGen/debug-prefix-map.c index 5366e19447ae2..354110d1b0da7 100644 --- a/clang/test/CodeGen/debug-prefix-map.c +++ b/clang/test/CodeGen/debug-prefix-map.c @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=/UNLIKELY_PATH=empty %s -emit-llvm -o - | FileCheck %s -check-prefix CHECK-EVIL // RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=/UNLIKELY_PATH/empty %s -emit-llvm -o - -main-file-name debug-prefix-map.c | FileCheck %s // RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=/UNLIKELY_PATH/empty %s -emit-llvm -o - -fdebug-compilation-dir %p | FileCheck %s -check-prefix CHECK-COMPILATION-DIR +// RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=/UNLIKELY_PATH/empty %s -emit-llvm -o - -isysroot %p -debugger-tuning=lldb | FileCheck %s -check-prefix CHECK-SYSROOT // RUN: %clang -g -fdebug-prefix-map=%p=/UNLIKELY_PATH/empty -S -c %s -emit-llvm -o - | FileCheck %s // RUN: %clang -g -ffile-prefix-map=%p=/UNLIKELY_PATH/empty -S -c %s -emit-llvm -o - | FileCheck %s @@ -40,3 +41,4 @@ void test_rewrite_includes() { // CHECK-COMPILATION-DIR: !DIFile(filename: "{{.*}}", directory: "/UNLIKELY_PATH/empty") // CHECK-COMPILATION-DIR: !DIFile(filename: "{{.*}}Inputs/stdio.h", directory: "/UNLIKELY_PATH/empty") // CHECK-COMPILATION-DIR-NOT: !DIFile(filename: +// CHECK-SYSROOT: !DICompileUnit({{.*}}sysroot: "/UNLIKELY_PATH/empty" diff --git a/clang/test/CodeGen/fp16-ops.c b/clang/test/CodeGen/fp16-ops.c index f74552b85921c..6401dd125d2a5 100644 --- a/clang/test/CodeGen/fp16-ops.c +++ b/clang/test/CodeGen/fp16-ops.c @@ -11,6 +11,7 @@ // RUN: %clang_cc1 -emit-llvm -o - -x renderscript %s \ // RUN: | FileCheck %s --check-prefix=NATIVE-HALF typedef unsigned cond_t; +typedef __fp16 float16_t; volatile cond_t test; volatile int i0; @@ -541,3 +542,15 @@ void foo(void) { // NOTNATIVE: store volatile half [[TRUNC]], half* @h0 h0 = s0; } + +// CHECK-LABEL: define void @testTypeDef( +// CHECK: %[[CONV:.*]] = fpext <4 x half> %{{.*}} to <4 x float> +// CHECK: %[[CONV1:.*]] = fpext <4 x half> %{{.*}} to <4 x float> +// CHECK: %[[ADD:.*]] = fadd <4 x float> %[[CONV]], %[[CONV1]] +// CHECK: fptrunc <4 x float> %[[ADD]] to <4 x half> + +void testTypeDef() { + __fp16 t0 __attribute__((vector_size(8))); + float16_t t1 __attribute__((vector_size(8))); + t1 = t0 + t1; +} diff --git a/clang/test/CodeGen/lanai-arguments.c b/clang/test/CodeGen/lanai-arguments.c index 9ce4ed98a78ce..ef06b3221bc52 100644 --- a/clang/test/CodeGen/lanai-arguments.c +++ b/clang/test/CodeGen/lanai-arguments.c @@ -16,7 +16,7 @@ void f1(s1 i) {} typedef struct { int cc; } s2; -// CHECK: define void @f2(%struct.s2* noalias sret %agg.result) +// CHECK: define void @f2(%struct.s2* noalias sret align 4 %agg.result) s2 f2() { s2 foo; return foo; @@ -26,7 +26,7 @@ typedef struct { int cc; int dd; } s3; -// CHECK: define void @f3(%struct.s3* noalias sret %agg.result) +// CHECK: define void @f3(%struct.s3* noalias sret align 4 %agg.result) s3 f3() { s3 foo; return foo; diff --git a/clang/test/CodeGen/le32-arguments.c b/clang/test/CodeGen/le32-arguments.c index 9e6908d7fc41c..ad368e1a3941a 100644 --- a/clang/test/CodeGen/le32-arguments.c +++ b/clang/test/CodeGen/le32-arguments.c @@ -17,7 +17,7 @@ typedef struct { int cc; } s2; // Structs should be returned sret and not simplified by the frontend -// CHECK-LABEL: define void @f2(%struct.s2* noalias sret %agg.result) +// CHECK-LABEL: define void @f2(%struct.s2* noalias sret align 4 %agg.result) s2 f2() { s2 foo; return foo; diff --git a/clang/test/CodeGen/mcu-struct-return.c b/clang/test/CodeGen/mcu-struct-return.c index 353c963dadb01..93325254bc8db 100644 --- a/clang/test/CodeGen/mcu-struct-return.c +++ b/clang/test/CodeGen/mcu-struct-return.c @@ -42,7 +42,7 @@ struct S1 bar1() { return s1; } struct S2 bar2() { return s2; } struct S1 bar3(union U1 u) { return s1; } // CHECK: define void @foo1() -// CHECK: define void @foo2([[UNION2_TYPE]]* noalias sret %{{.+}}) +// CHECK: define void @foo2([[UNION2_TYPE]]* noalias sret align 4 %{{.+}}) // CHECK: define i32 @foo3() // CHECK: define void @bar1() // CHECK: define i32 @bar2() @@ -62,7 +62,7 @@ void run() { // CHECK: [[Y1:%.+]] = alloca [[STRUCT1_TYPE]] // CHECK: [[Y2:%.+]] = alloca [[STRUCT2_TYPE]] // CHECK: call void @foo1() - // CHECK: call void @foo2([[UNION2_TYPE]]* sret [[X2]]) + // CHECK: call void @foo2([[UNION2_TYPE]]* sret align 4 [[X2]]) // CHECK: {{.+}} = call i32 @foo3() // CHECK: call void @bar1() // CHECK: {{.+}} = call i32 @bar2() diff --git a/clang/test/CodeGen/mingw-long-double.c b/clang/test/CodeGen/mingw-long-double.c index 57e4adaa5fabe..08e3ac754d6b3 100644 --- a/clang/test/CodeGen/mingw-long-double.c +++ b/clang/test/CodeGen/mingw-long-double.c @@ -32,15 +32,15 @@ long double TestLD(long double x) { return x * x; } // GNU32: define dso_local x86_fp80 @TestLD(x86_fp80 %x) -// GNU64: define dso_local void @TestLD(x86_fp80* noalias sret %agg.result, x86_fp80* %0) +// GNU64: define dso_local void @TestLD(x86_fp80* noalias sret align 16 %agg.result, x86_fp80* %0) // MSC64: define dso_local double @TestLD(double %x) long double _Complex TestLDC(long double _Complex x) { return x * x; } -// GNU32: define dso_local void @TestLDC({ x86_fp80, x86_fp80 }* noalias sret %agg.result, { x86_fp80, x86_fp80 }* byval({ x86_fp80, x86_fp80 }) align 4 %x) -// GNU64: define dso_local void @TestLDC({ x86_fp80, x86_fp80 }* noalias sret %agg.result, { x86_fp80, x86_fp80 }* %x) -// MSC64: define dso_local void @TestLDC({ double, double }* noalias sret %agg.result, { double, double }* %x) +// GNU32: define dso_local void @TestLDC({ x86_fp80, x86_fp80 }* noalias sret align 4 %agg.result, { x86_fp80, x86_fp80 }* byval({ x86_fp80, x86_fp80 }) align 4 %x) +// GNU64: define dso_local void @TestLDC({ x86_fp80, x86_fp80 }* noalias sret align 16 %agg.result, { x86_fp80, x86_fp80 }* %x) +// MSC64: define dso_local void @TestLDC({ double, double }* noalias sret align 8 %agg.result, { double, double }* %x) // GNU32: declare dso_local void @__mulxc3 // GNU64: declare dso_local void @__mulxc3 diff --git a/clang/test/CodeGen/mips-zero-sized-struct.c b/clang/test/CodeGen/mips-zero-sized-struct.c index 08ebf9df3e93b..5f0e660cf395b 100644 --- a/clang/test/CodeGen/mips-zero-sized-struct.c +++ b/clang/test/CodeGen/mips-zero-sized-struct.c @@ -19,7 +19,7 @@ // RUN: %clang_cc1 -triple mipsisa64r6-unknown-linux-gnuabi64 -S -emit-llvm -o - %s | FileCheck -check-prefix=N64 %s // RUN: %clang_cc1 -triple mipsisa64r6el-unknown-linux-gnuabi64 -S -emit-llvm -o - %s | FileCheck -check-prefix=N64 %s -// O32: define void @fn28(%struct.T2* noalias sret %agg.result, i8 signext %arg0) +// O32: define void @fn28(%struct.T2* noalias sret align 1 %agg.result, i8 signext %arg0) // N32: define void @fn28(i8 signext %arg0) // N64: define void @fn28(i8 signext %arg0) diff --git a/clang/test/CodeGen/mips64-padding-arg.c b/clang/test/CodeGen/mips64-padding-arg.c index a7c8f0ff6fdc0..d440743fd7238 100644 --- a/clang/test/CodeGen/mips64-padding-arg.c +++ b/clang/test/CodeGen/mips64-padding-arg.c @@ -33,9 +33,9 @@ void foo3(int a0, long double a1) { // Insert padding after hidden argument. // -// N64-LABEL: define void @foo5(%struct.S0* noalias sret %agg.result, i64 %0, fp128 %a0) -// N64: call void @foo6(%struct.S0* sret %agg.result, i32 signext 1, i32 signext 2, i64 undef, fp128 %a0) -// N64: declare void @foo6(%struct.S0* sret, i32 signext, i32 signext, i64, fp128) +// N64-LABEL: define void @foo5(%struct.S0* noalias sret align 16 %agg.result, i64 %0, fp128 %a0) +// N64: call void @foo6(%struct.S0* sret align 16 %agg.result, i32 signext 1, i32 signext 2, i64 undef, fp128 %a0) +// N64: declare void @foo6(%struct.S0* sret align 16, i32 signext, i32 signext, i64, fp128) extern S0 foo6(int, int, long double); diff --git a/clang/test/CodeGen/ms-intrinsics-rotations.c b/clang/test/CodeGen/ms-intrinsics-rotations.c index b1bb2e6eb0011..30428b12aa333 100644 --- a/clang/test/CodeGen/ms-intrinsics-rotations.c +++ b/clang/test/CodeGen/ms-intrinsics-rotations.c @@ -12,10 +12,17 @@ // RUN: | FileCheck %s --check-prefixes CHECK,CHECK-32BIT-LONG // RUN: %clang_cc1 -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=17.00 \ // RUN: -triple x86_64--linux -emit-llvm %s -o - \ -// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-64BIT-LONG +// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-32BIT-LONG // RUN: %clang_cc1 -ffreestanding -fms-extensions \ // RUN: -triple x86_64--darwin -emit-llvm %s -o - \ -// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-64BIT-LONG +// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-32BIT-LONG + +// LP64 targets use 'long' as 'int' for MS intrinsics (-fms-extensions) +#ifdef __LP64__ +#define LONG int +#else +#define LONG long +#endif // rotate left @@ -40,15 +47,12 @@ unsigned int test_rotl(unsigned int value, int shift) { // CHECK: [[R:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK: ret i32 [[R]] -unsigned long test_lrotl(unsigned long value, int shift) { +unsigned LONG test_lrotl(unsigned LONG value, int shift) { return _lrotl(value, shift); } // CHECK-32BIT-LONG: i32 @test_lrotl // CHECK-32BIT-LONG: [[R:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK-32BIT-LONG: ret i32 [[R]] -// CHECK-64BIT-LONG: i64 @test_lrotl -// CHECK-64BIT-LONG: [[R:%.*]] = call i64 @llvm.fshl.i64(i64 [[X:%.*]], i64 [[X]], i64 [[Y:%.*]]) -// CHECK-64BIT-LONG: ret i64 [[R]] unsigned __int64 test_rotl64(unsigned __int64 value, int shift) { return _rotl64(value, shift); @@ -80,15 +84,12 @@ unsigned int test_rotr(unsigned int value, int shift) { // CHECK: [[R:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK: ret i32 [[R]] -unsigned long test_lrotr(unsigned long value, int shift) { +unsigned LONG test_lrotr(unsigned LONG value, int shift) { return _lrotr(value, shift); } // CHECK-32BIT-LONG: i32 @test_lrotr // CHECK-32BIT-LONG: [[R:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK-32BIT-LONG: ret i32 [[R]] -// CHECK-64BIT-LONG: i64 @test_lrotr -// CHECK-64BIT-LONG: [[R:%.*]] = call i64 @llvm.fshr.i64(i64 [[X:%.*]], i64 [[X]], i64 [[Y:%.*]]) -// CHECK-64BIT-LONG: ret i64 [[R]] unsigned __int64 test_rotr64(unsigned __int64 value, int shift) { return _rotr64(value, shift); diff --git a/clang/test/CodeGen/ms_abi.c b/clang/test/CodeGen/ms_abi.c index 75e1caf922df9..8c66c5dc43610 100644 --- a/clang/test/CodeGen/ms_abi.c +++ b/clang/test/CodeGen/ms_abi.c @@ -155,7 +155,7 @@ struct i128 { }; __attribute__((ms_abi)) struct i128 f7(struct i128 a) { - // WIN64: define dso_local void @f7(%struct.i128* noalias sret %agg.result, %struct.i128* %a) - // FREEBSD: define win64cc void @f7(%struct.i128* noalias sret %agg.result, %struct.i128* %a) + // WIN64: define dso_local void @f7(%struct.i128* noalias sret align 8 %agg.result, %struct.i128* %a) + // FREEBSD: define win64cc void @f7(%struct.i128* noalias sret align 8 %agg.result, %struct.i128* %a) return a; } 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/ppc64-align-struct.c b/clang/test/CodeGen/ppc64-align-struct.c index bcff4920d0c49..3435a6e429396 100644 --- a/clang/test/CodeGen/ppc64-align-struct.c +++ b/clang/test/CodeGen/ppc64-align-struct.c @@ -48,7 +48,7 @@ void test7 (int x, struct test7 y) { } -// CHECK: define void @test1va(%struct.test1* noalias sret %[[AGG_RESULT:.*]], i32 signext %x, ...) +// CHECK: define void @test1va(%struct.test1* noalias sret align 4 %[[AGG_RESULT:.*]], i32 signext %x, ...) // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap // CHECK: %[[NEXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 8 // CHECK: store i8* %[[NEXT]], i8** %ap @@ -66,7 +66,7 @@ struct test1 test1va (int x, ...) return y; } -// CHECK: define void @test2va(%struct.test2* noalias sret %[[AGG_RESULT:.*]], i32 signext %x, ...) +// CHECK: define void @test2va(%struct.test2* noalias sret align 16 %[[AGG_RESULT:.*]], i32 signext %x, ...) // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap // CHECK: %[[TMP0:[^ ]+]] = ptrtoint i8* %[[CUR]] to i64 // CHECK: %[[TMP1:[^ ]+]] = add i64 %[[TMP0]], 15 @@ -88,7 +88,7 @@ struct test2 test2va (int x, ...) return y; } -// CHECK: define void @test3va(%struct.test3* noalias sret %[[AGG_RESULT:.*]], i32 signext %x, ...) +// CHECK: define void @test3va(%struct.test3* noalias sret align 32 %[[AGG_RESULT:.*]], i32 signext %x, ...) // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap // CHECK: %[[TMP0:[^ ]+]] = ptrtoint i8* %[[CUR]] to i64 // CHECK: %[[TMP1:[^ ]+]] = add i64 %[[TMP0]], 15 @@ -110,7 +110,7 @@ struct test3 test3va (int x, ...) return y; } -// CHECK: define void @test4va(%struct.test4* noalias sret %[[AGG_RESULT:.*]], i32 signext %x, ...) +// CHECK: define void @test4va(%struct.test4* noalias sret align 4 %[[AGG_RESULT:.*]], i32 signext %x, ...) // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap // CHECK: %[[NEXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 16 // CHECK: store i8* %[[NEXT]], i8** %ap @@ -128,7 +128,7 @@ struct test4 test4va (int x, ...) return y; } -// CHECK: define void @testva_longdouble(%struct.test_longdouble* noalias sret %[[AGG_RESULT:.*]], i32 signext %x, ...) +// CHECK: define void @testva_longdouble(%struct.test_longdouble* noalias sret align 16 %[[AGG_RESULT:.*]], i32 signext %x, ...) // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap // CHECK: %[[NEXT:[^ ]+]] = getelementptr inbounds i8, i8* %[[CUR]], i64 16 // CHECK: store i8* %[[NEXT]], i8** %ap @@ -147,7 +147,7 @@ struct test_longdouble testva_longdouble (int x, ...) return y; } -// CHECK: define void @testva_vector(%struct.test_vector* noalias sret %[[AGG_RESULT:.*]], i32 signext %x, ...) +// CHECK: define void @testva_vector(%struct.test_vector* noalias sret align 16 %[[AGG_RESULT:.*]], i32 signext %x, ...) // CHECK: %[[CUR:[^ ]+]] = load i8*, i8** %ap // CHECK: %[[TMP0:[^ ]+]] = ptrtoint i8* %[[CUR]] to i64 // CHECK: %[[TMP1:[^ ]+]] = add i64 %[[TMP0]], 15 diff --git a/clang/test/CodeGen/ppc64-elf-abi.c b/clang/test/CodeGen/ppc64-elf-abi.c index 59112a0baf4a7..4270ba2c799b8 100644 --- a/clang/test/CodeGen/ppc64-elf-abi.c +++ b/clang/test/CodeGen/ppc64-elf-abi.c @@ -17,7 +17,7 @@ // RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu -emit-llvm -o - %s \ // RUN: -target-abi elfv2 | FileCheck %s --check-prefix=CHECK-ELFv2 -// CHECK-ELFv1: define void @func_fab(%struct.fab* noalias sret %agg.result, i64 %x.coerce) +// CHECK-ELFv1: define void @func_fab(%struct.fab* noalias sret align 4 %agg.result, i64 %x.coerce) // CHECK-ELFv2: define [2 x float] @func_fab([2 x float] %x.coerce) struct fab { float a; float b; }; struct fab func_fab(struct fab x) { return x; } diff --git a/clang/test/CodeGen/ppc64-qpx-vector.c b/clang/test/CodeGen/ppc64-qpx-vector.c index e7c009328b232..0e55851b9f33e 100644 --- a/clang/test/CodeGen/ppc64-qpx-vector.c +++ b/clang/test/CodeGen/ppc64-qpx-vector.c @@ -24,6 +24,6 @@ v4df foo2(struct sdf a, v4df b, struct sdf2 c) { // QPX-LABEL: define <4 x double> @foo2(<4 x double> inreg %a.coerce, <4 x double> %b, [2 x i256] %c.coerce) // QPX: ret <4 x double> -// NORMAL-LABEL: define void @foo2(<4 x double>* noalias sret %agg.result, [2 x i128] %a.coerce, <4 x double>* %0, [4 x i128] %c.coerce) +// NORMAL-LABEL: define void @foo2(<4 x double>* noalias sret align 32 %agg.result, [2 x i128] %a.coerce, <4 x double>* %0, [4 x i128] %c.coerce) // NORMAL: ret void diff --git a/clang/test/CodeGen/ppc64-soft-float.c b/clang/test/CodeGen/ppc64-soft-float.c index 84ac2d55b636f..b033dea68fe20 100644 --- a/clang/test/CodeGen/ppc64-soft-float.c +++ b/clang/test/CodeGen/ppc64-soft-float.c @@ -30,53 +30,53 @@ struct fabc { float a; float b; float c; }; struct f2a2b { float a[2]; float b[2]; }; // CHECK-LE: define i32 @func_f1(float inreg %x.coerce) -// CHECK-BE: define void @func_f1(%struct.f1* noalias sret %agg.result, float inreg %x.coerce) +// CHECK-BE: define void @func_f1(%struct.f1* noalias sret align 4 %agg.result, float inreg %x.coerce) struct f1 func_f1(struct f1 x) { return x; } // CHECK-LE: define i64 @func_f2(i64 %x.coerce) -// CHECK-BE: define void @func_f2(%struct.f2* noalias sret %agg.result, i64 %x.coerce) +// CHECK-BE: define void @func_f2(%struct.f2* noalias sret align 4 %agg.result, i64 %x.coerce) struct f2 func_f2(struct f2 x) { return x; } // CHECK-LE: define { i64, i64 } @func_f3([2 x i64] %x.coerce) -// CHECK-BE: define void @func_f3(%struct.f3* noalias sret %agg.result, [2 x i64] %x.coerce) +// CHECK-BE: define void @func_f3(%struct.f3* noalias sret align 4 %agg.result, [2 x i64] %x.coerce) struct f3 func_f3(struct f3 x) { return x; } // CHECK-LE: define { i64, i64 } @func_f4([2 x i64] %x.coerce) -// CHECK-BE: define void @func_f4(%struct.f4* noalias sret %agg.result, [2 x i64] %x.coerce) +// CHECK-BE: define void @func_f4(%struct.f4* noalias sret align 4 %agg.result, [2 x i64] %x.coerce) struct f4 func_f4(struct f4 x) { return x; } -// CHECK: define void @func_f5(%struct.f5* noalias sret %agg.result, [3 x i64] %x.coerce) +// CHECK: define void @func_f5(%struct.f5* noalias sret align 4 %agg.result, [3 x i64] %x.coerce) struct f5 func_f5(struct f5 x) { return x; } -// CHECK: define void @func_f6(%struct.f6* noalias sret %agg.result, [3 x i64] %x.coerce) +// CHECK: define void @func_f6(%struct.f6* noalias sret align 4 %agg.result, [3 x i64] %x.coerce) struct f6 func_f6(struct f6 x) { return x; } -// CHECK: define void @func_f7(%struct.f7* noalias sret %agg.result, [4 x i64] %x.coerce) +// CHECK: define void @func_f7(%struct.f7* noalias sret align 4 %agg.result, [4 x i64] %x.coerce) struct f7 func_f7(struct f7 x) { return x; } -// CHECK: define void @func_f8(%struct.f8* noalias sret %agg.result, [4 x i64] %x.coerce) +// CHECK: define void @func_f8(%struct.f8* noalias sret align 4 %agg.result, [4 x i64] %x.coerce) struct f8 func_f8(struct f8 x) { return x; } -// CHECK: define void @func_f9(%struct.f9* noalias sret %agg.result, [5 x i64] %x.coerce) +// CHECK: define void @func_f9(%struct.f9* noalias sret align 4 %agg.result, [5 x i64] %x.coerce) struct f9 func_f9(struct f9 x) { return x; } // CHECK-LE: define i64 @func_fab(i64 %x.coerce) -// CHECK-BE: define void @func_fab(%struct.fab* noalias sret %agg.result, i64 %x.coerce) +// CHECK-BE: define void @func_fab(%struct.fab* noalias sret align 4 %agg.result, i64 %x.coerce) struct fab func_fab(struct fab x) { return x; } // CHECK-LE: define { i64, i64 } @func_fabc([2 x i64] %x.coerce) -// CHECK-BE: define void @func_fabc(%struct.fabc* noalias sret %agg.result, [2 x i64] %x.coerce) +// CHECK-BE: define void @func_fabc(%struct.fabc* noalias sret align 4 %agg.result, [2 x i64] %x.coerce) struct fabc func_fabc(struct fabc x) { return x; } // CHECK-LE: define { i64, i64 } @func_f2a2b([2 x i64] %x.coerce) -// CHECK-BE: define void @func_f2a2b(%struct.f2a2b* noalias sret %agg.result, [2 x i64] %x.coerce) +// CHECK-BE: define void @func_f2a2b(%struct.f2a2b* noalias sret align 4 %agg.result, [2 x i64] %x.coerce) struct f2a2b func_f2a2b(struct f2a2b x) { return x; } // CHECK-LABEL: @call_f1 // CHECK-BE: %[[TMP0:[^ ]+]] = alloca %struct.f1, align 4 // CHECK: %[[TMP:[^ ]+]] = load float, float* getelementptr inbounds (%struct.f1, %struct.f1* @global_f1, i32 0, i32 0, i32 0), align 4 // CHECK-LE: call i32 @func_f1(float inreg %[[TMP]]) -// CHECK-BE: call void @func_f1(%struct.f1* sret %[[TMP0]], float inreg %[[TMP]]) +// CHECK-BE: call void @func_f1(%struct.f1* sret align 4 %[[TMP0]], float inreg %[[TMP]]) struct f1 global_f1; void call_f1(void) { global_f1 = func_f1(global_f1); } @@ -84,7 +84,7 @@ void call_f1(void) { global_f1 = func_f1(global_f1); } // CHECK-BE: %[[TMP0:[^ ]+]] = alloca %struct.f2, align 4 // CHECK: %[[TMP:[^ ]+]] = load i64, i64* bitcast (%struct.f2* @global_f2 to i64*), align 4 // CHECK-LE: call i64 @func_f2(i64 %[[TMP]]) -// CHECK-BE: call void @func_f2(%struct.f2* sret %[[TMP0]], i64 %[[TMP]]) +// CHECK-BE: call void @func_f2(%struct.f2* sret align 4 %[[TMP0]], i64 %[[TMP]]) struct f2 global_f2; void call_f2(void) { global_f2 = func_f2(global_f2); } @@ -95,7 +95,7 @@ void call_f2(void) { global_f2 = func_f2(global_f2); } // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[TMP2]], i8* align 4 bitcast (%struct.f3* @global_f3 to i8*), i64 12, i1 false) // CHECK: %[[TMP3:[^ ]+]] = load [2 x i64], [2 x i64]* %[[TMP1]] // CHECK-LE: call { i64, i64 } @func_f3([2 x i64] %[[TMP3]]) -// CHECK-BE: call void @func_f3(%struct.f3* sret %[[TMP0]], [2 x i64] %[[TMP3]]) +// CHECK-BE: call void @func_f3(%struct.f3* sret align 4 %[[TMP0]], [2 x i64] %[[TMP3]]) struct f3 global_f3; void call_f3(void) { global_f3 = func_f3(global_f3); } @@ -103,7 +103,7 @@ void call_f3(void) { global_f3 = func_f3(global_f3); } // CHECK-BE: %[[TMP0:[^ ]+]] = alloca %struct.f4, align 4 // CHECK: %[[TMP:[^ ]+]] = load [2 x i64], [2 x i64]* bitcast (%struct.f4* @global_f4 to [2 x i64]*), align 4 // CHECK-LE: call { i64, i64 } @func_f4([2 x i64] %[[TMP]]) -// CHECK-BE: call void @func_f4(%struct.f4* sret %[[TMP0]], [2 x i64] %[[TMP]]) +// CHECK-BE: call void @func_f4(%struct.f4* sret align 4 %[[TMP0]], [2 x i64] %[[TMP]]) struct f4 global_f4; void call_f4(void) { global_f4 = func_f4(global_f4); } @@ -113,14 +113,14 @@ void call_f4(void) { global_f4 = func_f4(global_f4); } // CHECK: %[[TMP2:[^ ]+]] = bitcast [3 x i64]* %[[TMP1]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[TMP2]], i8* align 4 bitcast (%struct.f5* @global_f5 to i8*), i64 20, i1 false) // CHECK: %[[TMP3:[^ ]+]] = load [3 x i64], [3 x i64]* %[[TMP1]] -// CHECK: call void @func_f5(%struct.f5* sret %[[TMP0]], [3 x i64] %[[TMP3]]) +// CHECK: call void @func_f5(%struct.f5* sret align 4 %[[TMP0]], [3 x i64] %[[TMP3]]) struct f5 global_f5; void call_f5(void) { global_f5 = func_f5(global_f5); } // CHECK-LABEL: @call_f6 // CHECK: %[[TMP0:[^ ]+]] = alloca %struct.f6, align 4 // CHECK: %[[TMP:[^ ]+]] = load [3 x i64], [3 x i64]* bitcast (%struct.f6* @global_f6 to [3 x i64]*), align 4 -// CHECK: call void @func_f6(%struct.f6* sret %[[TMP0]], [3 x i64] %[[TMP]]) +// CHECK: call void @func_f6(%struct.f6* sret align 4 %[[TMP0]], [3 x i64] %[[TMP]]) struct f6 global_f6; void call_f6(void) { global_f6 = func_f6(global_f6); } @@ -130,14 +130,14 @@ void call_f6(void) { global_f6 = func_f6(global_f6); } // CHECK: %[[TMP2:[^ ]+]] = bitcast [4 x i64]* %[[TMP1]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[TMP2]], i8* align 4 bitcast (%struct.f7* @global_f7 to i8*), i64 28, i1 false) // CHECK: %[[TMP3:[^ ]+]] = load [4 x i64], [4 x i64]* %[[TMP1]], align 8 -// CHECK: call void @func_f7(%struct.f7* sret %[[TMP0]], [4 x i64] %[[TMP3]]) +// CHECK: call void @func_f7(%struct.f7* sret align 4 %[[TMP0]], [4 x i64] %[[TMP3]]) struct f7 global_f7; void call_f7(void) { global_f7 = func_f7(global_f7); } // CHECK-LABEL: @call_f8 // CHECK: %[[TMP0:[^ ]+]] = alloca %struct.f8, align 4 // CHECK: %[[TMP:[^ ]+]] = load [4 x i64], [4 x i64]* bitcast (%struct.f8* @global_f8 to [4 x i64]*), align 4 -// CHECK: call void @func_f8(%struct.f8* sret %[[TMP0]], [4 x i64] %[[TMP]]) +// CHECK: call void @func_f8(%struct.f8* sret align 4 %[[TMP0]], [4 x i64] %[[TMP]]) struct f8 global_f8; void call_f8(void) { global_f8 = func_f8(global_f8); } @@ -146,7 +146,7 @@ void call_f8(void) { global_f8 = func_f8(global_f8); } // CHECK: %[[TMP2:[^ ]+]] = bitcast [5 x i64]* %[[TMP1]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[TMP2]], i8* align 4 bitcast (%struct.f9* @global_f9 to i8*), i64 36, i1 false) // CHECK: %[[TMP3:[^ ]+]] = load [5 x i64], [5 x i64]* %[[TMP1]] -// CHECK: call void @func_f9(%struct.f9* sret %{{[^ ]+}}, [5 x i64] %[[TMP3]]) +// CHECK: call void @func_f9(%struct.f9* sret align 4 %{{[^ ]+}}, [5 x i64] %[[TMP3]]) struct f9 global_f9; void call_f9(void) { global_f9 = func_f9(global_f9); } @@ -154,7 +154,7 @@ void call_f9(void) { global_f9 = func_f9(global_f9); } // CHECK: %[[TMP0:[^ ]+]] = alloca %struct.fab, align 4 // CHECK: %[[TMP:[^ ]+]] = load i64, i64* bitcast (%struct.fab* @global_fab to i64*), align 4 // CHECK-LE: %call = call i64 @func_fab(i64 %[[TMP]]) -// CHECK-BE: call void @func_fab(%struct.fab* sret %[[TMP0]], i64 %[[TMP]]) +// CHECK-BE: call void @func_fab(%struct.fab* sret align 4 %[[TMP0]], i64 %[[TMP]]) struct fab global_fab; void call_fab(void) { global_fab = func_fab(global_fab); } @@ -165,7 +165,7 @@ void call_fab(void) { global_fab = func_fab(global_fab); } // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[TMP2]], i8* align 4 bitcast (%struct.fabc* @global_fabc to i8*), i64 12, i1 false) // CHECK: %[[TMP3:[^ ]+]] = load [2 x i64], [2 x i64]* %[[TMP0]], align 8 // CHECK-LE: %call = call { i64, i64 } @func_fabc([2 x i64] %[[TMP3]]) -// CHECK-BE: call void @func_fabc(%struct.fabc* sret %[[TMPX]], [2 x i64] %[[TMP3]]) +// CHECK-BE: call void @func_fabc(%struct.fabc* sret align 4 %[[TMPX]], [2 x i64] %[[TMP3]]) struct fabc global_fabc; void call_fabc(void) { global_fabc = func_fabc(global_fabc); } diff --git a/clang/test/CodeGen/ppc64-vector.c b/clang/test/CodeGen/ppc64-vector.c index 7ed0beade4cd0..7ea5b007d5bfc 100644 --- a/clang/test/CodeGen/ppc64-vector.c +++ b/clang/test/CodeGen/ppc64-vector.c @@ -39,13 +39,13 @@ v8i16 test_v8i16(v8i16 x) return x; } -// CHECK: define void @test_v16i16(<16 x i16>* noalias sret %agg.result, <16 x i16>* %0) +// CHECK: define void @test_v16i16(<16 x i16>* noalias sret align 32 %agg.result, <16 x i16>* %0) v16i16 test_v16i16(v16i16 x) { return x; } -// CHECK: define void @test_struct_v16i16(%struct.v16i16* noalias sret %agg.result, [2 x i128] %x.coerce) +// CHECK: define void @test_struct_v16i16(%struct.v16i16* noalias sret align 32 %agg.result, [2 x i128] %x.coerce) struct v16i16 test_struct_v16i16(struct v16i16 x) { return x; diff --git a/clang/test/CodeGen/ppc64le-aggregates.c b/clang/test/CodeGen/ppc64le-aggregates.c index e36faa2b80258..ea32d69b7cf95 100644 --- a/clang/test/CodeGen/ppc64le-aggregates.c +++ b/clang/test/CodeGen/ppc64le-aggregates.c @@ -41,7 +41,7 @@ struct f7 func_f7(struct f7 x) { return x; } // CHECK: define [8 x float] @func_f8([8 x float] %x.coerce) struct f8 func_f8(struct f8 x) { return x; } -// CHECK: define void @func_f9(%struct.f9* noalias sret %agg.result, [5 x i64] %x.coerce) +// CHECK: define void @func_f9(%struct.f9* noalias sret align 4 %agg.result, [5 x i64] %x.coerce) struct f9 func_f9(struct f9 x) { return x; } // CHECK: define [2 x float] @func_fab([2 x float] %x.coerce) @@ -106,7 +106,7 @@ void call_f8(void) { global_f8 = func_f8(global_f8); } // CHECK: %[[TMP2:[^ ]+]] = bitcast [5 x i64]* %[[TMP1]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[TMP2]], i8* align 4 bitcast (%struct.f9* @global_f9 to i8*), i64 36, i1 false) // CHECK: %[[TMP3:[^ ]+]] = load [5 x i64], [5 x i64]* %[[TMP1]] -// CHECK: call void @func_f9(%struct.f9* sret %{{[^ ]+}}, [5 x i64] %[[TMP3]]) +// CHECK: call void @func_f9(%struct.f9* sret align 4 %{{[^ ]+}}, [5 x i64] %[[TMP3]]) struct f9 global_f9; void call_f9(void) { global_f9 = func_f9(global_f9); } @@ -162,7 +162,7 @@ struct v7 func_v7(struct v7 x) { return x; } // CHECK: define [8 x <4 x i32>] @func_v8([8 x <4 x i32>] %x.coerce) struct v8 func_v8(struct v8 x) { return x; } -// CHECK: define void @func_v9(%struct.v9* noalias sret %agg.result, %struct.v9* byval(%struct.v9) align 16 %x) +// CHECK: define void @func_v9(%struct.v9* noalias sret align 16 %agg.result, %struct.v9* byval(%struct.v9) align 16 %x) struct v9 func_v9(struct v9 x) { return x; } // CHECK: define [2 x <4 x i32>] @func_vab([2 x <4 x i32>] %x.coerce) @@ -220,7 +220,7 @@ struct v8 global_v8; void call_v8(void) { global_v8 = func_v8(global_v8); } // CHECK-LABEL: @call_v9 -// CHECK: call void @func_v9(%struct.v9* sret %{{[^ ]+}}, %struct.v9* byval(%struct.v9) align 16 @global_v9) +// CHECK: call void @func_v9(%struct.v9* sret align 16 %{{[^ ]+}}, %struct.v9* byval(%struct.v9) align 16 @global_v9) struct v9 global_v9; void call_v9(void) { global_v9 = func_v9(global_v9); } @@ -279,7 +279,7 @@ struct v3f7 func_v3f7(struct v3f7 x) { return x; } // CHECK: define [8 x <4 x float>] @func_v3f8([8 x <4 x float>] %x.coerce) struct v3f8 func_v3f8(struct v3f8 x) { return x; } -// CHECK: define void @func_v3f9(%struct.v3f9* noalias sret %agg.result, %struct.v3f9* byval(%struct.v3f9) align 16 %x) +// CHECK: define void @func_v3f9(%struct.v3f9* noalias sret align 16 %agg.result, %struct.v3f9* byval(%struct.v3f9) align 16 %x) struct v3f9 func_v3f9(struct v3f9 x) { return x; } // CHECK: define [2 x <4 x float>] @func_v3fab([2 x <4 x float>] %x.coerce) @@ -337,7 +337,7 @@ struct v3f8 global_v3f8; void call_v3f8(void) { global_v3f8 = func_v3f8(global_v3f8); } // CHECK-LABEL: @call_v3f9 -// CHECK: call void @func_v3f9(%struct.v3f9* sret %{{[^ ]+}}, %struct.v3f9* byval(%struct.v3f9) align 16 @global_v3f9) +// CHECK: call void @func_v3f9(%struct.v3f9* sret align 16 %{{[^ ]+}}, %struct.v3f9* byval(%struct.v3f9) align 16 @global_v3f9) struct v3f9 global_v3f9; void call_v3f9(void) { global_v3f9 = func_v3f9(global_v3f9); } diff --git a/clang/test/CodeGen/ppc64le-f128Aggregates.c b/clang/test/CodeGen/ppc64le-f128Aggregates.c index 3b363bf0f2eac..acebea69b31dc 100644 --- a/clang/test/CodeGen/ppc64le-f128Aggregates.c +++ b/clang/test/CodeGen/ppc64le-f128Aggregates.c @@ -42,7 +42,7 @@ struct fp7 func_f7(struct fp7 x) { return x; } // CHECK: define [8 x fp128] @func_f8([8 x fp128] %x.coerce) struct fp8 func_f8(struct fp8 x) { return x; } -// CHECK: define void @func_f9(%struct.fp9* noalias sret %agg.result, %struct.fp9* byval(%struct.fp9) align 16 %x) +// CHECK: define void @func_f9(%struct.fp9* noalias sret align 16 %agg.result, %struct.fp9* byval(%struct.fp9) align 16 %x) struct fp9 func_f9(struct fp9 x) { return x; } // CHECK: define [2 x fp128] @func_fab([2 x fp128] %x.coerce) @@ -104,7 +104,7 @@ void call_fp8(void) { global_f8 = func_f8(global_f8); } // CHECK-LABEL: @call_fp9 // CHECK: %[[TMP1:[^ ]+]] = alloca %struct.fp9, align 16 -// CHECK: call void @func_f9(%struct.fp9* sret %[[TMP2:[^ ]+]], %struct.fp9* byval(%struct.fp9) align 16 @global_f9 +// CHECK: call void @func_f9(%struct.fp9* sret align 16 %[[TMP2:[^ ]+]], %struct.fp9* byval(%struct.fp9) align 16 @global_f9 // CHECK: %[[TMP3:[^ ]+]] = bitcast %struct.fp9* %[[TMP2]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 bitcast (%struct.fp9* @global_f9 to i8*), i8* align 16 %[[TMP3]], i64 144, i1 false // CHECK: ret void 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-function-init-fail.c b/clang/test/CodeGen/ptrauth-function-init-fail.c new file mode 100644 index 0000000000000..e310ffd2e776c --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-init-fail.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-calls %s -verify -emit-llvm -S -o - + +void f(void); + +int *pf = (int *)&f + 1; // expected-error{{cannot compile this static initializer yet}} diff --git a/clang/test/CodeGen/ptrauth-function-init.c b/clang/test/CodeGen/ptrauth-function-init.c new file mode 100644 index 0000000000000..9bc89e0cd2ad3 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-init.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s +// RUN: %clang_cc1 -xc++ %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CXX + +#ifdef __cplusplus +extern "C" { +#endif + +void f(void); + +// CHECK: @f.ptrauth = private constant { i8*, i32, i64, i64 } { i8* bitcast (void ()* @f to i8*), i32 0, i64 0, i64 0 }, section "llvm.ptrauth" + +#ifdef __cplusplus + +// CXX-LABEL: define internal void @__cxx_global_var_init() +// CXX: store void ()* bitcast (i32* getelementptr inbounds (i32, i32* bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth to i32*), i64 2) to void ()*), void ()** @_ZL2fp, align 8 + +__attribute__((used)) +void (*const fp)(void) = (void (*)(void))((int *)&f + 2); // Error in C mode. + +#endif + +// CHECK-LABEL: define void @t1() +void t1() { + // CHECK: [[PF:%.*]] = alloca void ()* + // CHECK: store void ()* bitcast (i32* getelementptr inbounds (i32, i32* bitcast ({ i8*, i32, i64, i64 }* @f.ptrauth to i32*), i64 2) to void ()*), void ()** [[PF]] + + void (*pf)(void) = (void (*)(void))((int *)&f + 2); + (void)pf; +} + +#ifdef __cplusplus +} +#endif 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..1b914d29b662d --- /dev/null +++ b/clang/test/CodeGen/ptrauth-in-c-struct.c @@ -0,0 +1,164 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fblocks -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -o - %s | FileCheck %s + +#define AQ1_50 __ptrauth(1,1,50) +#define AQ2_30 __ptrauth(2,1,30) +#define IQ __ptrauth(1,0,50) + +typedef void (^BlockTy)(void); + +// CHECK: %[[STRUCT_SA:.*]] = type { i32, i32* } +// CHECK: %[[STRUCT_SA2:.*]] = type { i32, i32* } +// CHECK: %[[STRUCT_SI:.*]] = type { i32* } + +typedef struct { + int f0; + int * AQ1_50 f1; // Signed using address discrimination. +} SA; + +typedef struct { + int f0; + int * AQ2_30 f1; // Signed using address discrimination. +} SA2; + +typedef struct { + int * IQ f; // No address discrimination. +} SI; + +SA getSA(void); +void calleeSA(SA); + +// CHECK: define void @test_copy_constructor_SA(%[[STRUCT_SA]]* %{{.*}}) +// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8( + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa1_50_8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// 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_constructor_SA2(%[[STRUCT_SA2]]* %{{.*}}) +// CHECK: call void @__copy_constructor_8_8_t0w4_pa2_30_8( + +// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa2_30_8(i8** %[[DST:.*]], i8** %[[SRC:.*]]) +// CHECK: %[[DST_ADDR:.*]] = alloca i8**, align 8 +// CHECK: %[[SRC_ADDR:.*]] = alloca i8**, align 8 +// CHECK: store i8** %[[DST]], i8*** %[[DST_ADDR]], align 8 +// CHECK: store i8** %[[SRC]], i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load i8**, i8*** %[[DST_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i8**, i8*** %[[SRC_ADDR]], align 8 +// CHECK: %[[V5:.*]] = bitcast i8** %[[V0]] to i8* +// CHECK: %[[V6:.*]] = getelementptr inbounds i8, i8* %[[V5]], i64 8 +// CHECK: %[[V7:.*]] = bitcast i8* %[[V6]] to i8** +// CHECK: %[[V8:.*]] = bitcast i8** %[[V1]] to i8* +// CHECK: %[[V9:.*]] = getelementptr inbounds i8, i8* %[[V8]], i64 8 +// CHECK: %[[V10:.*]] = bitcast i8* %[[V9]] to i8** +// CHECK: %[[V11:.*]] = load i8*, i8** %[[V10]], align 8 +// CHECK: %[[V12:.*]] = ptrtoint i8** %[[V10]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V12]], i64 30) +// CHECK: %[[V14:.*]] = ptrtoint i8** %[[V7]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.blend.i64(i64 %[[V14]], i64 30) +// CHECK: %[[V17:.*]] = ptrtoint i8* %[[V11]] to i64 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.resign.i64(i64 %[[V17]], i32 2, i64 %[[V13]], i32 2, i64 %[[V15]]) + +void test_copy_constructor_SA2(SA2 *s) { + SA2 t = *s; +} + +// CHECK: define void @test_copy_assignment_SA( +// CHECK: call void @__copy_assignment_8_8_t0w4_pa1_50_8( + +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_pa1_50_8( + +void test_copy_assignment_SA(SA *d, SA *s) { + *d = *s; +} + +// 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_pa1_50_8( + +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_pa1_50_8( +// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_pa1_50_8( + +void test_move_assignment_SA(SA *p) { + *p = getSA(); +} + +// 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_pa1_50_8(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 align 8 %[[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_pa1_50_8(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..7ad8566a0f55c --- /dev/null +++ b/clang/test/CodeGen/ptrauth.c @@ -0,0 +1,100 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=NOPCH %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK -check-prefix=PCH %s + +#define FNPTRKEY 0 + +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() { + // NOPCH: [[VAR:%.*]] = alloca {}*, + // NOPCH-NEXT: store {}* bitcast ({ i8*, i32, i64, i64 }* @returns_initially_incomplete.ptrauth to {}*), {}** [[VAR]], + // PCH: [[VAR:%.*]] = alloca i64 ()*, + // PCH-NEXT: store i64 ()* bitcast ({ i8*, i32, i64, i64 }* @returns_initially_incomplete.ptrauth to i64 ()*), i64 ()** [[VAR]], + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} +struct InitiallyIncomplete { int x; }; +// 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/regparm-struct.c b/clang/test/CodeGen/regparm-struct.c index 7f56ae094a69c..8c74c8b1f0586 100644 --- a/clang/test/CodeGen/regparm-struct.c +++ b/clang/test/CodeGen/regparm-struct.c @@ -159,7 +159,7 @@ void g16(void) { } __attribute__((regparm(3))) struct s12 f17(int a, int b, int c); -// CHECK: declare void @f17(%struct.s12* inreg sret, i32 inreg, i32 inreg, i32) +// CHECK: declare void @f17(%struct.s12* inreg sret align 4, i32 inreg, i32 inreg, i32) void g17(void) { f17(41, 42, 43); } diff --git a/clang/test/CodeGen/renderscript.c b/clang/test/CodeGen/renderscript.c index a85dc35c61496..fee97a154344e 100644 --- a/clang/test/CodeGen/renderscript.c +++ b/clang/test/CodeGen/renderscript.c @@ -83,15 +83,15 @@ void argLongInt(sLongInt s) {} // and coerced to [a x iNN] for 64-bit RenderScript // ============================================================================= -// CHECK-RS32: void @retShortCharShort(%struct.sShortCharShort* noalias sret %agg.result) +// CHECK-RS32: void @retShortCharShort(%struct.sShortCharShort* noalias sret align 2 %agg.result) // CHECK-RS64: [3 x i16] @retShortCharShort() sShortCharShort retShortCharShort() { sShortCharShort r; return r; } -// CHECK-RS32: void @retIntShortChar(%struct.sIntShortChar* noalias sret %agg.result) +// CHECK-RS32: void @retIntShortChar(%struct.sIntShortChar* noalias sret align 4 %agg.result) // CHECK-RS64: [2 x i32] @retIntShortChar() sIntShortChar retIntShortChar() { sIntShortChar r; return r; } -// CHECK-RS32: void @retLongInt(%struct.sLongInt* noalias sret %agg.result) +// CHECK-RS32: void @retLongInt(%struct.sLongInt* noalias sret align 8 %agg.result) // CHECK-RS64: [2 x i64] @retLongInt() sLongInt retLongInt() { sLongInt r; return r; } @@ -116,12 +116,12 @@ void argLong2Char(sLong2Char s) {} // 64-bit RenderScript // ============================================================================= -// CHECK-RS32: void @retInt5(%struct.sInt5* noalias sret %agg.result) -// CHECK-RS64: void @retInt5(%struct.sInt5* noalias sret %agg.result) +// CHECK-RS32: void @retInt5(%struct.sInt5* noalias sret align 4 %agg.result) +// CHECK-RS64: void @retInt5(%struct.sInt5* noalias sret align 4 %agg.result) sInt5 retInt5() { sInt5 r; return r;} -// CHECK-RS32: void @retLong2Char(%struct.sLong2Char* noalias sret %agg.result) -// CHECK-RS64: void @retLong2Char(%struct.sLong2Char* noalias sret %agg.result) +// CHECK-RS32: void @retLong2Char(%struct.sLong2Char* noalias sret align 8 %agg.result) +// CHECK-RS64: void @retLong2Char(%struct.sLong2Char* noalias sret align 8 %agg.result) sLong2Char retLong2Char() { sLong2Char r; return r;} // ============================================================================= @@ -135,6 +135,6 @@ typedef struct {long l1, l2, l3, l4, l5, l6, l7, l8, l9; } sLong9; // CHECK-RS64: void @argLong9(%struct.sLong9* %s) void argLong9(sLong9 s) {} -// CHECK-RS32: void @retLong9(%struct.sLong9* noalias sret %agg.result) -// CHECK-RS64: void @retLong9(%struct.sLong9* noalias sret %agg.result) +// CHECK-RS32: void @retLong9(%struct.sLong9* noalias sret align 8 %agg.result) +// CHECK-RS64: void @retLong9(%struct.sLong9* noalias sret align 8 %agg.result) sLong9 retLong9() { sLong9 r; return r; } diff --git a/clang/test/CodeGen/riscv32-ilp32-abi.c b/clang/test/CodeGen/riscv32-ilp32-abi.c index 59f0bb5683726..1b32024f51582 100644 --- a/clang/test/CodeGen/riscv32-ilp32-abi.c +++ b/clang/test/CodeGen/riscv32-ilp32-abi.c @@ -35,7 +35,7 @@ int f_scalar_stack_1(int32_t a, int64_t b, float c, double d, long double e, // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret %agg.result, float %a, i64 %b, double %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g) +// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret align 4 %agg.result, float %a, i64 %b, double %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g) struct large f_scalar_stack_2(float a, int64_t b, double c, long double d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; diff --git a/clang/test/CodeGen/riscv32-ilp32-ilp32f-abi.c b/clang/test/CodeGen/riscv32-ilp32-ilp32f-abi.c index 677040626f578..225b12358a0e3 100644 --- a/clang/test/CodeGen/riscv32-ilp32-ilp32f-abi.c +++ b/clang/test/CodeGen/riscv32-ilp32-ilp32f-abi.c @@ -37,7 +37,7 @@ int f_scalar_stack_1(int32_t a, int64_t b, int32_t c, double d, long double e, // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret %agg.result, i32 %a, i64 %b, double %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g) +// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret align 4 %agg.result, i32 %a, i64 %b, double %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g) struct large f_scalar_stack_2(int32_t a, int64_t b, double c, long double d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; diff --git a/clang/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c b/clang/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c index 86ad8fd370bca..740079d28d3be 100644 --- a/clang/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c +++ b/clang/test/CodeGen/riscv32-ilp32-ilp32f-ilp32d-abi.c @@ -177,7 +177,7 @@ void f_agg_large(struct large x) { // The address where the struct should be written to will be the first // argument -// CHECK-LABEL: define void @f_agg_large_ret(%struct.large* noalias sret %agg.result, i32 %i, i8 signext %j) +// CHECK-LABEL: define void @f_agg_large_ret(%struct.large* noalias sret align 4 %agg.result, i32 %i, i8 signext %j) struct large f_agg_large_ret(int32_t i, int8_t j) { return (struct large){1, 2, 3, 4}; } @@ -189,7 +189,7 @@ void f_vec_large_v16i8(v16i8 x) { x[0] = x[7]; } -// CHECK-LABEL: define void @f_vec_large_v16i8_ret(<16 x i8>* noalias sret %agg.result) +// CHECK-LABEL: define void @f_vec_large_v16i8_ret(<16 x i8>* noalias sret align 16 %agg.result) v16i8 f_vec_large_v16i8_ret() { return (v16i8){1, 2, 3, 4, 5, 6, 7, 8}; } @@ -207,7 +207,7 @@ int f_scalar_stack_1(struct tiny a, struct small b, struct small_aligned c, // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret %agg.result, i32 %a, i64 %b, i64 %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g) +// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret align 4 %agg.result, i32 %a, i64 %b, i64 %c, fp128 %d, i8 zeroext %e, i8 %f, i8 %g) struct large f_scalar_stack_2(int32_t a, int64_t b, int64_t c, long double d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; diff --git a/clang/test/CodeGen/riscv32-ilp32d-abi.c b/clang/test/CodeGen/riscv32-ilp32d-abi.c index 078fcb6b5ab11..bc4a1b58aaf83 100644 --- a/clang/test/CodeGen/riscv32-ilp32d-abi.c +++ b/clang/test/CodeGen/riscv32-ilp32d-abi.c @@ -119,7 +119,7 @@ struct double_int32_s f_ret_double_int32_s() { // CHECK: define void @f_double_int64_s_arg(%struct.double_int64_s* %a) void f_double_int64_s_arg(struct double_int64_s a) {} -// CHECK: define void @f_ret_double_int64_s(%struct.double_int64_s* noalias sret %agg.result) +// CHECK: define void @f_ret_double_int64_s(%struct.double_int64_s* noalias sret align 8 %agg.result) struct double_int64_s f_ret_double_int64_s() { return (struct double_int64_s){1.0, 2}; } @@ -243,7 +243,7 @@ struct int_double_int_s { int a; double b; int c; }; // CHECK: define void @f_int_double_int_s_arg(%struct.int_double_int_s* %a) void f_int_double_int_s_arg(struct int_double_int_s a) {} -// CHECK: define void @f_ret_int_double_int_s(%struct.int_double_int_s* noalias sret %agg.result) +// CHECK: define void @f_ret_int_double_int_s(%struct.int_double_int_s* noalias sret align 8 %agg.result) struct int_double_int_s f_ret_int_double_int_s() { return (struct int_double_int_s){1, 2.0, 3}; } @@ -253,7 +253,7 @@ struct int64_double_s { int64_t a; double b; }; // CHECK: define void @f_int64_double_s_arg(%struct.int64_double_s* %a) void f_int64_double_s_arg(struct int64_double_s a) {} -// CHECK: define void @f_ret_int64_double_s(%struct.int64_double_s* noalias sret %agg.result) +// CHECK: define void @f_ret_int64_double_s(%struct.int64_double_s* noalias sret align 8 %agg.result) struct int64_double_s f_ret_int64_double_s() { return (struct int64_double_s){1, 2.0}; } @@ -263,7 +263,7 @@ struct char_char_double_s { char a; char b; double c; }; // CHECK-LABEL: define void @f_char_char_double_s_arg(%struct.char_char_double_s* %a) void f_char_char_double_s_arg(struct char_char_double_s a) {} -// CHECK: define void @f_ret_char_char_double_s(%struct.char_char_double_s* noalias sret %agg.result) +// CHECK: define void @f_ret_char_char_double_s(%struct.char_char_double_s* noalias sret align 8 %agg.result) struct char_char_double_s f_ret_char_char_double_s() { return (struct char_char_double_s){1, 2, 3.0}; } diff --git a/clang/test/CodeGen/riscv32-ilp32f-abi.c b/clang/test/CodeGen/riscv32-ilp32f-abi.c index 76092958aeddf..c8e6418b9daae 100644 --- a/clang/test/CodeGen/riscv32-ilp32f-abi.c +++ b/clang/test/CodeGen/riscv32-ilp32f-abi.c @@ -26,7 +26,7 @@ struct double_double_s { double d; double e; }; // CHECK: define void @f_double_double_s_arg(%struct.double_double_s* %a) void f_double_double_s_arg(struct double_double_s a) {} -// CHECK: define void @f_ret_double_double_s(%struct.double_double_s* noalias sret %agg.result) +// CHECK: define void @f_ret_double_double_s(%struct.double_double_s* noalias sret align 8 %agg.result) struct double_double_s f_ret_double_double_s() { return (struct double_double_s){1.0, 2.0}; } @@ -38,7 +38,7 @@ struct int_double_s { int a; double b; }; // CHECK: define void @f_int_double_s_arg(%struct.int_double_s* %a) void f_int_double_s_arg(struct int_double_s a) {} -// CHECK: define void @f_ret_int_double_s(%struct.int_double_s* noalias sret %agg.result) +// CHECK: define void @f_ret_int_double_s(%struct.int_double_s* noalias sret align 8 %agg.result) struct int_double_s f_ret_int_double_s() { return (struct int_double_s){1, 2.0}; } diff --git a/clang/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c b/clang/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c index e9705ca3d62b3..419bd87fdecfa 100644 --- a/clang/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c +++ b/clang/test/CodeGen/riscv32-ilp32f-ilp32d-abi.c @@ -112,7 +112,7 @@ struct float_int32_s f_ret_float_int32_s() { // CHECK: define void @f_float_int64_s_arg(%struct.float_int64_s* %a) void f_float_int64_s_arg(struct float_int64_s a) {} -// CHECK: define void @f_ret_float_int64_s(%struct.float_int64_s* noalias sret %agg.result) +// CHECK: define void @f_ret_float_int64_s(%struct.float_int64_s* noalias sret align 8 %agg.result) struct float_int64_s f_ret_float_int64_s() { return (struct float_int64_s){1.0, 2}; } @@ -236,7 +236,7 @@ struct int_float_int_s { int a; float b; int c; }; // CHECK: define void @f_int_float_int_s_arg(%struct.int_float_int_s* %a) void f_int_float_int_s_arg(struct int_float_int_s a) {} -// CHECK: define void @f_ret_int_float_int_s(%struct.int_float_int_s* noalias sret %agg.result) +// CHECK: define void @f_ret_int_float_int_s(%struct.int_float_int_s* noalias sret align 4 %agg.result) struct int_float_int_s f_ret_int_float_int_s() { return (struct int_float_int_s){1, 2.0, 3}; } @@ -246,7 +246,7 @@ struct int64_float_s { int64_t a; float b; }; // CHECK: define void @f_int64_float_s_arg(%struct.int64_float_s* %a) void f_int64_float_s_arg(struct int64_float_s a) {} -// CHECK: define void @f_ret_int64_float_s(%struct.int64_float_s* noalias sret %agg.result) +// CHECK: define void @f_ret_int64_float_s(%struct.int64_float_s* noalias sret align 8 %agg.result) struct int64_float_s f_ret_int64_float_s() { return (struct int64_float_s){1, 2.0}; } diff --git a/clang/test/CodeGen/riscv64-lp64-abi.c b/clang/test/CodeGen/riscv64-lp64-abi.c index bae5470c377d9..8347056c54d35 100644 --- a/clang/test/CodeGen/riscv64-lp64-abi.c +++ b/clang/test/CodeGen/riscv64-lp64-abi.c @@ -25,7 +25,7 @@ int f_scalar_stack_1(int32_t a, __int128_t b, float c, long double d, v32i8 e, // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret %agg.result, double %a, i128 %b, fp128 %c, <32 x i8>* %0, i8 zeroext %e, i8 %f, i8 %g) +// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret align 8 %agg.result, double %a, i128 %b, fp128 %c, <32 x i8>* %0, i8 zeroext %e, i8 %f, i8 %g) struct large f_scalar_stack_2(double a, __int128_t b, long double c, v32i8 d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; diff --git a/clang/test/CodeGen/riscv64-lp64-lp64f-abi.c b/clang/test/CodeGen/riscv64-lp64-lp64f-abi.c index d9c909e88bd8f..489d0e83dcbc5 100644 --- a/clang/test/CodeGen/riscv64-lp64-lp64f-abi.c +++ b/clang/test/CodeGen/riscv64-lp64-lp64f-abi.c @@ -27,7 +27,7 @@ int f_scalar_stack_1(int32_t a, __int128_t b, double c, long double d, v32i8 e, // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret %agg.result, double %a, i128 %b, fp128 %c, <32 x i8>* %0, i8 zeroext %e, i8 %f, i8 %g) +// CHECK-LABEL: define void @f_scalar_stack_2(%struct.large* noalias sret align 8 %agg.result, double %a, i128 %b, fp128 %c, <32 x i8>* %0, i8 zeroext %e, i8 %f, i8 %g) struct large f_scalar_stack_2(double a, __int128_t b, long double c, v32i8 d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; diff --git a/clang/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c b/clang/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c index f50a8ca905757..8e263aeba25c5 100644 --- a/clang/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c +++ b/clang/test/CodeGen/riscv64-lp64-lp64f-lp64d-abi.c @@ -166,7 +166,7 @@ void f_agg_large(struct large x) { // The address where the struct should be written to will be the first // argument -// CHECK-LABEL: define void @f_agg_large_ret(%struct.large* noalias sret %agg.result, i32 signext %i, i8 signext %j) +// CHECK-LABEL: define void @f_agg_large_ret(%struct.large* noalias sret align 8 %agg.result, i32 signext %i, i8 signext %j) struct large f_agg_large_ret(int32_t i, int8_t j) { return (struct large){1, 2, 3, 4}; } @@ -178,7 +178,7 @@ void f_vec_large_v32i8(v32i8 x) { x[0] = x[7]; } -// CHECK-LABEL: define void @f_vec_large_v32i8_ret(<32 x i8>* noalias sret %agg.result) +// CHECK-LABEL: define void @f_vec_large_v32i8_ret(<32 x i8>* noalias sret align 32 %agg.result) v32i8 f_vec_large_v32i8_ret() { return (v32i8){1, 2, 3, 4, 5, 6, 7, 8}; } @@ -202,7 +202,7 @@ int f_scalar_stack_2(int32_t a, __int128_t b, int64_t c, long double d, v32i8 e, // the presence of large return values that consume a register due to the need // to pass a pointer. -// CHECK-LABEL: define void @f_scalar_stack_3(%struct.large* noalias sret %agg.result, i32 signext %a, i128 %b, fp128 %c, <32 x i8>* %0, i8 zeroext %e, i8 %f, i8 %g) +// CHECK-LABEL: define void @f_scalar_stack_3(%struct.large* noalias sret align 8 %agg.result, i32 signext %a, i128 %b, fp128 %c, <32 x i8>* %0, i8 zeroext %e, i8 %f, i8 %g) struct large f_scalar_stack_3(uint32_t a, __int128_t b, long double c, v32i8 d, uint8_t e, int8_t f, uint8_t g) { return (struct large){a, e, f, g}; diff --git a/clang/test/CodeGen/riscv64-lp64d-abi.c b/clang/test/CodeGen/riscv64-lp64d-abi.c index 83947def30851..ec47428e6ccab 100644 --- a/clang/test/CodeGen/riscv64-lp64d-abi.c +++ b/clang/test/CodeGen/riscv64-lp64d-abi.c @@ -243,7 +243,7 @@ struct int_double_int_s { int a; double b; int c; }; // CHECK: define void @f_int_double_int_s_arg(%struct.int_double_int_s* %a) void f_int_double_int_s_arg(struct int_double_int_s a) {} -// CHECK: define void @f_ret_int_double_int_s(%struct.int_double_int_s* noalias sret %agg.result) +// CHECK: define void @f_ret_int_double_int_s(%struct.int_double_int_s* noalias sret align 8 %agg.result) struct int_double_int_s f_ret_int_double_int_s() { return (struct int_double_int_s){1, 2.0, 3}; } diff --git a/clang/test/CodeGen/sparcv9-abi.c b/clang/test/CodeGen/sparcv9-abi.c index 5984fa558c83c..2d97001ab1ae4 100644 --- a/clang/test/CodeGen/sparcv9-abi.c +++ b/clang/test/CodeGen/sparcv9-abi.c @@ -53,7 +53,7 @@ struct large { int x; }; -// CHECK-LABEL: define void @f_large(%struct.large* noalias sret %agg.result, %struct.large* %x) +// CHECK-LABEL: define void @f_large(%struct.large* noalias sret align 8 %agg.result, %struct.large* %x) struct large f_large(struct large x) { x.a += *x.b; x.b = 0; 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/CodeGen/struct-passing.c b/clang/test/CodeGen/struct-passing.c index 80847b9fea64f..e3108b964bd26 100644 --- a/clang/test/CodeGen/struct-passing.c +++ b/clang/test/CodeGen/struct-passing.c @@ -18,8 +18,8 @@ void *ps[] = { f0, f1, f2, f3, f4, f5 }; // CHECK: declare i32 @f0() [[RN:#[0-9]+]] // CHECK: declare i32 @f1() [[RO:#[0-9]+]] -// CHECK: declare void @f2({{.*}} sret) -// CHECK: declare void @f3({{.*}} sret) +// CHECK: declare void @f2({{.*}} sret align 4) +// CHECK: declare void @f3({{.*}} sret align 4) // CHECK: declare void @f4({{.*}} byval({{.*}}) align 4) // CHECK: declare void @f5({{.*}} byval({{.*}}) align 4) diff --git a/clang/test/CodeGen/systemz-abi-vector.c b/clang/test/CodeGen/systemz-abi-vector.c index f2e6c13c718f5..896cc0994d6df 100644 --- a/clang/test/CodeGen/systemz-abi-vector.c +++ b/clang/test/CodeGen/systemz-abi-vector.c @@ -50,91 +50,91 @@ unsigned int align = __alignof__ (v16i8); // CHECK-VECTOR: @align = global i32 8 v1i8 pass_v1i8(v1i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1i8(<1 x i8>* noalias sret %{{.*}}, <1 x i8>* %0) +// CHECK-LABEL: define void @pass_v1i8(<1 x i8>* noalias sret align 1 %{{.*}}, <1 x i8>* %0) // CHECK-VECTOR-LABEL: define <1 x i8> @pass_v1i8(<1 x i8> %{{.*}}) v2i8 pass_v2i8(v2i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_v2i8(<2 x i8>* noalias sret %{{.*}}, <2 x i8>* %0) +// CHECK-LABEL: define void @pass_v2i8(<2 x i8>* noalias sret align 2 %{{.*}}, <2 x i8>* %0) // CHECK-VECTOR-LABEL: define <2 x i8> @pass_v2i8(<2 x i8> %{{.*}}) v4i8 pass_v4i8(v4i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_v4i8(<4 x i8>* noalias sret %{{.*}}, <4 x i8>* %0) +// CHECK-LABEL: define void @pass_v4i8(<4 x i8>* noalias sret align 4 %{{.*}}, <4 x i8>* %0) // CHECK-VECTOR-LABEL: define <4 x i8> @pass_v4i8(<4 x i8> %{{.*}}) v8i8 pass_v8i8(v8i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_v8i8(<8 x i8>* noalias sret %{{.*}}, <8 x i8>* %0) +// CHECK-LABEL: define void @pass_v8i8(<8 x i8>* noalias sret align 8 %{{.*}}, <8 x i8>* %0) // CHECK-VECTOR-LABEL: define <8 x i8> @pass_v8i8(<8 x i8> %{{.*}}) v16i8 pass_v16i8(v16i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_v16i8(<16 x i8>* noalias sret %{{.*}}, <16 x i8>* %0) +// CHECK-LABEL: define void @pass_v16i8(<16 x i8>* noalias sret align 16 %{{.*}}, <16 x i8>* %0) // CHECK-VECTOR-LABEL: define <16 x i8> @pass_v16i8(<16 x i8> %{{.*}}) v32i8 pass_v32i8(v32i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_v32i8(<32 x i8>* noalias sret %{{.*}}, <32 x i8>* %0) -// CHECK-VECTOR-LABEL: define void @pass_v32i8(<32 x i8>* noalias sret %{{.*}}, <32 x i8>* %0) +// CHECK-LABEL: define void @pass_v32i8(<32 x i8>* noalias sret align 32 %{{.*}}, <32 x i8>* %0) +// CHECK-VECTOR-LABEL: define void @pass_v32i8(<32 x i8>* noalias sret align 8 %{{.*}}, <32 x i8>* %0) v1i16 pass_v1i16(v1i16 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1i16(<1 x i16>* noalias sret %{{.*}}, <1 x i16>* %0) +// CHECK-LABEL: define void @pass_v1i16(<1 x i16>* noalias sret align 2 %{{.*}}, <1 x i16>* %0) // CHECK-VECTOR-LABEL: define <1 x i16> @pass_v1i16(<1 x i16> %{{.*}}) v2i16 pass_v2i16(v2i16 arg) { return arg; } -// CHECK-LABEL: define void @pass_v2i16(<2 x i16>* noalias sret %{{.*}}, <2 x i16>* %0) +// CHECK-LABEL: define void @pass_v2i16(<2 x i16>* noalias sret align 4 %{{.*}}, <2 x i16>* %0) // CHECK-VECTOR-LABEL: define <2 x i16> @pass_v2i16(<2 x i16> %{{.*}}) v4i16 pass_v4i16(v4i16 arg) { return arg; } -// CHECK-LABEL: define void @pass_v4i16(<4 x i16>* noalias sret %{{.*}}, <4 x i16>* %0) +// CHECK-LABEL: define void @pass_v4i16(<4 x i16>* noalias sret align 8 %{{.*}}, <4 x i16>* %0) // CHECK-VECTOR-LABEL: define <4 x i16> @pass_v4i16(<4 x i16> %{{.*}}) v8i16 pass_v8i16(v8i16 arg) { return arg; } -// CHECK-LABEL: define void @pass_v8i16(<8 x i16>* noalias sret %{{.*}}, <8 x i16>* %0) +// CHECK-LABEL: define void @pass_v8i16(<8 x i16>* noalias sret align 16 %{{.*}}, <8 x i16>* %0) // CHECK-VECTOR-LABEL: define <8 x i16> @pass_v8i16(<8 x i16> %{{.*}}) v1i32 pass_v1i32(v1i32 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1i32(<1 x i32>* noalias sret %{{.*}}, <1 x i32>* %0) +// CHECK-LABEL: define void @pass_v1i32(<1 x i32>* noalias sret align 4 %{{.*}}, <1 x i32>* %0) // CHECK-VECTOR-LABEL: define <1 x i32> @pass_v1i32(<1 x i32> %{{.*}}) v2i32 pass_v2i32(v2i32 arg) { return arg; } -// CHECK-LABEL: define void @pass_v2i32(<2 x i32>* noalias sret %{{.*}}, <2 x i32>* %0) +// CHECK-LABEL: define void @pass_v2i32(<2 x i32>* noalias sret align 8 %{{.*}}, <2 x i32>* %0) // CHECK-VECTOR-LABEL: define <2 x i32> @pass_v2i32(<2 x i32> %{{.*}}) v4i32 pass_v4i32(v4i32 arg) { return arg; } -// CHECK-LABEL: define void @pass_v4i32(<4 x i32>* noalias sret %{{.*}}, <4 x i32>* %0) +// CHECK-LABEL: define void @pass_v4i32(<4 x i32>* noalias sret align 16 %{{.*}}, <4 x i32>* %0) // CHECK-VECTOR-LABEL: define <4 x i32> @pass_v4i32(<4 x i32> %{{.*}}) v1i64 pass_v1i64(v1i64 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1i64(<1 x i64>* noalias sret %{{.*}}, <1 x i64>* %0) +// CHECK-LABEL: define void @pass_v1i64(<1 x i64>* noalias sret align 8 %{{.*}}, <1 x i64>* %0) // CHECK-VECTOR-LABEL: define <1 x i64> @pass_v1i64(<1 x i64> %{{.*}}) v2i64 pass_v2i64(v2i64 arg) { return arg; } -// CHECK-LABEL: define void @pass_v2i64(<2 x i64>* noalias sret %{{.*}}, <2 x i64>* %0) +// CHECK-LABEL: define void @pass_v2i64(<2 x i64>* noalias sret align 16 %{{.*}}, <2 x i64>* %0) // CHECK-VECTOR-LABEL: define <2 x i64> @pass_v2i64(<2 x i64> %{{.*}}) v1i128 pass_v1i128(v1i128 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1i128(<1 x i128>* noalias sret %{{.*}}, <1 x i128>* %0) +// CHECK-LABEL: define void @pass_v1i128(<1 x i128>* noalias sret align 16 %{{.*}}, <1 x i128>* %0) // CHECK-VECTOR-LABEL: define <1 x i128> @pass_v1i128(<1 x i128> %{{.*}}) v1f32 pass_v1f32(v1f32 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1f32(<1 x float>* noalias sret %{{.*}}, <1 x float>* %0) +// CHECK-LABEL: define void @pass_v1f32(<1 x float>* noalias sret align 4 %{{.*}}, <1 x float>* %0) // CHECK-VECTOR-LABEL: define <1 x float> @pass_v1f32(<1 x float> %{{.*}}) v2f32 pass_v2f32(v2f32 arg) { return arg; } -// CHECK-LABEL: define void @pass_v2f32(<2 x float>* noalias sret %{{.*}}, <2 x float>* %0) +// CHECK-LABEL: define void @pass_v2f32(<2 x float>* noalias sret align 8 %{{.*}}, <2 x float>* %0) // CHECK-VECTOR-LABEL: define <2 x float> @pass_v2f32(<2 x float> %{{.*}}) v4f32 pass_v4f32(v4f32 arg) { return arg; } -// CHECK-LABEL: define void @pass_v4f32(<4 x float>* noalias sret %{{.*}}, <4 x float>* %0) +// CHECK-LABEL: define void @pass_v4f32(<4 x float>* noalias sret align 16 %{{.*}}, <4 x float>* %0) // CHECK-VECTOR-LABEL: define <4 x float> @pass_v4f32(<4 x float> %{{.*}}) v1f64 pass_v1f64(v1f64 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1f64(<1 x double>* noalias sret %{{.*}}, <1 x double>* %0) +// CHECK-LABEL: define void @pass_v1f64(<1 x double>* noalias sret align 8 %{{.*}}, <1 x double>* %0) // CHECK-VECTOR-LABEL: define <1 x double> @pass_v1f64(<1 x double> %{{.*}}) v2f64 pass_v2f64(v2f64 arg) { return arg; } -// CHECK-LABEL: define void @pass_v2f64(<2 x double>* noalias sret %{{.*}}, <2 x double>* %0) +// CHECK-LABEL: define void @pass_v2f64(<2 x double>* noalias sret align 16 %{{.*}}, <2 x double>* %0) // CHECK-VECTOR-LABEL: define <2 x double> @pass_v2f64(<2 x double> %{{.*}}) v1f128 pass_v1f128(v1f128 arg) { return arg; } -// CHECK-LABEL: define void @pass_v1f128(<1 x fp128>* noalias sret %{{.*}}, <1 x fp128>* %0) +// CHECK-LABEL: define void @pass_v1f128(<1 x fp128>* noalias sret align 16 %{{.*}}, <1 x fp128>* %0) // CHECK-VECTOR-LABEL: define <1 x fp128> @pass_v1f128(<1 x fp128> %{{.*}}) @@ -142,62 +142,62 @@ v1f128 pass_v1f128(v1f128 arg) { return arg; } struct agg_v1i8 { v1i8 a; }; struct agg_v1i8 pass_agg_v1i8(struct agg_v1i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_v1i8(%struct.agg_v1i8* noalias sret %{{.*}}, i8 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_v1i8(%struct.agg_v1i8* noalias sret %{{.*}}, <1 x i8> %{{.*}}) +// CHECK-LABEL: define void @pass_agg_v1i8(%struct.agg_v1i8* noalias sret align 1 %{{.*}}, i8 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_v1i8(%struct.agg_v1i8* noalias sret align 1 %{{.*}}, <1 x i8> %{{.*}}) struct agg_v2i8 { v2i8 a; }; struct agg_v2i8 pass_agg_v2i8(struct agg_v2i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_v2i8(%struct.agg_v2i8* noalias sret %{{.*}}, i16 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_v2i8(%struct.agg_v2i8* noalias sret %{{.*}}, <2 x i8> %{{.*}}) +// CHECK-LABEL: define void @pass_agg_v2i8(%struct.agg_v2i8* noalias sret align 2 %{{.*}}, i16 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_v2i8(%struct.agg_v2i8* noalias sret align 2 %{{.*}}, <2 x i8> %{{.*}}) struct agg_v4i8 { v4i8 a; }; struct agg_v4i8 pass_agg_v4i8(struct agg_v4i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_v4i8(%struct.agg_v4i8* noalias sret %{{.*}}, i32 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_v4i8(%struct.agg_v4i8* noalias sret %{{.*}}, <4 x i8> %{{.*}}) +// CHECK-LABEL: define void @pass_agg_v4i8(%struct.agg_v4i8* noalias sret align 4 %{{.*}}, i32 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_v4i8(%struct.agg_v4i8* noalias sret align 4 %{{.*}}, <4 x i8> %{{.*}}) struct agg_v8i8 { v8i8 a; }; struct agg_v8i8 pass_agg_v8i8(struct agg_v8i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_v8i8(%struct.agg_v8i8* noalias sret %{{.*}}, i64 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_v8i8(%struct.agg_v8i8* noalias sret %{{.*}}, <8 x i8> %{{.*}}) +// CHECK-LABEL: define void @pass_agg_v8i8(%struct.agg_v8i8* noalias sret align 8 %{{.*}}, i64 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_v8i8(%struct.agg_v8i8* noalias sret align 8 %{{.*}}, <8 x i8> %{{.*}}) struct agg_v16i8 { v16i8 a; }; struct agg_v16i8 pass_agg_v16i8(struct agg_v16i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_v16i8(%struct.agg_v16i8* noalias sret %{{.*}}, %struct.agg_v16i8* %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_v16i8(%struct.agg_v16i8* noalias sret %{{.*}}, <16 x i8> %{{.*}}) +// CHECK-LABEL: define void @pass_agg_v16i8(%struct.agg_v16i8* noalias sret align 16 %{{.*}}, %struct.agg_v16i8* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_v16i8(%struct.agg_v16i8* noalias sret align 8 %{{.*}}, <16 x i8> %{{.*}}) struct agg_v32i8 { v32i8 a; }; struct agg_v32i8 pass_agg_v32i8(struct agg_v32i8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_v32i8(%struct.agg_v32i8* noalias sret %{{.*}}, %struct.agg_v32i8* %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_v32i8(%struct.agg_v32i8* noalias sret %{{.*}}, %struct.agg_v32i8* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_v32i8(%struct.agg_v32i8* noalias sret align 32 %{{.*}}, %struct.agg_v32i8* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_v32i8(%struct.agg_v32i8* noalias sret align 8 %{{.*}}, %struct.agg_v32i8* %{{.*}}) // Verify that the following are *not* vector-like aggregate types struct agg_novector1 { v4i8 a; v4i8 b; }; struct agg_novector1 pass_agg_novector1(struct agg_novector1 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_novector1(%struct.agg_novector1* noalias sret %{{.*}}, i64 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_novector1(%struct.agg_novector1* noalias sret %{{.*}}, i64 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_novector1(%struct.agg_novector1* noalias sret align 4 %{{.*}}, i64 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_novector1(%struct.agg_novector1* noalias sret align 4 %{{.*}}, i64 %{{.*}}) struct agg_novector2 { v4i8 a; float b; }; struct agg_novector2 pass_agg_novector2(struct agg_novector2 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_novector2(%struct.agg_novector2* noalias sret %{{.*}}, i64 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_novector2(%struct.agg_novector2* noalias sret %{{.*}}, i64 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_novector2(%struct.agg_novector2* noalias sret align 4 %{{.*}}, i64 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_novector2(%struct.agg_novector2* noalias sret align 4 %{{.*}}, i64 %{{.*}}) struct agg_novector3 { v4i8 a; int : 0; }; struct agg_novector3 pass_agg_novector3(struct agg_novector3 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_novector3(%struct.agg_novector3* noalias sret %{{.*}}, i32 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_novector3(%struct.agg_novector3* noalias sret %{{.*}}, i32 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_novector3(%struct.agg_novector3* noalias sret align 4 %{{.*}}, i32 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_novector3(%struct.agg_novector3* noalias sret align 4 %{{.*}}, i32 %{{.*}}) struct agg_novector4 { v4i8 a __attribute__((aligned (8))); }; struct agg_novector4 pass_agg_novector4(struct agg_novector4 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_novector4(%struct.agg_novector4* noalias sret %{{.*}}, i64 %{{.*}}) -// CHECK-VECTOR-LABEL: define void @pass_agg_novector4(%struct.agg_novector4* noalias sret %{{.*}}, i64 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_novector4(%struct.agg_novector4* noalias sret align 8 %{{.*}}, i64 %{{.*}}) +// CHECK-VECTOR-LABEL: define void @pass_agg_novector4(%struct.agg_novector4* noalias sret align 8 %{{.*}}, i64 %{{.*}}) // Accessing variable argument lists v1i8 va_v1i8(__builtin_va_list l) { return __builtin_va_arg(l, v1i8); } -// CHECK-LABEL: define void @va_v1i8(<1 x i8>* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_v1i8(<1 x i8>* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -229,7 +229,7 @@ v1i8 va_v1i8(__builtin_va_list l) { return __builtin_va_arg(l, v1i8); } // CHECK-VECTOR: ret <1 x i8> [[RET]] v2i8 va_v2i8(__builtin_va_list l) { return __builtin_va_arg(l, v2i8); } -// CHECK-LABEL: define void @va_v2i8(<2 x i8>* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_v2i8(<2 x i8>* noalias sret align 2 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -261,7 +261,7 @@ v2i8 va_v2i8(__builtin_va_list l) { return __builtin_va_arg(l, v2i8); } // CHECK-VECTOR: ret <2 x i8> [[RET]] v4i8 va_v4i8(__builtin_va_list l) { return __builtin_va_arg(l, v4i8); } -// CHECK-LABEL: define void @va_v4i8(<4 x i8>* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_v4i8(<4 x i8>* noalias sret align 4 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -293,7 +293,7 @@ v4i8 va_v4i8(__builtin_va_list l) { return __builtin_va_arg(l, v4i8); } // CHECK-VECTOR: ret <4 x i8> [[RET]] v8i8 va_v8i8(__builtin_va_list l) { return __builtin_va_arg(l, v8i8); } -// CHECK-LABEL: define void @va_v8i8(<8 x i8>* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_v8i8(<8 x i8>* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -325,7 +325,7 @@ v8i8 va_v8i8(__builtin_va_list l) { return __builtin_va_arg(l, v8i8); } // CHECK-VECTOR: ret <8 x i8> [[RET]] v16i8 va_v16i8(__builtin_va_list l) { return __builtin_va_arg(l, v16i8); } -// CHECK-LABEL: define void @va_v16i8(<16 x i8>* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_v16i8(<16 x i8>* noalias sret align 16 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -357,7 +357,7 @@ v16i8 va_v16i8(__builtin_va_list l) { return __builtin_va_arg(l, v16i8); } // CHECK-VECTOR: ret <16 x i8> [[RET]] v32i8 va_v32i8(__builtin_va_list l) { return __builtin_va_arg(l, v32i8); } -// CHECK-LABEL: define void @va_v32i8(<32 x i8>* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_v32i8(<32 x i8>* noalias sret align 32 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -379,7 +379,7 @@ v32i8 va_v32i8(__builtin_va_list l) { return __builtin_va_arg(l, v32i8); } // CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi <32 x i8>** [ [[REG_ADDR]], %{{.*}} ], [ [[MEM_ADDR]], %{{.*}} ] // CHECK: [[INDIRECT_ARG:%[^ ]+]] = load <32 x i8>*, <32 x i8>** [[VA_ARG_ADDR]] // CHECK: ret void -// CHECK-VECTOR-LABEL: define void @va_v32i8(<32 x i8>* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @va_v32i8(<32 x i8>* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK-VECTOR: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK-VECTOR: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK-VECTOR: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -403,7 +403,7 @@ v32i8 va_v32i8(__builtin_va_list l) { return __builtin_va_arg(l, v32i8); } // CHECK-VECTOR: ret void struct agg_v1i8 va_agg_v1i8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_v1i8); } -// CHECK-LABEL: define void @va_agg_v1i8(%struct.agg_v1i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_agg_v1i8(%struct.agg_v1i8* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -424,7 +424,7 @@ struct agg_v1i8 va_agg_v1i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK: store i8* [[OVERFLOW_ARG_AREA2]], i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi %struct.agg_v1i8* [ [[REG_ADDR]], %{{.*}} ], [ [[MEM_ADDR]], %{{.*}} ] // CHECK: ret void -// CHECK-VECTOR-LABEL: define void @va_agg_v1i8(%struct.agg_v1i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @va_agg_v1i8(%struct.agg_v1i8* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK-VECTOR: [[OVERFLOW_ARG_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 2 // CHECK-VECTOR: [[OVERFLOW_ARG_AREA:%[^ ]+]] = load i8*, i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK-VECTOR: [[MEM_ADDR:%[^ ]+]] = bitcast i8* [[OVERFLOW_ARG_AREA]] to %struct.agg_v1i8* @@ -433,7 +433,7 @@ struct agg_v1i8 va_agg_v1i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK-VECTOR: ret void struct agg_v2i8 va_agg_v2i8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_v2i8); } -// CHECK-LABEL: define void @va_agg_v2i8(%struct.agg_v2i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_agg_v2i8(%struct.agg_v2i8* noalias sret align 2 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -454,7 +454,7 @@ struct agg_v2i8 va_agg_v2i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK: store i8* [[OVERFLOW_ARG_AREA2]], i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi %struct.agg_v2i8* [ [[REG_ADDR]], %{{.*}} ], [ [[MEM_ADDR]], %{{.*}} ] // CHECK: ret void -// CHECK-VECTOR-LABEL: define void @va_agg_v2i8(%struct.agg_v2i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @va_agg_v2i8(%struct.agg_v2i8* noalias sret align 2 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK-VECTOR: [[OVERFLOW_ARG_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 2 // CHECK-VECTOR: [[OVERFLOW_ARG_AREA:%[^ ]+]] = load i8*, i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK-VECTOR: [[MEM_ADDR:%[^ ]+]] = bitcast i8* [[OVERFLOW_ARG_AREA]] to %struct.agg_v2i8* @@ -463,7 +463,7 @@ struct agg_v2i8 va_agg_v2i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK-VECTOR: ret void struct agg_v4i8 va_agg_v4i8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_v4i8); } -// CHECK-LABEL: define void @va_agg_v4i8(%struct.agg_v4i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_agg_v4i8(%struct.agg_v4i8* noalias sret align 4 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -484,7 +484,7 @@ struct agg_v4i8 va_agg_v4i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK: store i8* [[OVERFLOW_ARG_AREA2]], i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi %struct.agg_v4i8* [ [[REG_ADDR]], %{{.*}} ], [ [[MEM_ADDR]], %{{.*}} ] // CHECK: ret void -// CHECK-VECTOR-LABEL: define void @va_agg_v4i8(%struct.agg_v4i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @va_agg_v4i8(%struct.agg_v4i8* noalias sret align 4 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK-VECTOR: [[OVERFLOW_ARG_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 2 // CHECK-VECTOR: [[OVERFLOW_ARG_AREA:%[^ ]+]] = load i8*, i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK-VECTOR: [[MEM_ADDR:%[^ ]+]] = bitcast i8* [[OVERFLOW_ARG_AREA]] to %struct.agg_v4i8* @@ -493,7 +493,7 @@ struct agg_v4i8 va_agg_v4i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK-VECTOR: ret void struct agg_v8i8 va_agg_v8i8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_v8i8); } -// CHECK-LABEL: define void @va_agg_v8i8(%struct.agg_v8i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_agg_v8i8(%struct.agg_v8i8* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -514,7 +514,7 @@ struct agg_v8i8 va_agg_v8i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK: store i8* [[OVERFLOW_ARG_AREA2]], i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi %struct.agg_v8i8* [ [[REG_ADDR]], %{{.*}} ], [ [[MEM_ADDR]], %{{.*}} ] // CHECK: ret void -// CHECK-VECTOR-LABEL: define void @va_agg_v8i8(%struct.agg_v8i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @va_agg_v8i8(%struct.agg_v8i8* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK-VECTOR: [[OVERFLOW_ARG_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 2 // CHECK-VECTOR: [[OVERFLOW_ARG_AREA:%[^ ]+]] = load i8*, i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK-VECTOR: [[MEM_ADDR:%[^ ]+]] = bitcast i8* [[OVERFLOW_ARG_AREA]] to %struct.agg_v8i8* @@ -523,7 +523,7 @@ struct agg_v8i8 va_agg_v8i8(__builtin_va_list l) { return __builtin_va_arg(l, st // CHECK-VECTOR: ret void struct agg_v16i8 va_agg_v16i8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_v16i8); } -// CHECK-LABEL: define void @va_agg_v16i8(%struct.agg_v16i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_agg_v16i8(%struct.agg_v16i8* noalias sret align 16 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -545,7 +545,7 @@ struct agg_v16i8 va_agg_v16i8(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi %struct.agg_v16i8** [ [[REG_ADDR]], %{{.*}} ], [ [[MEM_ADDR]], %{{.*}} ] // CHECK: [[INDIRECT_ARG:%[^ ]+]] = load %struct.agg_v16i8*, %struct.agg_v16i8** [[VA_ARG_ADDR]] // CHECK: ret void -// CHECK-VECTOR-LABEL: define void @va_agg_v16i8(%struct.agg_v16i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @va_agg_v16i8(%struct.agg_v16i8* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK-VECTOR: [[OVERFLOW_ARG_AREA_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 2 // CHECK-VECTOR: [[OVERFLOW_ARG_AREA:%[^ ]+]] = load i8*, i8** [[OVERFLOW_ARG_AREA_PTR]] // CHECK-VECTOR: [[MEM_ADDR:%[^ ]+]] = bitcast i8* [[OVERFLOW_ARG_AREA]] to %struct.agg_v16i8* @@ -554,7 +554,7 @@ struct agg_v16i8 va_agg_v16i8(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK-VECTOR: ret void struct agg_v32i8 va_agg_v32i8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_v32i8); } -// CHECK-LABEL: define void @va_agg_v32i8(%struct.agg_v32i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_agg_v32i8(%struct.agg_v32i8* noalias sret align 32 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -576,7 +576,7 @@ struct agg_v32i8 va_agg_v32i8(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: [[VA_ARG_ADDR:%[^ ]+]] = phi %struct.agg_v32i8** [ [[REG_ADDR]], %{{.*}} ], [ [[MEM_ADDR]], %{{.*}} ] // CHECK: [[INDIRECT_ARG:%[^ ]+]] = load %struct.agg_v32i8*, %struct.agg_v32i8** [[VA_ARG_ADDR]] // CHECK: ret void -// CHECK-VECTOR-LABEL: define void @va_agg_v32i8(%struct.agg_v32i8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-VECTOR-LABEL: define void @va_agg_v32i8(%struct.agg_v32i8* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK-VECTOR: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK-VECTOR: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK-VECTOR: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 diff --git a/clang/test/CodeGen/systemz-abi.c b/clang/test/CodeGen/systemz-abi.c index 3511983e32d76..88fd4bf9c1269 100644 --- a/clang/test/CodeGen/systemz-abi.c +++ b/clang/test/CodeGen/systemz-abi.c @@ -33,7 +33,7 @@ long long pass_longlong(long long arg) { return arg; } // CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}}) __int128 pass_int128(__int128 arg) { return arg; } -// CHECK-LABEL: define void @pass_int128(i128* noalias sret %{{.*}}, i128* %0) +// CHECK-LABEL: define void @pass_int128(i128* noalias sret align 16 %{{.*}}, i128* %0) float pass_float(float arg) { return arg; } // CHECK-LABEL: define float @pass_float(float %{{.*}}) @@ -42,111 +42,111 @@ double pass_double(double arg) { return arg; } // CHECK-LABEL: define double @pass_double(double %{{.*}}) long double pass_longdouble(long double arg) { return arg; } -// CHECK-LABEL: define void @pass_longdouble(fp128* noalias sret %{{.*}}, fp128* %0) +// CHECK-LABEL: define void @pass_longdouble(fp128* noalias sret align 8 %{{.*}}, fp128* %0) // Complex types _Complex char pass_complex_char(_Complex char arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_char({ i8, i8 }* noalias sret %{{.*}}, { i8, i8 }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_char({ i8, i8 }* noalias sret align 1 %{{.*}}, { i8, i8 }* %{{.*}}arg) _Complex short pass_complex_short(_Complex short arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_short({ i16, i16 }* noalias sret %{{.*}}, { i16, i16 }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_short({ i16, i16 }* noalias sret align 2 %{{.*}}, { i16, i16 }* %{{.*}}arg) _Complex int pass_complex_int(_Complex int arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_int({ i32, i32 }* noalias sret %{{.*}}, { i32, i32 }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_int({ i32, i32 }* noalias sret align 4 %{{.*}}, { i32, i32 }* %{{.*}}arg) _Complex long pass_complex_long(_Complex long arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_long({ i64, i64 }* noalias sret %{{.*}}, { i64, i64 }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_long({ i64, i64 }* noalias sret align 8 %{{.*}}, { i64, i64 }* %{{.*}}arg) _Complex long long pass_complex_longlong(_Complex long long arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_longlong({ i64, i64 }* noalias sret %{{.*}}, { i64, i64 }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_longlong({ i64, i64 }* noalias sret align 8 %{{.*}}, { i64, i64 }* %{{.*}}arg) _Complex float pass_complex_float(_Complex float arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_float({ float, float }* noalias sret %{{.*}}, { float, float }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_float({ float, float }* noalias sret align 4 %{{.*}}, { float, float }* %{{.*}}arg) _Complex double pass_complex_double(_Complex double arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_double({ double, double }* noalias sret %{{.*}}, { double, double }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_double({ double, double }* noalias sret align 8 %{{.*}}, { double, double }* %{{.*}}arg) _Complex long double pass_complex_longdouble(_Complex long double arg) { return arg; } -// CHECK-LABEL: define void @pass_complex_longdouble({ fp128, fp128 }* noalias sret %{{.*}}, { fp128, fp128 }* %{{.*}}arg) +// CHECK-LABEL: define void @pass_complex_longdouble({ fp128, fp128 }* noalias sret align 8 %{{.*}}, { fp128, fp128 }* %{{.*}}arg) // Aggregate types struct agg_1byte { char a[1]; }; struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_1byte(%struct.agg_1byte* noalias sret %{{.*}}, i8 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_1byte(%struct.agg_1byte* noalias sret align 1 %{{.*}}, i8 %{{.*}}) struct agg_2byte { char a[2]; }; struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_2byte(%struct.agg_2byte* noalias sret %{{.*}}, i16 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_2byte(%struct.agg_2byte* noalias sret align 1 %{{.*}}, i16 %{{.*}}) struct agg_3byte { char a[3]; }; struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_3byte(%struct.agg_3byte* noalias sret %{{.*}}, %struct.agg_3byte* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_3byte(%struct.agg_3byte* noalias sret align 1 %{{.*}}, %struct.agg_3byte* %{{.*}}) struct agg_4byte { char a[4]; }; struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_4byte(%struct.agg_4byte* noalias sret %{{.*}}, i32 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_4byte(%struct.agg_4byte* noalias sret align 1 %{{.*}}, i32 %{{.*}}) struct agg_5byte { char a[5]; }; struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_5byte(%struct.agg_5byte* noalias sret %{{.*}}, %struct.agg_5byte* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_5byte(%struct.agg_5byte* noalias sret align 1 %{{.*}}, %struct.agg_5byte* %{{.*}}) struct agg_6byte { char a[6]; }; struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_6byte(%struct.agg_6byte* noalias sret %{{.*}}, %struct.agg_6byte* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_6byte(%struct.agg_6byte* noalias sret align 1 %{{.*}}, %struct.agg_6byte* %{{.*}}) struct agg_7byte { char a[7]; }; struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_7byte(%struct.agg_7byte* noalias sret %{{.*}}, %struct.agg_7byte* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_7byte(%struct.agg_7byte* noalias sret align 1 %{{.*}}, %struct.agg_7byte* %{{.*}}) struct agg_8byte { char a[8]; }; struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_8byte(%struct.agg_8byte* noalias sret %{{.*}}, i64 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_8byte(%struct.agg_8byte* noalias sret align 1 %{{.*}}, i64 %{{.*}}) struct agg_16byte { char a[16]; }; struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_16byte(%struct.agg_16byte* noalias sret %{{.*}}, %struct.agg_16byte* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_16byte(%struct.agg_16byte* noalias sret align 1 %{{.*}}, %struct.agg_16byte* %{{.*}}) // Float-like aggregate types struct agg_float { float a; }; struct agg_float pass_agg_float(struct agg_float arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_float(%struct.agg_float* noalias sret %{{.*}}, float %{{.*}}) +// CHECK-LABEL: define void @pass_agg_float(%struct.agg_float* noalias sret align 4 %{{.*}}, float %{{.*}}) struct agg_double { double a; }; struct agg_double pass_agg_double(struct agg_double arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_double(%struct.agg_double* noalias sret %{{.*}}, double %{{.*}}) +// CHECK-LABEL: define void @pass_agg_double(%struct.agg_double* noalias sret align 8 %{{.*}}, double %{{.*}}) struct agg_longdouble { long double a; }; struct agg_longdouble pass_agg_longdouble(struct agg_longdouble arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_longdouble(%struct.agg_longdouble* noalias sret %{{.*}}, %struct.agg_longdouble* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_longdouble(%struct.agg_longdouble* noalias sret align 8 %{{.*}}, %struct.agg_longdouble* %{{.*}}) struct agg_float_a8 { float a __attribute__((aligned (8))); }; struct agg_float_a8 pass_agg_float_a8(struct agg_float_a8 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_float_a8(%struct.agg_float_a8* noalias sret %{{.*}}, double %{{.*}}) +// CHECK-LABEL: define void @pass_agg_float_a8(%struct.agg_float_a8* noalias sret align 8 %{{.*}}, double %{{.*}}) struct agg_float_a16 { float a __attribute__((aligned (16))); }; struct agg_float_a16 pass_agg_float_a16(struct agg_float_a16 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_float_a16(%struct.agg_float_a16* noalias sret %{{.*}}, %struct.agg_float_a16* %{{.*}}) +// CHECK-LABEL: define void @pass_agg_float_a16(%struct.agg_float_a16* noalias sret align 16 %{{.*}}, %struct.agg_float_a16* %{{.*}}) // Verify that the following are *not* float-like aggregate types struct agg_nofloat1 { float a; float b; }; struct agg_nofloat1 pass_agg_nofloat1(struct agg_nofloat1 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_nofloat1(%struct.agg_nofloat1* noalias sret %{{.*}}, i64 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_nofloat1(%struct.agg_nofloat1* noalias sret align 4 %{{.*}}, i64 %{{.*}}) struct agg_nofloat2 { float a; int b; }; struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_nofloat2(%struct.agg_nofloat2* noalias sret %{{.*}}, i64 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_nofloat2(%struct.agg_nofloat2* noalias sret align 4 %{{.*}}, i64 %{{.*}}) struct agg_nofloat3 { float a; int : 0; }; struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; } -// CHECK-LABEL: define void @pass_agg_nofloat3(%struct.agg_nofloat3* noalias sret %{{.*}}, i32 %{{.*}}) +// CHECK-LABEL: define void @pass_agg_nofloat3(%struct.agg_nofloat3* noalias sret align 4 %{{.*}}, i32 %{{.*}}) // Accessing variable argument lists @@ -248,7 +248,7 @@ double va_double(__builtin_va_list l) { return __builtin_va_arg(l, double); } // CHECK: ret double [[RET]] long double va_longdouble(__builtin_va_list l) { return __builtin_va_arg(l, long double); } -// CHECK-LABEL: define void @va_longdouble(fp128* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}}) +// CHECK-LABEL: define void @va_longdouble(fp128* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}}) // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -274,7 +274,7 @@ long double va_longdouble(__builtin_va_list l) { return __builtin_va_arg(l, long // CHECK: ret void _Complex char va_complex_char(__builtin_va_list l) { return __builtin_va_arg(l, _Complex char); } -// CHECK-LABEL: define void @va_complex_char({ i8, i8 }* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_complex_char({ i8, i8 }* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -298,7 +298,7 @@ _Complex char va_complex_char(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: ret void struct agg_1byte va_agg_1byte(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_1byte); } -// CHECK-LABEL: define void @va_agg_1byte(%struct.agg_1byte* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_1byte(%struct.agg_1byte* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -321,7 +321,7 @@ struct agg_1byte va_agg_1byte(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: ret void struct agg_2byte va_agg_2byte(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_2byte); } -// CHECK-LABEL: define void @va_agg_2byte(%struct.agg_2byte* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_2byte(%struct.agg_2byte* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -344,7 +344,7 @@ struct agg_2byte va_agg_2byte(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: ret void struct agg_3byte va_agg_3byte(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_3byte); } -// CHECK-LABEL: define void @va_agg_3byte(%struct.agg_3byte* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_3byte(%struct.agg_3byte* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -368,7 +368,7 @@ struct agg_3byte va_agg_3byte(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: ret void struct agg_4byte va_agg_4byte(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_4byte); } -// CHECK-LABEL: define void @va_agg_4byte(%struct.agg_4byte* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_4byte(%struct.agg_4byte* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -391,7 +391,7 @@ struct agg_4byte va_agg_4byte(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: ret void struct agg_8byte va_agg_8byte(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_8byte); } -// CHECK-LABEL: define void @va_agg_8byte(%struct.agg_8byte* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_8byte(%struct.agg_8byte* noalias sret align 1 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -414,7 +414,7 @@ struct agg_8byte va_agg_8byte(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: ret void struct agg_float va_agg_float(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_float); } -// CHECK-LABEL: define void @va_agg_float(%struct.agg_float* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_float(%struct.agg_float* noalias sret align 4 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 @@ -437,7 +437,7 @@ struct agg_float va_agg_float(__builtin_va_list l) { return __builtin_va_arg(l, // CHECK: ret void struct agg_double va_agg_double(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_double); } -// CHECK-LABEL: define void @va_agg_double(%struct.agg_double* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_double(%struct.agg_double* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 @@ -460,7 +460,7 @@ struct agg_double va_agg_double(__builtin_va_list l) { return __builtin_va_arg(l // CHECK: ret void struct agg_longdouble va_agg_longdouble(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_longdouble); } -// CHECK-LABEL: define void @va_agg_longdouble(%struct.agg_longdouble* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_longdouble(%struct.agg_longdouble* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -484,7 +484,7 @@ struct agg_longdouble va_agg_longdouble(__builtin_va_list l) { return __builtin_ // CHECK: ret void struct agg_float_a8 va_agg_float_a8(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_float_a8); } -// CHECK-LABEL: define void @va_agg_float_a8(%struct.agg_float_a8* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_float_a8(%struct.agg_float_a8* noalias sret align 8 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 1 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 4 @@ -507,7 +507,7 @@ struct agg_float_a8 va_agg_float_a8(__builtin_va_list l) { return __builtin_va_a // CHECK: ret void struct agg_float_a16 va_agg_float_a16(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_float_a16); } -// CHECK-LABEL: define void @va_agg_float_a16(%struct.agg_float_a16* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_float_a16(%struct.agg_float_a16* noalias sret align 16 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -531,7 +531,7 @@ struct agg_float_a16 va_agg_float_a16(__builtin_va_list l) { return __builtin_va // CHECK: ret void struct agg_nofloat1 va_agg_nofloat1(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_nofloat1); } -// CHECK-LABEL: define void @va_agg_nofloat1(%struct.agg_nofloat1* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_nofloat1(%struct.agg_nofloat1* noalias sret align 4 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -554,7 +554,7 @@ struct agg_nofloat1 va_agg_nofloat1(__builtin_va_list l) { return __builtin_va_a // CHECK: ret void struct agg_nofloat2 va_agg_nofloat2(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_nofloat2); } -// CHECK-LABEL: define void @va_agg_nofloat2(%struct.agg_nofloat2* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_nofloat2(%struct.agg_nofloat2* noalias sret align 4 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 @@ -577,7 +577,7 @@ struct agg_nofloat2 va_agg_nofloat2(__builtin_va_list l) { return __builtin_va_a // CHECK: ret void struct agg_nofloat3 va_agg_nofloat3(__builtin_va_list l) { return __builtin_va_arg(l, struct agg_nofloat3); } -// CHECK-LABEL: define void @va_agg_nofloat3(%struct.agg_nofloat3* noalias sret %{{.*}}, %struct.__va_list_tag* %{{.*}} +// CHECK-LABEL: define void @va_agg_nofloat3(%struct.agg_nofloat3* noalias sret align 4 %{{.*}}, %struct.__va_list_tag* %{{.*}} // CHECK: [[REG_COUNT_PTR:%[^ ]+]] = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %{{.*}}, i32 0, i32 0 // CHECK: [[REG_COUNT:%[^ ]+]] = load i64, i64* [[REG_COUNT_PTR]] // CHECK: [[FITS_IN_REGS:%[^ ]+]] = icmp ult i64 [[REG_COUNT]], 5 diff --git a/clang/test/CodeGen/systemz-abi.cpp b/clang/test/CodeGen/systemz-abi.cpp index 0249e9f63c9bf..5589c7eba1abd 100644 --- a/clang/test/CodeGen/systemz-abi.cpp +++ b/clang/test/CodeGen/systemz-abi.cpp @@ -5,5 +5,4 @@ struct agg_float_cpp { float a; int : 0; }; struct agg_float_cpp pass_agg_float_cpp(struct agg_float_cpp arg) { return arg; } -// CHECK-LABEL: define void @_Z18pass_agg_float_cpp13agg_float_cpp(%struct.agg_float_cpp* noalias sret %{{.*}}, float %{{.*}}) - +// CHECK-LABEL: define void @_Z18pass_agg_float_cpp13agg_float_cpp(%struct.agg_float_cpp* noalias sret align 4 %{{.*}}, float %{{.*}}) diff --git a/clang/test/CodeGen/systemz-inline-asm.c b/clang/test/CodeGen/systemz-inline-asm.c index 7c273dac579e8..2dc5023c55cb0 100644 --- a/clang/test/CodeGen/systemz-inline-asm.c +++ b/clang/test/CodeGen/systemz-inline-asm.c @@ -123,7 +123,7 @@ double test_f64(double f, double g) { long double test_f128(long double f, long double g) { asm("axbr %0, %2" : "=f" (f) : "0" (f), "f" (g)); return f; -// CHECK: define void @test_f128(fp128* noalias nocapture sret [[DEST:%.*]], fp128* nocapture readonly %0, fp128* nocapture readonly %1) +// CHECK: define void @test_f128(fp128* noalias nocapture sret align 8 [[DEST:%.*]], fp128* nocapture readonly %0, fp128* nocapture readonly %1) // CHECK: %f = load fp128, fp128* %0 // CHECK: %g = load fp128, fp128* %1 // CHECK: [[RESULT:%.*]] = tail call fp128 asm "axbr $0, $2", "=f,0,f"(fp128 %f, fp128 %g) diff --git a/clang/test/CodeGen/vectorcall.c b/clang/test/CodeGen/vectorcall.c index c8e8931a084c5..0aa4346fcc5c2 100644 --- a/clang/test/CodeGen/vectorcall.c +++ b/clang/test/CodeGen/vectorcall.c @@ -86,8 +86,8 @@ struct HVA4 __vectorcall hva6(struct HVA4 a, struct HVA4 b) { return b;} // X64: define dso_local x86_vectorcallcc %struct.HVA4 @"\01hva6@@128"(%struct.HVA4 inreg %a.coerce, %struct.HVA4* %b) struct HVA5 __vectorcall hva7() {struct HVA5 a = {}; return a;} -// X32: define dso_local x86_vectorcallcc void @"\01hva7@@0"(%struct.HVA5* inreg noalias sret %agg.result) -// X64: define dso_local x86_vectorcallcc void @"\01hva7@@0"(%struct.HVA5* noalias sret %agg.result) +// X32: define dso_local x86_vectorcallcc void @"\01hva7@@0"(%struct.HVA5* inreg noalias sret align 16 %agg.result) +// X64: define dso_local x86_vectorcallcc void @"\01hva7@@0"(%struct.HVA5* noalias sret align 16 %agg.result) v4f32 __vectorcall hva8(v4f32 a, v4f32 b, v4f32 c, v4f32 d, int e, v4f32 f) {return f;} // X32: define dso_local x86_vectorcallcc <4 x float> @"\01hva8@@84"(<4 x float> %a, <4 x float> %b, <4 x float> %c, <4 x float> %d, i32 inreg %e, <4 x float> %f) diff --git a/clang/test/CodeGen/wasm-arguments.c b/clang/test/CodeGen/wasm-arguments.c index c92028bae2db0..2ee3c3f4cecfb 100644 --- a/clang/test/CodeGen/wasm-arguments.c +++ b/clang/test/CodeGen/wasm-arguments.c @@ -34,8 +34,8 @@ typedef struct { int dd; } s3; // Structs should be returned sret and not simplified by the frontend. -// WEBASSEMBLY32: define void @f3(%struct.s3* noalias sret %agg.result) -// WEBASSEMBLY64: define void @f3(%struct.s3* noalias sret %agg.result) +// WEBASSEMBLY32: define void @f3(%struct.s3* noalias sret align 4 %agg.result) +// WEBASSEMBLY64: define void @f3(%struct.s3* noalias sret align 4 %agg.result) s3 f3(void) { s3 foo; return foo; diff --git a/clang/test/CodeGen/wasm-varargs.c b/clang/test/CodeGen/wasm-varargs.c index 23506875ac9d7..ba1f2d632b4ec 100644 --- a/clang/test/CodeGen/wasm-varargs.c +++ b/clang/test/CodeGen/wasm-varargs.c @@ -80,7 +80,7 @@ struct S test_struct(char *fmt, ...) { return v; } -// CHECK: define void @test_struct([[STRUCT_S:%[^,=]+]]*{{.*}} noalias sret [[AGG_RESULT:%.*]], i8*{{.*}} %fmt, ...) {{.*}} { +// CHECK: define void @test_struct([[STRUCT_S:%[^,=]+]]*{{.*}} noalias sret align 4 [[AGG_RESULT:%.*]], i8*{{.*}} %fmt, ...) {{.*}} { // CHECK: [[FMT_ADDR:%[^,=]+]] = alloca i8*, align 4 // CHECK-NEXT: [[VA:%[^,=]+]] = alloca i8*, align 4 // CHECK-NEXT: store i8* %fmt, i8** [[FMT_ADDR]], align 4 @@ -112,7 +112,7 @@ struct S test_empty_struct(char *fmt, ...) { return v; } -// CHECK: define void @test_empty_struct([[STRUCT_S:%[^,=]+]]*{{.*}} noalias sret [[AGG_RESULT:%.*]], i8*{{.*}} %fmt, ...) {{.*}} { +// CHECK: define void @test_empty_struct([[STRUCT_S:%[^,=]+]]*{{.*}} noalias sret align 4 [[AGG_RESULT:%.*]], i8*{{.*}} %fmt, ...) {{.*}} { // CHECK: [[FMT_ADDR:%[^,=]+]] = alloca i8*, align 4 // CHECK-NEXT: [[VA:%[^,=]+]] = alloca i8*, align 4 // CHECK-NEXT: [[U:%[^,=]+]] = alloca [[STRUCT_Z:%[^,=]+]], align 1 diff --git a/clang/test/CodeGen/windows-struct-abi.c b/clang/test/CodeGen/windows-struct-abi.c index 5ffc4fad64730..9fa175f136587 100644 --- a/clang/test/CodeGen/windows-struct-abi.c +++ b/clang/test/CodeGen/windows-struct-abi.c @@ -34,7 +34,7 @@ struct f4 { struct f4 return_f4(void) { while (1); } -// CHECK: define dso_local void @return_f4(%struct.f4* noalias sret %agg.result) +// CHECK: define dso_local void @return_f4(%struct.f4* noalias sret align 4 %agg.result) void receive_f4(struct f4 a0) { } diff --git a/clang/test/CodeGen/x86_32-arguments-darwin.c b/clang/test/CodeGen/x86_32-arguments-darwin.c index 71b8a2b9fc848..c88c1b8603b67 100644 --- a/clang/test/CodeGen/x86_32-arguments-darwin.c +++ b/clang/test/CodeGen/x86_32-arguments-darwin.c @@ -71,7 +71,7 @@ struct s10 { // Small vectors and 1 x {i64,double} are returned in registers // CHECK: i32 @f11() -// CHECK: void @f12(<2 x i32>* noalias sret %agg.result) +// CHECK: void @f12(<2 x i32>* noalias sret align 8 %agg.result) // CHECK: i64 @f13() // CHECK: i64 @f14() // CHECK: <2 x i64> @f15() @@ -93,11 +93,11 @@ T16 f16(void) { while (1) {} } // 128-bits). // CHECK: i32 @f17() -// CHECK: void @f18(%{{.*}}* noalias sret %agg.result) -// CHECK: void @f19(%{{.*}}* noalias sret %agg.result) -// CHECK: void @f20(%{{.*}}* noalias sret %agg.result) -// CHECK: void @f21(%{{.*}}* noalias sret %agg.result) -// CHECK: void @f22(%{{.*}}* noalias sret %agg.result) +// CHECK: void @f18(%{{.*}}* noalias sret align 8 %agg.result) +// CHECK: void @f19(%{{.*}}* noalias sret align 8 %agg.result) +// CHECK: void @f20(%{{.*}}* noalias sret align 8 %agg.result) +// CHECK: void @f21(%{{.*}}* noalias sret align 16 %agg.result) +// CHECK: void @f22(%{{.*}}* noalias sret align 16 %agg.result) struct { T11 a; } f17(void) { while (1) {} } struct { T12 a; } f18(void) { while (1) {} } struct { T13 a; } f19(void) { while (1) {} } @@ -116,11 +116,11 @@ struct { struct {} a; struct { float a[1]; } b; } f25(void) { while (1) {} } // Small structures are handled recursively // CHECK: i32 @f26() -// CHECK: void @f27(%struct.s27* noalias sret %agg.result) +// CHECK: void @f27(%struct.s27* noalias sret align 1 %agg.result) struct s26 { struct { char a, b; } a; struct { char a, b; } b; } f26(void) { while (1) {} } struct s27 { struct { char a, b, c; } a; struct { char a; } b; } f27(void) { while (1) {} } -// CHECK: void @f28(%struct.s28* noalias sret %agg.result) +// CHECK: void @f28(%struct.s28* noalias sret align 4 %agg.result) struct s28 { int a; int b[]; } f28(void) { while (1) {} } // CHECK-LABEL: define i16 @f29() @@ -150,7 +150,7 @@ struct s36 { struct { int : 0; } a[2][10]; char b; char c; } f36(void) { while ( // CHECK-LABEL: define float @f37() struct s37 { float c[1][1]; } f37(void) { while (1) {} } -// CHECK-LABEL: define void @f38(%struct.s38* noalias sret %agg.result) +// CHECK-LABEL: define void @f38(%struct.s38* noalias sret align 2 %agg.result) struct s38 { char a[3]; short b; } f38(void) { while (1) {} } // CHECK-LABEL: define void @f39(%struct.s39* byval(%struct.s39) align 16 %x) diff --git a/clang/test/CodeGen/x86_32-arguments-iamcu.c b/clang/test/CodeGen/x86_32-arguments-iamcu.c index e391c711ea101..a134f5d84a77b 100644 --- a/clang/test/CodeGen/x86_32-arguments-iamcu.c +++ b/clang/test/CodeGen/x86_32-arguments-iamcu.c @@ -58,7 +58,7 @@ st4_t retSmallStruct(st4_t r) { return r; } // CHECK-LABEL: define i64 @retPaddedStruct(i32 %r.coerce0, i32 %r.coerce1) st5_t retPaddedStruct(st5_t r) { return r; } -// CHECK-LABEL: define void @retLargeStruct(%struct.st12_t* noalias sret %agg.result, i32 %i1, %struct.st12_t* byval(%struct.st12_t) align 4 %r) +// CHECK-LABEL: define void @retLargeStruct(%struct.st12_t* noalias sret align 4 %agg.result, i32 %i1, %struct.st12_t* byval(%struct.st12_t) align 4 %r) st12_t retLargeStruct(int i1, st12_t r) { return r; } // CHECK-LABEL: define i32 @varArgs(i32 %i1, ...) diff --git a/clang/test/CodeGen/x86_64-arguments-nacl.c b/clang/test/CodeGen/x86_64-arguments-nacl.c index ea4483422dfe2..e7287a90765bd 100644 --- a/clang/test/CodeGen/x86_64-arguments-nacl.c +++ b/clang/test/CodeGen/x86_64-arguments-nacl.c @@ -61,7 +61,7 @@ void f12_1(struct s12 a0) {} // Check that sret parameter is accounted for when checking available integer // registers. -// CHECK: define void @f13(%struct.s13_0* noalias sret %agg.result, i32 %a, i32 %b, i32 %c, i32 %d, {{.*}}* byval({{.*}}) align 8 %e, i32 %f) +// CHECK: define void @f13(%struct.s13_0* noalias sret align 8 %agg.result, i32 %a, i32 %b, i32 %c, i32 %d, {{.*}}* byval({{.*}}) align 8 %e, i32 %f) struct s13_0 { long long f0[3]; }; struct s13_1 { long long f0[2]; }; diff --git a/clang/test/CodeGen/x86_64-arguments-win32.c b/clang/test/CodeGen/x86_64-arguments-win32.c index b43107c65ef64..4f7c4ded4b167 100644 --- a/clang/test/CodeGen/x86_64-arguments-win32.c +++ b/clang/test/CodeGen/x86_64-arguments-win32.c @@ -27,5 +27,5 @@ void f6(_Complex double a) {} // CHECK-LABEL: define dso_local i64 @f7() _Complex float f7() { return 1.0; } -// CHECK-LABEL: define dso_local void @f8({ double, double }* noalias sret %agg.result) +// CHECK-LABEL: define dso_local void @f8({ double, double }* noalias sret align 8 %agg.result) _Complex double f8() { return 1.0; } diff --git a/clang/test/CodeGen/x86_64-arguments.c b/clang/test/CodeGen/x86_64-arguments.c index 107571d8140bb..273b2706f10a9 100644 --- a/clang/test/CodeGen/x86_64-arguments.c +++ b/clang/test/CodeGen/x86_64-arguments.c @@ -47,7 +47,7 @@ void f7(e7 a0) { // Test merging/passing of upper eightbyte with X87 class. // -// CHECK-LABEL: define void @f8_1(%union.u8* noalias sret %agg.result) +// CHECK-LABEL: define void @f8_1(%union.u8* noalias sret align 16 %agg.result) // CHECK-LABEL: define void @f8_2(%union.u8* byval(%union.u8) align 16 %a0) union u8 { long double a; @@ -63,7 +63,7 @@ struct s9 { int a; int b; int : 0; } f9(void) { while (1) {} } struct s10 { int a; int b; int : 0; }; void f10(struct s10 a0) {} -// CHECK-LABEL: define void @f11(%union.anon* noalias sret %agg.result) +// CHECK-LABEL: define void @f11(%union.anon* noalias sret align 16 %agg.result) union { long double a; float b; } f11() { while (1) {} } // CHECK-LABEL: define i32 @f12_0() @@ -74,7 +74,7 @@ void f12_1(struct s12 a0) {} // Check that sret parameter is accounted for when checking available integer // registers. -// CHECK: define void @f13(%struct.s13_0* noalias sret %agg.result, i32 %a, i32 %b, i32 %c, i32 %d, {{.*}}* byval({{.*}}) align 8 %e, i32 %f) +// CHECK: define void @f13(%struct.s13_0* noalias sret align 8 %agg.result, i32 %a, i32 %b, i32 %c, i32 %d, {{.*}}* byval({{.*}}) align 8 %e, i32 %f) struct s13_0 { long long f0[3]; }; struct s13_1 { long long f0[2]; }; diff --git a/clang/test/CodeGenCXX/arm-cc.cpp b/clang/test/CodeGenCXX/arm-cc.cpp index 6027746b9ae80..e738cd31fb544 100644 --- a/clang/test/CodeGenCXX/arm-cc.cpp +++ b/clang/test/CodeGenCXX/arm-cc.cpp @@ -16,5 +16,5 @@ void baz() { zed(a); } -// CHECK: declare void @_Z3fooPv(%class.SMLoc* sret, i8*) +// CHECK: declare void @_Z3fooPv(%class.SMLoc* sret align 4, i8*) // CHECK: declare void @_Z3zed5SMLoc(%class.SMLoc*) diff --git a/clang/test/CodeGenCXX/builtin-source-location.cpp b/clang/test/CodeGenCXX/builtin-source-location.cpp index f8bfd7d940b91..cdc896209c85b 100644 --- a/clang/test/CodeGenCXX/builtin-source-location.cpp +++ b/clang/test/CodeGenCXX/builtin-source-location.cpp @@ -65,7 +65,7 @@ SL const_init_global = SL::current(); // // CHECK-GLOBAL-TWO: define internal void @__cxx_global_var_init() // CHECK-GLOBAL-TWO-NOT: ret -// CHECK-GLOBAL-TWO: call void @_ZN15source_location11bad_currentEjjPKcS1_(%struct.source_location* sret @runtime_init_global, +// CHECK-GLOBAL-TWO: call void @_ZN15source_location11bad_currentEjjPKcS1_(%struct.source_location* sret align 8 @runtime_init_global, // CHECK-GLOBAL-TWO-SAME: i32 1100, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], #line 1100 "test_runtime_init.cpp" SL runtime_init_global = SL::bad_current(); @@ -77,7 +77,7 @@ extern "C" void test_function() { // CHECK-LOCAL-ONE-DAG: @[[FILE:.*]] = {{.*}}c"test_current.cpp\00" // CHECK-LOCAL-ONE-DAG: @[[FUNC:.*]] = {{.*}}c"test_function\00" // -// CHECK-LOCAL-ONE: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %local, +// CHECK-LOCAL-ONE: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %local, // CHECK-LOCAL-ONE-SAME: i32 2100, i32 {{[0-9]+}}, // CHECK-LOCAL-ONE-SAME: {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], #line 2100 "test_current.cpp" @@ -102,7 +102,7 @@ struct TestInit { // CHECK-CTOR-GLOBAL: define internal void @__cxx_global_var_init.{{[0-9]+}}() // CHECK-CTOR-GLOBAL-NOT: ret // -// CHECK-CTOR-GLOBAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP_ONE:[^,]*]], +// CHECK-CTOR-GLOBAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[TMP_ONE:[^,]*]], // CHECK-CTOR-GLOBAL-SAME: i32 3400, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], // CHECK-CTOR-GLOBAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* @GlobalInitVal, %struct.source_location* {{.*}}%[[TMP_ONE]]) #line 3400 "GlobalInitVal.cpp" @@ -117,7 +117,7 @@ extern "C" void test_init_function() { // CHECK-CTOR-LOCAL: define void @test_init_function() // CHECK-CTOR-LOCAL-NOT: ret // -// CHECK-CTOR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]], +// CHECK-CTOR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[TMP:[^,]*]], // CHECK-CTOR-LOCAL-SAME: i32 3500, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], // CHECK-CTOR-LOCAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* %init_local, %struct.source_location* {{.*}}%[[TMP]]) #line 3500 "LocalInitVal.cpp" @@ -153,7 +153,7 @@ extern "C" void test_init_function_constexpr() { // CHECK-CONSTEXPR-LOCAL-DAG: @[[FILE:.*]] = {{.*}}c"ConstexprLocal.cpp\00" // // CHECK-CONSTEXPR-LOCAL: define void @test_init_function_constexpr() -// CHECK-CONSTEXPR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]], +// CHECK-CONSTEXPR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[TMP:[^,]*]], // CHECK-CONSTEXPR-LOCAL-SAME: i32 4600, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] // CHECK-CONSTEXPR-LOCAL: call void @_ZN17TestInitConstexprC1E15source_location(%struct.TestInitConstexpr* %local_val, {{.*}}%[[TMP]]) #line 4600 "ConstexprLocal.cpp" @@ -189,7 +189,7 @@ extern "C" void test_agg_init() { // // CHECK-AGG-BRACE: define void @test_agg_init() // CHECK-AGG-BRACE: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_brace_init, i32 0, i32 1 -// CHECK-AGG-BRACE-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I2]], +// CHECK-AGG-BRACE-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[I2]], // CHECK-AGG-BRACE-SAME: i32 5700, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] #line 5600 "BraceInitStart.cpp" TestInitAgg local_brace_init{ @@ -203,7 +203,7 @@ extern "C" void test_agg_init() { // // CHECK-AGG-EQUAL: define void @test_agg_init() // CHECK-AGG-EQUAL: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_equal_init, i32 0, i32 1 -// CHECK-AGG-EQUAL-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I2]], +// CHECK-AGG-EQUAL-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[I2]], // CHECK-AGG-EQUAL-SAME: i32 5900, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] #line 5800 "EqualInitStart.cpp" TestInitAgg local_equal_init = @@ -220,11 +220,11 @@ extern "C" void test_agg_init() { // CHECK-AGG-LIST: define void @test_agg_init() // // CHECK-AGG-LIST: %[[I1:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_list_init, i32 0, i32 0 -// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I1]], +// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[I1]], // CHECK-AGG-LIST-SAME: i32 6100, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_ELEM]], {{[^@]*}}@[[FUNC]] // // CHECK-AGG-LIST: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_list_init, i32 0, i32 1 -// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[I2]], +// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[I2]], // CHECK-AGG-LIST-SAME: i32 6200, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_DEFAULT]], {{[^@]*}}@[[FUNC]] #line 6000 "InitListStart.cpp" TestInitAgg local_list_init = @@ -258,7 +258,7 @@ void test_template() { // CHECK-TEMPL-NEXT: entry: // CHECK-TEMPL-NOT: ret // -// CHECK-TEMPL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]], +// CHECK-TEMPL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret align 8 %[[TMP:[^,]*]], // CHECK-TEMPL-SAME: i32 7300, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] #line 7300 "local_templ.cpp" TestTemplate local_templ; diff --git a/clang/test/CodeGenCXX/call-with-static-chain.cpp b/clang/test/CodeGenCXX/call-with-static-chain.cpp index ac1149b52eddf..17e676433e1a4 100644 --- a/clang/test/CodeGenCXX/call-with-static-chain.cpp +++ b/clang/test/CodeGenCXX/call-with-static-chain.cpp @@ -25,8 +25,8 @@ void test() { // CHECK64: call i32 bitcast (i32 (i64, i64, i64, i64, i64, i64, %struct.A*)* @f1 to i32 (i8*, i64, i64, i64, i64, i64, i64, %struct.A*)*)(i8* nest bitcast (i32 (i64, i64, i64, i64, i64, i64, %struct.A*)* @f1 to i8*) __builtin_call_with_static_chain(f1(a, a, a, a), f1); - // CHECK32: call void bitcast (void (%struct.B*)* @f2 to void (%struct.B*, i8*)*)(%struct.B* sret %{{[0-9a-z]+}}, i8* nest bitcast (void (%struct.B*)* @f2 to i8*)) - // CHECK64: call void bitcast (void (%struct.B*)* @f2 to void (%struct.B*, i8*)*)(%struct.B* sret %{{[0-9a-z]+}}, i8* nest bitcast (void (%struct.B*)* @f2 to i8*)) + // CHECK32: call void bitcast (void (%struct.B*)* @f2 to void (%struct.B*, i8*)*)(%struct.B* sret align 4 %{{[0-9a-z]+}}, i8* nest bitcast (void (%struct.B*)* @f2 to i8*)) + // CHECK64: call void bitcast (void (%struct.B*)* @f2 to void (%struct.B*, i8*)*)(%struct.B* sret align 8 %{{[0-9a-z]+}}, i8* nest bitcast (void (%struct.B*)* @f2 to i8*)) __builtin_call_with_static_chain(f2(), f2); // CHECK32: call i64 bitcast (i64 ()* @f3 to i64 (i8*)*)(i8* nest bitcast (i64 ()* @f3 to i8*)) diff --git a/clang/test/CodeGenCXX/catch-undef-behavior.cpp b/clang/test/CodeGenCXX/catch-undef-behavior.cpp index ba72af038abaf..c5aec53ad724e 100644 --- a/clang/test/CodeGenCXX/catch-undef-behavior.cpp +++ b/clang/test/CodeGenCXX/catch-undef-behavior.cpp @@ -426,6 +426,25 @@ void indirect_function_call(void (*p)(int)) { p(42); } +namespace VBaseObjectSize { + // Note: C is laid out such that offsetof(C, B) + sizeof(B) extends outside + // the C object. + struct alignas(16) A { void *a1, *a2; }; + struct B : virtual A { void *b; }; + struct C : virtual A, virtual B {}; + // CHECK-LABEL: define {{.*}} @_ZN15VBaseObjectSize1fERNS_1BE( + B &f(B &b) { + // Size check: check for nvsize(B) == 16 (do not require size(B) == 32) + // CHECK: [[SIZE:%.+]] = call i{{32|64}} @llvm.objectsize.i64.p0i8( + // CHECK: icmp uge i{{32|64}} [[SIZE]], 16, + + // Alignment check: check for nvalign(B) == 8 (do not require align(B) == 16) + // CHECK: [[PTRTOINT:%.+]] = ptrtoint {{.*}} to i64, + // CHECK: and i64 [[PTRTOINT]], 7, + return b; + } +} + namespace FunctionSanitizerVirtualCalls { struct A { virtual void f() {} diff --git a/clang/test/CodeGenCXX/conditional-gnu-ext.cpp b/clang/test/CodeGenCXX/conditional-gnu-ext.cpp index 613dd65ee7c96..ec6d097994183 100644 --- a/clang/test/CodeGenCXX/conditional-gnu-ext.cpp +++ b/clang/test/CodeGenCXX/conditional-gnu-ext.cpp @@ -94,7 +94,7 @@ namespace test3 { B test1() { // CHECK-LABEL: define void @_ZN5test35test1Ev( // CHECK: [[TEMP:%.*]] = alloca [[B]], - // CHECK: call void @_ZN5test312test1_helperEv([[B]]* sret [[TEMP]]) + // CHECK: call void @_ZN5test312test1_helperEv([[B]]* sret align 1 [[TEMP]]) // CHECK-NEXT: [[BOOL:%.*]] = call zeroext i1 @_ZN5test31BcvbEv([[B]]* [[TEMP]]) // CHECK-NEXT: br i1 [[BOOL]] // CHECK: call void @_ZN5test31BC1ERKS0_([[B]]* [[RESULT:%.*]], [[B]]* dereferenceable({{[0-9]+}}) [[TEMP]]) @@ -115,7 +115,7 @@ namespace test3 { // CHECK-NEXT: [[T0:%.*]] = load [[B]]*, [[B]]** [[X]] // CHECK-NEXT: [[BOOL:%.*]] = call zeroext i1 @_ZN5test31BcvbEv([[B]]* [[T0]]) // CHECK-NEXT: br i1 [[BOOL]] - // CHECK: call void @_ZN5test31BcvNS_1AEEv([[A:%.*]]* sret [[RESULT:%.*]], [[B]]* [[T0]]) + // CHECK: call void @_ZN5test31BcvNS_1AEEv([[A:%.*]]* sret align 1 [[RESULT:%.*]], [[B]]* [[T0]]) // CHECK-NEXT: br label // CHECK: call void @_ZN5test31AC1Ev([[A]]* [[RESULT]]) // CHECK-NEXT: br label @@ -126,10 +126,10 @@ namespace test3 { A test3() { // CHECK-LABEL: define void @_ZN5test35test3Ev( // CHECK: [[TEMP:%.*]] = alloca [[B]], - // CHECK: call void @_ZN5test312test3_helperEv([[B]]* sret [[TEMP]]) + // CHECK: call void @_ZN5test312test3_helperEv([[B]]* sret align 1 [[TEMP]]) // CHECK-NEXT: [[BOOL:%.*]] = call zeroext i1 @_ZN5test31BcvbEv([[B]]* [[TEMP]]) // CHECK-NEXT: br i1 [[BOOL]] - // CHECK: call void @_ZN5test31BcvNS_1AEEv([[A]]* sret [[RESULT:%.*]], [[B]]* [[TEMP]]) + // CHECK: call void @_ZN5test31BcvNS_1AEEv([[A]]* sret align 1 [[RESULT:%.*]], [[B]]* [[TEMP]]) // CHECK-NEXT: br label // CHECK: call void @_ZN5test31AC1Ev([[A]]* [[RESULT]]) // CHECK-NEXT: br label diff --git a/clang/test/CodeGenCXX/cxx1z-copy-omission.cpp b/clang/test/CodeGenCXX/cxx1z-copy-omission.cpp index b33a21808175a..dd821949772a7 100644 --- a/clang/test/CodeGenCXX/cxx1z-copy-omission.cpp +++ b/clang/test/CodeGenCXX/cxx1z-copy-omission.cpp @@ -19,7 +19,7 @@ void g() { // CHECK: %[[A:.*]] = alloca // CHECK-NOT: alloca // CHECK-NOT: call - // CHECK: call {{.*}} @_Z1fv({{.*}}* sret %[[A]]) + // CHECK: call {{.*}} @_Z1fv({{.*}}* sret align 4 %[[A]]) A a = A( A{ f() } ); // CHECK-NOT: call @@ -40,7 +40,7 @@ void h() { // CHECK-NOT: alloca // CHECK-NOT: call - // CHECK: call {{.*}} @_Z1fv({{.*}}* sret %[[A]]) + // CHECK: call {{.*}} @_Z1fv({{.*}}* sret align 4 %[[A]]) // CHECK-NOT: call // CHECK: call {{.*}} @_Z1f1A({{.*}}* %[[A]]) f(f()); diff --git a/clang/test/CodeGenCXX/cxx1z-lambda-star-this.cpp b/clang/test/CodeGenCXX/cxx1z-lambda-star-this.cpp index 114791c6558b3..fc13c197076f4 100644 --- a/clang/test/CodeGenCXX/cxx1z-lambda-star-this.cpp +++ b/clang/test/CodeGenCXX/cxx1z-lambda-star-this.cpp @@ -10,7 +10,7 @@ namespace ns1 { int X = A{}.foo()(); } //end ns1 -//CHECK: @"?foo@A@@QAE?A?@@XZ"(%struct.A* %this, %class.anon* noalias sret %[[A_LAMBDA_RETVAL:.*]]) +//CHECK: @"?foo@A@@QAE?A?@@XZ"(%struct.A* %this, %class.anon* noalias sret align 8 %[[A_LAMBDA_RETVAL:.*]]) // get the first object with the closure type, which is of type 'struct.A' //CHECK: %[[I0:.+]] = getelementptr inbounds %[[A_LAMBDA]], %[[A_LAMBDA]]* %[[A_LAMBDA_RETVAL]], i32 0, i32 0 //CHECK: %[[I1:.+]] = bitcast %struct.A* %[[I0]] to i8* @@ -26,6 +26,6 @@ struct B { namespace ns2 { int X = B{}.bar()(); } -//CHECK: @"?bar@B@@QAE?A?@@XZ"(%struct.B* %this, %class.anon.0* noalias sret %agg.result) +//CHECK: @"?bar@B@@QAE?A?@@XZ"(%struct.B* %this, %class.anon.0* noalias sret align 4 %agg.result) //CHECK: %[[I20:.+]] = getelementptr inbounds %class.anon.0, %class.anon.0* %agg.result, i32 0, i32 0 //CHECK: store %struct.B* %this1, %struct.B** %[[I20]], align 4 diff --git a/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp b/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp index af46a92c7dc2d..26e4568f7c59f 100644 --- a/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp +++ b/clang/test/CodeGenCXX/cxx2a-thread-local-constinit.cpp @@ -1,41 +1,55 @@ -// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++2a %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++2a %s -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin12 -std=c++2a %s -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s + +// Check variable definitions/declarations. Note that on Darwin, typically the +// variable's symbol is marked internal, and only the _ZTW function is +// exported. Except: constinit variables do get exported, even on darwin. + +// CHECK-DAG: @a = external thread_local global i32 +// CHECK-DAG: @b = external thread_local global i32 +// LINUX-DAG: @c = thread_local global i32 0, align 4 +// DARWIN-DAG: @c = internal thread_local global i32 0, align 4 +// LINUX-DAG: @d = thread_local global i32 0, align 4 +// DARWIN-DAG: @d = internal thread_local global i32 0, align 4 +// CHECK-DAG: @e = external thread_local global %struct.Destructed, align 4 +// CHECK-DAG: @e2 = thread_local global %struct.Destructed zeroinitializer, align 4 +// CHECK-DAG: @f = thread_local global i32 4, align 4 -// CHECK-DAG: @a = external thread_local global i32 extern thread_local int a; - -// CHECK-DAG: @b = external thread_local global i32 extern thread_local constinit int b; -// CHECK-LABEL: define i32 @_Z1fv() -// CHECK: call i32* @_ZTW1a() +// CHECK-LABEL: define i32 @_Z5get_av() +// CHECK: call {{(cxx_fast_tlscc )?}}i32* @_ZTW1a() // CHECK: } -int f() { return a; } +int get_a() { return a; } -// CHECK-LABEL: define linkonce_odr {{.*}} @_ZTW1a() -// CHECK: br i1 -// CHECK: call void @_ZTH1a() -// CHECK: } +// LINUX-LABEL: define linkonce_odr {{.*}} @_ZTW1a() +// LINUX: br i1 +// LINUX: call void @_ZTH1a() +// LINUX: } +// DARWIN-NOT: define {{.*}}@_ZTW1a() -// CHECK-LABEL: define i32 @_Z1gv() +// CHECK-LABEL: define i32 @_Z5get_bv() // CHECK-NOT: call // CHECK: load i32, i32* @b // CHECK-NOT: call // CHECK: } -int g() { return b; } +int get_b() { return b; } // CHECK-NOT: define {{.*}} @_ZTW1b() extern thread_local int c; -// CHECK-LABEL: define i32 @_Z1hv() -// CHECK: call i32* @_ZTW1c() +// CHECK-LABEL: define i32 @_Z5get_cv() +// LINUX: call {{(cxx_fast_tlscc )?}}i32* @_ZTW1c() // CHECK: load i32, i32* % // CHECK: } -int h() { return c; } +int get_c() { return c; } // Note: use of 'c' does not trigger initialization of 'd', because 'c' has a // constant initializer. -// CHECK-LABEL: define weak_odr {{.*}} @_ZTW1c() +// DARWIN-LABEL: define cxx_fast_tlscc {{.*}} @_ZTW1c() +// LINUX-LABEL: define weak_odr {{.*}} @_ZTW1c() // CHECK-NOT: br i1 // CHECK-NOT: call // CHECK: ret i32* @c @@ -55,15 +69,18 @@ struct Destructed { }; extern thread_local constinit Destructed e; -// CHECK-LABEL: define i32 @_Z1iv() +// CHECK-LABEL: define i32 @_Z5get_ev() // CHECK: call {{.*}}* @_ZTW1e() // CHECK: } -int i() { return e.n; } +int get_e() { return e.n; } // CHECK: define {{.*}}[[E2_INIT:@__cxx_global_var_init[^(]*]]( -// CHECK: call {{.*}} @__cxa_thread_atexit({{.*}} @_ZN10DestructedD1Ev {{.*}} @e2 +// LINUX: call {{.*}} @__cxa_thread_atexit({{.*}} @_ZN10DestructedD1Ev {{.*}} @e2 +// DARWIN: call {{.*}} @_tlv_atexit({{.*}} @_ZN10DestructedD1Ev {{.*}} @e2 thread_local constinit Destructed e2; +thread_local constinit int f = 4; + // CHECK-LABEL: define {{.*}}__tls_init // CHECK: call {{.*}} [[D_INIT]] // CHECK: call {{.*}} [[E2_INIT]] diff --git a/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp b/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp index 667c2469b55ea..540ef02c908af 100644 --- a/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp +++ b/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp @@ -15,22 +15,22 @@ // RUN: | FileCheck %s -check-prefix=HAS-ATTR \ // RUN: -implicit-check-not=DISubprogram -implicit-check-not=DIFlagAllCallsDescribed -// Supported: DWARF4 + GDB tuning by using '-femit-debug-entry-values' -// RUN: %clang_cc1 -femit-debug-entry-values -emit-llvm -triple x86_64-linux-gnu \ +// Supported: DWARF4 + GDB tuning, -O1 +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu \ // RUN: %s -o - -O1 -disable-llvm-passes -debugger-tuning=gdb \ // RUN: -debug-info-kind=standalone -dwarf-version=4 \ // RUN: | FileCheck %s -check-prefix=HAS-ATTR \ // RUN: -implicit-check-not=DIFlagAllCallsDescribed -// Supported: DWARF4 + LLDB tuning by using '-femit-debug-entry-values' -// RUN: %clang_cc1 -femit-debug-entry-values -emit-llvm -triple x86_64-linux-gnu \ +// Supported: DWARF4 + LLDB tuning, -O1 +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu \ // RUN: %s -o - -O1 -disable-llvm-passes -debugger-tuning=lldb \ // RUN: -debug-info-kind=standalone -dwarf-version=4 \ // RUN: | FileCheck %s -check-prefix=HAS-ATTR \ // RUN: -implicit-check-not=DIFlagAllCallsDescribed -// Unsupported: -O0 + '-femit-debug-entry-values' -// RUN: %clang_cc1 -femit-debug-entry-values -emit-llvm -triple x86_64-linux-gnu \ +// Unsupported: -O0 +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu \ // RUN: %s -o - -O0 -disable-llvm-passes -debugger-tuning=gdb \ // RUN: -debug-info-kind=standalone -dwarf-version=4 \ // RUN: | FileCheck %s -check-prefix=NO-ATTR diff --git a/clang/test/CodeGenCXX/debug-info-block-invocation-linkage-name.cpp b/clang/test/CodeGenCXX/debug-info-block-invocation-linkage-name.cpp new file mode 100644 index 0000000000000..5fadae9b439fb --- /dev/null +++ b/clang/test/CodeGenCXX/debug-info-block-invocation-linkage-name.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -fblocks -triple %itanium_abi_triple %s -o - | FileCheck %s + +// CHECK: !DISubprogram(name: "___Z1fU13block_pointerFviE_block_invoke", linkageName: "___Z1fU13block_pointerFviE_block_invoke" +void g(void (^call)(int)); + +void f(void (^callback)(int)) { + g(^(int x) { + callback(x); + }); +} + +void h() { + f(^(int x){ + }); +} diff --git a/clang/test/CodeGenCXX/dereferenceable.cpp b/clang/test/CodeGenCXX/dereferenceable.cpp new file mode 100644 index 0000000000000..df8e6acab2db0 --- /dev/null +++ b/clang/test/CodeGenCXX/dereferenceable.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-linux-gnu | FileCheck %s + +struct A { void *p; void *q; void *r; }; + +struct B : A {}; +static_assert(sizeof(B) == 24); + +// CHECK: define dereferenceable(24) {{.*}} @_Z1fR1B({{.*}} dereferenceable(24) +B &f(B &b) { return b; } + +struct C : virtual A {}; +static_assert(sizeof(C) == 32); + +// CHECK: define dereferenceable(8) {{.*}} @_Z1fR1C({{.*}} dereferenceable(8) +C &f(C &c) { return c; } diff --git a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp index 130103de97aeb..6f5e844b587e1 100644 --- a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp +++ b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp @@ -255,6 +255,49 @@ namespace Test10 { } } +namespace TestVBase { + struct A { virtual void f(); }; + struct B : virtual A {}; + struct C : virtual A { void f() override; }; + + extern struct BC final : B, C {} &bc; + extern struct BCusingA final : B, C { using A::f; } &bc_using_a; + extern struct BCusingB final : B, C { using B::f; } &bc_using_b; + extern struct BCusingC final : B, C { using C::f; } &bc_using_c; + + extern struct CB final : C, B {} &cb; + extern struct CBusingA final : C, B { using A::f; } &cb_using_a; + extern struct CBusingB final : C, B { using B::f; } &cb_using_b; + extern struct CBusingC final : C, B { using C::f; } &cb_using_c; + + // CHECK-LABEL: @_ZN9TestVBase4testEv( + void test() { + // FIXME: The 'using A' case can be devirtualized to call A's virtual + // adjustment thunk for C::f. + // FIXME: The 'using B' case can be devirtualized, but requires us to emit + // a derived-to-base or base-to-derived conversion as part of + // devirtualization. + + // CHECK: call void @_ZN9TestVBase1C1fEv( + bc.f(); + // CHECK: call void % + bc_using_a.f(); + // CHECK: call void % + bc_using_b.f(); + // CHECK: call void @_ZN9TestVBase1C1fEv( + bc_using_c.f(); + + // CHECK: call void @_ZN9TestVBase1C1fEv( + cb.f(); + // CHECK: call void % + cb_using_a.f(); + // CHECK: call void % + cb_using_b.f(); + // CHECK: call void @_ZN9TestVBase1C1fEv( + cb_using_c.f(); + } +} + namespace Test11 { // Check that the definitions of Derived's operators are emitted. diff --git a/clang/test/CodeGenCXX/exceptions.cpp b/clang/test/CodeGenCXX/exceptions.cpp index 302488aa568d2..da3a87a4b0da3 100644 --- a/clang/test/CodeGenCXX/exceptions.cpp +++ b/clang/test/CodeGenCXX/exceptions.cpp @@ -146,12 +146,12 @@ namespace test1 { // CHECK: [[NEW:%.*]] = call i8* @_Znwm(i64 8) // CHECK-NEXT: store i1 true, i1* [[ACTIVE]] // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* - // CHECK-NEXT: invoke void @_ZN5test15makeBEv([[B:%.*]]* sret [[T0:%.*]]) + // CHECK-NEXT: invoke void @_ZN5test15makeBEv([[B:%.*]]* sret align 4 [[T0:%.*]]) // CHECK: [[T1:%.*]] = invoke i32 @_ZN5test11BcviEv([[B]]* [[T0]]) // CHECK: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 [[T1]]) // CHECK: store i1 false, i1* [[ACTIVE]] // CHECK-NEXT: store [[A]]* [[CAST]], [[A]]** [[X]], align 8 - // CHECK: invoke void @_ZN5test15makeBEv([[B:%.*]]* sret [[T2:%.*]]) + // CHECK: invoke void @_ZN5test15makeBEv([[B:%.*]]* sret align 4 [[T2:%.*]]) // CHECK: [[RET:%.*]] = load [[A]]*, [[A]]** [[X]], align 8 // CHECK98: invoke void @_ZN5test11BD1Ev([[B]]* [[T2]]) @@ -239,7 +239,7 @@ namespace test3 { // CHECK-NEXT: store i8* [[FOO]], i8** [[SAVED1]] // CHECK-NEXT: store i1 true, i1* [[CLEANUPACTIVE]] // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* - // CHECK-NEXT: invoke void @_ZN5test35makeAEv([[A]]* sret [[CAST]]) + // CHECK-NEXT: invoke void @_ZN5test35makeAEv([[A]]* sret align 8 [[CAST]]) // CHECK: br label // -> cond.end new(foo(),10.0) A(makeA()) : diff --git a/clang/test/CodeGenCXX/global-init.cpp b/clang/test/CodeGenCXX/global-init.cpp index 1970de8825e20..eaa37456774a9 100644 --- a/clang/test/CodeGenCXX/global-init.cpp +++ b/clang/test/CodeGenCXX/global-init.cpp @@ -2,6 +2,8 @@ // RUN: %clang_cc1 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - |FileCheck -check-prefix CHECK-NOEXC %s // RUN: %clang_cc1 -triple=x86_64-apple-darwin10 -emit-llvm -mframe-pointer=non-leaf %s -o - \ // RUN: | FileCheck -check-prefix CHECK-FP %s +// RUN: %clang_cc1 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - -fno-builtin \ +// RUN: | FileCheck -check-prefix CHECK-NOBUILTIN %s struct A { A(); @@ -202,9 +204,12 @@ namespace test7 { // rdar://problem/8090834: this should be nounwind // CHECK-NOEXC: define internal void @_GLOBAL__sub_I_global_init.cpp() [[NUW:#[0-9]+]] section "__TEXT,__StaticInit,regular,pure_instructions" { - // CHECK-NOEXC: attributes [[NUW]] = { noinline nounwind{{.*}} } +// Make sure we mark global initializers with the no-builtins attribute. +// CHECK-NOBUILTIN: define internal void @_GLOBAL__sub_I_global_init.cpp() [[NUW:#[0-9]+]] section "__TEXT,__StaticInit,regular,pure_instructions" { +// CHECK-NOBUILTIN: attributes [[NUW]] = { noinline nounwind{{.*}}"no-builtins"{{.*}} } + // PR21811: attach the appropriate attribute to the global init function // CHECK-FP: define internal void @_GLOBAL__sub_I_global_init.cpp() [[NUX:#[0-9]+]] section "__TEXT,__StaticInit,regular,pure_instructions" { // CHECK-FP: attributes [[NUX]] = { noinline nounwind {{.*}}"frame-pointer"="non-leaf"{{.*}} } diff --git a/clang/test/CodeGenCXX/homogeneous-aggregates.cpp b/clang/test/CodeGenCXX/homogeneous-aggregates.cpp index 05fb7f1d20a4b..51a4549d38d76 100644 --- a/clang/test/CodeGenCXX/homogeneous-aggregates.cpp +++ b/clang/test/CodeGenCXX/homogeneous-aggregates.cpp @@ -38,10 +38,10 @@ struct I2 : Base2 {}; struct I3 : Base2 {}; struct D5 : I1, I2, I3 {}; // homogeneous aggregate -// PPC: define void @_Z7func_D12D1(%struct.D1* noalias sret %agg.result, [3 x i64] %x.coerce) -// ARM32: define arm_aapcs_vfpcc void @_Z7func_D12D1(%struct.D1* noalias sret %agg.result, [3 x i64] %x.coerce) -// ARM64: define void @_Z7func_D12D1(%struct.D1* noalias sret %agg.result, %struct.D1* %x) -// X64: define dso_local x86_vectorcallcc void @"\01_Z7func_D12D1@@24"(%struct.D1* noalias sret %agg.result, %struct.D1* %x) +// PPC: define void @_Z7func_D12D1(%struct.D1* noalias sret align 8 %agg.result, [3 x i64] %x.coerce) +// ARM32: define arm_aapcs_vfpcc void @_Z7func_D12D1(%struct.D1* noalias sret align 8 %agg.result, [3 x i64] %x.coerce) +// ARM64: define void @_Z7func_D12D1(%struct.D1* noalias sret align 8 %agg.result, %struct.D1* %x) +// X64: define dso_local x86_vectorcallcc void @"\01_Z7func_D12D1@@24"(%struct.D1* noalias sret align 8 %agg.result, %struct.D1* %x) D1 CC func_D1(D1 x) { return x; } // PPC: define [3 x double] @_Z7func_D22D2([3 x double] %x.coerce) @@ -50,9 +50,9 @@ D1 CC func_D1(D1 x) { return x; } // X64: define dso_local x86_vectorcallcc %struct.D2 @"\01_Z7func_D22D2@@24"(%struct.D2 inreg %x.coerce) D2 CC func_D2(D2 x) { return x; } -// PPC: define void @_Z7func_D32D3(%struct.D3* noalias sret %agg.result, [4 x i64] %x.coerce) -// ARM32: define arm_aapcs_vfpcc void @_Z7func_D32D3(%struct.D3* noalias sret %agg.result, [4 x i64] %x.coerce) -// ARM64: define void @_Z7func_D32D3(%struct.D3* noalias sret %agg.result, %struct.D3* %x) +// PPC: define void @_Z7func_D32D3(%struct.D3* noalias sret align 8 %agg.result, [4 x i64] %x.coerce) +// ARM32: define arm_aapcs_vfpcc void @_Z7func_D32D3(%struct.D3* noalias sret align 8 %agg.result, [4 x i64] %x.coerce) +// ARM64: define void @_Z7func_D32D3(%struct.D3* noalias sret align 8 %agg.result, %struct.D3* %x) D3 CC func_D3(D3 x) { return x; } // PPC: define [4 x double] @_Z7func_D42D4([4 x double] %x.coerce) diff --git a/clang/test/CodeGenCXX/lambda-expressions.cpp b/clang/test/CodeGenCXX/lambda-expressions.cpp index 566132ad64e30..c75f84f038715 100644 --- a/clang/test/CodeGenCXX/lambda-expressions.cpp +++ b/clang/test/CodeGenCXX/lambda-expressions.cpp @@ -194,8 +194,8 @@ namespace pr28595 { // CHECK-NEXT: call i32 @"_ZZ1fvENK3$_6clEii" // CHECK-NEXT: ret i32 -// CHECK-LABEL: define internal void @"_ZZ1hvEN4$_118__invokeEv"(%struct.A* noalias sret %agg.result) {{.*}} { -// CHECK: call void @"_ZZ1hvENK4$_11clEv"(%struct.A* sret %agg.result, +// CHECK-LABEL: define internal void @"_ZZ1hvEN4$_118__invokeEv"(%struct.A* noalias sret align 1 %agg.result) {{.*}} { +// CHECK: call void @"_ZZ1hvENK4$_11clEv"(%struct.A* sret align 1 %agg.result, // CHECK-NEXT: ret void struct A { ~A(); }; void h() { 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/microsoft-abi-byval-sret.cpp b/clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp index 2c940d22010bd..a92049c3a7996 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-byval-sret.cpp @@ -49,7 +49,7 @@ A B::qux(A x) { } // CHECK-LABEL: define dso_local x86_fastcallcc void @"?qux@B@@QAI?AUA@@U2@@Z" -// CHECK: (%struct.B* inreg %this, %struct.A* inreg noalias sret %agg.result, <{ %struct.A }>* inalloca %0) +// CHECK: (%struct.B* inreg %this, %struct.A* inreg noalias sret align 4 %agg.result, <{ %struct.A }>* inalloca %0) // CHECK: ret void int main() { @@ -67,4 +67,4 @@ int main() { // CHECK: call x86_stdcallcc %struct.A* @"?baz@B@@QAG?AUA@@U2@@Z" // CHECK: (<{ %struct.B*, %struct.A*, %struct.A }>* inalloca %{{[^,]*}}) // CHECK: call x86_fastcallcc void @"?qux@B@@QAI?AUA@@U2@@Z" -// CHECK: (%struct.B* inreg %{{[^,]*}}, %struct.A* inreg sret %{{.*}}, <{ %struct.A }>* inalloca %{{[^,]*}}) +// CHECK: (%struct.B* inreg %{{[^,]*}}, %struct.A* inreg sret align 4 %{{.*}}, <{ %struct.A }>* inalloca %{{[^,]*}}) diff --git a/clang/test/CodeGenCXX/microsoft-abi-byval-thunks.cpp b/clang/test/CodeGenCXX/microsoft-abi-byval-thunks.cpp index ed4e1fbb36fdb..0ca68cccb7906 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-byval-thunks.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-byval-thunks.cpp @@ -86,10 +86,10 @@ C::C() {} // force emission // CHECK32-NEXT: ret %"struct.sret_thunk::Agg"* %[[rv]] // CHECK64-LABEL: define linkonce_odr dso_local void @"?foo@C@sret_thunk@@W7EAA?AUAgg@2@U32@@Z" -// CHECK64: (%"struct.sret_thunk::C"* %this, %"struct.sret_thunk::Agg"* noalias sret %agg.result, %"struct.sret_thunk::Agg"* %x) +// CHECK64: (%"struct.sret_thunk::C"* %this, %"struct.sret_thunk::Agg"* noalias sret align 4 %agg.result, %"struct.sret_thunk::Agg"* %x) // CHECK64: getelementptr i8, i8* %{{.*}}, i32 -8 // CHECK64: call void @"?foo@C@sret_thunk@@UEAA?AUAgg@2@U32@@Z" -// CHECK64: (%"struct.sret_thunk::C"* %{{.*}}, %"struct.sret_thunk::Agg"* sret %agg.result, %"struct.sret_thunk::Agg"* %x) +// CHECK64: (%"struct.sret_thunk::C"* %{{.*}}, %"struct.sret_thunk::Agg"* sret align 4 %agg.result, %"struct.sret_thunk::Agg"* %x) // CHECK64-NOT: call // CHECK64: ret void } diff --git a/clang/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp b/clang/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp index 5a8bdf78100f4..534aa7f804695 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-cdecl-method-sret.cpp @@ -19,9 +19,9 @@ S C::variadic_sret(const char *f, ...) { return S(); } S C::cdecl_sret() { return S(); } S C::byval_and_sret(S a) { return S(); } -// CHECK: define dso_local void @"?variadic_sret@C@@QAA?AUS@@PBDZZ"(%struct.C* %this, %struct.S* noalias sret %agg.result, i8* %f, ...) -// CHECK: define dso_local void @"?cdecl_sret@C@@QAA?AUS@@XZ"(%struct.C* %this, %struct.S* noalias sret %agg.result) -// CHECK: define dso_local void @"?byval_and_sret@C@@QAA?AUS@@U2@@Z"(%struct.C* %this, %struct.S* noalias sret %agg.result, %struct.S* byval(%struct.S) align 4 %a) +// CHECK: define dso_local void @"?variadic_sret@C@@QAA?AUS@@PBDZZ"(%struct.C* %this, %struct.S* noalias sret align 4 %agg.result, i8* %f, ...) +// CHECK: define dso_local void @"?cdecl_sret@C@@QAA?AUS@@XZ"(%struct.C* %this, %struct.S* noalias sret align 4 %agg.result) +// CHECK: define dso_local void @"?byval_and_sret@C@@QAA?AUS@@U2@@Z"(%struct.C* %this, %struct.S* noalias sret align 4 %agg.result, %struct.S* byval(%struct.S) align 4 %a) int main() { C c; @@ -41,4 +41,4 @@ struct A { S A::f(int x) { return S(); } -// CHECK-LABEL: define dso_local x86_fastcallcc void @"?f@A@@QAI?AUS@@H@Z"(%struct.A* inreg %this, %struct.S* inreg noalias sret %agg.result, i32 %x) +// CHECK-LABEL: define dso_local x86_fastcallcc void @"?f@A@@QAI?AUS@@H@Z"(%struct.A* inreg %this, %struct.S* inreg noalias sret align 4 %agg.result, i32 %x) diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp index 7e8619b8b0ecf..60fa5c7991119 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp @@ -18,9 +18,9 @@ void HasEHCleanup() { // WIN32-LABEL: define dso_local void @"?HasEHCleanup@@YAXXZ"() {{.*}} { // WIN32: %[[base:.*]] = call i8* @llvm.stacksave() // If this call throws, we have to restore the stack. -// WIN32: call void @"?getA@@YA?AUA@@XZ"(%struct.A* sret %{{.*}}) +// WIN32: call void @"?getA@@YA?AUA@@XZ"(%struct.A* sret align 4 %{{.*}}) // If this call throws, we have to cleanup the first temporary. -// WIN32: invoke void @"?getA@@YA?AUA@@XZ"(%struct.A* sret %{{.*}}) +// WIN32: invoke void @"?getA@@YA?AUA@@XZ"(%struct.A* sret align 4 %{{.*}}) // If this call throws, we have to cleanup the stacksave. // WIN32: call i32 @"?TakesTwo@@YAHUA@@0@Z" // WIN32: call void @llvm.stackrestore diff --git a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp index 9fb9f39cb0832..8c8d4b7383d63 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-sret-and-byval.cpp @@ -84,45 +84,45 @@ void call_bools_and_chars() { // Returning structs that fit into a register. Small small_return() { return Small(); } -// LINUX-LABEL: define void @_Z12small_returnv(%struct.Small* noalias sret %agg.result) +// LINUX-LABEL: define void @_Z12small_returnv(%struct.Small* noalias sret align 4 %agg.result) // WIN32: define dso_local i32 @"?small_return@@YA?AUSmall@@XZ"() // WIN64: define dso_local i32 @"?small_return@@YA?AUSmall@@XZ"() Medium medium_return() { return Medium(); } -// LINUX-LABEL: define void @_Z13medium_returnv(%struct.Medium* noalias sret %agg.result) +// LINUX-LABEL: define void @_Z13medium_returnv(%struct.Medium* noalias sret align 4 %agg.result) // WIN32: define dso_local i64 @"?medium_return@@YA?AUMedium@@XZ"() // WIN64: define dso_local i64 @"?medium_return@@YA?AUMedium@@XZ"() // Returning structs that fit into a register but are not POD. SmallCpp11NotCpp03Pod small_non_pod_return() { return SmallCpp11NotCpp03Pod(); } -// LINUX-LABEL: define void @_Z20small_non_pod_returnv(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result) -// WIN32: define dso_local void @"?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result) -// WIN64: define dso_local void @"?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret %agg.result) +// LINUX-LABEL: define void @_Z20small_non_pod_returnv(%struct.SmallCpp11NotCpp03Pod* noalias sret align 4 %agg.result) +// WIN32: define dso_local void @"?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret align 4 %agg.result) +// WIN64: define dso_local void @"?small_non_pod_return@@YA?AUSmallCpp11NotCpp03Pod@@XZ"(%struct.SmallCpp11NotCpp03Pod* noalias sret align 4 %agg.result) SmallWithCtor small_with_ctor_return() { return SmallWithCtor(); } -// LINUX-LABEL: define void @_Z22small_with_ctor_returnv(%struct.SmallWithCtor* noalias sret %agg.result) -// WIN32: define dso_local void @"?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result) -// WIN64: define dso_local void @"?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result) +// LINUX-LABEL: define void @_Z22small_with_ctor_returnv(%struct.SmallWithCtor* noalias sret align 4 %agg.result) +// WIN32: define dso_local void @"?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret align 4 %agg.result) +// WIN64: define dso_local void @"?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret align 4 %agg.result) // FIXME: The 'sret' mark here doesn't seem to be enough to convince LLVM to // preserve the hidden sret pointer in R0 across the function. -// WOA: define dso_local arm_aapcs_vfpcc void @"?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret %agg.result) +// WOA: define dso_local arm_aapcs_vfpcc void @"?small_with_ctor_return@@YA?AUSmallWithCtor@@XZ"(%struct.SmallWithCtor* noalias sret align 4 %agg.result) SmallWithVftable small_with_vftable_return() { return SmallWithVftable(); } -// LINUX-LABEL: define void @_Z25small_with_vftable_returnv(%struct.SmallWithVftable* noalias sret %agg.result) -// WIN32: define dso_local void @"?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret %agg.result) -// WIN64: define dso_local void @"?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret %agg.result) +// LINUX-LABEL: define void @_Z25small_with_vftable_returnv(%struct.SmallWithVftable* noalias sret align 4 %agg.result) +// WIN32: define dso_local void @"?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret align 4 %agg.result) +// WIN64: define dso_local void @"?small_with_vftable_return@@YA?AUSmallWithVftable@@XZ"(%struct.SmallWithVftable* noalias sret align 8 %agg.result) MediumWithCopyCtor medium_with_copy_ctor_return() { return MediumWithCopyCtor(); } -// LINUX-LABEL: define void @_Z28medium_with_copy_ctor_returnv(%struct.MediumWithCopyCtor* noalias sret %agg.result) -// WIN32: define dso_local void @"?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret %agg.result) -// WIN64: define dso_local void @"?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret %agg.result) -// WOA: define dso_local arm_aapcs_vfpcc void @"?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret %agg.result) +// LINUX-LABEL: define void @_Z28medium_with_copy_ctor_returnv(%struct.MediumWithCopyCtor* noalias sret align 4 %agg.result) +// WIN32: define dso_local void @"?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret align 4 %agg.result) +// WIN64: define dso_local void @"?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret align 4 %agg.result) +// WOA: define dso_local arm_aapcs_vfpcc void @"?medium_with_copy_ctor_return@@YA?AUMediumWithCopyCtor@@XZ"(%struct.MediumWithCopyCtor* noalias sret align 4 %agg.result) // Returning a large struct that doesn't fit into a register. Big big_return() { return Big(); } -// LINUX-LABEL: define void @_Z10big_returnv(%struct.Big* noalias sret %agg.result) -// WIN32: define dso_local void @"?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result) -// WIN64: define dso_local void @"?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret %agg.result) +// LINUX-LABEL: define void @_Z10big_returnv(%struct.Big* noalias sret align 4 %agg.result) +// WIN32: define dso_local void @"?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret align 4 %agg.result) +// WIN64: define dso_local void @"?big_return@@YA?AUBig@@XZ"(%struct.Big* noalias sret align 4 %agg.result) void small_arg(Small s) {} @@ -181,7 +181,7 @@ void small_arg_with_dtor(SmallWithDtor s) {} // Test that the eligible non-aggregate is passed directly, but returned // indirectly on ARM64 Windows. -// WOA64: define dso_local void @"?small_arg_with_private_member@@YA?AUSmallWithPrivate@@U1@@Z"(%struct.SmallWithPrivate* inreg noalias sret %agg.result, i64 %s.coerce) {{.*}} { +// WOA64: define dso_local void @"?small_arg_with_private_member@@YA?AUSmallWithPrivate@@U1@@Z"(%struct.SmallWithPrivate* inreg noalias sret align 4 %agg.result, i64 %s.coerce) {{.*}} { SmallWithPrivate small_arg_with_private_member(SmallWithPrivate s) { return s; } void call_small_arg_with_dtor() { @@ -281,24 +281,24 @@ void pass_ref_field() { class Class { public: Small thiscall_method_small() { return Small(); } - // LINUX: define {{.*}} void @_ZN5Class21thiscall_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} x86_thiscallcc void @"?thiscall_method_small@Class@@QAE?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret %agg.result) - // WIN64: define linkonce_odr dso_local void @"?thiscall_method_small@Class@@QEAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret %agg.result) + // LINUX: define {{.*}} void @_ZN5Class21thiscall_method_smallEv(%struct.Small* noalias sret align 4 %agg.result, %class.Class* %this) + // WIN32: define {{.*}} x86_thiscallcc void @"?thiscall_method_small@Class@@QAE?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret align 4 %agg.result) + // WIN64: define linkonce_odr dso_local void @"?thiscall_method_small@Class@@QEAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret align 4 %agg.result) SmallWithCtor thiscall_method_small_with_ctor() { return SmallWithCtor(); } - // LINUX: define {{.*}} void @_ZN5Class31thiscall_method_small_with_ctorEv(%struct.SmallWithCtor* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} x86_thiscallcc void @"?thiscall_method_small_with_ctor@Class@@QAE?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* noalias sret %agg.result) - // WIN64: define linkonce_odr dso_local void @"?thiscall_method_small_with_ctor@Class@@QEAA?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* noalias sret %agg.result) + // LINUX: define {{.*}} void @_ZN5Class31thiscall_method_small_with_ctorEv(%struct.SmallWithCtor* noalias sret align 4 %agg.result, %class.Class* %this) + // WIN32: define {{.*}} x86_thiscallcc void @"?thiscall_method_small_with_ctor@Class@@QAE?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* noalias sret align 4 %agg.result) + // WIN64: define linkonce_odr dso_local void @"?thiscall_method_small_with_ctor@Class@@QEAA?AUSmallWithCtor@@XZ"(%class.Class* %this, %struct.SmallWithCtor* noalias sret align 4 %agg.result) Small __cdecl cdecl_method_small() { return Small(); } - // LINUX: define {{.*}} void @_ZN5Class18cdecl_method_smallEv(%struct.Small* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} void @"?cdecl_method_small@Class@@QAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret %agg.result) - // WIN64: define linkonce_odr dso_local void @"?cdecl_method_small@Class@@QEAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret %agg.result) + // LINUX: define {{.*}} void @_ZN5Class18cdecl_method_smallEv(%struct.Small* noalias sret align 4 %agg.result, %class.Class* %this) + // WIN32: define {{.*}} void @"?cdecl_method_small@Class@@QAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret align 4 %agg.result) + // WIN64: define linkonce_odr dso_local void @"?cdecl_method_small@Class@@QEAA?AUSmall@@XZ"(%class.Class* %this, %struct.Small* noalias sret align 4 %agg.result) Big __cdecl cdecl_method_big() { return Big(); } - // LINUX: define {{.*}} void @_ZN5Class16cdecl_method_bigEv(%struct.Big* noalias sret %agg.result, %class.Class* %this) - // WIN32: define {{.*}} void @"?cdecl_method_big@Class@@QAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* noalias sret %agg.result) - // WIN64: define linkonce_odr dso_local void @"?cdecl_method_big@Class@@QEAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* noalias sret %agg.result) + // LINUX: define {{.*}} void @_ZN5Class16cdecl_method_bigEv(%struct.Big* noalias sret align 4 %agg.result, %class.Class* %this) + // WIN32: define {{.*}} void @"?cdecl_method_big@Class@@QAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* noalias sret align 4 %agg.result) + // WIN64: define linkonce_odr dso_local void @"?cdecl_method_big@Class@@QEAA?AUBig@@XZ"(%class.Class* %this, %struct.Big* noalias sret align 4 %agg.result) void thiscall_method_arg(Empty s) {} // LINUX: define {{.*}} void @_ZN5Class19thiscall_method_argE5Empty(%class.Class* %this) diff --git a/clang/test/CodeGenCXX/microsoft-abi-vmemptr-conflicts.cpp b/clang/test/CodeGenCXX/microsoft-abi-vmemptr-conflicts.cpp index 607ec816aefb5..1ab60a5261287 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-vmemptr-conflicts.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-vmemptr-conflicts.cpp @@ -65,7 +65,7 @@ void f(C *c) { // CHECK-LABEL: define dso_local void @"?f@sret@@YAXPAUC@1@@Z"(%"struct.sret::C"* %c) // CHECK: call x86_thiscallcc i32 bitcast (void (%"struct.sret::C"*, ...)* @"??_9C@sret@@$BA@AE" to i32 (%"struct.sret::C"*)*)(%"struct.sret::C"* %{{.*}}) -// CHECK: call x86_thiscallcc void bitcast (void (%"struct.sret::C"*, ...)* @"??_9C@sret@@$BA@AE" to void (%"struct.sret::C"*, %"struct.sret::Big"*)*)(%"struct.sret::C"* %{{.*}}, %"struct.sret::Big"* sret %{{.*}}) +// CHECK: call x86_thiscallcc void bitcast (void (%"struct.sret::C"*, ...)* @"??_9C@sret@@$BA@AE" to void (%"struct.sret::C"*, %"struct.sret::Big"*)*)(%"struct.sret::C"* %{{.*}}, %"struct.sret::Big"* sret align 4 %{{.*}}) // CHECK-LABEL: define linkonce_odr x86_thiscallcc void @"??_9C@sret@@$BA@AE"(%"struct.sret::C"* %this, ...) {{.*}} comdat // CHECK: musttail call x86_thiscallcc void (%"struct.sret::C"*, ...) %{{.*}}(%"struct.sret::C"* %{{.*}}, ...) diff --git a/clang/test/CodeGenCXX/ms-thunks-ehspec.cpp b/clang/test/CodeGenCXX/ms-thunks-ehspec.cpp new file mode 100644 index 0000000000000..f72100d5078b1 --- /dev/null +++ b/clang/test/CodeGenCXX/ms-thunks-ehspec.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fexceptions -fcxx-exceptions %s -triple=i686-windows-msvc -emit-llvm -o - | FileCheck %s + +// When generating thunks using musttail due to inalloca parameters, don't push +// and pop terminate scopes. PR44987 + +struct NonTrivial { + NonTrivial(); + NonTrivial(const NonTrivial &o); + ~NonTrivial(); + int x; +}; +struct A { + virtual void f(NonTrivial o) noexcept; +}; +struct B { + virtual void f(NonTrivial o) noexcept; +}; +class C : A, B { + virtual void f(NonTrivial o) noexcept; +}; +C c; + +// CHECK-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"?f@C@@G3AEXUNonTrivial@@@Z"(%class.C* %this, <{ %struct.NonTrivial }>* inalloca %0) +// CHECK-NOT: invoke +// CHECK: musttail call x86_thiscallcc void @"?f@C@@EAEXUNonTrivial@@@Z"(%class.C* %{{.*}}, <{ %struct.NonTrivial }>* inalloca %0) +// CHECK-NEXT ret void + diff --git a/clang/test/CodeGenCXX/nrvo.cpp b/clang/test/CodeGenCXX/nrvo.cpp index 74a5af765d130..8fa25261f9c93 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..d219e8f5a351e --- /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 align 4 %[[AGG_RESULT:.*]], %[[STRUCT_DERIVED0]]* %{{.*}}) +// CHECK: musttail call void %{{.*}}(%[[STRUCT_A1]]* sret align 4 %[[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..826a8fbb7fa6f --- /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:.*]] = call i64 @llvm.ptrauth.auth.i64(i64 %[[SignedVTable]], i32 2, i64 0) 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/CodeGenCXX/regcall.cpp b/clang/test/CodeGenCXX/regcall.cpp index bdf76964bf231..9eca868fc31d2 100644 --- a/clang/test/CodeGenCXX/regcall.cpp +++ b/clang/test/CodeGenCXX/regcall.cpp @@ -74,8 +74,8 @@ bool __regcall operator ==(const test_class&, const test_class&){ --x; return fa // CHECK-WIN32-DAG: define dso_local x86_regcallcc zeroext i1 @"??8@Yw_NABVtest_class@@0@Z" test_class __regcall operator""_test_class (unsigned long long) { ++x; return test_class{};} -// CHECK-LIN64-DAG: define x86_regcallcc void @_Zli11_test_classy(%class.test_class* noalias sret %agg.result, i64 %0) -// CHECK-LIN32-DAG: define x86_regcallcc void @_Zli11_test_classy(%class.test_class* inreg noalias sret %agg.result, i64 %0) +// CHECK-LIN64-DAG: define x86_regcallcc void @_Zli11_test_classy(%class.test_class* noalias sret align 4 %agg.result, i64 %0) +// CHECK-LIN32-DAG: define x86_regcallcc void @_Zli11_test_classy(%class.test_class* inreg noalias sret align 4 %agg.result, i64 %0) // CHECK-WIN64-DAG: ??__K_test_class@@Yw?AVtest_class@@_K@Z" // CHECK-WIN32-DAG: ??__K_test_class@@Yw?AVtest_class@@_K@Z" @@ -99,7 +99,7 @@ void force_gen() { long double _Complex __regcall foo(long double _Complex f) { return f; } -// CHECK-LIN64-DAG: define x86_regcallcc void @_Z15__regcall3__fooCe({ x86_fp80, x86_fp80 }* noalias sret %agg.result, { x86_fp80, x86_fp80 }* byval({ x86_fp80, x86_fp80 }) align 16 %f) -// CHECK-LIN32-DAG: define x86_regcallcc void @_Z15__regcall3__fooCe({ x86_fp80, x86_fp80 }* inreg noalias sret %agg.result, { x86_fp80, x86_fp80 }* byval({ x86_fp80, x86_fp80 }) align 4 %f) +// CHECK-LIN64-DAG: define x86_regcallcc void @_Z15__regcall3__fooCe({ x86_fp80, x86_fp80 }* noalias sret align 16 %agg.result, { x86_fp80, x86_fp80 }* byval({ x86_fp80, x86_fp80 }) align 16 %f) +// CHECK-LIN32-DAG: define x86_regcallcc void @_Z15__regcall3__fooCe({ x86_fp80, x86_fp80 }* inreg noalias sret align 4 %agg.result, { x86_fp80, x86_fp80 }* byval({ x86_fp80, x86_fp80 }) align 4 %f) // CHECK-WIN64-DAG: define dso_local x86_regcallcc { double, double } @"?foo@@YwU?$_Complex@O@__clang@@U12@@Z"(double %f.0, double %f.1) // CHECK-WIN32-DAG: define dso_local x86_regcallcc { double, double } @"?foo@@YwU?$_Complex@O@__clang@@U12@@Z"(double %f.0, double %f.1) diff --git a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp index 4e824d94f510e..7d86ea8447b41 100644 --- a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp +++ b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp @@ -39,7 +39,7 @@ const char * f(S s) // CHECK: call void @llvm.lifetime.start.p0i8(i64 16, i8* [[T3i8]]) // CHECK: [[T5:%.*]] = call %class.T* @_ZN1TC1E1S(%class.T* [[T3]], [2 x i32] %{{.*}}) // -// CHECK: call void @_ZNK1T6concatERKS_(%class.T* sret [[T1]], %class.T* [[T2]], %class.T* dereferenceable(16) [[T3]]) +// CHECK: call void @_ZNK1T6concatERKS_(%class.T* sret align 4 [[T1]], %class.T* [[T2]], %class.T* dereferenceable(16) [[T3]]) // CHECK: [[T6:%.*]] = call i8* @_ZNK1T3strEv(%class.T* [[T1]]) // // CHECK: call void @llvm.lifetime.end.p0i8( diff --git a/clang/test/CodeGenCXX/stack-reuse.cpp b/clang/test/CodeGenCXX/stack-reuse.cpp index 35dcb5b349c3e..94e5e3d9b364b 100644 --- a/clang/test/CodeGenCXX/stack-reuse.cpp +++ b/clang/test/CodeGenCXX/stack-reuse.cpp @@ -135,7 +135,7 @@ int large_combiner_test(S_large s) { // CHECK: [[T2:%.*]] = alloca %struct.Combiner // CHECK: [[T1:%.*]] = alloca %struct.Combiner // CHECK: [[T3:%.*]] = call %struct.Combiner* @_ZN8CombinerC1E7S_large(%struct.Combiner* nonnull [[T1]], [9 x i32] %s.coerce) -// CHECK: call void @_ZN8Combiner1fEv(%struct.Combiner* nonnull sret [[T2]], %struct.Combiner* nonnull [[T1]]) +// CHECK: call void @_ZN8Combiner1fEv(%struct.Combiner* nonnull sret align 4 [[T2]], %struct.Combiner* nonnull [[T1]]) // CHECK: [[T4:%.*]] = getelementptr inbounds %struct.Combiner, %struct.Combiner* [[T2]], i32 0, i32 0, i32 0, i32 0 // CHECK: [[T5:%.*]] = load i32, i32* [[T4]] // CHECK: ret i32 [[T5]] diff --git a/clang/test/CodeGenCXX/temporaries.cpp b/clang/test/CodeGenCXX/temporaries.cpp index d15e0fa05bd86..175b475c8cd7f 100644 --- a/clang/test/CodeGenCXX/temporaries.cpp +++ b/clang/test/CodeGenCXX/temporaries.cpp @@ -403,13 +403,13 @@ namespace Elision { // CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[I]]) A i = (foo(), A()); - // CHECK-NEXT: call void @_ZN7Elision4fooAEv([[A]]* sret [[T0]]) + // CHECK-NEXT: call void @_ZN7Elision4fooAEv([[A]]* sret align 8 [[T0]]) // CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[J]]) // CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[T0]]) A j = (fooA(), A()); // CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[T1]]) - // CHECK-NEXT: call void @_ZN7Elision4fooAEv([[A]]* sret [[K]]) + // CHECK-NEXT: call void @_ZN7Elision4fooAEv([[A]]* sret align 8 [[K]]) // CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[T1]]) A k = (A(), fooA()); @@ -436,7 +436,7 @@ namespace Elision { // CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[I]]) } - // CHECK: define void @_ZN7Elision5test2Ev([[A]]* noalias sret + // CHECK: define void @_ZN7Elision5test2Ev([[A]]* noalias sret align 8 A test2() { // CHECK: call void @_ZN7Elision3fooEv() // CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[RET:%.*]]) @@ -444,7 +444,7 @@ namespace Elision { return (foo(), A()); } - // CHECK: define void @_ZN7Elision5test3EiNS_1AE([[A]]* noalias sret + // CHECK: define void @_ZN7Elision5test3EiNS_1AE([[A]]* noalias sret align 8 A test3(int v, A x) { if (v < 5) // CHECK: call void @_ZN7Elision1AC1Ev([[A]]* [[RET:%.*]]) @@ -485,7 +485,7 @@ namespace Elision { } // rdar://problem/8433352 - // CHECK: define void @_ZN7Elision5test5Ev([[A]]* noalias sret + // CHECK: define void @_ZN7Elision5test5Ev([[A]]* noalias sret align 8 struct B { A a; B(); }; A test5() { // CHECK: [[AT0:%.*]] = alloca [[A]], align 8 @@ -523,7 +523,7 @@ namespace Elision { void test6(const C *x) { // CHECK: [[T0:%.*]] = alloca [[A]], align 8 // CHECK: [[X:%.*]] = load [[C]]*, [[C]]** {{%.*}}, align 8 - // CHECK-NEXT: call void @_ZNK7Elision1CcvNS_1AEEv([[A]]* sret [[T0]], [[C]]* [[X]]) + // CHECK-NEXT: call void @_ZNK7Elision1CcvNS_1AEEv([[A]]* sret align 8 [[T0]], [[C]]* [[X]]) // CHECK-NEXT: call void @_ZNK7Elision1A3fooEv([[A]]* [[T0]]) // CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[T0]]) // CHECK-NEXT: ret void diff --git a/clang/test/CodeGenCXX/thiscall-struct-return.cpp b/clang/test/CodeGenCXX/thiscall-struct-return.cpp index a6be5aa494e1b..35d5cc479177a 100644 --- a/clang/test/CodeGenCXX/thiscall-struct-return.cpp +++ b/clang/test/CodeGenCXX/thiscall-struct-return.cpp @@ -34,8 +34,8 @@ void test( void ) { // CHECK: call void @_ZN1CC1Ev(%class.C* [[C:%.+]]) C c; -// CHECK: call x86_thiscallcc void @_ZNK1C5SmallEv(%struct.S* sret %{{.+}}, %class.C* [[C]]) +// CHECK: call x86_thiscallcc void @_ZNK1C5SmallEv(%struct.S* sret align 4 %{{.+}}, %class.C* [[C]]) (void)c.Small(); -// CHECK: call x86_thiscallcc void @_ZNK1C6MediumEv(%struct.M* sret %{{.+}}, %class.C* [[C]]) +// CHECK: call x86_thiscallcc void @_ZNK1C6MediumEv(%struct.M* sret align 4 %{{.+}}, %class.C* [[C]]) (void)c.Medium(); } diff --git a/clang/test/CodeGenCXX/thunk-returning-memptr.cpp b/clang/test/CodeGenCXX/thunk-returning-memptr.cpp index 0b7870c6d6582..63bb3d68472d7 100644 --- a/clang/test/CodeGenCXX/thunk-returning-memptr.cpp +++ b/clang/test/CodeGenCXX/thunk-returning-memptr.cpp @@ -23,5 +23,5 @@ C::C() {} // Because of the tail call, the return value cannot be copied into a local // alloca. (PR39901) -// CHECK-LABEL: define linkonce_odr void @_ZThn4_N1C1fEv({ i32, i32 }* noalias sret %agg.result, %struct.C* %this) -// CHECK: tail call void @_ZN1C1fEv({ i32, i32 }* sret %agg.result +// CHECK-LABEL: define linkonce_odr void @_ZThn4_N1C1fEv({ i32, i32 }* noalias sret align 4 %agg.result, %struct.C* %this) +// CHECK: tail call void @_ZN1C1fEv({ i32, i32 }* sret align 4 %agg.result diff --git a/clang/test/CodeGenCXX/thunks-ehspec.cpp b/clang/test/CodeGenCXX/thunks-ehspec.cpp new file mode 100644 index 0000000000000..30276948d3fcd --- /dev/null +++ b/clang/test/CodeGenCXX/thunks-ehspec.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fexceptions -fcxx-exceptions %s -triple=x86_64-pc-linux-gnu -munwind-tables -emit-llvm -o - -O1 -disable-llvm-passes | FileCheck %s + +// When generating the thunk for secondary, do not push terminate scopes for +// either the varargs or non-varargs case. Related to PR44987. + +struct A { + virtual void primary_key(); +}; +struct B { + virtual void secondary(); + virtual void secondary_vararg(int, ...); +}; +class C : A, B { + virtual void primary_key(); + void secondary() noexcept; + void secondary_vararg(int, ...) noexcept; +}; +void C::primary_key() {} + +// CHECK-LABEL: define available_externally void @_ZThn8_N1C9secondaryEv(%class.C* %this) +// CHECK-NOT: invoke +// CHECK: tail call void @_ZN1C9secondaryEv(%class.C* %{{.*}}) +// CHECK-NOT: invoke +// CHECK: ret void + +// CHECK-LABEL: define available_externally void @_ZThn8_N1C16secondary_varargEiz(%class.C* %this, i32 %0, ...) +// CHECK-NOT: invoke +// CHECK: musttail call void (%class.C*, i32, ...) @_ZN1C16secondary_varargEiz(%class.C* %{{.*}}, i32 %{{.*}}, ...) #2 +// CHECK-NEXT: ret void diff --git a/clang/test/CodeGenCXX/thunks.cpp b/clang/test/CodeGenCXX/thunks.cpp index 4a0610ed488d3..b5c2852f87703 100644 --- a/clang/test/CodeGenCXX/thunks.cpp +++ b/clang/test/CodeGenCXX/thunks.cpp @@ -206,13 +206,13 @@ namespace Test6 { // CHECK-LABEL: define void @_ZThn16_N5Test66Thunks1fEv // CHECK-DBG-NOT: dbg.declare // CHECK-NOT: memcpy - // CHECK: {{call void @_ZN5Test66Thunks1fEv.*sret}} + // CHECK: {{call void @_ZN5Test66Thunks1fEv.*sret align 1}} // CHECK: ret void X Thunks::f() { return X(); } - // WIN64-LABEL: define linkonce_odr dso_local void @"?f@Thunks@Test6@@WBA@EAA?AUX@2@XZ"({{.*}} sret %{{.*}}) + // WIN64-LABEL: define linkonce_odr dso_local void @"?f@Thunks@Test6@@WBA@EAA?AUX@2@XZ"({{.*}} sret align 1 %{{.*}}) // WIN64-NOT: memcpy - // WIN64: tail call void @"?f@Thunks@Test6@@UEAA?AUX@2@XZ"({{.*}} sret %{{.*}}) + // WIN64: tail call void @"?f@Thunks@Test6@@UEAA?AUX@2@XZ"({{.*}} sret align 1 %{{.*}}) } namespace Test7 { @@ -412,7 +412,7 @@ namespace Test13 { // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 8 // CHECK: ret %"struct.Test13::D"* - // WIN64-LABEL: define weak_odr dso_local dereferenceable(32) %"struct.Test13::D"* @"?foo1@D@Test13@@$4PPPPPPPE@A@EAAAEAUB1@2@XZ"( + // WIN64-LABEL: define weak_odr dso_local dereferenceable(8) %"struct.Test13::D"* @"?foo1@D@Test13@@$4PPPPPPPE@A@EAAAEAUB1@2@XZ"( // This adjustment. // WIN64: getelementptr inbounds i8, i8* {{.*}}, i64 -12 // Call implementation. diff --git a/clang/test/CodeGenCXX/trivial_abi.cpp b/clang/test/CodeGenCXX/trivial_abi.cpp index 2cf07b22581a2..23c589dacd7e2 100644 --- a/clang/test/CodeGenCXX/trivial_abi.cpp +++ b/clang/test/CodeGenCXX/trivial_abi.cpp @@ -126,7 +126,7 @@ void testIgnoredSmall() { void testParamLarge(Large a) noexcept { } -// CHECK: define void @_Z15testReturnLargev(%[[STRUCT_LARGE:.*]]* noalias sret %[[AGG_RESULT:.*]]) +// CHECK: define void @_Z15testReturnLargev(%[[STRUCT_LARGE:.*]]* noalias sret align 8 %[[AGG_RESULT:.*]]) // CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeC1Ev(%[[STRUCT_LARGE]]* %[[AGG_RESULT]]) // CHECK: ret void // CHECK: } @@ -153,7 +153,7 @@ void testCallLarge0() { // CHECK: define void @_Z14testCallLarge1v() // CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8 -// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret %[[AGG_TMP]]) +// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret align 8 %[[AGG_TMP]]) // CHECK: call void @_Z14testParamLarge5Large(%[[STRUCT_LARGE]]* %[[AGG_TMP]]) // CHECK: ret void // CHECK: } @@ -164,7 +164,7 @@ void testCallLarge1() { // CHECK: define void @_Z16testIgnoredLargev() // CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_LARGE:.*]], align 8 -// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret %[[AGG_TMP_ENSURED]]) +// CHECK: call void @_Z15testReturnLargev(%[[STRUCT_LARGE]]* sret align 8 %[[AGG_TMP_ENSURED]]) // CHECK: %[[CALL:.*]] = call %[[STRUCT_LARGE]]* @_ZN5LargeD1Ev(%[[STRUCT_LARGE]]* %[[AGG_TMP_ENSURED]]) // CHECK: ret void // CHECK: } @@ -186,7 +186,7 @@ Trivial testReturnHasTrivial() { return t; } -// CHECK: define void @_Z23testReturnHasNonTrivialv(%[[STRUCT_NONTRIVIAL:.*]]* noalias sret %[[AGG_RESULT:.*]]) +// CHECK: define void @_Z23testReturnHasNonTrivialv(%[[STRUCT_NONTRIVIAL:.*]]* noalias sret align 4 %[[AGG_RESULT:.*]]) // CHECK: %[[CALL:.*]] = call %[[STRUCT_NONTRIVIAL]]* @_ZN10NonTrivialC1Ev(%[[STRUCT_NONTRIVIAL]]* %[[AGG_RESULT]]) // CHECK: ret void // CHECK: } diff --git a/clang/test/CodeGenCXX/unknown-anytype.cpp b/clang/test/CodeGenCXX/unknown-anytype.cpp index 42ed472380b15..0a7ab53b7af6c 100644 --- a/clang/test/CodeGenCXX/unknown-anytype.cpp +++ b/clang/test/CodeGenCXX/unknown-anytype.cpp @@ -71,7 +71,7 @@ struct Test7 { }; extern "C" __unknown_anytype test7_any(int); Test7 test7() { - // COMMON: call void @test7_any({{%.*}}* sret {{%.*}}, i32 5) + // COMMON: call void @test7_any({{%.*}}* sret align 1 {{%.*}}, i32 5) return (Test7) test7_any(5); } diff --git a/clang/test/CodeGenCXX/vcall-visibility-metadata.cpp b/clang/test/CodeGenCXX/vcall-visibility-metadata.cpp deleted file mode 100644 index 7cf48395673cc..0000000000000 --- a/clang/test/CodeGenCXX/vcall-visibility-metadata.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -emit-llvm -fvirtual-function-elimination -fwhole-program-vtables -o - %s | FileCheck %s - - -// Anonymous namespace. -namespace { -// CHECK: @_ZTVN12_GLOBAL__N_11AE = {{.*}} !vcall_visibility [[VIS_TU:![0-9]+]] -struct A { - A() {} - virtual int f() { return 1; } -}; -} -void *construct_A() { - return new A(); -} - - -// Hidden visibility. -// CHECK: @_ZTV1B = {{.*}} !vcall_visibility [[VIS_DSO:![0-9]+]] -struct __attribute__((visibility("hidden"))) B { - B() {} - virtual int f() { return 1; } -}; -B *construct_B() { - return new B(); -} - - -// Default visibility. -// CHECK-NOT: @_ZTV1C = {{.*}} !vcall_visibility -struct __attribute__((visibility("default"))) C { - C() {} - virtual int f() { return 1; } -}; -C *construct_C() { - return new C(); -} - - -// Hidden visibility, public LTO visibility. -// CHECK-NOT: @_ZTV1D = {{.*}} !vcall_visibility -struct __attribute__((visibility("hidden"))) [[clang::lto_visibility_public]] D { - D() {} - virtual int f() { return 1; } -}; -D *construct_D() { - return new D(); -} - - -// Hidden visibility, but inherits from class with default visibility. -// CHECK-NOT: @_ZTV1E = {{.*}} !vcall_visibility -struct __attribute__((visibility("hidden"))) E : C { - E() {} - virtual int f() { return 1; } -}; -E *construct_E() { - return new E(); -} - - -// Anonymous namespace, but inherits from class with default visibility. -// CHECK-NOT: @_ZTVN12_GLOBAL__N_11FE = {{.*}} !vcall_visibility -namespace { -struct __attribute__((visibility("hidden"))) F : C { - F() {} - virtual int f() { return 1; } -}; -} -void *construct_F() { - return new F(); -} - - -// Anonymous namespace, but inherits from class with hidden visibility. -// CHECK: @_ZTVN12_GLOBAL__N_11GE = {{.*}} !vcall_visibility [[VIS_DSO:![0-9]+]] -namespace { -struct __attribute__((visibility("hidden"))) G : B { - G() {} - virtual int f() { return 1; } -}; -} -void *construct_G() { - return new G(); -} - - -// CHECK-DAG: [[VIS_DSO]] = !{i64 1} -// CHECK-DAG: [[VIS_TU]] = !{i64 2} diff --git a/clang/test/CodeGenCXX/virtual-function-elimination.cpp b/clang/test/CodeGenCXX/virtual-function-elimination.cpp deleted file mode 100644 index a89e6ebceeaf9..0000000000000 --- a/clang/test/CodeGenCXX/virtual-function-elimination.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux -flto -flto-unit -fvirtual-function-elimination -fwhole-program-vtables -emit-llvm -o - %s | FileCheck %s - - -struct __attribute__((visibility("default"))) A { - virtual void foo(); -}; - -void test_1(A *p) { - // A has default visibility, so no need for type.checked.load. -// CHECK-LABEL: define void @_Z6test_1P1A -// CHECK: [[FN_PTR_ADDR:%.+]] = getelementptr inbounds void (%struct.A*)*, void (%struct.A*)** {{%.+}}, i64 0 -// CHECK: [[FN_PTR:%.+]] = load void (%struct.A*)*, void (%struct.A*)** [[FN_PTR_ADDR]] -// CHECK: call void [[FN_PTR]]( - p->foo(); -} - - -struct __attribute__((visibility("hidden"))) [[clang::lto_visibility_public]] B { - virtual void foo(); -}; - -void test_2(B *p) { - // B has public LTO visibility, so no need for type.checked.load. -// CHECK-LABEL: define void @_Z6test_2P1B -// CHECK: [[FN_PTR_ADDR:%.+]] = getelementptr inbounds void (%struct.B*)*, void (%struct.B*)** {{%.+}}, i64 0 -// CHECK: [[FN_PTR:%.+]] = load void (%struct.B*)*, void (%struct.B*)** [[FN_PTR_ADDR]] -// CHECK: call void [[FN_PTR]]( - p->foo(); -} - - -struct __attribute__((visibility("hidden"))) C { - virtual void foo(); - virtual void bar(); -}; - -void test_3(C *p) { - // C has hidden visibility, so we generate type.checked.load to allow VFE. -// CHECK-LABEL: define void @_Z6test_3P1C -// CHECK: [[LOAD:%.+]] = call { i8*, i1 } @llvm.type.checked.load(i8* {{%.+}}, i32 0, metadata !"_ZTS1C") -// CHECK: [[FN_PTR_I8:%.+]] = extractvalue { i8*, i1 } [[LOAD]], 0 -// CHECK: [[FN_PTR:%.+]] = bitcast i8* [[FN_PTR_I8]] to void (%struct.C*)* -// CHECK: call void [[FN_PTR]]( - p->foo(); -} - -void test_4(C *p) { - // When using type.checked.load, we pass the vtable offset to the intrinsic, - // rather than adding it to the pointer with a GEP. -// CHECK-LABEL: define void @_Z6test_4P1C -// CHECK: [[LOAD:%.+]] = call { i8*, i1 } @llvm.type.checked.load(i8* {{%.+}}, i32 8, metadata !"_ZTS1C") -// CHECK: [[FN_PTR_I8:%.+]] = extractvalue { i8*, i1 } [[LOAD]], 0 -// CHECK: [[FN_PTR:%.+]] = bitcast i8* [[FN_PTR_I8]] to void (%struct.C*)* -// CHECK: call void [[FN_PTR]]( - p->bar(); -} - -void test_5(C *p, void (C::*q)(void)) { - // We also use type.checked.load for the virtual side of member function - // pointer calls. We use a GEP to calculate the address to load from and pass - // 0 as the offset to the intrinsic, because we know that the load must be - // from exactly the point marked by one of the function-type metadatas (in - // this case "_ZTSM1CFvvE.virtual"). If we passed the offset from the member - // function pointer to the intrinsic, this information would be lost. No - // codegen changes on the non-virtual side. -// CHECK-LABEL: define void @_Z6test_5P1CMS_FvvE( -// CHECK: [[FN_PTR_ADDR:%.+]] = getelementptr i8, i8* %vtable, i64 {{%.+}} -// CHECK: [[LOAD:%.+]] = call { i8*, i1 } @llvm.type.checked.load(i8* [[FN_PTR_ADDR]], i32 0, metadata !"_ZTSM1CFvvE.virtual") -// CHECK: [[FN_PTR_I8:%.+]] = extractvalue { i8*, i1 } [[LOAD]], 0 -// CHECK: [[FN_PTR:%.+]] = bitcast i8* [[FN_PTR_I8]] to void (%struct.C*)* - -// CHECK: [[PHI:%.+]] = phi void (%struct.C*)* {{.*}}[ [[FN_PTR]], {{.*}} ] -// CHECK: call void [[PHI]]( - (p->*q)(); -} diff --git a/clang/test/CodeGenCXX/wasm-args-returns.cpp b/clang/test/CodeGenCXX/wasm-args-returns.cpp index c547eb85390da..3c57961eb2fcc 100644 --- a/clang/test/CodeGenCXX/wasm-args-returns.cpp +++ b/clang/test/CodeGenCXX/wasm-args-returns.cpp @@ -30,52 +30,52 @@ struct two_fields { double d, e; }; test(two_fields); -// CHECK: define void @_Z7forward10two_fields(%struct.two_fields* noalias nocapture sret %{{.*}}, %struct.two_fields* nocapture readonly byval(%struct.two_fields) align 8 %{{.*}}) +// CHECK: define void @_Z7forward10two_fields(%struct.two_fields* noalias nocapture sret align 8 %{{.*}}, %struct.two_fields* nocapture readonly byval(%struct.two_fields) align 8 %{{.*}}) // // CHECK: define void @_Z15test_two_fieldsv() // CHECK: %[[tmp:.*]] = alloca %struct.two_fields, align 8 -// CHECK: call void @_Z14def_two_fieldsv(%struct.two_fields* nonnull sret %[[tmp]]) +// CHECK: call void @_Z14def_two_fieldsv(%struct.two_fields* nonnull sret align 8 %[[tmp]]) // CHECK: call void @_Z3use10two_fields(%struct.two_fields* nonnull byval(%struct.two_fields) align 8 %[[tmp]]) // CHECK: ret void // // CHECK: declare void @_Z3use10two_fields(%struct.two_fields* byval(%struct.two_fields) align 8) -// CHECK: declare void @_Z14def_two_fieldsv(%struct.two_fields* sret) +// CHECK: declare void @_Z14def_two_fieldsv(%struct.two_fields* sret align 8) struct copy_ctor { double d; copy_ctor(copy_ctor const &); }; test(copy_ctor); -// CHECK: define void @_Z7forward9copy_ctor(%struct.copy_ctor* noalias sret %{{.*}}, %struct.copy_ctor* nonnull %{{.*}}) +// CHECK: define void @_Z7forward9copy_ctor(%struct.copy_ctor* noalias sret align 8 %{{.*}}, %struct.copy_ctor* nonnull %{{.*}}) // // CHECK: declare %struct.copy_ctor* @_ZN9copy_ctorC1ERKS_(%struct.copy_ctor* returned, %struct.copy_ctor* dereferenceable(8)) // // CHECK: define void @_Z14test_copy_ctorv() // CHECK: %[[tmp:.*]] = alloca %struct.copy_ctor, align 8 -// CHECK: call void @_Z13def_copy_ctorv(%struct.copy_ctor* nonnull sret %[[tmp]]) +// CHECK: call void @_Z13def_copy_ctorv(%struct.copy_ctor* nonnull sret align 8 %[[tmp]]) // CHECK: call void @_Z3use9copy_ctor(%struct.copy_ctor* nonnull %[[tmp]]) // CHECK: ret void // // CHECK: declare void @_Z3use9copy_ctor(%struct.copy_ctor*) -// CHECK: declare void @_Z13def_copy_ctorv(%struct.copy_ctor* sret) +// CHECK: declare void @_Z13def_copy_ctorv(%struct.copy_ctor* sret align 8) struct __attribute__((aligned(16))) aligned_copy_ctor { double d, e; aligned_copy_ctor(aligned_copy_ctor const &); }; test(aligned_copy_ctor); -// CHECK: define void @_Z7forward17aligned_copy_ctor(%struct.aligned_copy_ctor* noalias sret %{{.*}}, %struct.aligned_copy_ctor* nonnull %{{.*}}) +// CHECK: define void @_Z7forward17aligned_copy_ctor(%struct.aligned_copy_ctor* noalias sret align 16 %{{.*}}, %struct.aligned_copy_ctor* nonnull %{{.*}}) // // CHECK: declare %struct.aligned_copy_ctor* @_ZN17aligned_copy_ctorC1ERKS_(%struct.aligned_copy_ctor* returned, %struct.aligned_copy_ctor* dereferenceable(16)) // // CHECK: define void @_Z22test_aligned_copy_ctorv() // CHECK: %[[tmp:.*]] = alloca %struct.aligned_copy_ctor, align 16 -// CHECK: call void @_Z21def_aligned_copy_ctorv(%struct.aligned_copy_ctor* nonnull sret %[[tmp]]) +// CHECK: call void @_Z21def_aligned_copy_ctorv(%struct.aligned_copy_ctor* nonnull sret align 16 %[[tmp]]) // CHECK: call void @_Z3use17aligned_copy_ctor(%struct.aligned_copy_ctor* nonnull %[[tmp]]) // CHECK: ret void // // CHECK: declare void @_Z3use17aligned_copy_ctor(%struct.aligned_copy_ctor*) -// CHECK: declare void @_Z21def_aligned_copy_ctorv(%struct.aligned_copy_ctor* sret) +// CHECK: declare void @_Z21def_aligned_copy_ctorv(%struct.aligned_copy_ctor* sret align 16) struct empty {}; test(empty); diff --git a/clang/test/CodeGenCXX/x86_32-arguments.cpp b/clang/test/CodeGenCXX/x86_32-arguments.cpp index 830168635b529..c7ff59e943d2e 100644 --- a/clang/test/CodeGenCXX/x86_32-arguments.cpp +++ b/clang/test/CodeGenCXX/x86_32-arguments.cpp @@ -6,7 +6,7 @@ struct S { short s; }; -// CHECK-LABEL: define void @_Z1fv(%struct.S* noalias sret % +// CHECK-LABEL: define void @_Z1fv(%struct.S* noalias sret align 2 % S f() { return S(); } // CHECK-LABEL: define void @_Z1f1S(%struct.S* %0) void f(S) { } @@ -18,7 +18,7 @@ class C { double c; }; -// CHECK-LABEL: define void @_Z1gv(%class.C* noalias sret % +// CHECK-LABEL: define void @_Z1gv(%class.C* noalias sret align 4 % C g() { return C(); } // CHECK-LABEL: define void @_Z1f1C(%class.C* %0) @@ -103,13 +103,13 @@ struct s7_1 { double x; }; struct s7 : s7_0, s7_1 { }; s7 f7() { return s7(); } -// CHECK-LABEL: define void @_Z2f8v(%struct.s8* noalias sret %agg.result) +// CHECK-LABEL: define void @_Z2f8v(%struct.s8* noalias sret align 4 %agg.result) struct s8_0 { }; struct s8_1 { double x; }; struct s8 { s8_0 a; s8_1 b; }; s8 f8() { return s8(); } -// CHECK-LABEL: define void @_Z2f9v(%struct.s9* noalias sret %agg.result) +// CHECK-LABEL: define void @_Z2f9v(%struct.s9* noalias sret align 4 %agg.result) struct s9_0 { unsigned : 0; }; struct s9_1 { double x; }; struct s9 { s9_0 a; s9_1 b; }; diff --git a/clang/test/CodeGenCXX/x86_64-arguments.cpp b/clang/test/CodeGenCXX/x86_64-arguments.cpp index e905907788950..f7a898b220af7 100644 --- a/clang/test/CodeGenCXX/x86_64-arguments.cpp +++ b/clang/test/CodeGenCXX/x86_64-arguments.cpp @@ -176,7 +176,7 @@ namespace test9 { // CHECK: define void @_ZN5test93fooEPNS_1SEPNS_1TE([[S:%.*]]* %0, [[T:%.*]]* %1) void foo(S*, T*) {} - // CHECK: define void @_ZN5test91aEiiiiNS_1TEPv([[S]]* noalias sret {{%.*}}, i32 %0, i32 %1, i32 %2, i32 %3, [[T]]* byval([[T]]) align 8 %4, i8* %5) + // CHECK: define void @_ZN5test91aEiiiiNS_1TEPv([[S]]* noalias sret align 8 {{%.*}}, i32 %0, i32 %1, i32 %2, i32 %3, [[T]]* byval([[T]]) align 8 %4, i8* %5) S a(int, int, int, int, T, void*) { return S(); } @@ -186,7 +186,7 @@ namespace test9 { return sret; } - // CHECK: define void @_ZN5test91cEiiiNS_1TEPv([[S]]* noalias sret {{%.*}}, i32 %0, i32 %1, i32 %2, i8* {{%.*}}, i8* {{%.*}}, i8* %3) + // CHECK: define void @_ZN5test91cEiiiNS_1TEPv([[S]]* noalias sret align 8 {{%.*}}, i32 %0, i32 %1, i32 %2, i8* {{%.*}}, i8* {{%.*}}, i8* %3) S c(int, int, int, T, void*) { return S(); } diff --git a/clang/test/CodeGenCoroutines/coro-await.cpp b/clang/test/CodeGenCoroutines/coro-await.cpp index 86bacc766db3f..99097f376aa57 100644 --- a/clang/test/CodeGenCoroutines/coro-await.cpp +++ b/clang/test/CodeGenCoroutines/coro-await.cpp @@ -130,7 +130,7 @@ extern "C" void f1(int) { // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::experimental::coroutine_traits::promise_type" // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( co_yield 42; - // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret %[[AWAITER:.+]], %"struct.std::experimental::coroutine_traits::promise_type"* %[[PROMISE]], i32 42) + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret align 4 %[[AWAITER:.+]], %"struct.std::experimental::coroutine_traits::promise_type"* %[[PROMISE]], i32 42) // See if we need to suspend: // -------------------------- @@ -197,20 +197,20 @@ extern "C" void UseAggr(Aggr&&); extern "C" void TestAggr() { UseAggr(co_await AggrAwaiter{}); Whatever(); - // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume:.+]], + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret align 4 %[[AwaitResume:.+]], // CHECK: call void @UseAggr(%struct.Aggr* dereferenceable(12) %[[AwaitResume]]) // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume]]) // CHECK: call void @Whatever() co_await AggrAwaiter{}; Whatever(); - // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume2:.+]], + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret align 4 %[[AwaitResume2:.+]], // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume2]]) // CHECK: call void @Whatever() Aggr Val = co_await AggrAwaiter{}; Whatever(); - // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume3:.+]], + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret align 4 %[[AwaitResume3:.+]], // CHECK: call void @Whatever() // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume3]]) } @@ -253,7 +253,7 @@ extern "C" void TestOpAwait() { co_await MyAgg{}; // CHECK: call void @_ZN5MyAggawEv(%struct.MyAgg* % - // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret % + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret align 4 % } // CHECK-LABEL: EndlessLoop( diff --git a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp b/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp index f4a71864ea0ec..890dc85991206 100644 --- a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp @@ -34,14 +34,14 @@ struct coro { }; // Verify that the NRVO is applied to the Gro object. -// CHECK-LABEL: define void @_Z1fi(%struct.coro* noalias sret %agg.result, i32 %0) +// CHECK-LABEL: define void @_Z1fi(%struct.coro* noalias sret align 8 %agg.result, i32 %0) coro f(int) { // CHECK: %call = call i8* @_Znwm( // CHECK-NEXT: br label %[[CoroInit:.*]] // CHECK: {{.*}}[[CoroInit]]: // CHECK: store i1 false, i1* %gro.active -// CHECK: call void @{{.*get_return_objectEv}}(%struct.coro* sret %agg.result +// CHECK: call void @{{.*get_return_objectEv}}(%struct.coro* sret align 8 %agg.result // CHECK-NEXT: store i1 true, i1* %gro.active co_return; } @@ -65,7 +65,7 @@ struct coro_two { }; // Verify that the NRVO is applied to the Gro object. -// CHECK-LABEL: define void @_Z1hi(%struct.coro_two* noalias sret %agg.result, i32 %0) +// CHECK-LABEL: define void @_Z1hi(%struct.coro_two* noalias sret align 8 %agg.result, i32 %0) coro_two h(int) { // CHECK: %call = call i8* @_ZnwmRKSt9nothrow_t @@ -73,12 +73,12 @@ struct coro_two { // CHECK-NEXT: br i1 %[[CheckNull]], label %[[InitOnSuccess:.*]], label %[[InitOnFailure:.*]] // CHECK: {{.*}}[[InitOnFailure]]: -// CHECK-NEXT: call void @{{.*get_return_object_on_allocation_failureEv}}(%struct.coro_two* sret %agg.result +// CHECK-NEXT: call void @{{.*get_return_object_on_allocation_failureEv}}(%struct.coro_two* sret align 8 %agg.result // CHECK-NEXT: br label %[[RetLabel:.*]] // CHECK: {{.*}}[[InitOnSuccess]]: // CHECK: store i1 false, i1* %gro.active -// CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret %agg.result +// CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret align 8 %agg.result // CHECK-NEXT: store i1 true, i1* %gro.active // CHECK: [[RetLabel]]: diff --git a/clang/test/CodeGenObjC/arc-ternary-op.m b/clang/test/CodeGenObjC/arc-ternary-op.m index 4883143791a24..28567fcbe22e6 100644 --- a/clang/test/CodeGenObjC/arc-ternary-op.m +++ b/clang/test/CodeGenObjC/arc-ternary-op.m @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s +id g0, g1; + void test0(_Bool cond) { id test0_helper(void) __attribute__((ns_returns_retained)); @@ -147,4 +149,58 @@ void test2(int cond) { // CHECK: call void @llvm.objc.release(i8* [[RESULT]]) } +void test3(int cond) { + __strong id *p = cond ? (__strong id[]){g0, g1} : (__strong id[]){g1, g0}; + test2(cond); + + // CHECK: define void @test3( + // CHECK: %[[P:.*]] = alloca i8**, align 8 + // CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca [2 x i8*], align 8 + // CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1 + // CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca [2 x i8*], align 8 + // CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1 + + // CHECK: %[[ARRAYINIT_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i64 0, i64 0 + // CHECK: %[[V2:.*]] = load i8*, i8** @g0, align 8 + // CHECK: %[[V3:.*]] = call i8* @llvm.objc.retain(i8* %[[V2]]) + // CHECK: store i8* %[[V3]], i8** %[[ARRAYINIT_BEGIN]], align 8 + // CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN]], i64 1 + // CHECK: %[[V4:.*]] = load i8*, i8** @g1, align 8 + // CHECK: %[[V5:.*]] = call i8* @llvm.objc.retain(i8* %[[V4]]) + // CHECK: store i8* %[[V5]], i8** %[[ARRAYINIT_ELEMENT]], align 8 + // CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1 + // CHECK: %[[ARRAYDECAY:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i64 0, i64 0 + + // CHECK: %[[ARRAYINIT_BEGIN2:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i64 0, i64 0 + // CHECK: %[[V6:.*]] = load i8*, i8** @g1, align 8 + // CHECK: %[[V7:.*]] = call i8* @llvm.objc.retain(i8* %[[V6]]) + // CHECK: store i8* %[[V7]], i8** %[[ARRAYINIT_BEGIN2]], align 8 + // CHECK: %[[ARRAYINIT_ELEMENT3:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN2]], i64 1 + // CHECK: %[[V8:.*]] = load i8*, i8** @g0, align 8 + // CHECK: %[[V9:.*]] = call i8* @llvm.objc.retain(i8* %[[V8]]) + // CHECK: store i8* %[[V9]], i8** %[[ARRAYINIT_ELEMENT3]], align 8 + // CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1 + // CHECK: %[[ARRAYDECAY5:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i64 0, i64 0 + + // CHECK: %[[COND6:.*]] = phi i8** [ %[[ARRAYDECAY]], %{{.*}} ], [ %[[ARRAYDECAY5]], %{{.*}} ] + // CHECK: store i8** %[[COND6]], i8*** %[[P]], align 8 + // CHECK: call void @test2( + + // CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0 + // CHECK: %[[V11:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN]], i64 2 + + // CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi i8** [ %[[V11]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ] + // CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1 + // CHECK: %[[V12:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT]], align 8 + // CHECK: call void @llvm.objc.release(i8* %[[V12]]) + + // CHECK: %[[ARRAY_BEGIN10:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i32 0, i32 0 + // CHECK: %[[V13:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN10]], i64 2 + + // CHECK: %[[ARRAYDESTROY_ELEMENTPAST12:.*]] = phi i8** [ %[[V13]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT13:.*]], %{{.*}} ] + // CHECK: %[[ARRAYDESTROY_ELEMENT13]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST12]], i64 -1 + // CHECK: %[[V14:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT13]], align 8 + // CHECK: call void @llvm.objc.release(i8* %[[V14]]) +} + // CHECK: attributes [[NUW]] = { nounwind } diff --git a/clang/test/CodeGenObjC/arc.m b/clang/test/CodeGenObjC/arc.m index 31ecb53713be8..375ad8ed7b416 100644 --- a/clang/test/CodeGenObjC/arc.m +++ b/clang/test/CodeGenObjC/arc.m @@ -1536,27 +1536,61 @@ void test70(id i) { // CHECK-LABEL: define void @test71 void test71(void) { - // FIXME: It would be nice if the __destructor_8_s40 for the first call (and - // the following lifetime.end) came before the second call. - // // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]]) - // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP1]]) + // CHECK: call void @getAggDtor(%struct.AggDtor* sret align 8 %[[TMP1]]) + // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8** + // CHECK: call void @__destructor_8_s40(i8** %[[T]]) + // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* + // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]]) - // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP2]]) + // CHECK: call void @getAggDtor(%struct.AggDtor* sret align 8 %[[TMP2]]) // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2]] to i8** // CHECK: call void @__destructor_8_s40(i8** %[[T]]) // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8* // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) - // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8** - // CHECK: call void @__destructor_8_s40(i8** %[[T]]) - // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8* - // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]]) getAggDtor(); getAggDtor(); } +// Check that no extra release calls are emitted to detruct the compond literal. + +// CHECK: define void @test72(i8* %[[A:.*]], i8* %[[B:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca i8*, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca i8*, align 8 +// CHECK: %[[T:.*]] = alloca [2 x i8*], align 16 +// CHECK: %[[V0:.*]] = call i8* @llvm.objc.retain(i8* %[[A]]) +// CHECK: %[[V1:.*]] = call i8* @llvm.objc.retain(i8* %[[B]]) #2 +// CHECK: %[[ARRAYINIT_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[T]], i64 0, i64 0 +// CHECK: %[[V3:.*]] = load i8*, i8** %[[A_ADDR]], align 8, !tbaa !7 +// CHECK: %[[V4:.*]] = call i8* @llvm.objc.retain(i8* %[[V3]]) #2 +// CHECK: store i8* %[[V4]], i8** %[[ARRAYINIT_BEGIN]], align 8, !tbaa !7 +// CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN]], i64 1 +// CHECK: %[[V5:.*]] = load i8*, i8** %[[B_ADDR]], align 8, !tbaa !7 +// CHECK: %[[V6:.*]] = call i8* @llvm.objc.retain(i8* %[[V5]]) #2 +// CHECK: store i8* %[[V6]], i8** %[[ARRAYINIT_ELEMENT]], align 8, !tbaa !7 +// CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[T]], i32 0, i32 0 +// CHECK: %[[V7:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN]], i64 2 + +// CHECK-NOT: call void @llvm.objc.release + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi i8** [ %[[V7]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +// CHECK: %[[V8:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT]], align 8 +// CHECK: call void @llvm.objc.release(i8* %[[V8]]) #2, !clang.imprecise_release !10 + +// CHECK-NOT: call void @llvm.objc.release + +// CHECK: %[[V10:.*]] = load i8*, i8** %[[B_ADDR]], align 8 +// CHECK: call void @llvm.objc.release(i8* %[[V10]]) #2, !clang.imprecise_release !10 +// CHECK: %[[V11:.*]] = load i8*, i8** %[[A_ADDR]], align 8 +// CHECK: call void @llvm.objc.release(i8* %[[V11]]) #2, !clang.imprecise_release !10 + +void test72(id a, id b) { + __strong id t[] = (__strong id[]){a, b}; +} + // ARC-ALIEN: attributes [[NLB]] = { nonlazybind } // ARC-NATIVE: attributes [[NLB]] = { nonlazybind } // CHECK: attributes [[NUW]] = { nounwind } diff --git a/clang/test/CodeGenObjC/availability-check.m b/clang/test/CodeGenObjC/availability-check.m index 71c5ff77c96a4..173fa56e71a80 100644 --- a/clang/test/CodeGenObjC/availability-check.m +++ b/clang/test/CodeGenObjC/availability-check.m @@ -26,6 +26,15 @@ void use_at_available() { // CHECK: br i1 true if (__builtin_available(macos 10.11, *)) ; + + // CHECK: call i32 @__isOSVersionAtLeast(i32 10, i32 16, i32 0) + // CHECK-NEXT: icmp ne + if (__builtin_available(macos 10.16, *)) + ; + // CHECK: call i32 @__isOSVersionAtLeast(i32 11, i32 0, i32 0) + // CHECK-NEXT: icmp ne + if (__builtin_available(macos 11.0, *)) + ; } // CHECK: declare i32 @__isOSVersionAtLeast(i32, i32, i32) diff --git a/clang/test/CodeGenObjC/class-stubs.m b/clang/test/CodeGenObjC/class-stubs.m index fadb4433b8054..d5ed15c5a36bc 100644 --- a/clang/test/CodeGenObjC/class-stubs.m +++ b/clang/test/CodeGenObjC/class-stubs.m @@ -11,18 +11,18 @@ // // Metaclasses do not use the "stub" mechanism and are referenced statically. // -// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_" = internal global %struct._class_t* @"OBJC_METACLASS_$_Derived", section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_" = private global %struct._class_t* @"OBJC_METACLASS_$_Derived", section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 // -- classref for the super message send in anotherInstanceMethod() // // The class is declared with objc_class_stub, so LSB of the class pointer // must be set to 1. // -// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = internal global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 // -- category list for class stubs goes in __objc_catlist2. // -// CHECK-LABEL: @"OBJC_LABEL_STUB_CATEGORY_$" = internal global [1 x i8*] [i8* bitcast (%struct._category_t* @"_OBJC_$_CATEGORY_Derived_$_MyCategory" to i8*)], section "__DATA,__objc_catlist2,regular,no_dead_strip", align 8 +// CHECK-LABEL: @"OBJC_LABEL_STUB_CATEGORY_$" = private global [1 x i8*] [i8* bitcast (%struct._category_t* @"_OBJC_$_CATEGORY_Derived_$_MyCategory" to i8*)], section "__DATA,__objc_catlist2,regular,no_dead_strip", align 8 __attribute__((objc_class_stub)) __attribute__((objc_subclassing_restricted)) diff --git a/clang/test/CodeGenObjC/debug-info-blocks.m b/clang/test/CodeGenObjC/debug-info-blocks.m index 9204f8d56c071..257045b05c32b 100644 --- a/clang/test/CodeGenObjC/debug-info-blocks.m +++ b/clang/test/CodeGenObjC/debug-info-blocks.m @@ -17,13 +17,17 @@ // CHECK-NOT: ret // CHECK: call {{.*}}, !dbg ![[DBG_LINE:[0-9]+]] // CHECK-NOT: ret -// CHECK: load {{.*}}, !dbg ![[COPY_LINE:[0-9]+]] -// CHECK: ret void, !dbg ![[COPY_LINE]] -// CHECK: define {{.*}} @__destroy_helper_block_{{.*}}(i8* %0) +// CHECK: load {{.*}}, !dbg ![[DBG_LINE]] +// CHECK: ret {{.*}}, !dbg ![[DBG_LINE]] +// CHECK: define {{.*}} @__destroy_helper_block_{{.*}}(i8* // CHECK-NOT: ret // CHECK: load {{.*}}, !dbg ![[DESTROY_LINE:[0-9]+]] -// CHECK: ret void, !dbg ![[DESTROY_LINE]] +// CHECK: ret {{.*}}, !dbg ![[DESTROY_LINE]] +// CHECK-DAG: [[DBG_LINE]] = !DILocation(line: 0, scope: ![[COPY_SP:[0-9]+]]) +// CHECK-DAG: [[COPY_SP]] = distinct !DISubprogram(name: "__copy_helper_block_ +// CHECK-DAG: [[DESTROY_LINE]] = !DILocation(line: 0, scope: ![[DESTROY_SP:[0-9]+]]) +// CHECK-DAG: [[DESTROY_SP]] = distinct !DISubprogram(name: "__destroy_helper_block_ typedef unsigned int NSUInteger; @protocol NSObject @@ -57,11 +61,6 @@ @implementation A - (id)init { if ((self = [super init])) { - // CHECK-DAG: [[DBG_LINE]] = !DILocation(line: 0, scope: ![[COPY_SP:[0-9]+]]) - // CHECK-DAG: [[COPY_LINE]] = !DILocation(line: [[@LINE+7]], scope: ![[COPY_SP:[0-9]+]]) - // CHECK-DAG: [[COPY_SP]] = distinct !DISubprogram(name: "__copy_helper_block_8_32o" - // CHECK-DAG: [[DESTROY_LINE]] = !DILocation(line: [[@LINE+5]], scope: ![[DESTROY_SP:[0-9]+]]) - // CHECK-DAG: [[DESTROY_SP]] = distinct !DISubprogram(name: "__destroy_helper_block_8_32o" // CHECK-DAG: !DILocalVariable(arg: 1, scope: ![[COPY_SP]], {{.*}}, flags: DIFlagArtificial) // CHECK-DAG: !DILocalVariable(arg: 2, scope: ![[COPY_SP]], {{.*}}, flags: DIFlagArtificial) // CHECK-DAG: !DILocalVariable(arg: 1, scope: ![[DESTROY_SP]], {{.*}}, flags: DIFlagArtificial) diff --git a/clang/test/CodeGenObjC/direct-method-ret-mismatch.m b/clang/test/CodeGenObjC/direct-method-ret-mismatch.m new file mode 100644 index 0000000000000..65b253de1476a --- /dev/null +++ b/clang/test/CodeGenObjC/direct-method-ret-mismatch.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %s -o - | FileCheck %s + +__attribute__((objc_root_class)) +@interface Root +- (Root *)method __attribute__((objc_direct)); +@end + +@implementation Root +// CHECK-LABEL: define internal i8* @"\01-[Root something]"( +- (id)something { + // CHECK: %{{[^ ]*}} = call {{.*}} @"\01-[Root method]" + return [self method]; +} + +// CHECK-LABEL: define hidden i8* @"\01-[Root method]"( +- (id)method { + return self; +} +@end diff --git a/clang/test/CodeGenObjC/direct-method.m b/clang/test/CodeGenObjC/direct-method.m index e53c99bc0f5e8..5bb84de1ddb58 100644 --- a/clang/test/CodeGenObjC/direct-method.m +++ b/clang/test/CodeGenObjC/direct-method.m @@ -120,7 +120,7 @@ + (struct my_complex_struct)classGetComplex __attribute__((objc_direct)) { // CHECK-LABEL: define hidden void @"\01-[Root getAggregate]"( - (struct my_aggregate_struct)getAggregate __attribute__((objc_direct)) { - // CHECK: %struct.my_aggregate_struct* noalias sret [[RETVAL:%[^,]*]], + // CHECK: %struct.my_aggregate_struct* noalias sret align 4 [[RETVAL:%[^,]*]], // loading parameters // CHECK-LABEL: entry: diff --git a/clang/test/CodeGenObjC/direct-properties.m b/clang/test/CodeGenObjC/direct-properties.m new file mode 100644 index 0000000000000..113ac12f18bfe --- /dev/null +++ b/clang/test/CodeGenObjC/direct-properties.m @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %s -o - | FileCheck %s + +__attribute__((objc_root_class)) +@interface A +@property(direct, readonly) int i; +@end + +__attribute__((objc_root_class)) +@interface B +@property(direct, readonly) int i; +@property(readonly) int j; +@end + +// CHECK-NOT: @"__OBJC_$_PROP_LIST_A" +@implementation A +@synthesize i = _i; +@end + +// CHECK: @"_OBJC_$_PROP_LIST_B" = internal global { i32, i32, [1 x %struct._prop_t] } { i32 16, i32 1 +@implementation B +@synthesize i = _i; +@synthesize j = _j; +@end diff --git a/clang/test/CodeGenObjC/exceptions-asm-attribute.m b/clang/test/CodeGenObjC/exceptions-asm-attribute.m index b684be013c40f..93e89b8e0a8ac 100644 --- a/clang/test/CodeGenObjC/exceptions-asm-attribute.m +++ b/clang/test/CodeGenObjC/exceptions-asm-attribute.m @@ -15,7 +15,7 @@ // CHECK-X86_64: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak global {{.*}}, align 8 // CHECK-X86_64: @"OBJC_EHTYPE_$_MySecretNamespace.EH2" = external global // CHECK-X86_64: @"OBJC_EHTYPE_$_MySecretNamespace.EH3" = global {{.*}}, section "__DATA,__objc_const", align 8 -// CHECK-X86_64: @"OBJC_LABEL_CLASS_$" = internal global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 +// CHECK-X86_64: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 // CHECK-X86_64: define internal void @"\01-[A im0]" // CHECK-X86_64: define internal void @"\01-[A(Cat) im1]" @@ -39,7 +39,7 @@ // CHECK-ARMV6: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak global {{.*}}, align 4 // CHECK-ARMV6: @"OBJC_EHTYPE_$_MySecretNamespace.EH2" = external global // CHECK-ARMV6: @"OBJC_EHTYPE_$_MySecretNamespace.EH3" = global {{.*}}, section "__DATA,__objc_const", align 4 -// CHECK-ARMV6: @"OBJC_LABEL_CLASS_$" = internal global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 4 +// CHECK-ARMV6: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 4 // CHECK-ARMV6: define internal void @"\01-[A im0]" // CHECK-ARMV6: define internal void @"\01-[A(Cat) im1]" 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/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/metadata-symbols-64.m b/clang/test/CodeGenObjC/metadata-symbols-64.m index 2edec10248e33..bf02053013ab6 100644 --- a/clang/test/CodeGenObjC/metadata-symbols-64.m +++ b/clang/test/CodeGenObjC/metadata-symbols-64.m @@ -23,14 +23,14 @@ // CHECK: @"_OBJC_$_CATEGORY_INSTANCE_METHODS_A_$_Cat" = internal global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"_OBJC_$_CATEGORY_CLASS_METHODS_A_$_Cat" = internal global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"_OBJC_$_CATEGORY_A_$_Cat" = internal global {{.*}} section "__DATA, __objc_const", align 8 -// CHECK: @"OBJC_CLASSLIST_SUP_REFS_$_{{[0-9]*}}" = internal global {{.*}} section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 +// CHECK: @"OBJC_CLASSLIST_SUP_REFS_$_{{[0-9]*}}" = private global {{.*}} section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 // CHECK: @OBJC_SELECTOR_REFERENCES_ = internal externally_initialized global {{.*}} section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip" -// CHECK: @"OBJC_CLASSLIST_SUP_REFS_$_{{[\.0-9]*}}" = internal global {{.*}} section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 +// CHECK: @"OBJC_CLASSLIST_SUP_REFS_$_{{[\.0-9]*}}" = private global {{.*}} section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 // CHECK: @"OBJC_CLASS_$_B" = external global // CHECK: @"OBJC_CLASSLIST_REFERENCES_$_{{[0-9]*}}" = internal global {{.*}} section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8 // CHECK: @_objc_msgSend_fixup_alloc = weak hidden global {{.*}} section "__DATA,__objc_msgrefs,coalesced", align 16 -// CHECK: @"OBJC_LABEL_CLASS_$" = internal global {{.*}} section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 -// CHECK: @"OBJC_LABEL_CATEGORY_$" = internal global {{.*}} section "__DATA,__objc_catlist,regular,no_dead_strip", align 8 +// CHECK: @"OBJC_LABEL_CLASS_$" = private global {{.*}} section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 +// CHECK: @"OBJC_LABEL_CATEGORY_$" = private global {{.*}} section "__DATA,__objc_catlist,regular,no_dead_strip", align 8 // CHECK: @objc_msgSend_fpret( // CHECK: @objc_msgSend_fixup( diff --git a/clang/test/CodeGenObjC/metadata_symbols.m b/clang/test/CodeGenObjC/metadata_symbols.m index 0f8b54be8545d..9aee55bb8e1a6 100644 --- a/clang/test/CodeGenObjC/metadata_symbols.m +++ b/clang/test/CodeGenObjC/metadata_symbols.m @@ -14,7 +14,7 @@ // CHECK-X86_64: @"OBJC_EHTYPE_$_EH1" = weak global {{.*}}, align 8 // CHECK-X86_64: @"OBJC_EHTYPE_$_EH2" = external global // CHECK-X86_64: @"OBJC_EHTYPE_$_EH3" = global {{.*}}, section "__DATA,__objc_const", align 8 -// CHECK-X86_64: @"OBJC_LABEL_CLASS_$" = internal global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 +// CHECK-X86_64: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 // CHECK-X86_64: define internal void @"\01-[A im0]" // CHECK-X86_64: define internal void @"\01-[A(Cat) im1]" @@ -38,7 +38,7 @@ // CHECK-ARMV6: @"OBJC_EHTYPE_$_EH1" = weak global {{.*}}, align 4 // CHECK-ARMV6: @"OBJC_EHTYPE_$_EH2" = external global // CHECK-ARMV6: @"OBJC_EHTYPE_$_EH3" = global {{.*}}, section "__DATA,__objc_const", align 4 -// CHECK-ARMV6: @"OBJC_LABEL_CLASS_$" = internal global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 4 +// CHECK-ARMV6: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip", align 4 // CHECK-ARMV6: define internal void @"\01-[A im0]" // CHECK-ARMV6: define internal void @"\01-[A(Cat) im1]" diff --git a/clang/test/CodeGenObjC/non-lazy-classes.m b/clang/test/CodeGenObjC/non-lazy-classes.m index 03f7a2e049d86..fc861f0017c0c 100644 --- a/clang/test/CodeGenObjC/non-lazy-classes.m +++ b/clang/test/CodeGenObjC/non-lazy-classes.m @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | FileCheck %s -// CHECK: @"OBJC_LABEL_NONLAZY_CLASS_$" = internal global [3 x {{.*}}]{{.*}}@"OBJC_CLASS_$_A"{{.*}},{{.*}}@"OBJC_CLASS_$_D"{{.*}},{{.*}}"OBJC_CLASS_$_E"{{.*}} section "__DATA,__objc_nlclslist,regular,no_dead_strip", align 8 -// CHECK: @"OBJC_LABEL_NONLAZY_CATEGORY_$" = internal global [2 x {{.*}}] {{.*}}@"_OBJC_$_CATEGORY_A_$_Cat"{{.*}},{{.*}}@"_OBJC_$_CATEGORY_E_$_MyCat"{{.*}}, section "__DATA,__objc_nlcatlist,regular,no_dead_strip", align 8 +// CHECK: @"OBJC_LABEL_NONLAZY_CLASS_$" = private global [3 x {{.*}}]{{.*}}@"OBJC_CLASS_$_A"{{.*}},{{.*}}@"OBJC_CLASS_$_D"{{.*}},{{.*}}"OBJC_CLASS_$_E"{{.*}} section "__DATA,__objc_nlclslist,regular,no_dead_strip", align 8 +// CHECK: @"OBJC_LABEL_NONLAZY_CATEGORY_$" = private global [2 x {{.*}}] {{.*}}@"_OBJC_$_CATEGORY_A_$_Cat"{{.*}},{{.*}}@"_OBJC_$_CATEGORY_E_$_MyCat"{{.*}}, section "__DATA,__objc_nlcatlist,regular,no_dead_strip", align 8 @interface A @end @implementation A diff --git a/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m b/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m index 1733a019026c0..8d66485959a8c 100644 --- a/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m +++ b/clang/test/CodeGenObjC/nontrivial-c-struct-exception.m @@ -41,8 +41,8 @@ void testStrongException(void) { // CHECK: define void @testWeakException() // CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_WEAK]], align 8 // CHECK: %[[AGG_TMP1:.*]] = alloca %[[STRUCT_WEAK]], align 8 -// CHECK: call void @genWeak(%[[STRUCT_WEAK]]* sret %[[AGG_TMP]]) -// CHECK: invoke void @genWeak(%[[STRUCT_WEAK]]* sret %[[AGG_TMP1]]) +// CHECK: call void @genWeak(%[[STRUCT_WEAK]]* sret align 8 %[[AGG_TMP]]) +// CHECK: invoke void @genWeak(%[[STRUCT_WEAK]]* sret align 8 %[[AGG_TMP1]]) // CHECK: call void @calleeWeak(%[[STRUCT_WEAK]]* %[[AGG_TMP]], %[[STRUCT_WEAK]]* %[[AGG_TMP1]]) // CHECK: ret void diff --git a/clang/test/CodeGenObjC/objc-alloc-init.m b/clang/test/CodeGenObjC/objc-alloc-init.m index c5c1a763b7c76..8370202748e6f 100644 --- a/clang/test/CodeGenObjC/objc-alloc-init.m +++ b/clang/test/CodeGenObjC/objc-alloc-init.m @@ -22,21 +22,30 @@ void f() { } @interface Y : X ++(Class)class; +(void)meth; -(void)instanceMeth; @end @implementation Y ++(Class)class { + return self; +} +(void)meth { [[self alloc] init]; // OPTIMIZED: call i8* @objc_alloc_init( // NOT_OPTIMIZED: call i8* @objc_alloc( } ++ (void)meth2 { + [[[self class] alloc] init]; + // OPTIMIZED: call i8* @objc_alloc_init( + // NOT_OPTIMIZED: call i8* @objc_alloc( +} -(void)instanceMeth { // EITHER-NOT: call i8* @objc_alloc // EITHER: call {{.*}} @objc_msgSend // EITHER: call {{.*}} @objc_msgSend - [[self alloc] init]; + [[(id)self alloc] init]; } @end diff --git a/clang/test/CodeGenObjC/objc-non-trivial-struct-nrvo.m b/clang/test/CodeGenObjC/objc-non-trivial-struct-nrvo.m index 53ff433989e29..93f348185412a 100644 --- a/clang/test/CodeGenObjC/objc-non-trivial-struct-nrvo.m +++ b/clang/test/CodeGenObjC/objc-non-trivial-struct-nrvo.m @@ -37,7 +37,7 @@ Trivial testTrivial(void) { void func1(TrivialBig *); -// CHECK: define void @testTrivialBig(%[[STRUCT_TRIVIALBIG]]* noalias sret %[[AGG_RESULT:.*]]) +// CHECK: define void @testTrivialBig(%[[STRUCT_TRIVIALBIG]]* noalias sret align 4 %[[AGG_RESULT:.*]]) // CHECK: call void @func1(%[[STRUCT_TRIVIALBIG]]* %[[AGG_RESULT]]) // CHECK-NEXT: ret void @@ -69,7 +69,7 @@ Strong testStrong(void) { return a; } -// CHECK: define void @testWeak(%[[STRUCT_WEAK]]* noalias sret %[[AGG_RESULT:.*]]) +// CHECK: define void @testWeak(%[[STRUCT_WEAK]]* noalias sret align 8 %[[AGG_RESULT:.*]]) // CHECK: %[[NRVO:.*]] = alloca i1, align 1 // CHECK: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** // CHECK: call void @__default_constructor_8_w0(i8** %[[V0]]) @@ -105,7 +105,7 @@ Weak testWeak2(int c) { return b; } -// CHECK: define internal void @"\01-[C1 foo1]"(%[[STRUCT_WEAK]]* noalias sret %[[AGG_RESULT:.*]], %{{.*}}* %{{.*}}, i8* %{{.*}}) +// CHECK: define internal void @"\01-[C1 foo1]"(%[[STRUCT_WEAK]]* noalias sret align 8 %[[AGG_RESULT:.*]], %{{.*}}* %{{.*}}, i8* %{{.*}}) // CHECK: %[[NRVO:.*]] = alloca i1, align 1 // CHECK: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** // CHECK: call void @__default_constructor_8_w0(i8** %[[V0]]) diff --git a/clang/test/CodeGenObjC/os_log.m b/clang/test/CodeGenObjC/os_log.m index d41a4ce346db6..4e70ef3e75743 100644 --- a/clang/test/CodeGenObjC/os_log.m +++ b/clang/test/CodeGenObjC/os_log.m @@ -1,6 +1,7 @@ // RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -fobjc-arc -O2 -fno-experimental-new-pass-manager | FileCheck %s --check-prefixes=CHECK,CHECK-LEGACY // RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -fobjc-arc -O2 -fexperimental-new-pass-manager | FileCheck %s --check-prefixes=CHECK,CHECK-NEWPM // RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -fobjc-arc -O0 | FileCheck %s -check-prefix=CHECK-O0 +// RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -O2 -disable-llvm-passes | FileCheck %s -check-prefix=CHECK-MRR // Make sure we emit clang.arc.use before calling objc_release as part of the // cleanup. This way we make sure the object will not be released until the @@ -24,7 +25,8 @@ // CHECK: %[[V0:.*]] = bitcast %[[TY0]]* %[[CALL]] to i8* // CHECK: %[[V1:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V0]]) // CHECK-LEGACY: %[[V2:.*]] = ptrtoint %[[TY0]]* %[[CALL]] to i64 - // CHECK-NEWPM: %[[V2:.*]] = ptrtoint i8* %[[V1]] to i64 + // CHECK-NEWPM: %[[RETAINED:.*]] = tail call i8* @llvm.objc.retain(i8* %[[V1]]) + // CHECK-NEWPM: %[[V2:.*]] = ptrtoint i8* %[[RETAINED]] to i64 // CHECK: store i8 2, i8* %[[BUF]], align 1 // CHECK: %[[NUMARGS_I:.*]] = getelementptr i8, i8* %[[BUF]], i64 1 // CHECK: store i8 1, i8* %[[NUMARGS_I]], align 1 @@ -37,8 +39,8 @@ // CHECK: store i64 %[[V2]], i64* %[[ARGDATACAST_I]], align 1 // CHECK-LEGACY: tail call void (...) @llvm.objc.clang.arc.use(%[[TY0]]* %[[CALL]]) // CHECK-LEGACY: tail call void @llvm.objc.release(i8* %[[V0]]) - // CHECK-NEWPM: tail call void (...) @llvm.objc.clang.arc.use(i8* %[[V1]]) - // CHECK-NEWPM: tail call void @llvm.objc.release(i8* %[[V1]]) + // CHECK-NEWPM: tail call void (...) @llvm.objc.clang.arc.use(i8* %[[RETAINED]]) + // CHECK-NEWPM: tail call void @llvm.objc.release(i8* %[[RETAINED]]) // CHECK: ret i8* %[[BUF]] // clang.arc.use is used and removed in IR optimizations. At O0, we should not @@ -51,8 +53,11 @@ // CHECK-O0: %[[V1:.*]] = bitcast %[[TY0]]* %[[CALL]] to i8* // CHECK-O0: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V1]]) // CHECK-O0: %[[V3:.*]] = bitcast i8* %[[V2]] to %[[TY0]]* - // CHECK-O0: %[[V4:.*]] = ptrtoint %[[TY0]]* %[[V3]] to i64 - // CHECK-O0: call void @__os_log_helper_1_2_1_8_64(i8* %[[V0]], i64 %[[V4]]) + // CHECK-O0: %[[V4:.*]] = bitcast %0* %[[V3]] to i8* + // CHECK-O0: %[[V5:.*]] = call i8* @llvm.objc.retain(i8* %[[V4]]) + // CHECK-O0: %[[V6:.*]] = bitcast i8* %[[V5]] to %0* + // CHECK-O0: %[[V7:.*]] = ptrtoint %0* %[[V6]] to i64 + // CHECK-O0: call void @__os_log_helper_1_2_1_8_64(i8* %[[V0]], i64 %[[V7]]) // CHECK-O0: %[[V5:.*]] = bitcast %[[TY0]]* %[[V3]] to i8* // CHECK-O0-NOT: call void (...) @llvm.objc.clang.arc.use({{.*}} // CHECK-O0: call void @llvm.objc.release(i8* %[[V5]]) @@ -80,4 +85,65 @@ // CHECK-O0: %[[V0:.*]] = load i64, i64* %[[ARG0_ADDR]], align 8 // CHECK-O0: store i64 %[[V0]], i64* %[[ARGDATACAST]], align 1 +void os_log_pack_send(void *); + +// CHECK-NEWPM: define void @test_builtin_os_log2(i8* %[[BUF:.*]], i8* %[[A:.*]]) +// CHECK-NEWPM: %[[V0:.*]] = tail call i8* @llvm.objc.retain(i8* %[[A]]) +// CHECK-NEWPM: %[[CALL:.*]] = tail call %{{.*}}* (...) @GenString() +// CHECK-NEWPM: %[[V1:.*]] = bitcast %{{.*}}* %[[CALL]] to i8* +// CHECK-NEWPM: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V1]]) +// CHECK-NEWPM: %[[V3:.*]] = tail call i8* @llvm.objc.retain(i8* %[[V2]]) +// CHECK-NEWPM: %[[V4:.*]] = ptrtoint i8* %[[V3]] to i64 +// CHECK-NEWPM: %[[V5:.*]] = tail call i8* @llvm.objc.retain(i8* %[[V0]]) +// CHECK-NEWPM: %[[V6:.*]] = ptrtoint i8* %[[V5]] to i64 +// CHECK-NEWPM: %[[ARGDATA_I:.*]] = getelementptr i8, i8* %[[BUF]], i64 4 +// CHECK-NEWPM: %[[ARGDATACAST_I:.*]] = bitcast i8* %[[ARGDATA_I]] to i64* +// CHECK-NEWPM: store i64 %[[V4]], i64* %[[ARGDATACAST_I]], align 1 +// CHECK-NEWPM: %[[ARGDATA3_I:.*]] = getelementptr i8, i8* %[[BUF]], i64 14 +// CHECK-NEWPM: %[[ARGDATACAST4_I:.*]] = bitcast i8* %[[ARGDATA3_I]] to i64* +// CHECK-NEWPM: store i64 %[[V6]], i64* %[[ARGDATACAST4_I]], align 1 +// CHECK-NEWPM: tail call void @llvm.objc.release(i8* %[[V2]]) +// CHECK-NEWPM: tail call void @os_log_pack_send(i8* nonnull %[[BUF]]) +// CHECK-NEWPM: tail call void (...) @llvm.objc.clang.arc.use(i8* %[[V5]]) +// CHECK-NEWPM: tail call void @llvm.objc.release(i8* %[[V5]]) +// CHECK-NEWPM: tail call void (...) @llvm.objc.clang.arc.use(i8* %[[V3]]) +// CHECK-NEWPM: tail call void @llvm.objc.release(i8* %[[V3]]) +// CHECK-NEWPM: tail call void @llvm.objc.release(i8* %[[V0]]) + +// CHECK-O0: define void @test_builtin_os_log2(i8* %{{.*}}, i8* %[[A:.*]]) +// CHECK-O0: alloca i8*, align 8 +// CHECK-O0: %[[A_ADDR:.*]] = alloca i8*, align 8 +// CHECK-O0: %[[OS_LOG_ARG:.*]] = alloca %[[V0]]*, align 8 +// CHECK-O0: %[[OS_LOG_ARG1:.*]] = alloca i8*, align 8 +// CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[A_ADDR]], i8* %[[A]]) +// CHECK-O0: %[[CALL:.*]] = call %{{.*}}* (...) @GenString() +// CHECK-O0: %[[V1:.*]] = bitcast %[[V0]]* %[[CALL]] to i8* +// CHECK-O0: %[[V2:.*]] = notail call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[V1]]) #2 +// CHECK-O0: %[[V3:.*]] = bitcast i8* %[[V2]] to %[[V0]]* +// CHECK-O0: %[[V4:.*]] = bitcast %{{.*}}* %[[V3]] to i8* +// CHECK-O0: %[[V5:.*]] = call i8* @llvm.objc.retain(i8* %[[V4]]) #2 +// CHECK-O0: %[[V6:.*]] = bitcast i8* %[[V5]] to %[[V0]]* +// CHECK-O0: store %{{.*}}* %[[V6]], %{{.*}}** %[[OS_LOG_ARG]], align 8 +// CHECK-O0: %[[V7:.*]] = ptrtoint %[[V0]]* %[[V6]] to i64 +// CHECK-O0: %[[V8:.*]] = load i8*, i8** %[[A_ADDR]], align 8 +// CHECK-O0: %[[V9:.*]] = call i8* @llvm.objc.retain(i8* %[[V8]]) #2 +// CHECK-O0: store i8* %[[V9]], i8** %[[OS_LOG_ARG1]], align 8 +// CHECK-O0: %[[V10:.*]] = ptrtoint i8* %[[V9]] to i64 +// CHECK-O0: call void @__os_log_helper_1_2_2_8_64_8_64(i8* %{{.*}}, i64 %[[V7]], i64 %[[V10]]) +// CHECK-O0: %[[V11:.*]] = bitcast %{{.*}}* %[[V3]] to i8* +// CHECK-O0: call void @llvm.objc.release(i8* %[[V11]]) +// CHECK-O0: call void @os_log_pack_send( +// CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[OS_LOG_ARG1]], i8* null) +// CHECK-O0: %[[V13:.*]] = bitcast %{{.*}}** %[[OS_LOG_ARG]] to i8** +// CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[V13]], i8* null) +// CHECK-O0: call void @llvm.objc.storeStrong(i8** %[[A_ADDR]], i8* null) + +// CHECK-MRR: define void @test_builtin_os_log2( +// CHECK-MRR-NOT: call {{.*}} @llvm.objc + +void test_builtin_os_log2(void *buf, id a) { + __builtin_os_log_format(buf, "capabilities: %@ %@", GenString(), a); + os_log_pack_send(buf); +} + #endif 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/sections.m b/clang/test/CodeGenObjC/sections.m index 6a1812efba5d8..73a0984a568b4 100644 --- a/clang/test/CodeGenObjC/sections.m +++ b/clang/test/CodeGenObjC/sections.m @@ -61,15 +61,15 @@ _Bool f(J *j) { // CHECK-ELF: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"objc_imageinfo"} // CHECK-MACHO: @"_OBJC_$_CLASS_METHODS_I" = internal {{.*}}, section "__DATA, __objc_const" -// CHECK-MACHO: @"OBJC_CLASSLIST_SUP_REFS_$_" = internal {{.*}}, section "__DATA,__objc_superrefs,regular,no_dead_strip" +// CHECK-MACHO: @"OBJC_CLASSLIST_SUP_REFS_$_" = private {{.*}}, section "__DATA,__objc_superrefs,regular,no_dead_strip" // CHECK-MACHO: @OBJC_SELECTOR_REFERENCES_ = internal {{.*}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip" // CHECK-MACHO: @"OBJC_CLASSLIST_REFERENCES_$_" = internal {{.*}}, section "__DATA,__objc_classrefs,regular,no_dead_strip" // CHECK-MACHO: @_objc_msgSend_fixup_class = {{.*}}, section "__DATA,__objc_msgrefs,coalesced" // CHECK-MACHO: @"_OBJC_LABEL_PROTOCOL_$_P" = {{.*}}, section "__DATA,__objc_protolist,coalesced,no_dead_strip" // CHECK-MACHO: @"_OBJC_PROTOCOL_REFERENCE_$_P" = {{.*}}, section "__DATA,__objc_protorefs,coalesced,no_dead_strip" -// CHECK-MACHO: @"OBJC_LABEL_CLASS_$" = internal {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip" -// CHECK-MACHO: @"OBJC_LABEL_NONLAZY_CLASS_$" = internal {{.*}}, section "__DATA,__objc_nlclslist,regular,no_dead_strip" -// CHECK-MACHO: @"OBJC_LABEL_CATEGORY_$" = internal {{.*}}, section "__DATA,__objc_catlist,regular,no_dead_strip" -// CHECK-MACHO: @"OBJC_LABEL_NONLAZY_CATEGORY_$" = internal {{.*}}, section "__DATA,__objc_nlcatlist,regular,no_dead_strip" +// CHECK-MACHO: @"OBJC_LABEL_CLASS_$" = private {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip" +// CHECK-MACHO: @"OBJC_LABEL_NONLAZY_CLASS_$" = private {{.*}}, section "__DATA,__objc_nlclslist,regular,no_dead_strip" +// CHECK-MACHO: @"OBJC_LABEL_CATEGORY_$" = private {{.*}}, section "__DATA,__objc_catlist,regular,no_dead_strip" +// CHECK-MACHO: @"OBJC_LABEL_NONLAZY_CATEGORY_$" = private {{.*}}, section "__DATA,__objc_nlcatlist,regular,no_dead_strip" // CHECK-MACHO: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"} diff --git a/clang/test/CodeGenObjC/stret-1.m b/clang/test/CodeGenObjC/stret-1.m index 1122c28a468bd..f25c40438e590 100644 --- a/clang/test/CodeGenObjC/stret-1.m +++ b/clang/test/CodeGenObjC/stret-1.m @@ -14,19 +14,19 @@ int main(int argc, const char **argv) { struct stret s; s = [(id)(argc&~255) method]; - // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T0:%[^,]+]] + // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret align 4 [[T0:%[^,]+]] // CHECK: [[T0P:%.*]] = bitcast %struct.stret* [[T0]] to i8* // CHECK: call void @llvm.memset.p0i8.i64(i8* align 4 [[T0P]], i8 0, i64 400, i1 false) s = [Test method]; - // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T1:%[^,]+]] + // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret align 4 [[T1:%[^,]+]] // CHECK-NOT: call void @llvm.memset.p0i8.i64( [(id)(argc&~255) method]; - // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T1:%[^,]+]] + // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret align 4 [[T1:%[^,]+]] // CHECK-NOT: call void @llvm.memset.p0i8.i64( [Test method]; - // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret [[T1:%[^,]+]] + // CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (%struct.stret*, i8*, i8*)*)(%struct.stret* sret align 4 [[T1:%[^,]+]] // CHECK-NOT: call void @llvm.memset.p0i8.i64( } diff --git a/clang/test/CodeGenObjC/strong-in-c-struct.m b/clang/test/CodeGenObjC/strong-in-c-struct.m index eae5013dd3fcb..f0227119279f4 100644 --- a/clang/test/CodeGenObjC/strong-in-c-struct.m +++ b/clang/test/CodeGenObjC/strong-in-c-struct.m @@ -89,6 +89,13 @@ void calleeStrongSmall(StrongSmall); void func(Strong *); +@interface C +- (StrongSmall)getStrongSmall; ++ (StrongSmall)getStrongSmallClass; +@end + +id g0; + // CHECK: %[[STRUCT_STRONGOUTER:.*]] = type { %[[STRUCT_STRONG:.*]], i8*, double } // CHECK: %[[STRUCT_STRONG]] = type { %[[STRUCT_TRIVIAL:.*]], i8* } // CHECK: %[[STRUCT_TRIVIAL]] = type { [4 x i32] } @@ -476,6 +483,18 @@ void test_destructor_ignored_result(void) { getStrongSmall(); } +// CHECK: define void @test_destructor_ignored_result2(%{{.*}}* %[[C:.*]]) +// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[CALL:.*]] = call [2 x i64]{{.*}}@objc_msgSend +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to [2 x i64]* +// CHECK: store [2 x i64] %[[CALL]], [2 x i64]* %[[V5]], align 8 +// CHECK: %[[V6:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V6]]) + +void test_destructor_ignored_result2(C *c) { + [c getStrongSmall]; +} + // CHECK: define void @test_copy_constructor_StrongBlock( // CHECK: call void @__copy_constructor_8_8_sb0( // CHECK: call void @__destructor_8_sb0( @@ -520,7 +539,9 @@ void test_copy_assignment_StrongBlock(StrongBlock *d, StrongBlock *s) { // CHECK: define void @test_copy_constructor_StrongVolatile0( // CHECK: call void @__copy_constructor_8_8_t0w4_sv8( +// CHECK-NOT: call // CHECK: call void @__destructor_8_sv8( +// CHECK-NOT: call // CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_sv8( // CHECK: %[[V8:.*]] = load volatile i8*, i8** %{{.*}}, align 8 @@ -709,4 +730,174 @@ void test_copy_constructor_VolatileArray(VolatileArray *a) { VolatileArray t = *a; } +// CHECK: define void @test_compound_literal0( +// CHECK: %[[P:.*]] = alloca %[[STRUCT_STRONGSMALL]]*, align 8 +// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1 +// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1 + +// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 0 +// CHECK: store i32 1, i32* %[[I]], align 8 +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 1 +// CHECK: store i8* null, i8** %[[F1]], align 8 +// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1 + +// CHECK: %[[I2:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0 +// CHECK: store i32 2, i32* %[[I2]], align 8 +// CHECK: %[[F13:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 1 +// CHECK: store i8* null, i8** %[[F13]], align 8 +// CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1 + +// CHECK: %[[COND:.*]] = phi %[[STRUCT_STRONGSMALL]]* [ %[[_COMPOUNDLITERAL]], %{{.*}} ], [ %[[_COMPOUNDLITERAL1]], %{{.*}} ] +// CHECK: store %[[STRUCT_STRONGSMALL]]* %[[COND]], %[[STRUCT_STRONGSMALL]]** %[[P]], align 8 +// CHECK: call void @func( + +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V1]]) + +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V2]]) + +void test_compound_literal0(int c) { + StrongSmall *p = c ? &(StrongSmall){ 1, 0 } : &(StrongSmall){ 2, 0 }; + func(0); +} + +// Check that there is only one destructor call, which destructs 't'. + +// CHECK: define void @test_compound_literal1( +// CHECK: %[[T:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 + +// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 0 +// CHECK: store i32 1, i32* %[[I]], align 8 +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 1 +// CHECK: store i8* null, i8** %[[F1]], align 8 + +// CHECK: %[[I1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 0 +// CHECK: store i32 2, i32* %[[I1]], align 8 +// CHECK: %[[F12:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 1 +// CHECK: store i8* null, i8** %[[F12]], align 8 + +// CHECK: call void @func( +// CHECK-NOT: call void +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[T]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V1]]) +// CHECK-NOT: call void + +void test_compound_literal1(int c) { + StrongSmall t = c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 }; + func(0); +} + +// CHECK: define void @test_compound_literal2( +// CHECK: %[[P_ADDR:.*]] = alloca %[[STRUCT_STRONGSMALL]]*, align 8 +// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1 +// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1 +// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGSMALL]]*, %[[STRUCT_STRONGSMALL]]** %[[P_ADDR]], align 8 + +// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 0 +// CHECK: store i32 1, i32* %[[I]], align 8 +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 1 +// CHECK: store i8* null, i8** %[[F1]], align 8 +// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1 +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[V0]] to i8** +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8** +// CHECK: call void @__copy_assignment_8_8_t0w4_s8(i8** %[[V2]], i8** %[[V3]]) + +// CHECK: %[[I2:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0 +// CHECK: store i32 2, i32* %[[I2]], align 8 +// CHECK: %[[F13:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 1 +// CHECK: store i8* null, i8** %[[F13]], align 8 +// CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1 +// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[V0]] to i8** +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8** +// CHECK: call void @__copy_assignment_8_8_t0w4_s8(i8** %[[V4]], i8** %[[V5]]) + +// CHECK: call void @func( + +// CHECK: %[[V6:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V6]]) + +// CHECK: %[[V7:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V7]]) + +void test_compound_literal2(int c, StrongSmall *p) { + *p = c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 }; + func(0); +} + +// CHECK: define void @test_member_access( +// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V3]]) +// CHECK: call void @func( + +void test_member_access(void) { + g0 = getStrongSmall().f1; + func(0); +} + +// CHECK: define void @test_member_access2(%{{.*}}* %[[C:.*]]) +// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[V8:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V8]]) +// CHECK: call void @func( + +void test_member_access2(C *c) { + g0 = [c getStrongSmall].f1; + func(0); +} + +// CHECK: define void @test_member_access3( +// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[V8:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V8]]) +// CHECK: call void @func( + +void test_member_access3(void) { + g0 = [C getStrongSmallClass].f1; + func(0); +} + +// CHECK: define void @test_member_access4() +// CHECK: %[[COERCE:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8 +// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[COERCE]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V5]]) +// CHECK: call void @func( + +void test_member_access4(void) { + g0 = ^{ StrongSmall s; return s; }().f1; + func(0); +} + +// CHECK: define void @test_volatile_variable_reference( +// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_STRONGSMALL]], +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8** +// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %{{.*}} to i8** +// CHECK: call void @__copy_constructor_8_8_tv0w32_sv8(i8** %[[V1]], i8** %[[V2]]) +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8** +// CHECK: call void @__destructor_8_s8(i8** %[[V3]]) +// CHECK: call void @func( + +void test_volatile_variable_reference(volatile StrongSmall *a) { + (void)*a; + func(0); +} + +struct ZeroBitfield { + int : 0; + id strong; +}; + + +// CHECK: define linkonce_odr hidden void @__default_constructor_8_sv0 +// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_sv0 +void test_zero_bitfield() { + struct ZeroBitfield volatile a, b; + a = b; +} + #endif /* USESTRUCT */ 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/CodeGenObjC/ubsan-nullability-return-unreachable.m b/clang/test/CodeGenObjC/ubsan-nullability-return-unreachable.m new file mode 100644 index 0000000000000..eabc33c91e78f --- /dev/null +++ b/clang/test/CodeGenObjC/ubsan-nullability-return-unreachable.m @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fsanitize=nullability-return -emit-llvm %s -o - -triple x86_64-apple-macosx10.10.0 -Wno-objc-root-class | FileCheck %s + +// CHECK-LABEL: define internal i8* @"\01-[I init]" +// CHECK: unreachable +// CHECK-NEXT: } + +#pragma clang assume_nonnull begin +@interface I +- (instancetype)init __attribute__((unavailable)); +@end +@implementation I +- (instancetype)init __attribute__((unavailable)) { __builtin_unreachable(); } +@end +#pragma clang assume_nonnull end diff --git a/clang/test/CodeGenObjC/weak-in-c-struct.m b/clang/test/CodeGenObjC/weak-in-c-struct.m index 001a7ed96dec8..90c799298253b 100644 --- a/clang/test/CodeGenObjC/weak-in-c-struct.m +++ b/clang/test/CodeGenObjC/weak-in-c-struct.m @@ -179,7 +179,7 @@ void test_argument_Weak(Weak *a) { calleeWeak(*a); } -// COMMON: define void @test_return_Weak(%[[STRUCT_WEAK]]* noalias sret %[[AGG_RESULT:.*]], %[[STRUCT_WEAK]]* %[[A:.*]]) +// COMMON: define void @test_return_Weak(%[[STRUCT_WEAK]]* noalias sret align {{.*}} %[[AGG_RESULT:.*]], %[[STRUCT_WEAK]]* %[[A:.*]]) // COMMON: %[[A_ADDR:.*]] = alloca %[[STRUCT_WEAK]]* // COMMON: store %[[STRUCT_WEAK]]* %[[A]], %[[STRUCT_WEAK]]** %[[A_ADDR]] // COMMON: %[[V0:.*]] = load %[[STRUCT_WEAK]]*, %[[STRUCT_WEAK]]** %[[A_ADDR]] diff --git a/clang/test/CodeGenObjCXX/block-nested-in-lambda.mm b/clang/test/CodeGenObjCXX/block-nested-in-lambda.mm index be1ad8117cd25..c7b9a043a9e95 100644 --- a/clang/test/CodeGenObjCXX/block-nested-in-lambda.mm +++ b/clang/test/CodeGenObjCXX/block-nested-in-lambda.mm @@ -1,6 +1,9 @@ -// RUN: %clang_cc1 -triple=x86_64-apple-darwin10 -emit-llvm -std=c++11 -fblocks -fobjc-arc -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-apple-darwin10 -emit-llvm -std=c++14 -fblocks -fobjc-arc -o - %s | FileCheck %s // CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } +// CHECK: %[[S:.*]] = type { i32 } +// CHECK: %[[CLASS_ANON_2:.*]] = type { %[[S]]* } +// CHECK: %[[CLASS_ANON_3:.*]] = type { %[[S]] } // CHECK: %[[BLOCK_CAPTURED0:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i32*, i32* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i32*, i32* }>* %[[BLOCK:.*]], i32 0, i32 5 // CHECK: %[[V0:.*]] = getelementptr inbounds %[[LAMBDA_CLASS:.*]], %[[LAMBDA_CLASS]]* %[[THIS:.*]], i32 0, i32 0 @@ -33,7 +36,7 @@ void block_in_lambda(int &s1, int &s2) { // reference. // CHECK-LABEL: define void @_ZN18CaptureByReference5test0Ev( -// CHECK-LABEL: define internal void @"_ZZN18CaptureByReference5test0EvENK3$_1clEv"( +// CHECK-LABEL: define internal void @"_ZZN18CaptureByReference5test0EvENK3$_3clEv"( // CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>* %{{.*}}, i32 0, i32 4 // CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i64 }* @"__block_descriptor_40_e5_v8\01?0ls32l8" to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 @@ -46,10 +49,60 @@ void test0() { // is captured by reference. // CHECK-LABEL: define void @_ZN18CaptureByReference5test1Ev( -// CHECK-LABEL: define internal void @"_ZZN18CaptureByReference5test1EvENK3$_2clEv"( +// CHECK-LABEL: define internal void @"_ZZN18CaptureByReference5test1EvENK3$_4clEv"( // CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8** }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8** }>* %{{.*}}, i32 0, i32 4 // CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i8*, i8*, i64 }* @"__block_descriptor_56_8_32s40s_e5_v8\01?0l" to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 +void test1() { + id a = getObj(), b = getObj(), c = getObj(); + [&a, b, c]{ ^{ a = 0; use(b); use(c); }(); }(); +} + +struct S { + int val() const; + int a; + S(); + S(const S&); + S &operator=(const S&); + S(S&&); + S &operator=(S&&); +}; + +S getS(); + +// CHECK: define internal i32 @"_ZZN18CaptureByReference5test2EvENK3$_1clIiEEDaT_"(%[[CLASS_ANON_2]]* %{{.*}}, i32 %{{.*}}) +// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %{{.*}}, %[[S]]* }>, align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %{{.*}}, %[[S]]* }>, <{ i8*, i32, i32, i8*, %{{.*}}, %[[S]]* }>* %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON_2]], %[[CLASS_ANON_2]]* %{{.*}}, i32 0, i32 0 +// CHECK: %[[V1:.*]] = load %[[S]]*, %[[S]]** %[[V0]], align 8 +// CHECK: store %[[S]]* %[[V1]], %[[S]]** %[[BLOCK_CAPTURED]], align 8 + +int test2() { + S s; + auto fn = [&](const auto a){ + return ^{ + return s.val(); + }(); + }; + return fn(123); +} + +// CHECK: define internal i32 @"_ZZN18CaptureByReference5test3EvENK3$_2clIiEEDaT_"(%[[CLASS_ANON_3]]* %{{.*}}, i32 %{{.*}}) +// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %{{.*}}*, %[[S]] }>, align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %{{.*}}*, %[[S]] }>, <{ i8*, i32, i32, i8*, %{{.*}}*, %[[S]] }>* %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON_3]], %[[CLASS_ANON_3]]* %{{.*}}, i32 0, i32 0 +// CHECK: call void @_ZN18CaptureByReference1SC1ERKS0_(%[[S]]* %[[BLOCK_CAPTURED]], %[[S]]* {{.*}} %[[V0]]) + +int test3() { + const S &s = getS(); + auto fn = [=](const auto a){ + return ^{ + return s.val(); + }(); + }; + return fn(123); +} + // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_32s40s( // CHECK-NOT: call void @llvm.objc.storeStrong( // CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8** }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8** }>* %{{.*}}, i32 0, i32 5 @@ -74,9 +127,4 @@ void test0() { // CHECK-NOT: call void @llvm.objc.storeStrong( // CHECK: ret void -void test1() { - id a = getObj(), b = getObj(), c = getObj(); - [&a, b, c]{ ^{ a = 0; use(b); use(c); }(); }(); -} - } 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/objc-struct-cxx-abi.mm b/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm index dd9b88b0234d0..8bb694705fef1 100644 --- a/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm +++ b/clang/test/CodeGenObjCXX/objc-struct-cxx-abi.mm @@ -90,7 +90,7 @@ void testCallStrongWeak(StrongWeak *a) { testParamStrongWeak(*a); } -// CHECK: define void @_Z20testReturnStrongWeakP10StrongWeak(%[[STRUCT_STRONGWEAK:.*]]* noalias sret %[[AGG_RESULT:.*]], %[[STRUCT_STRONGWEAK]]* %[[A:.*]]) +// CHECK: define void @_Z20testReturnStrongWeakP10StrongWeak(%[[STRUCT_STRONGWEAK:.*]]* noalias sret align 8 %[[AGG_RESULT:.*]], %[[STRUCT_STRONGWEAK]]* %[[A:.*]]) // CHECK: %[[A_ADDR:.*]] = alloca %[[STRUCT_STRONGWEAK]]*, align 8 // CHECK: store %[[STRUCT_STRONGWEAK]]* %[[A]], %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8 // CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGWEAK]]*, %[[STRUCT_STRONGWEAK]]** %[[A_ADDR]], align 8 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/CodeGenObjCXX/ubsan-nullability-return-notypeloc.mm b/clang/test/CodeGenObjCXX/ubsan-nullability-return-notypeloc.mm new file mode 100644 index 0000000000000..23025c9eb8452 --- /dev/null +++ b/clang/test/CodeGenObjCXX/ubsan-nullability-return-notypeloc.mm @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsanitize=nullability-return -emit-llvm %s -o - -triple x86_64-apple-macosx10.10.0 | FileCheck %s + +// CHECK: [[ATTR_LOC:@[0-9]+]] = {{.*}} global { {{.*}} i32 15, i32 38 + +// CHECK-LABEL: define i8* @_Z3foov() +// CHECK: [[CALL:%.*]] = call i8* @_Z6helperv() +// CHECK: icmp ne i8* [[CALL]] +// CHECK: call void @__ubsan_handle_nullability_return_v1_abort({{.*}}[[ATTR_LOC]] + +struct S { + using PtrTy = id; +}; + +#pragma clang assume_nonnull begin +__attribute__((ns_returns_retained)) S::PtrTy foo(void) { + extern S::PtrTy helper(void); + return helper(); +} +#pragma clang assume_nonnull end diff --git a/clang/test/CodeGenOpenCL/addr-space-struct-arg.cl b/clang/test/CodeGenOpenCL/addr-space-struct-arg.cl index cdbf28bbcad87..35cc54c50d6f2 100644 --- a/clang/test/CodeGenOpenCL/addr-space-struct-arg.cl +++ b/clang/test/CodeGenOpenCL/addr-space-struct-arg.cl @@ -43,7 +43,7 @@ struct LargeStructTwoMember { struct LargeStructOneMember g_s; #endif -// X86-LABEL: define void @foo(%struct.Mat4X4* noalias sret %agg.result, %struct.Mat3X3* byval(%struct.Mat3X3) align 4 %in) +// X86-LABEL: define void @foo(%struct.Mat4X4* noalias sret align 4 %agg.result, %struct.Mat3X3* byval(%struct.Mat3X3) align 4 %in) // AMDGCN-LABEL: define %struct.Mat4X4 @foo([9 x i32] %in.coerce) Mat4X4 __attribute__((noinline)) foo(Mat3X3 in) { Mat4X4 out; @@ -63,8 +63,8 @@ kernel void ker(global Mat3X3 *in, global Mat4X4 *out) { out[0] = foo(in[1]); } -// X86-LABEL: define void @foo_large(%struct.Mat64X64* noalias sret %agg.result, %struct.Mat32X32* byval(%struct.Mat32X32) align 4 %in) -// AMDGCN-LABEL: define void @foo_large(%struct.Mat64X64 addrspace(5)* noalias sret %agg.result, %struct.Mat32X32 addrspace(5)* byval(%struct.Mat32X32) align 4 %in) +// X86-LABEL: define void @foo_large(%struct.Mat64X64* noalias sret align 4 %agg.result, %struct.Mat32X32* byval(%struct.Mat32X32) align 4 %in) +// AMDGCN-LABEL: define void @foo_large(%struct.Mat64X64 addrspace(5)* noalias sret align 4 %agg.result, %struct.Mat32X32 addrspace(5)* byval(%struct.Mat32X32) align 4 %in) Mat64X64 __attribute__((noinline)) foo_large(Mat32X32 in) { Mat64X64 out; return out; diff --git a/clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl b/clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl index 0a7f289cb2f7c..fd46d3cce22e4 100644 --- a/clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl +++ b/clang/test/CodeGenOpenCL/amdgpu-abi-struct-coerce.cl @@ -404,14 +404,14 @@ struct_arr16 func_ret_struct_arr16() return s; } -// CHECK: define void @func_ret_struct_arr32(%struct.struct_arr32 addrspace(5)* noalias nocapture sret %agg.result) +// CHECK: define void @func_ret_struct_arr32(%struct.struct_arr32 addrspace(5)* noalias nocapture sret align 4 %agg.result) struct_arr32 func_ret_struct_arr32() { struct_arr32 s = { 0 }; return s; } -// CHECK: define void @func_ret_struct_arr33(%struct.struct_arr33 addrspace(5)* noalias nocapture sret %agg.result) +// CHECK: define void @func_ret_struct_arr33(%struct.struct_arr33 addrspace(5)* noalias nocapture sret align 4 %agg.result) struct_arr33 func_ret_struct_arr33() { struct_arr33 s = { 0 }; @@ -440,7 +440,7 @@ different_size_type_pair func_different_size_type_pair_ret() return s; } -// CHECK: define void @func_flexible_array_ret(%struct.flexible_array addrspace(5)* noalias nocapture sret %agg.result) +// CHECK: define void @func_flexible_array_ret(%struct.flexible_array addrspace(5)* noalias nocapture sret align 4 %agg.result) flexible_array func_flexible_array_ret() { flexible_array s = { 0 }; diff --git a/clang/test/CodeGenOpenCLCXX/addrspace-of-this.cl b/clang/test/CodeGenOpenCLCXX/addrspace-of-this.cl index 07e3b0b7314ea..16495d38b9421 100644 --- a/clang/test/CodeGenOpenCLCXX/addrspace-of-this.cl +++ b/clang/test/CodeGenOpenCLCXX/addrspace-of-this.cl @@ -114,7 +114,7 @@ __kernel void test__global() { // Test the address space of 'this' when invoking the operator+ // COMMON: [[C1GEN:%[.a-z0-9]+]] = addrspacecast %class.C* %c1 to %class.C addrspace(4)* // COMMON: [[C2GEN:%[.a-z0-9]+]] = addrspacecast %class.C* %c2 to %class.C addrspace(4)* -// COMMON: call spir_func void @_ZNU3AS41CplERU3AS4KS_(%class.C* sret %c3, %class.C addrspace(4)* [[C1GEN]], %class.C addrspace(4)* dereferenceable(4) [[C2GEN]]) +// COMMON: call spir_func void @_ZNU3AS41CplERU3AS4KS_(%class.C* sret align 4 %c3, %class.C addrspace(4)* [[C1GEN]], %class.C addrspace(4)* dereferenceable(4) [[C2GEN]]) // Test the address space of 'this' when invoking the move constructor // COMMON: [[C4GEN:%[.a-z0-9]+]] = addrspacecast %class.C* %c4 to %class.C addrspace(4)* @@ -134,7 +134,7 @@ __kernel void test__global() { // Tests address space of inline members //COMMON: @_ZNU3AS41C3getEv(%class.C addrspace(4)* %this) -//COMMON: @_ZNU3AS41CplERU3AS4KS_(%class.C* noalias sret %agg.result, %class.C addrspace(4)* %this +//COMMON: @_ZNU3AS41CplERU3AS4KS_(%class.C* noalias sret align 4 %agg.result, %class.C addrspace(4)* %this #define TEST(AS) \ __kernel void test##AS() { \ AS C c; \ diff --git a/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json b/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json new file mode 100644 index 0000000000000..90b25e1f1d674 --- /dev/null +++ b/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json @@ -0,0 +1,25 @@ +{ + "Version":"10.14", + "VersionMap" : { + "macOS_iOSMac" : { + "10.14.4" : "12.4", + "10.14.3" : "12.3", + "10.14.2" : "12.2", + "10.14.1" : "12.1", + "10.15" : "13.0", + "10.14" : "12.0", + "10.14.5" : "12.5", + "10.15.1" : "13.2" + }, + "iOSMac_macOS" : { + "13.0" : "10.15", + "12.3" : "10.14.3", + "12.0" : "10.14", + "12.4" : "10.14.4", + "12.1" : "10.14.1", + "12.5" : "10.14.5", + "12.2" : "10.14.2", + "13.2" : "10.15.1" + } + } +} diff --git a/clang/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json b/clang/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json new file mode 100644 index 0000000000000..cc26f6bcfac37 --- /dev/null +++ b/clang/test/Driver/Inputs/MacOSX10.15.versioned.sdk/SDKSettings.json @@ -0,0 +1,13 @@ +{ + "Version":"10.15", + "VersionMap" : { + "macOS_iOSMac" : { + "10.15" : "13.1", + "10.15.1" : "13.2" + }, + "iOSMac_macOS" : { + "13.1" : "10.15", + "13.2" : "10.15.1" + } + } +} diff --git a/clang/test/Driver/aarch64-cpus.c b/clang/test/Driver/aarch64-cpus.c index c7dc5f63d6786..1b6a21e7ab1fc 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" "apple-a7" // 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 arm64-apple-darwin -arch arm64_32 -### -c %s 2>&1 | FileCheck -check-prefix=ARM64_32-DARWIN %s // ARM64_32-DARWIN: "-cc1"{{.*}} "-triple" "aarch64_32{{.*}}" "-target-cpu" "apple-s4" @@ -253,6 +256,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-arm64-arch.c b/clang/test/Driver/apple-arm64-arch.c new file mode 100644 index 0000000000000..a37346b1a9bb0 --- /dev/null +++ b/clang/test/Driver/apple-arm64-arch.c @@ -0,0 +1,7 @@ +// RUN: env SDKROOT="/" %clang -arch arm64 -c -### %s 2>&1 | \ +// RUN: FileCheck %s +// +// REQUIRES: system-darwin +// XFAIL: apple-silicon-mac +// +// CHECK: "-triple" "arm64-apple-ios{{[0-9.]+}}" 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/apple-silicon-arch.c b/clang/test/Driver/apple-silicon-arch.c new file mode 100644 index 0000000000000..b1201fa2d7ddb --- /dev/null +++ b/clang/test/Driver/apple-silicon-arch.c @@ -0,0 +1,6 @@ +// RUN: env SDKROOT="/" %clang -arch arm64 -c -### %s 2>&1 | \ +// RUN: FileCheck %s +// +// REQUIRES: apple-silicon-mac +// +// CHECK: "-triple" "arm64-apple-macosx{{[0-9.]+}}" diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c new file mode 100644 index 0000000000000..8d875e8dd175d --- /dev/null +++ b/clang/test/Driver/arch-arm64e.c @@ -0,0 +1,69 @@ +// Check that we can manually enable specific ptrauth features. + +// RUN: %clang -target arm64-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix NONE +// NONE: "-cc1" +// NONE-NOT: "-fptrauth-intrinsics" +// NONE-NOT: "-fptrauth-calls" +// NONE-NOT: "-fptrauth-returns" +// NONE-NOT: "-fptrauth-indirect-gotos" +// NONE-NOT: "-fptrauth-auth-traps" +// NONE-NOT: "-fptrauth-soft" + +// RUN: %clang -target arm64-apple-darwin -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL +// CALL: "-cc1"{{.*}} {{.*}} "-fptrauth-calls" + +// RUN: %clang -target arm64-apple-darwin -fptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix INTRIN +// INTRIN: "-cc1"{{.*}} {{.*}} "-fptrauth-intrinsics" + +// RUN: %clang -target arm64-apple-darwin -fptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix RETURN +// RETURN: "-cc1"{{.*}} {{.*}} "-fptrauth-returns" + +// RUN: %clang -target arm64-apple-darwin -fptrauth-indirect-gotos -c %s -### 2>&1 | FileCheck %s --check-prefix INDGOTO +// INDGOTO: "-cc1"{{.*}} {{.*}} "-fptrauth-indirect-gotos" + +// RUN: %clang -target arm64-apple-darwin -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS +// TRAPS: "-cc1"{{.*}} {{.*}} "-fptrauth-auth-traps" + +// RUN: %clang -target arm64-apple-darwin -fptrauth-soft -c %s -### 2>&1 | FileCheck %s --check-prefix SOFT +// SOFT: "-cc1"{{.*}} {{.*}} "-fptrauth-soft" + + +// Check the arm64e defaults. + +// RUN: %clang -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -mkernel -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -fapple-kext -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// DEFAULT: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex"{{.*}} + + +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -mkernel -target arm64e-apple-darwin -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -fapple-kext -target arm64e-apple-darwin -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// DEFAULT-NOCALL-NOT: "-fptrauth-calls" +// DEFAULT-NOCALL: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex" + + +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix NORET + +// NORET-NOT: "-fptrauth-returns" +// NORET: "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex" + +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix NOINTRIN + +// NOINTRIN: "-fptrauth-returns" +// NOINTRIN-NOT: "-fptrauth-intrinsics" +// NOINTRIN: "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "vortex"{{.*}} + + +// RUN: %clang -target arm64e-apple-darwin -fno-ptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix NOTRAP +// NOTRAP: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-target-cpu" "vortex" + + +// Check the CPU defaults and overrides. + +// RUN: %clang -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -target arm64e-apple-darwin -mcpu=vortex -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -target arm64e-apple-darwin -mcpu=cyclone -c %s -### 2>&1 | FileCheck %s --check-prefix VORTEX +// RUN: %clang -target arm64e-apple-darwin -mcpu=lightning -c %s -### 2>&1 | FileCheck %s --check-prefix LIGHTNING +// VORTEX: "-cc1"{{.*}} "-target-cpu" "vortex" +// LIGHTNING: "-cc1"{{.*}} "-target-cpu" "lightning" diff --git a/clang/test/Driver/arclite-link.c b/clang/test/Driver/arclite-link.c index 2cc0271fc0222..a53b12daa47bd 100644 --- a/clang/test/Driver/arclite-link.c +++ b/clang/test/Driver/arclite-link.c @@ -15,3 +15,6 @@ // RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -fobjc-arc -mmacosx-version-min=10.10 %s 2>&1 | FileCheck -check-prefix=CHECK-UNUSED %s // CHECK-UNUSED-NOT: warning: argument unused during compilation: '-fobjc-link-runtime' + +// RUN: %clang -### -target arm64-apple-macos10.8 -fobjc-link-runtime %t.o 2>&1 | FileCheck -check-prefix=CHECK-ARCLITE-ARM-MAC %s +// CHECK-ARCLITE-ARM-MAC-NOT: libarclite diff --git a/clang/test/Driver/darwin-header-search-system.cpp b/clang/test/Driver/darwin-header-search-system.cpp index fa2ad0ec62b35..a8cd487551792 100644 --- a/clang/test/Driver/darwin-header-search-system.cpp +++ b/clang/test/Driver/darwin-header-search-system.cpp @@ -101,3 +101,77 @@ // CHECK-NOSYSROOT: "-internal-isystem" "/usr/local/include" // CHECK-NOSYSROOT: "-internal-isystem" "[[RESOURCE]]/include" // CHECK-NOSYSROOT: "-internal-externc-isystem" "/usr/include" + +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -nostdinc -ibuiltininc \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -DRESOURCE=%S/Inputs/resource_dir \ +// RUN: --check-prefix=CHECK-NOSTDINC-BUILTINC %s +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -ibuiltininc -nostdinc \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -DRESOURCE=%S/Inputs/resource_dir \ +// RUN: --check-prefix=CHECK-NOSTDINC-BUILTINC %s +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -nostdinc -nobuiltininc -ibuiltininc \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -DRESOURCE=%S/Inputs/resource_dir \ +// RUN: --check-prefix=CHECK-NOSTDINC-BUILTINC %s +// CHECK-NOSTDINC-BUILTINC: "{{[^"]*}}clang{{[^"]*}}" "-cc1" +// CHECK-NOSTDINC-BUILTINC-NOT: "-internal-isystem" "[[SYSROOT]]/usr/local/include" +// CHECK-NOSTDINC-BUILTINC: "-internal-isystem" "[[RESOURCE]]/include" +// CHECK-NOSTDINC-BUILTINC-NOT: "-internal-externc-isystem" "[[SYSROOT]]/usr/include" + +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -nobuiltininc -ibuiltininc \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -DRESOURCE=%S/Inputs/resource_dir \ +// RUN: --check-prefix=CHECK-NOBUILTININC-BUILTINC %s +// CHECK-NOBUILTININC-BUILTINC: "{{[^"]*}}clang{{[^"]*}}" "-cc1" +// CHECK-NOBUILTININC-BUILTINC: "-internal-isystem" "[[SYSROOT]]/usr/local/include" +// CHECK-NOBUILTININC-BUILTINC: "-internal-isystem" "[[RESOURCE]]/include" +// CHECK-NOBUILTININC-BUILTINC: "-internal-externc-isystem" "[[SYSROOT]]/usr/include" + +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -nostdinc -ibuiltininc -nobuiltininc \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -DRESOURCE=%S/Inputs/resource_dir \ +// RUN: --check-prefix=CHECK-NOSTDINC-NO-BUILTINC %s +// CHECK-NOSTDINC-NO-BUILTINC: "{{[^"]*}}clang{{[^"]*}}" "-cc1" +// CHECK-NOSTDINC-NO-BUILTINC-NOT: "-internal-isystem" "[[SYSROOT]]/usr/local/include" +// CHECK-NOSTDINC-NO-BUILTINC-NOT: "-internal-isystem" "[[RESOURCE]]/include" +// CHECK-NOSTDINC-NO-BUILTINC-NOT: "-internal-externc-isystem" "[[SYSROOT]]/usr/include" + +// RUN: %clang -no-canonical-prefixes %s -### -fsyntax-only 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: -ccc-install-dir %S/Inputs/basic_darwin_toolchain_no_libcxx/usr/bin \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: -isysroot %S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -ibuiltininc -nobuiltininc \ +// RUN: | FileCheck -DSYSROOT=%S/Inputs/basic_darwin_sdk_usr_and_usr_local \ +// RUN: -DRESOURCE=%S/Inputs/resource_dir \ +// RUN: --check-prefix=CHECK-BUILTINC-NOBUILTININC %s +// CHECK-BUILTINC-NOBUILTININC: "{{[^"]*}}clang{{[^"]*}}" "-cc1" +// CHECK-BUILTINC-NOBUILTININC: "-internal-isystem" "[[SYSROOT]]/usr/local/include" +// CHECK-BUILTINC-NOBUILTININC-NOT: "-internal-isystem" "[[RESOURCE]]/include" +// CHECK-BUILTINC-NOBUILTININC: "-internal-externc-isystem" "[[SYSROOT]]/usr/include" diff --git a/clang/test/Driver/darwin-ld-platform-version-maccatalyst.c b/clang/test/Driver/darwin-ld-platform-version-maccatalyst.c new file mode 100644 index 0000000000000..201a7e03fedeb --- /dev/null +++ b/clang/test/Driver/darwin-ld-platform-version-maccatalyst.c @@ -0,0 +1,12 @@ +// RUN: touch %t.o + +// RUN: %clang -target x86_64-apple-ios13.3-macabi -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck %s +// RUN: %clang -target x86_64-apple-ios13.3-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-MAPPED-SDK %s +// RUN: %clang -target x86_64-apple-ios12.0-macabi -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-OLD %s + +// CHECK: "-platform_version" "mac catalyst" "13.3.0" "0.0.0" +// CHECK-MAPPED-SDK: "-platform_version" "mac catalyst" "13.3.0" "12.0" +// CHECK-OLD: "-platform_version" "mac catalyst" "13.0" "0.0.0" diff --git a/clang/test/Driver/darwin-ld-platform-version-macos.c b/clang/test/Driver/darwin-ld-platform-version-macos.c index 1b849a6d2dfa0..41cc16494e8f9 100644 --- a/clang/test/Driver/darwin-ld-platform-version-macos.c +++ b/clang/test/Driver/darwin-ld-platform-version-macos.c @@ -5,8 +5,23 @@ // RUN: env SDKROOT=%S/Inputs/MacOSX10.14.sdk %clang -target x86_64-apple-macos10.13.0.1 -mlinker-version=520 -### %t.o 2>&1 \ // RUN: | FileCheck %s + // CHECK: "-platform_version" "macos" "10.13.0" "10.14" -// RUN: %clang -target x86_64-apple-macos10.13 -mlinker-version=520 -### %t.o 2>&1 \ -// RUN: | FileCheck --check-prefix=NOSDK %s -// NOSDK: "-platform_version" "macos" "10.13.0" "0.0.0" +// RUN: %clang -target arm64-apple-macos10.13 -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=ARM64_NEW %s +// RUN: %clang -target arm64-apple-darwin19 -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=ARM64_NEW %s +// RUN: %clang -target arm64-apple-macos11.1 -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=ARM64_NEW_1 %s +// RUN: %clang -target arm64-apple-macos10.13 -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=400 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=ARM64_OLD %s +// RUN: %clang -target arm64e-apple-macos10.13 -isysroot %S/Inputs/MacOSX10.14.sdk -mlinker-version=520 -### %t.o 2>&1 \ +// RUN: | FileCheck --check-prefix=ARM64_NEW %s + +// LINKER-OLD: "-macosx_version_min" "10.13.0" +// LINKER-NEW: "-platform_version" "macos" "10.13.0" "10.14" + +// ARM64_NEW: "-platform_version" "macos" "11.0.0" "10.14" +// ARM64_NEW_1: "-platform_version" "macos" "11.1.0" "10.14" +// ARM64_OLD: "-macosx_version_min" "11.0.0" diff --git a/clang/test/Driver/darwin-ld.c b/clang/test/Driver/darwin-ld.c index 001ed871efe64..63764c4a97755 100644 --- a/clang/test/Driver/darwin-ld.c +++ b/clang/test/Driver/darwin-ld.c @@ -140,6 +140,13 @@ // LINK_VERSION_MIN: {{ld(.exe)?"}} // LINK_VERSION_MIN: "-macosx_version_min" "10.7.0" +// RUN: %clang -target x86_64-apple-ios13-macabi -mlinker-version=400 -### %t.o 2>> %t.log +// RUN: FileCheck -check-prefix=LINK_VERSION_MIN_MACABI %s < %t.log +// LINK_VERSION_MIN_MACABI: {{ld(.exe)?"}} +// LINK_VERSION_MIN_MACABI: "-maccatalyst_version_min" "13.0.0" +// LINK_VERSION_MIN_MACABI-NOT: macosx_version_min +// LINK_VERSION_MIN_MACABI-NOT: macos_version_min + // RUN: %clang -target x86_64-apple-darwin12 -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=LINK_NO_CRT1 %s < %t.log // LINK_NO_CRT1-NOT: crt @@ -158,6 +165,11 @@ // LINK_IOSSIM_PROFILE: libclang_rt.profile_iossim.a // LINK_IOSSIM_PROFILE: libclang_rt.ios.a +// RUN: %clang -target x86_64-apple-ios13-macabi -mlinker-version=400 -fprofile-instr-generate -### %t.o 2> %t.log +// RUN: FileCheck -check-prefix=LINK_MACABI_PROFILE %s < %t.log +// LINK_MACABI_PROFILE: {{ld(.exe)?"}} +// LINK_MACABI_PROFILE: libclang_rt.profile_osx.a + // RUN: %clang -target arm64-apple-tvos8.3 -mlinker-version=400 -mtvos-version-min=8.3 -resource-dir=%S/Inputs/resource_dir -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=LINK_TVOS_ARM64 %s < %t.log // LINK_TVOS_ARM64: {{ld(.exe)?"}} @@ -309,32 +321,6 @@ // LINK_VERSION_DIGITS: invalid version number in '-mlinker-version=133.3.0.1.a' // LINK_VERSION_DIGITS: invalid version number in '-mlinker-version=133.3.0.1a' -// Check that we're passing -lto-pass-remarks-output for LTO -// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -### -o foo/bar.out 2> %t.log -// RUN: FileCheck -check-prefix=PASS_REMARKS_OUTPUT %s < %t.log -// PASS_REMARKS_OUTPUT: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" -// PASS_REMARKS_OUTPUT-NOT: -lto-pass-remarks-with-hotness - -// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -### 2> %t.log -// RUN: FileCheck -check-prefix=PASS_REMARKS_OUTPUT_NO_O %s < %t.log -// PASS_REMARKS_OUTPUT_NO_O: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "a.out.opt.yaml" - -// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -fprofile-instr-use=blah -### -o foo/bar.out 2> %t.log -// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_HOTNESS %s < %t.log -// PASS_REMARKS_WITH_HOTNESS: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-with-hotness" - -// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -fprofile-instr-use=blah -fdiagnostics-hotness-threshold=100 -### -o foo/bar.out 2> %t.log -// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_HOTNESS_THRESHOLD %s < %t.log -// PASS_REMARKS_WITH_HOTNESS_THRESHOLD: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-with-hotness" "-mllvm" "-lto-pass-remarks-hotness-threshold=100" - -// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -foptimization-record-passes=inline -### -o foo/bar.out 2> %t.log -// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_PASSES %s < %t.log -// PASS_REMARKS_WITH_PASSES: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-filter=inline" -// -// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record=some-format -### -o foo/bar.out 2> %t.log -// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_FORMAT %s < %t.log -// PASS_REMARKS_WITH_FORMAT: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.some-format" "-mllvm" "-lto-pass-remarks-format=some-format" - // RUN: %clang -target x86_64-apple-ios6.0 -miphoneos-version-min=6.0 -fprofile-instr-generate -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=LINK_PROFILE_FIRST %s < %t.log // RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -### %t.o 2> %t.log diff --git a/clang/test/Driver/darwin-mac-catalyst-32bit-not-supported.c b/clang/test/Driver/darwin-mac-catalyst-32bit-not-supported.c new file mode 100644 index 0000000000000..03ff51cbb47d3 --- /dev/null +++ b/clang/test/Driver/darwin-mac-catalyst-32bit-not-supported.c @@ -0,0 +1,4 @@ +// RUN: %clang --target=i386-apple-ios13.0-macabi -c -### %s 2>&1 \ +// RUN: | FileCheck %s + +// CHECK: error: 32-bit targets are not supported when building for Mac Catalyst diff --git a/clang/test/Driver/darwin-maccatalyst.c b/clang/test/Driver/darwin-maccatalyst.c new file mode 100644 index 0000000000000..c023b11d7f103 --- /dev/null +++ b/clang/test/Driver/darwin-maccatalyst.c @@ -0,0 +1,6 @@ +// RUN: %clang -target x86_64-apple-ios13-macabi -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-VERSION1 %s + +// CHECK-VERSION1: "x86_64-apple-ios13.0.0-macabi" + +// FIXME: refuse versions earlier than ios13. diff --git a/clang/test/Driver/darwin-objc-defaults.m b/clang/test/Driver/darwin-objc-defaults.m index 1b3f7a844445d..331369ff5c0b3 100644 --- a/clang/test/Driver/darwin-objc-defaults.m +++ b/clang/test/Driver/darwin-objc-defaults.m @@ -92,3 +92,11 @@ // CHECK-CHECK-ARMV7_IPHONE3_0: -fobjc-runtime=ios-3.0 // CHECK-CHECK-ARMV7_IPHONE3_0-NOT: -fobjc-dispatch-method // CHECK-CHECK-ARMV7_IPHONE3_0: darwin-objc-defaults + +// RUN: %clang -target x86_64-apple-ios13-macabi -S -### %s 2> %t +// RUN: FileCheck --check-prefix CHECK-CHECK-MACCATALYST < %t %s + +// CHECK-CHECK-MACCATALYST: "-cc1" +// CHECK-CHECK-MACCATALYST: -fobjc-runtime=macosx-10.15 +// CHECK-CHECK-MACCATALYST-NOT: -fobjc-dispatch-method +// CHECK-CHECK-MACCATALYST: darwin-objc-defaults diff --git a/clang/test/Driver/darwin-objc-runtime-maccatalyst.m b/clang/test/Driver/darwin-objc-runtime-maccatalyst.m new file mode 100644 index 0000000000000..ce5d887af9d46 --- /dev/null +++ b/clang/test/Driver/darwin-objc-runtime-maccatalyst.m @@ -0,0 +1,13 @@ +// RUN: %clang -target x86_64-apple-ios13.2-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck %s +// RUN: %clang -target x86_64-apple-ios13.2.0-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck %s +// RUN: %clang -target x86_64-apple-ios13.2-macabi -isysroot %S/Inputs/MacOSX10.14.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=FALLBACK-DEFAULT %s +// RUN: %clang -target x86_64-apple-ios12.99.99-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=FALLBACK-DEFAULT %s +// RUN: %clang -target x86_64-apple-ios-macabi -isysroot %S/Inputs/MacOSX10.14.versioned.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=FALLBACK-DEFAULT %s + +// CHECK: -fobjc-runtime=macosx-10.15.1 +// FALLBACK-DEFAULT: -fobjc-runtime=macosx-10.15 diff --git a/clang/test/Driver/darwin-opt-record-ld.c b/clang/test/Driver/darwin-opt-record-ld.c new file mode 100644 index 0000000000000..83630ed01da8b --- /dev/null +++ b/clang/test/Driver/darwin-opt-record-ld.c @@ -0,0 +1,46 @@ +// REQUIRES: system-darwin + +// RUN: touch %t.o +// +// Check that we're not passing -lto-pass-remarks-output if not requested +// RUN: %clang -target x86_64-apple-darwin12 %t.o -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=NO_PASS_REMARKS_OUTPUT %s < %t.log +// NO_PASS_REMARKS_OUTPUT-NOT: -lto-pass-remarks +// Check that we're passing -lto-pass-remarks-output for LTO +// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_OUTPUT %s < %t.log +// PASS_REMARKS_OUTPUT: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-format=yaml" +// PASS_REMARKS_OUTPUT-NOT: -lto-pass-remarks-with-hotness + +// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -### 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_OUTPUT_NO_O %s < %t.log +// PASS_REMARKS_OUTPUT_NO_O: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "a.out.opt.yaml" + +// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -fprofile-instr-use=blah -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_HOTNESS %s < %t.log +// PASS_REMARKS_WITH_HOTNESS: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-format=yaml" "-mllvm" "-lto-pass-remarks-with-hotness" + +// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -fprofile-instr-use=blah -fdiagnostics-hotness-threshold=100 -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_HOTNESS_THRESHOLD %s < %t.log +// PASS_REMARKS_WITH_HOTNESS_THRESHOLD: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-format=yaml" "-mllvm" "-lto-pass-remarks-with-hotness" "-mllvm" "-lto-pass-remarks-hotness-threshold=100" + +// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -foptimization-record-passes=inline -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_PASSES %s < %t.log +// PASS_REMARKS_WITH_PASSES: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-filter=inline" +// +// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record=some-format -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_FORMAT %s < %t.log +// PASS_REMARKS_WITH_FORMAT: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.some-format" "-mllvm" "-lto-pass-remarks-format=some-format" + +// RUN: %clang -target x86_64-apple-darwin12 %t.o -foptimization-record-file=remarks-custom.opt.yaml -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_FILE %s < %t.log +// PASS_REMARKS_WITH_FILE: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "remarks-custom.opt.yaml" + +// RUN: %clang -target x86_64-apple-darwin12 -arch x86_64 -arch x86_64h %t.o -fsave-optimization-record -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_FAT %s < %t.log +// PASS_REMARKS_WITH_FAT: "-arch" "x86_64"{{.*}}"-mllvm" "-lto-pass-remarks-output" +// PASS_REMARKS_WITH_FAT-NEXT: "-arch" "x86_64h"{{.*}}"-mllvm" "-lto-pass-remarks-output" +// +// RUN: %clang -target x86_64-apple-darwin12 -arch x86_64 -arch x86_64h %t.o -foptimization-record-file=custom.opt.yaml -### -o foo/bar.out 2> %t.log +// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_FILE_FAT %s < %t.log +// PASS_REMARKS_WITH_FILE_FAT: error: cannot use '-foptimization-record-file' output with multiple -arch options diff --git a/clang/test/Driver/darwin-sanitizer-ld.c b/clang/test/Driver/darwin-sanitizer-ld.c index 53c7fce115e71..6388766bb8a9c 100644 --- a/clang/test/Driver/darwin-sanitizer-ld.c +++ b/clang/test/Driver/darwin-sanitizer-ld.c @@ -93,6 +93,18 @@ // CHECK-ASAN-WATCHOSSIM: "-rpath" "@executable_path" // CHECK-ASAN-WATCHOSSIM: "-rpath" "{{.*}}lib{{.*}}darwin" +// RUN: %clang -no-canonical-prefixes -### -target x86_64-apple-ios13-macabi \ +// RUN: -stdlib=platform -fsanitize=address \ +// RUN: -resource-dir %S/Inputs/resource_dir \ +// RUN: %s -o %t.o 2>&1 | FileCheck --check-prefix=CHECK-ASAN-MACCATALYST %s + +// CHECK-ASAN-MACCATALYST: "{{.*}}ld{{(.exe)?}}" +// CHECK-ASAN-MACCATALYST-NOT: "-lstdc++" +// CHECK-ASAN-MACCATALYST-NOT: "-lc++" +// CHECK-ASAN-MACCATALYST: libclang_rt.asan_osx_dynamic.dylib" +// CHECK-ASAN-MACCATALYST: "-rpath" "@executable_path" +// CHECK-ASAN-MACCATALYST: "-rpath" "{{.*}}lib{{.*}}darwin" + // RUN: %clang -no-canonical-prefixes -### -target armv7-apple-ios \ // RUN: -stdlib=platform -fsanitize=address -miphoneos-version-min=7 \ // RUN: %s -o %t.o 2>&1 | FileCheck --check-prefix=CHECK-ASAN-IOS %s diff --git a/clang/test/Driver/darwin-sdk-version-maccatalyst.c b/clang/test/Driver/darwin-sdk-version-maccatalyst.c new file mode 100644 index 0000000000000..40597aefdd67a --- /dev/null +++ b/clang/test/Driver/darwin-sdk-version-maccatalyst.c @@ -0,0 +1,4 @@ +// RUN: %clang -target x86_64-apple-ios13-macabi -isysroot %S/Inputs/MacOSX10.15.versioned.sdk -c -### %s 2>&1 \ +// RUN: | FileCheck %s + +// CHECK: "-target-sdk-version=13.1" diff --git a/clang/test/Driver/darwin-version.c b/clang/test/Driver/darwin-version.c index 7885b59646266..3471552c937f2 100644 --- a/clang/test/Driver/darwin-version.c +++ b/clang/test/Driver/darwin-version.c @@ -305,3 +305,13 @@ // RUN: %clang -target armv7k-apple-ios10.1-simulator -c %s -### 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK-VERSION-TENV-SIM2 %s // CHECK-VERSION-TENV-SIM2: "thumbv7k-apple-ios10.1.0-simulator" + + +// RUN: %clang -target x86_64-apple-macos11 -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-MACOS11 %s +// RUN: %clang -target x86_64-apple-darwin20 -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-MACOS11 %s +// RUN: %clang -target x86_64-apple-darwin -mmacos-version-min=11 -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-MACOS11 %s + +// CHECK-MACOS11: "x86_64-apple-macosx11.0.0" 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/macos-apple-silicon-slice-link-libs.cpp b/clang/test/Driver/macos-apple-silicon-slice-link-libs.cpp new file mode 100644 index 0000000000000..522fda34987e9 --- /dev/null +++ b/clang/test/Driver/macos-apple-silicon-slice-link-libs.cpp @@ -0,0 +1,42 @@ +// RUN: %clang -### -target arm64-apple-macos10.7 %s 2>&1 | FileCheck -check-prefix=ARM64-10_7 %s +// RUN: %clang -### -target x86_64-apple-macos10.7 %s 2>&1 | FileCheck -check-prefix=x86_64-10_7 %s + +// RUN: %clang -### -target arm64-apple-macos10.5 %s 2>&1 | FileCheck -check-prefix=ARM64-10_5 %s +// RUN: %clang -### -target x86_64-apple-macos10.5 %s 2>&1 | FileCheck -check-prefix=x86_64-10_5 %s + +// RUN: %clang -### -target arm64-apple-macos10.4 %s 2>&1 | FileCheck -check-prefix=ARM64-10_4 %s +// RUN: %clang -### -target x86_64-apple-macos10.4 %s 2>&1 | FileCheck -check-prefix=x86_64-10_4 %s + +// RUN: %clang -### -target arm64-apple-macos10.5 -bundle %s 2>&1 | FileCheck -check-prefix=ARM64-BUNDLE %s +// RUN: %clang -### -target x86_64-apple-macos10.5 -bundle %s 2>&1 | FileCheck -check-prefix=x86_64-BUNDLE %s + +// RUN: %clang -### -target arm64-apple-macos10.5 -dynamiclib %s 2>&1 | FileCheck -check-prefix=ARM64-10_5-DYNAMICLIB %s +// RUN: %clang -### -target x86_64-apple-macos10.5 -dynamiclib %s 2>&1 | FileCheck -check-prefix=x86_64-10_5-DYNAMICLIB %s + +// RUN: %clang -### -target arm64-apple-macos10.4 -dynamiclib %s 2>&1 | FileCheck -check-prefix=ARM64-10_4-DYNAMICLIB %s +// RUN: %clang -### -target arm64-apple-darwin8 -dynamiclib %s 2>&1 | FileCheck -check-prefix=ARM64-10_4-DYNAMICLIB %s +// RUN: %clang -### -target x86_64-apple-macos10.4 -dynamiclib %s 2>&1 | FileCheck -check-prefix=x86_64-10_4-DYNAMICLIB %s +// RUN: %clang -### -target x86_64-apple-darwin8 -dynamiclib %s 2>&1 | FileCheck -check-prefix=x86_64-10_4-DYNAMICLIB %s + +// RUN: %clang -### -target arm64-apple-macos10.7 -static %s 2>&1 | FileCheck -check-prefix=STATIC %s +// RUN: %clang -### -target x86_64-apple-macos10.7 -static %s 2>&1 | FileCheck -check-prefix=STATIC %s + +// ARM64-10_7-NOT: -lcrt1.10.6.o +// x86_64-10_7: -lcrt1.10.6.o + +// ARM64-10_5-NOT: -lcrt1.10.5.o +// x86_64-10_5: -lcrt1.10.5.o + +// ARM64-10_4-NOT: -lcrt1.o +// x86_64-10_4: -lcrt1.o + +// ARM64-BUNDLE-NOT: -lbundle1.o +// x86_64-BUNDLE: -lbundle1.o + +// ARM64-10_5-DYNAMICLIB-NOT: -ldylib1.10.5.o +// x86_64-10_5-DYNAMICLIB: -ldylib1.10.5.o + +// ARM64-10_4-DYNAMICLIB-NOT: -ldylib1.o +// x86_64-10_4-DYNAMICLIB: -ldylib1.o + +// STATIC: -lcrt0.o 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/Driver/virtual-function-elimination.cpp b/clang/test/Driver/virtual-function-elimination.cpp deleted file mode 100644 index 3a026bbb434e0..0000000000000 --- a/clang/test/Driver/virtual-function-elimination.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang -target x86_64-unknown-linux -fvirtual-function-elimination -### %s 2>&1 | FileCheck --check-prefix=BAD-LTO %s -// RUN: %clang -target x86_64-unknown-linux -fvirtual-function-elimination -flto=thin -### %s 2>&1 | FileCheck --check-prefix=BAD-LTO %s -// BAD-LTO: invalid argument '-fvirtual-function-elimination' only allowed with '-flto=full' - -// RUN: %clang -target x86_64-unknown-linux -fvirtual-function-elimination -flto -### %s 2>&1 | FileCheck --check-prefix=GOOD %s -// RUN: %clang -target x86_64-unknown-linux -fvirtual-function-elimination -flto=full -### %s 2>&1 | FileCheck --check-prefix=GOOD %s -// RUN: %clang -target x86_64-unknown-linux -fvirtual-function-elimination -flto -fwhole-program-vtables -### %s 2>&1 | FileCheck --check-prefix=GOOD %s -// GOOD: "-fvirtual-function-elimination" "-fwhole-program-vtables" - -// RUN: %clang -target x86_64-unknown-linux -fvirtual-function-elimination -fno-whole-program-vtables -flto -### %s 2>&1 | FileCheck --check-prefix=NO-WHOLE-PROGRAM-VTABLES %s -// NO-WHOLE-PROGRAM-VTABLES: invalid argument '-fno-whole-program-vtables' not allowed with '-fvirtual-function-elimination' 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/FixIt/fixit-objc-direct.m b/clang/test/FixIt/fixit-objc-direct.m new file mode 100644 index 0000000000000..67d0debf10c16 --- /dev/null +++ b/clang/test/FixIt/fixit-objc-direct.m @@ -0,0 +1,30 @@ +// Objective-C recovery +// RUN: not %clang_cc1 -triple x86_64-apple-darwin10 -fdiagnostics-parseable-fixits -x objective-c %s 2>&1 | FileCheck -check-prefix=CHECK-MRR %s +// RUN: not %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc -fdiagnostics-parseable-fixits -x objective-c %s 2>&1 | FileCheck -check-prefix=CHECK-ARC %s + +__attribute__((objc_root_class)) +@interface Root ++ (void)classDirectMethod __attribute__((objc_direct)); ++ (void)classDirectMethod2 __attribute__((objc_direct)); +- (void)instanceDirectMethod __attribute__((objc_direct)); +@end + +@interface A : Root +@end + +@implementation A ++ (void)classMethod { + // CHECK-MRR: {18:4-18:8}:"Root" + [self classDirectMethod]; +} ++ (void)classMethod2 { + // CHECK-MRR: {23:4-23:9}:"Root" + // CHECK-ARC: {23:4-23:9}:"self" + [super classDirectMethod2]; +} +- (void)instanceMethod { + // CHECK-MRR: {28:4-28:9}:"self" + // CHECK-ARC: {28:4-28:9}:"self" + [super instanceDirectMethod]; +} +@end diff --git a/lldb/packages/Python/lldbsuite/test/.categories b/clang/test/Frontend/Inputs/resource_dir_with_sanitizer_blacklist/share/ubsan_blacklist.txt similarity index 100% rename from lldb/packages/Python/lldbsuite/test/.categories rename to clang/test/Frontend/Inputs/resource_dir_with_sanitizer_blacklist/share/ubsan_blacklist.txt diff --git a/clang/test/Frontend/dependency-gen.c b/clang/test/Frontend/dependency-gen.c index 1db9b04c1d9f5..a42804d9f2145 100644 --- a/clang/test/Frontend/dependency-gen.c +++ b/clang/test/Frontend/dependency-gen.c @@ -21,7 +21,7 @@ // RUN: %clang -MD -MF - %s -fsyntax-only -I ./ | FileCheck -check-prefix=CHECK-SIX %s // CHECK-SIX: {{ }}x.h // RUN: echo "fun:foo" > %t.blacklist -// RUN: %clang -MD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_cfi_blacklist -fsanitize=cfi-vcall -flto -fvisibility=hidden -fsanitize-blacklist=%t.blacklist -I ./ | FileCheck -check-prefix=CHECK-SEVEN %s +// RUN: %clang -MD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_sanitizer_blacklist -fsanitize=undefined -flto -fvisibility=hidden -fsanitize-blacklist=%t.blacklist -I ./ | FileCheck -check-prefix=CHECK-SEVEN %s // CHECK-SEVEN: .blacklist // CHECK-SEVEN: {{ }}x.h #ifndef INCLUDE_FLAG_TEST @@ -30,17 +30,17 @@ // RUN: echo "fun:foo" > %t.blacklist1 // RUN: echo "fun:foo" > %t.blacklist2 -// RUN: %clang -MD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_cfi_blacklist -fsanitize=cfi-vcall -flto -fvisibility=hidden -fsanitize-blacklist=%t.blacklist1 -fsanitize-blacklist=%t.blacklist2 -I ./ | FileCheck -check-prefix=TWO-BLACK-LISTS %s +// RUN: %clang -MD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_sanitizer_blacklist -fsanitize=undefined -flto -fvisibility=hidden -fsanitize-blacklist=%t.blacklist1 -fsanitize-blacklist=%t.blacklist2 -I ./ | FileCheck -check-prefix=TWO-BLACK-LISTS %s // TWO-BLACK-LISTS: dependency-gen.o: // TWO-BLACK-LISTS-DAG: blacklist1 // TWO-BLACK-LISTS-DAG: blacklist2 // TWO-BLACK-LISTS-DAG: x.h // TWO-BLACK-LISTS-DAG: dependency-gen.c -// RUN: %clang -MD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_cfi_blacklist -fsanitize=cfi-vcall -flto -fvisibility=hidden -I ./ | FileCheck -check-prefix=USER-AND-SYS-DEPS %s +// RUN: %clang -MD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_sanitizer_blacklist -fsanitize=undefined -flto -fvisibility=hidden -I ./ | FileCheck -check-prefix=USER-AND-SYS-DEPS %s // USER-AND-SYS-DEPS: dependency-gen.o: -// USER-AND-SYS-DEPS-DAG: cfi_blacklist.txt +// USER-AND-SYS-DEPS-DAG: ubsan_blacklist.txt -// RUN: %clang -MMD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_cfi_blacklist -fsanitize=cfi-vcall -flto -fvisibility=hidden -I ./ | FileCheck -check-prefix=ONLY-USER-DEPS %s +// RUN: %clang -MMD -MF - %s -fsyntax-only -resource-dir=%S/Inputs/resource_dir_with_sanitizer_blacklist -fsanitize=undefined -flto -fvisibility=hidden -I ./ | FileCheck -check-prefix=ONLY-USER-DEPS %s // ONLY-USER-DEPS: dependency-gen.o: -// NOT-ONLY-USER-DEPS: cfi_blacklist.txt \ No newline at end of file +// NOT-ONLY-USER-DEPS: ubsan_blacklist.txt 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/Frontend/verify-any-file.c b/clang/test/Frontend/verify-any-file.c new file mode 100644 index 0000000000000..d2c0d90b9db5a --- /dev/null +++ b/clang/test/Frontend/verify-any-file.c @@ -0,0 +1,14 @@ +// RUN: not %clang_cc1 -verify %s 2>&1 | FileCheck %s + +#include "verify-any-file.h" +// expected-error@*:* {{unknown type name 'unexpected'}} + +// expected-error@*:* {{missing error}} + +// expected-error@*:123 {{invalid line : "*" required}} +// +// CHECK: error: 'error' diagnostics expected but not seen: +// CHECK-NEXT: File * Line * (directive at {{.*}}verify-any-file.c:6): missing error +// CHECK-NEXT: error: 'error' diagnostics seen but not expected: +// CHECK-NEXT: File {{.*}}verify-any-file.c Line 8: missing or invalid line number following '@' in expected '*' +// CHECK-NEXT: 2 errors generated. diff --git a/clang/test/Frontend/verify-any-file.h b/clang/test/Frontend/verify-any-file.h new file mode 100644 index 0000000000000..dee8fa3a74897 --- /dev/null +++ b/clang/test/Frontend/verify-any-file.h @@ -0,0 +1 @@ +unexpected var; diff --git a/clang/test/Headers/arm-acle-header.c b/clang/test/Headers/arm-acle-header.c index 932c59da3d962..e50a68c64b0f0 100644 --- a/clang/test/Headers/arm-acle-header.c +++ b/clang/test/Headers/arm-acle-header.c @@ -6,6 +6,7 @@ // RUN: %clang_cc1 -x c++ -triple thumbv7-windows -target-cpu cortex-a15 -fsyntax-only -ffreestanding %s // RUN: %clang_cc1 -x c++ -triple thumbv7-windows -target-cpu cortex-a15 -fsyntax-only -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=19.11 %s // RUN: %clang_cc1 -x c++ -triple aarch64-windows -target-cpu cortex-a53 -fsyntax-only -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=19.11 %s +// RUN: %clang_cc1 -x c++ -triple arm64-apple-ios -target-cpu apple-a7 -fsyntax-only -ffreestanding -fms-extensions %s // expected-no-diagnostics #include diff --git a/clang/test/Import/objc-arc/Inputs/cleanup-objects.m b/clang/test/Import/objc-arc/Inputs/cleanup-objects.m new file mode 100644 index 0000000000000..9c18399259f9d --- /dev/null +++ b/clang/test/Import/objc-arc/Inputs/cleanup-objects.m @@ -0,0 +1,10 @@ +typedef struct { + id x; +} S; + +id getObj(int c, id a) { + // Commenting out the following line because AST importer crashes when trying + // to import a BlockExpr. + // return c ? ^{ return a; }() : ((S){ .x = a }).x; + return ((S){ .x = a }).x; +} diff --git a/clang/test/Import/objc-arc/test-cleanup-object.m b/clang/test/Import/objc-arc/test-cleanup-object.m new file mode 100644 index 0000000000000..aab1cd377a2e7 --- /dev/null +++ b/clang/test/Import/objc-arc/test-cleanup-object.m @@ -0,0 +1,10 @@ +// RUN: clang-import-test -x objective-c -objc-arc -import %S/Inputs/cleanup-objects.m -dump-ast -expression %s | FileCheck %s + +// CHECK: FunctionDecl {{.*}} getObj ' +// CHECK: ExprWithCleanups +// CHECK-NEXT: cleanup CompoundLiteralExpr + +void test(int c, id a) { + (void)getObj(c, a); +} + diff --git a/clang/test/Index/Core/external-source-symbol-attr.m b/clang/test/Index/Core/external-source-symbol-attr.m index 41bb7a264ab3e..d2cef35ffab23 100644 --- a/clang/test/Index/Core/external-source-symbol-attr.m +++ b/clang/test/Index/Core/external-source-symbol-attr.m @@ -21,6 +21,10 @@ @interface I2 // CHECK: [[@LINE-1]]:12 | class/Swift | I2 | c:@M@some_module@objc(cs)I2 | {{.*}} | Decl | rel: 0 -(void)method; // CHECK: [[@LINE-1]]:8 | instance-method/Swift | method | c:@M@some_module@objc(cs)I2(im)method | -[I2 method] | Decl,Dyn,RelChild | rel: 1 +@property int prop; +// CHECK: [[@LINE-1]]:15 | instance-method/acc-get/Swift | prop | c:@M@some_module@objc(cs)I2(im)prop | +// CHECK: [[@LINE-2]]:15 | instance-method/acc-set/Swift | setProp: | c:@M@some_module@objc(cs)I2(im)setProp: | +// CHECK: [[@LINE-3]]:15 | instance-property/Swift | prop | c:@M@some_module@objc(cs)I2(py)prop | @end void test1(I1 *o) { @@ -45,6 +49,10 @@ @interface I1(cat2) // CHECK: [[@LINE-1]]:15 | extension/Swift | cat2 | c:@CM@cat_module@some_module@objc(cy)I1@cat2 | -(void)cat_method2; // CHECK: [[@LINE-1]]:8 | instance-method/Swift | cat_method2 | c:@CM@cat_module@some_module@objc(cs)I1(im)cat_method2 +@property int cat_prop2; +// CHECK: [[@LINE-1]]:15 | instance-method/acc-get/Swift | cat_prop2 | c:@CM@cat_module@some_module@objc(cs)I1(im)cat_prop2 | +// CHECK: [[@LINE-2]]:15 | instance-method/acc-set/Swift | setCat_prop2: | c:@CM@cat_module@some_module@objc(cs)I1(im)setCat_prop2: | +// CHECK: [[@LINE-3]]:15 | instance-property/Swift | cat_prop2 | c:@CM@cat_module@some_module@objc(cs)I1(py)cat_prop2 | @end #define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type diff --git a/clang/test/Index/Core/scan-deps.m b/clang/test/Index/Core/scan-deps.m new file mode 100644 index 0000000000000..6bb38db04b81b --- /dev/null +++ b/clang/test/Index/Core/scan-deps.m @@ -0,0 +1,29 @@ +// RUN: rm -rf %t.mcp +// RUN: echo %S > %t.result +// RUN: c-index-test core --scan-deps %S -- %clang -cc1 -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t.mcp -fimplicit-module-maps \ +// RUN: -o FoE.o -x objective-c %s >> %t.result +// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck %s + +@import ModA; + +// CHECK: [[PREFIX:.*]] +// CHECK-NEXT: modules: +// CHECK-NEXT: module: +// CHECK-NEXT: name: ModA +// CHECK-NEXT: context-hash: [[CONTEXT_HASH:[A-Z0-9]+]] +// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: module-deps: +// CHECK-NEXT: file-deps: +// CHECK-NEXT: [[PREFIX]]/Inputs/module/ModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubSubModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: build-args: -remove-preceeding-explicit-module-build-incompatible-options -fno-implicit-modules -emit-module -fmodule-name=ModA +// CHECK-NEXT: dependencies: +// CHECK-NEXT: context-hash: [[CONTEXT_HASH]] +// CHECK-NEXT: module-deps: +// CHECK-NEXT: ModA:[[CONTEXT_HASH]] +// CHECK-NEXT: file-deps: +// CHECK-NEXT: [[PREFIX]]/scan-deps.m +// CHECK-NEXT: additional-build-args: -fno-implicit-modules -fno-implicit-module-maps 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